manta_server/server/common/
vault.rs1pub mod http_client {
9
10 use manta_backend_dispatcher::error::Error;
11 use serde_json::{Value, json};
12
13 const VAULT_API_PREFIX: &str = "/v1";
15
16 const VAULT_SECRET_PATH_PREFIX: &str = "manta/data";
18
19 const VAULT_ROLE: &str = "manta";
21
22 pub async fn auth_oidc_jwt(
25 vault_base_url: &str,
26 shasta_token: &str,
27 site_name: &str,
28 ) -> Result<String, Error> {
29 let role = VAULT_ROLE;
30
31 let client = reqwest::Client::builder().build()?;
32
33 let api_url = format!(
34 "{vault_base_url}{VAULT_API_PREFIX}/auth/jwt-manta-{site_name}/login"
35 );
36
37 tracing::debug!("Accessing/login to {}", api_url);
38
39 let request_payload = json!({ "jwt": shasta_token, "role": role });
40
41 let resp = client
42 .post(api_url)
43 .header("X-Vault-Request", "true")
44 .json(&request_payload)
45 .send()
46 .await?
47 .error_for_status()?;
48
49 let resp_value = resp.json::<Value>().await?;
50 let client_token = resp_value["auth"]
51 .get("client_token")
52 .and_then(Value::as_str)
53 .ok_or_else(|| {
54 Error::MissingField(
55 "Vault auth response missing 'client_token' field".to_string(),
56 )
57 })?;
58 Ok(client_token.to_string())
59 }
60
61 pub async fn fetch_secret(
63 vault_auth_token: &str,
64 vault_base_url: &str,
65 secret_path: &str,
66 ) -> Result<Value, Error> {
67 let client = reqwest::Client::builder().build()?;
68
69 let api_url = vault_base_url.to_owned() + secret_path;
70
71 tracing::debug!("Vault url to fetch VCS secrets is '{}'", api_url);
72
73 let resp = client
74 .get(api_url)
75 .header("X-Vault-Token", vault_auth_token)
76 .send()
77 .await?
78 .error_for_status()?;
79
80 let secret_value: Value = resp.json().await?;
81 Ok(secret_value["data"].clone())
82 }
83
84 pub async fn fetch_shasta_vcs_token(
86 shasta_token: &str,
87 vault_base_url: &str,
88 site_name: &str,
89 ) -> Result<String, Error> {
90 let vault_token =
91 auth_oidc_jwt(vault_base_url, shasta_token, site_name).await?;
92
93 let vault_secret_path = format!("{VAULT_SECRET_PATH_PREFIX}/{site_name}");
94
95 let vault_secret = fetch_secret(
96 &vault_token,
97 vault_base_url,
98 &format!("{VAULT_API_PREFIX}/{vault_secret_path}/vcs"),
99 )
100 .await?;
101
102 let vcs_token = vault_secret["data"]
103 .get("token")
104 .and_then(Value::as_str)
105 .ok_or_else(|| {
106 Error::MissingField(
107 "Vault secret response missing 'token' field".to_string(),
108 )
109 })?;
110
111 Ok(vcs_token.to_string())
112 }
113
114 pub async fn fetch_shasta_k8s_secrets_from_vault(
117 vault_base_url: &str,
118 site_name: &str,
119 shasta_token: &str,
120 ) -> Result<Value, Error> {
121 let vault_token =
122 auth_oidc_jwt(vault_base_url, shasta_token, site_name).await?;
123
124 let vault_secret_path = format!("{VAULT_SECRET_PATH_PREFIX}/{site_name}");
125
126 let secret = fetch_secret(
127 &vault_token,
128 vault_base_url,
129 &format!("{VAULT_API_PREFIX}/{vault_secret_path}/k8s"),
130 )
131 .await?;
132
133 Ok(secret["data"].clone())
134 }
135}