wasmcloud_provider_keyvalue_vault/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Configuration for kv-vault capability provider
//!

use core::time::Duration;

use std::collections::HashMap;
use std::env;

use anyhow::{Context, Result};
use tracing::warn;
use url::Url;
use wasmcloud_provider_sdk::{core::secrets::SecretValue, LinkConfig};

use crate::TOKEN_REFRESH_INTERVAL;

/// Default address at which Vault is expected to be running,
/// used if unspecified by configuration
const DEFAULT_VAULT_ADDR: &str = "http://127.0.0.1:8200";

/// KV-Vault configuration
#[derive(Clone, Debug)]
pub struct Config {
    /// Token for connecting to vault, can be set in environment with VAULT_TOKEN.
    /// Required
    pub token: String,
    /// Url for connecting to vault, can be set in environment with VAULT_ADDR.
    /// Defaults to 'http://127.0.0.1:8200'
    pub addr: Url,
    /// Vault mount point, can be set with in environment with VAULT_MOUNT.
    /// Defaults to "secret/"
    pub mount: String,
    /// certificate files - path to CA certificate file(s). Setting this enables TLS
    /// The linkdef value `certs` and the environment variable `VAULT_CERTS`
    /// are parsed as a comma-separated string of file paths to generate this list.
    pub certs: Vec<String>,

    /// Renewal TTL for tokens used by this provider. Defaults to 72 hours.
    pub token_increment_ttl: Option<String>,

    /// Refresh interval for tokens used by this provider. Defaults to 12 hours.
    pub token_refresh_interval: Option<std::time::Duration>,
}

impl Default for Config {
    /// default constructor - Gets all values from environment & defaults
    fn default() -> Self {
        Self::from_values(&HashMap::new()).unwrap()
    }
}

impl Config {
    /// Initialize from a [`LinkConfig`]
    pub fn from_link_config(link_config: &LinkConfig) -> Result<Config> {
        let mut map = HashMap::clone(link_config.config);

        // Attempt to retrieve the vault token from secrets
        if let Some(token) = env::var("VAULT_TOKEN").ok().or_else(|| {
            link_config
                .secrets
                .get("token")
                .and_then(SecretValue::as_string)
                .map(String::from)
        }) {
            map.insert("VAULT_TOKEN".into(), token);
        } else {
            warn!("Secret value [token] (ENV: VAULT_TOKEN) was not found in env or secrets. Please prefer ENV variables or secrets for sensitive values.")
        }

        Self::from_values(&map)
    }

    /// Initialize from linkdef values, environment, and defaults
    ///
    /// NOTE: Prefer [`Self::from_link_config`] rather than this method directly
    pub fn from_values(values: &HashMap<String, String>) -> Result<Config> {
        let addr = env::var("VAULT_ADDR")
            .ok()
            .or_else(|| values.get("addr").cloned())
            .or_else(|| values.get("ADDR").cloned())
            .unwrap_or_else(|| DEFAULT_VAULT_ADDR.to_string());
        let addr = addr.parse().unwrap_or_else(|_| {
            eprintln!(
                "Could not parse VAULT_ADDR [{addr}] as Url, using default of {DEFAULT_VAULT_ADDR}"
            );
            DEFAULT_VAULT_ADDR.parse().unwrap()
        });
        let token = env::var("VAULT_TOKEN")
            .ok()
            .or_else(|| values.get("token").cloned())
            .or_else(|| values.get("TOKEN").cloned())
            .context("missing setting for 'token' or VAULT_TOKEN")?;
        let mount = env::var("VAULT_MOUNT")
            .ok()
            .or_else(|| values.get("mount").cloned())
            .or_else(|| values.get("MOUNT").cloned())
            .unwrap_or_else(|| "secret".to_string());
        let certs = env::var("VAULT_CERTS")
            .ok()
            .or_else(|| values.get("certs").cloned())
            .or_else(|| values.get("CERTS").cloned())
            .map(|certs| certs.split(',').map(|s| s.trim().to_string()).collect())
            .unwrap_or_default();
        Ok(Config {
            addr,
            token,
            mount,
            certs,
            token_increment_ttl: env::var("VAULT_TOKEN_INCREMENT_TTL")
                .ok()
                .or_else(|| values.get("token_increment_ttl").cloned())
                .or_else(|| values.get("TOKEN_INCREMENT_TTL").cloned()),
            token_refresh_interval: match env::var("VAULT_TOKEN_REFRESH_INTERVAL")
                .ok()
                .or_else(|| values.get("token_refresh_interval").cloned())
                .or_else(|| values.get("TOKEN_REFRESH_INTERVAL").cloned())
            {
                Some(val) => {
                    let secs = val.parse::<u64>().unwrap_or_else(|_| {
                        eprintln!(
                            "Could not parse VAULT_TOKEN_REFRESH_INTERVAL as u64, using default of {}",
                            TOKEN_REFRESH_INTERVAL.as_secs()
                        );
                        TOKEN_REFRESH_INTERVAL.as_secs()
                    });
                    Some(Duration::from_secs(secs))
                }
                _ => None,
            },
        })
    }
}