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
use std::collections::{HashMap, HashSet};

use aws_sdk_secretsmanager::{config, Client};
use vector_lib::configurable::{component::GenerateConfig, configurable_component};

use crate::aws::{create_client, AwsAuthentication, ClientBuilder, RegionOrEndpoint};
use crate::config::ProxyConfig;
use crate::tls::TlsConfig;
use crate::{config::SecretBackend, signal};

pub(crate) struct SecretsManagerClientBuilder;

impl ClientBuilder for SecretsManagerClientBuilder {
    type Client = Client;

    fn build(&self, config: &aws_types::SdkConfig) -> Self::Client {
        let config = config::Builder::from(config).build();
        Client::from_conf(config)
    }
}

/// Configuration for the `aws_secrets_manager` secrets backend.
#[configurable_component(secrets("aws_secrets_manager"))]
#[derive(Clone, Debug)]
pub struct AwsSecretsManagerBackend {
    /// ID of the secret to resolve.
    pub secret_id: String,

    #[serde(flatten)]
    #[configurable(derived)]
    pub region: RegionOrEndpoint,

    #[configurable(derived)]
    #[serde(default)]
    pub auth: AwsAuthentication,

    #[configurable(derived)]
    pub tls: Option<TlsConfig>,
}

impl GenerateConfig for AwsSecretsManagerBackend {
    fn generate_config() -> toml::Value {
        toml::Value::try_from(AwsSecretsManagerBackend {
            secret_id: String::from("secret-id"),
            region: Default::default(),
            auth: Default::default(),
            tls: None,
        })
        .unwrap()
    }
}

impl SecretBackend for AwsSecretsManagerBackend {
    async fn retrieve(
        &mut self,
        secret_keys: HashSet<String>,
        _: &mut signal::SignalRx,
    ) -> crate::Result<HashMap<String, String>> {
        let client = create_client::<SecretsManagerClientBuilder>(
            &SecretsManagerClientBuilder {},
            &self.auth,
            self.region.region(),
            self.region.endpoint(),
            &ProxyConfig::default(),
            &self.tls,
            &None,
        )
        .await?;

        let get_secret_value_response = client
            .get_secret_value()
            .secret_id(&self.secret_id)
            .send()
            .await?;

        let secret_string = get_secret_value_response
            .secret_string
            .ok_or::<String>(format!(
                "secret for secret-id '{}' could not be retrieved",
                &self.secret_id
            ))?;

        let output = serde_json::from_str::<HashMap<String, String>>(secret_string.as_str())?;

        let mut secrets = HashMap::new();
        for k in secret_keys.into_iter() {
            if let Some(secret) = output.get(&k) {
                if secret.is_empty() {
                    return Err(format!(
                        "value for key '{}' in secret with id '{}' was empty",
                        k, &self.secret_id
                    )
                    .into());
                }
                secrets.insert(k.to_string(), secret.to_string());
            } else {
                return Err(format!(
                    "key '{}' in secret with id '{}' does not exist",
                    k, &self.secret_id
                )
                .into());
            }
        }
        Ok(secrets)
    }
}