manta_server/server/common/
vault.rs1pub mod http_client {
9
10 use std::sync::LazyLock;
11
12 use manta_backend_dispatcher::error::Error;
13 use serde_json::{Value, json};
14
15 const VAULT_API_PREFIX: &str = "/v1";
17
18 const VAULT_SECRET_PATH_PREFIX: &str = "manta/data";
20
21 const VAULT_ROLE: &str = "manta";
23
24 static VAULT_HTTP_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
36 reqwest::Client::builder()
37 .build()
38 .expect("default reqwest::ClientBuilder build cannot fail")
39 });
40
41 pub async fn auth_oidc_jwt(
44 vault_base_url: &str,
45 shasta_token: &str,
46 site_name: &str,
47 ) -> Result<String, Error> {
48 let role = VAULT_ROLE;
49
50 let api_url = format!(
51 "{vault_base_url}{VAULT_API_PREFIX}/auth/jwt-manta-{site_name}/login"
52 );
53
54 tracing::debug!("Accessing/login to {}", api_url);
55
56 let request_payload = json!({ "jwt": shasta_token, "role": role });
57
58 let resp = VAULT_HTTP_CLIENT
59 .post(api_url)
60 .header("X-Vault-Request", "true")
61 .json(&request_payload)
62 .send()
63 .await?
64 .error_for_status()?;
65
66 let resp_value = resp.json::<Value>().await?;
67 let client_token = resp_value["auth"]
68 .get("client_token")
69 .and_then(Value::as_str)
70 .ok_or_else(|| {
71 Error::MissingField(
72 "Vault auth response missing 'client_token' field".to_string(),
73 )
74 })?;
75 Ok(client_token.to_string())
76 }
77
78 pub async fn get_secret(
80 vault_auth_token: &str,
81 vault_base_url: &str,
82 secret_path: &str,
83 ) -> Result<Value, Error> {
84 let api_url = vault_base_url.to_owned() + secret_path;
85
86 tracing::debug!("Vault url to fetch VCS secrets is '{}'", api_url);
87
88 let resp = VAULT_HTTP_CLIENT
89 .get(api_url)
90 .header("X-Vault-Token", vault_auth_token)
91 .send()
92 .await?
93 .error_for_status()?;
94
95 let secret_value: Value = resp.json().await?;
96 Ok(secret_value["data"].clone())
97 }
98
99 pub async fn get_shasta_vcs_token(
101 shasta_token: &str,
102 vault_base_url: &str,
103 site_name: &str,
104 ) -> Result<String, Error> {
105 let vault_token =
106 auth_oidc_jwt(vault_base_url, shasta_token, site_name).await?;
107
108 let vault_secret_path = format!("{VAULT_SECRET_PATH_PREFIX}/{site_name}");
109
110 let vault_secret = get_secret(
111 &vault_token,
112 vault_base_url,
113 &format!("{VAULT_API_PREFIX}/{vault_secret_path}/vcs"),
114 )
115 .await?;
116
117 let vcs_token = vault_secret["data"]
118 .get("token")
119 .and_then(Value::as_str)
120 .ok_or_else(|| {
121 Error::MissingField(
122 "Vault secret response missing 'token' field".to_string(),
123 )
124 })?;
125
126 Ok(vcs_token.to_string())
127 }
128}