wasmcloud_provider_keyvalue_vault/
config.rs

1//! Configuration for kv-vault capability provider
2//!
3
4use core::time::Duration;
5
6use std::collections::HashMap;
7use std::env;
8
9use anyhow::{Context, Result};
10use tracing::warn;
11use url::Url;
12use wasmcloud_provider_sdk::{core::secrets::SecretValue, LinkConfig};
13
14use crate::TOKEN_REFRESH_INTERVAL;
15
16/// Default address at which Vault is expected to be running,
17/// used if unspecified by configuration
18const DEFAULT_VAULT_ADDR: &str = "http://127.0.0.1:8200";
19
20/// KV-Vault configuration
21#[derive(Clone, Debug)]
22pub struct Config {
23    /// Token for connecting to vault, can be set in environment with VAULT_TOKEN.
24    /// Required
25    pub token: String,
26    /// Url for connecting to vault, can be set in environment with VAULT_ADDR.
27    /// Defaults to 'http://127.0.0.1:8200'
28    pub addr: Url,
29    /// Vault mount point, can be set with in environment with VAULT_MOUNT.
30    /// Defaults to "secret/"
31    pub mount: String,
32    /// certificate files - path to CA certificate file(s). Setting this enables TLS
33    /// The linkdef value `certs` and the environment variable `VAULT_CERTS`
34    /// are parsed as a comma-separated string of file paths to generate this list.
35    pub certs: Vec<String>,
36
37    /// Renewal TTL for tokens used by this provider. Defaults to 72 hours.
38    pub token_increment_ttl: Option<String>,
39
40    /// Refresh interval for tokens used by this provider. Defaults to 12 hours.
41    pub token_refresh_interval: Option<std::time::Duration>,
42}
43
44impl Default for Config {
45    /// default constructor - Gets all values from environment & defaults
46    fn default() -> Self {
47        Self::from_values(&HashMap::new()).unwrap()
48    }
49}
50
51impl Config {
52    /// Initialize from a [`LinkConfig`]
53    pub fn from_link_config(link_config: &LinkConfig) -> Result<Config> {
54        let mut map = HashMap::clone(link_config.config);
55
56        // Attempt to retrieve the vault token from secrets
57        if let Some(token) = env::var("VAULT_TOKEN").ok().or_else(|| {
58            link_config
59                .secrets
60                .get("token")
61                .and_then(SecretValue::as_string)
62                .map(String::from)
63        }) {
64            map.insert("VAULT_TOKEN".into(), token);
65        } else {
66            warn!("Secret value [token] (ENV: VAULT_TOKEN) was not found in env or secrets. Please prefer ENV variables or secrets for sensitive values.")
67        }
68
69        Self::from_values(&map)
70    }
71
72    /// Initialize from linkdef values, environment, and defaults
73    ///
74    /// NOTE: Prefer [`Self::from_link_config`] rather than this method directly
75    pub fn from_values(values: &HashMap<String, String>) -> Result<Config> {
76        let addr = env::var("VAULT_ADDR")
77            .ok()
78            .or_else(|| values.get("addr").cloned())
79            .or_else(|| values.get("ADDR").cloned())
80            .unwrap_or_else(|| DEFAULT_VAULT_ADDR.to_string());
81        let addr = addr.parse().unwrap_or_else(|_| {
82            eprintln!(
83                "Could not parse VAULT_ADDR [{addr}] as Url, using default of {DEFAULT_VAULT_ADDR}"
84            );
85            DEFAULT_VAULT_ADDR.parse().unwrap()
86        });
87        let token = env::var("VAULT_TOKEN")
88            .ok()
89            .or_else(|| values.get("token").cloned())
90            .or_else(|| values.get("TOKEN").cloned())
91            .context("missing setting for 'token' or VAULT_TOKEN")?;
92        let mount = env::var("VAULT_MOUNT")
93            .ok()
94            .or_else(|| values.get("mount").cloned())
95            .or_else(|| values.get("MOUNT").cloned())
96            .unwrap_or_else(|| "secret".to_string());
97        let certs = env::var("VAULT_CERTS")
98            .ok()
99            .or_else(|| values.get("certs").cloned())
100            .or_else(|| values.get("CERTS").cloned())
101            .map(|certs| certs.split(',').map(|s| s.trim().to_string()).collect())
102            .unwrap_or_default();
103        Ok(Config {
104            addr,
105            token,
106            mount,
107            certs,
108            token_increment_ttl: env::var("VAULT_TOKEN_INCREMENT_TTL")
109                .ok()
110                .or_else(|| values.get("token_increment_ttl").cloned())
111                .or_else(|| values.get("TOKEN_INCREMENT_TTL").cloned()),
112            token_refresh_interval: match env::var("VAULT_TOKEN_REFRESH_INTERVAL")
113                .ok()
114                .or_else(|| values.get("token_refresh_interval").cloned())
115                .or_else(|| values.get("TOKEN_REFRESH_INTERVAL").cloned())
116            {
117                Some(val) => {
118                    let secs = val.parse::<u64>().unwrap_or_else(|_| {
119                        eprintln!(
120                            "Could not parse VAULT_TOKEN_REFRESH_INTERVAL as u64, using default of {}",
121                            TOKEN_REFRESH_INTERVAL.as_secs()
122                        );
123                        TOKEN_REFRESH_INTERVAL.as_secs()
124                    });
125                    Some(Duration::from_secs(secs))
126                }
127                _ => None,
128            },
129        })
130    }
131}