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
use std::env;
use serde_json::json;
use reqwest;
use std::fs;

/// # Vault Credentials
/// Rust Library that fetch secrets from Vault and load them as environment variables.
/// Inspired by [Spring Cloud Vault](https://cloud.spring.io/spring-cloud-vault/reference/html/#vault.config.authentication).

pub fn initialize() {
   retrieve_token();
   env_setter();
}

fn retrieve_token() {
    let vault_addr = env::var("VAULT_ADDR")
        .expect("Cannot get environment variable VAULT_ADDR");

    let authentication_type = match env::var("VAULT_TYPE") {
        Ok(value) => value,
        _ => String::from("token")
    };
    let request_url = format!("{}/v1/auth/{}/login", vault_addr, authentication_type);

    match authentication_type.as_str() {
        "token" => {
            env::var("VAULT_TOKEN")
                .expect("Cannot get environment variable VAULT_TOKEN");
        },
        "approle" => {
            let role_id = env::var("VAULT_ROLE_ID")
                .expect("Cannot get environment variable VAULT_ROLE_ID");
            let secret_id = env::var("VAULT_SECRET_ID")
                .expect("Cannot get environment variable VAULT_SECRET_ID");

            let payload = json!({
                "role_id": role_id,
                "secret_id": secret_id
            });

            call_vault_login(&request_url, &payload);
        },
        "userpass" | "ldap" => {
            let username = env::var("VAULT_USERNAME")
                .expect("Cannot get environment variable VAULT_USERNAME");
            let password = env::var("VAULT_PASSWORD")
                .expect("Cannot get environment variable VAULT_PASSWORD");

            let payload = json!({
                "username": username,
                "password": password
            });

            call_vault_login(&request_url, &payload);
        },
        "kubernetes" => {
            let auth_path = env::var("VAULT_K8S_AUTH_PATH")
                .expect("Cannot get environment variable VAULT_K8S_AUTH_PATH");
            let role_name = env::var("VAULT_ROLE_NAME")
                .expect("Cannot get environment variable VAULT_ROLE_NAME");

            let jwt = fs::read_to_string(auth_path)
                .expect("Cannot kubernetes auth file from path");
            let payload = json!({
                "jwt": jwt,
                "role": role_name
            });

            call_vault_login(&request_url, &payload);
        },
        _ => panic!("{} is not supported.", authentication_type)
    }
}

fn call_vault_login(request_url: &str, payload: &serde_json::Value) {
    let response: serde_json::Value = reqwest::blocking::Client::new()
        .post(request_url)
        .json(payload)
        .send()
        .unwrap()
        .json()
        .unwrap();

    if let Some(errors) = response.get("errors") {
        panic!("Cannot retrieve token: {}", errors.as_array().unwrap().first().unwrap());
    }

    let client_token = response
        .get("auth").unwrap()
        .get("client_token").unwrap();
    env::set_var("VAULT_TOKEN", client_token.as_str().unwrap());
}

fn env_setter() {
    let vault_addr = std::env::var("VAULT_ADDR").unwrap();
    let vault_token = std::env::var("VAULT_TOKEN").unwrap();
    let vault_path = std::env::var("VAULT_PATH").unwrap();

    let request_uri = format!("{}/v1/secret/data/{}", vault_addr, vault_path);

    let response: serde_json::Value = reqwest::blocking::Client::new()
        .get(&request_uri)
        .header("X-Vault-Token", vault_token)
        .send()
        .unwrap()
        .json()
        .unwrap();

    if let Some(errors) = response.get("errors") {
        panic!("Cannot retrieve token: {}", errors.as_array().unwrap().first().unwrap());
    }

    let object = response.get("data").unwrap().get("data").unwrap().as_object().unwrap();

    for (key, value) in object {
        add_to_env(key, value);
    }
}

fn add_to_env(key_path: &String, value: &serde_json::Value) {
    if value.is_object() {
        for (key, value) in value.as_object().unwrap() {
            add_to_env(&format!("{}.{}", key_path, key), value);
        }
    } else if !value.is_array() {
        env::set_var(key_path, value.as_str().unwrap());
    } else {
        unimplemented!("Vault secrets shouldn't have arrays and must use objects to ensure unique keys.");
    }
}