From de46128e22c3e1b345c952b0dae3a566a44d5165 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Mon, 18 Mar 2024 15:35:32 +0800 Subject: [PATCH 01/30] support workload identity auth for Azure --- src/azure/storage/config.rs | 18 ++++ src/azure/storage/loader.rs | 18 +++- src/azure/storage/mod.rs | 2 + .../storage/workload_identity_credential.rs | 85 +++++++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/azure/storage/workload_identity_credential.rs diff --git a/src/azure/storage/config.rs b/src/azure/storage/config.rs index fac6d7a1..667fc184 100644 --- a/src/azure/storage/config.rs +++ b/src/azure/storage/config.rs @@ -44,4 +44,22 @@ pub struct Config { /// /// This is part of use AAD(Azure Active Directory) authenticate on Azure VM pub endpoint: Option, + /// `azure_federated_token` value will be loaded from: + /// + /// - this field if it's `is_some` + /// - env value: [`AZURE_FEDERATED_TOKEN`] + /// - profile config: `azure_federated_token_file` + pub azure_federated_token: Option, + /// `azure_federated_token_file` value will be loaded from: + /// + /// - this field if it's `is_some` + /// - env value: [`AZURE_FEDERATED_TOKEN_FILE`] + /// - profile config: `azure_federated_token_file` + pub azure_federated_token_file: Option, + /// `azure_tenant_id_env_key` value will be loaded from: + /// + /// - this field if it's `is_some` + /// - env value: [`AZURE_TENANT_ID_ENV_KEY`] + /// - profile config: `azure_tenant_id_env_key` + pub azure_tenant_id_env_key: Option, } diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index 5a445bcc..b0b29d36 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -3,9 +3,9 @@ use std::sync::Mutex; use anyhow::Result; -use super::config::Config; use super::credential::Credential; use super::imds_credential; +use super::{config::Config, workload_identity_credential}; /// Loader will load credential from different methods. #[cfg_attr(test, derive(Debug))] @@ -45,6 +45,10 @@ impl Loader { return Ok(Some(cred)); } + if let Some(cred) = self.load_via_workload_identity().await? { + return Ok(Some(cred)); + } + // try to load credential using AAD(Azure Active Directory) authenticate on Azure VM // we may get an error if not running on Azure VM // see https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal,http#using-the-rest-protocol @@ -72,4 +76,16 @@ impl Loader { Ok(cred) } + + async fn load_via_workload_identity(&self) -> Result> { + let workload_identity_token = workload_identity_credential::get_workload_identity_token( + "https://storage.azure.com/", + &self.config, + ) + .await?; + match workload_identity_token { + Some(token) => Ok(Some(Credential::BearerToken(token.access_token))), + None => Ok(None), + } + } } diff --git a/src/azure/storage/mod.rs b/src/azure/storage/mod.rs index a2549958..3fb2afa2 100644 --- a/src/azure/storage/mod.rs +++ b/src/azure/storage/mod.rs @@ -14,6 +14,8 @@ pub use credential::Credential as AzureStorageCredential; mod imds_credential; +mod workload_identity_credential; + mod loader; pub use loader::Loader as AzureStorageLoader; diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs new file mode 100644 index 00000000..afa3467d --- /dev/null +++ b/src/azure/storage/workload_identity_credential.rs @@ -0,0 +1,85 @@ +use std::str; + +use http::HeaderValue; +use http::Method; +use http::Request; +use reqwest::Client; +use reqwest::Url; +use serde::Deserialize; +use std::fs; + +use super::config::Config; + +const MSI_API_VERSION: &str = "2019-08-01"; +const MSI_ENDPOINT: &str = "http://169.254.169.254/metadata/identity/oauth2/token"; + +/// Gets an access token for the specified resource and configuration. +/// +/// See +pub async fn get_workload_identity_token( + resource: &str, + config: &Config, +) -> anyhow::Result> { + let token = match ( + &config.azure_federated_token, + &config.azure_federated_token_file, + ) { + (Some(token), Some(_)) | (Some(token), None) => token.clone(), + (None, Some(token_file)) => { + let token = fs::read_to_string(token_file)?; + token + } + _ => return Ok(None), + }; + let tenant_id = if let Some(tenant_id) = &config.azure_tenant_id_env_key { + tenant_id + } else { + return Ok(None); + }; + let client_id = if let Some(client_id) = &config.client_id { + client_id + } else { + return Ok(None); + }; + + let endpoint = config.endpoint.as_deref().unwrap_or(MSI_ENDPOINT); + + let mut query_items = vec![("api-version", MSI_API_VERSION), ("resource", resource)]; + query_items.push(("token", &token)); + query_items.push(("tenant_id", &tenant_id)); + query_items.push(("client_id", &client_id)); + + let url = Url::parse_with_params(endpoint, &query_items)?; + let mut req = Request::builder() + .method(Method::GET) + .uri(url.to_string()) + .body("")?; + + req.headers_mut() + .insert("metadata", HeaderValue::from_static("true")); + + if let Some(secret) = &config.msi_secret { + req.headers_mut() + .insert("x-identity-header", HeaderValue::from_str(secret)?); + }; + + let res = Client::new().execute(req.try_into()?).await?; + let rsp_status = res.status(); + let rsp_body = res.text().await?; +; + + if !rsp_status.is_success() { + return Err(anyhow::anyhow!("Failed to get token from working identity credential")); + } + + let token: AccessToken = serde_json::from_str(&rsp_body)?; + Ok(Some(token)) +} + +#[derive(Debug, Clone, Deserialize)] +#[allow(unused)] +pub struct AccessToken { + pub access_token: String, + pub expires_on: String, + +} From 646a45198ccdad60e1e08ffdc4f181ad9752b8ee Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Tue, 19 Mar 2024 09:50:26 +0800 Subject: [PATCH 02/30] fmt --- src/azure/storage/workload_identity_credential.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index afa3467d..8514fde4 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -66,10 +66,11 @@ pub async fn get_workload_identity_token( let res = Client::new().execute(req.try_into()?).await?; let rsp_status = res.status(); let rsp_body = res.text().await?; -; if !rsp_status.is_success() { - return Err(anyhow::anyhow!("Failed to get token from working identity credential")); + return Err(anyhow::anyhow!( + "Failed to get token from working identity credential" + )); } let token: AccessToken = serde_json::from_str(&rsp_body)?; @@ -81,5 +82,4 @@ pub async fn get_workload_identity_token( pub struct AccessToken { pub access_token: String, pub expires_on: String, - } From ad6fc8059d3f746c5fedf923aa46645a820377d6 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Tue, 19 Mar 2024 09:53:11 +0800 Subject: [PATCH 03/30] make clippy happy --- src/azure/storage/workload_identity_credential.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 8514fde4..425e968b 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -25,10 +25,7 @@ pub async fn get_workload_identity_token( &config.azure_federated_token_file, ) { (Some(token), Some(_)) | (Some(token), None) => token.clone(), - (None, Some(token_file)) => { - let token = fs::read_to_string(token_file)?; - token - } + (None, Some(token_file)) => fs::read_to_string(token_file)?, _ => return Ok(None), }; let tenant_id = if let Some(tenant_id) = &config.azure_tenant_id_env_key { From 90cbdaf52ad488af71934473ee1b386761c5cf18 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Wed, 20 Mar 2024 16:02:17 +0800 Subject: [PATCH 04/30] resolve comments --- Cargo.toml | 1 + src/azure/storage/config.rs | 59 +++++++++--- src/azure/storage/loader.rs | 16 ++-- .../storage/workload_identity_credential.rs | 89 ++++++++++--------- 4 files changed, 105 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1c39726..52d346ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,7 @@ serde = { version = "1", features = ["derive"], optional = true } serde_json = { version = "1", optional = true } sha1 = "0.10" sha2 = { version = "0.10", features = ["oid"] } +time = { version = "0.3.10" } toml = { version = "0.8.9", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/src/azure/storage/config.rs b/src/azure/storage/config.rs index 667fc184..e233526f 100644 --- a/src/azure/storage/config.rs +++ b/src/azure/storage/config.rs @@ -1,3 +1,6 @@ +use std::env; +use std::{collections::HashMap, fs}; + /// Config carries all the configuration for Azure Storage services. #[derive(Clone, Default)] #[cfg_attr(test, derive(Debug))] @@ -44,22 +47,56 @@ pub struct Config { /// /// This is part of use AAD(Azure Active Directory) authenticate on Azure VM pub endpoint: Option, - /// `azure_federated_token` value will be loaded from: + /// `federated_token` value will be loaded from: /// /// - this field if it's `is_some` /// - env value: [`AZURE_FEDERATED_TOKEN`] - /// - profile config: `azure_federated_token_file` - pub azure_federated_token: Option, - /// `azure_federated_token_file` value will be loaded from: + /// - profile config: `federated_toen_file` + pub federated_token: Option, + /// `tenant_id` value will be loaded from: /// /// - this field if it's `is_some` - /// - env value: [`AZURE_FEDERATED_TOKEN_FILE`] - /// - profile config: `azure_federated_token_file` - pub azure_federated_token_file: Option, - /// `azure_tenant_id_env_key` value will be loaded from: + /// - env value: [`AZURE_TENANT_ID`] + /// - profile config: `tenant_id` + pub tenant_id: Option, + /// `authority_host` value will be loaded from: /// /// - this field if it's `is_some` - /// - env value: [`AZURE_TENANT_ID_ENV_KEY`] - /// - profile config: `azure_tenant_id_env_key` - pub azure_tenant_id_env_key: Option, + /// - env value: [`AZURE_AUTHORITY_HOST_ENV_KEY`] + /// - profile config: `authority_host` + pub authority_host: Option, +} + +pub const AZURE_FEDERATED_TOKEN: &str = "AZURE_FEDERATED_TOKEN"; +pub const AZURE_FEDERATED_TOKEN_FILE: &str = "AZURE_FEDERATED_TOKEN_FILE"; +pub const AZURE_TENANT_ID: &str = "AZURE_TENANT_ID_ENV_KEY"; +pub const AZURE_AUTHORITY_HOST_ENV_KEY: &str = "AZURE_AUTHORITY_HOST_ENV_KEY"; +const AZURE_PUBLIC_CLOUD: &str = "https://login.microsoftonline.com"; + +impl Config { + /// Load config from env. + pub fn from_env(mut self) -> Self { + let envs = env::vars().collect::>(); + + // federated_token can be loaded from both `AZURE_FEDERATED_TOKEN` and `AZURE_FEDERATED_TOKEN_FILE`. + if let Some(v) = envs.get(AZURE_FEDERATED_TOKEN_FILE) { + self.federated_token = Some(fs::read_to_string(v).unwrap_or_default()); + } + + if let Some(v) = envs.get(AZURE_FEDERATED_TOKEN) { + self.federated_token = Some(v.to_string()); + } + + if let Some(v) = envs.get(AZURE_TENANT_ID) { + self.tenant_id = Some(v.to_string()); + } + + if let Some(v) = envs.get(AZURE_AUTHORITY_HOST_ENV_KEY) { + self.authority_host = Some(v.to_string()); + } else { + self.authority_host = Some(AZURE_PUBLIC_CLOUD.to_string()); + } + + self + } } diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index b0b29d36..1ec4082a 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -25,6 +25,15 @@ impl Loader { } } + /// Create a new loader via loading config from env. + pub fn from_env() -> Self { + Self { + config: Config::default().from_env(), + + credential: Arc::default(), + } + } + /// Load credential. pub async fn load(&self) -> Result> { // Return cached credential if it's valid. @@ -78,11 +87,8 @@ impl Loader { } async fn load_via_workload_identity(&self) -> Result> { - let workload_identity_token = workload_identity_credential::get_workload_identity_token( - "https://storage.azure.com/", - &self.config, - ) - .await?; + let workload_identity_token = + workload_identity_credential::get_workload_identity_token(&self.config).await?; match workload_identity_token { Some(token) => Ok(Some(Credential::BearerToken(token.access_token))), None => Ok(None), diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 425e968b..56c7fc67 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -6,59 +6,48 @@ use http::Request; use reqwest::Client; use reqwest::Url; use serde::Deserialize; -use std::fs; use super::config::Config; -const MSI_API_VERSION: &str = "2019-08-01"; -const MSI_ENDPOINT: &str = "http://169.254.169.254/metadata/identity/oauth2/token"; - /// Gets an access token for the specified resource and configuration. /// /// See -pub async fn get_workload_identity_token( - resource: &str, - config: &Config, -) -> anyhow::Result> { - let token = match ( - &config.azure_federated_token, - &config.azure_federated_token_file, +pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result> { + let (token, tenant_id, client_id, authority_host) = match ( + &config.federated_token, + &config.tenant_id, + &config.client_id, + &config.authority_host, ) { - (Some(token), Some(_)) | (Some(token), None) => token.clone(), - (None, Some(token_file)) => fs::read_to_string(token_file)?, + (Some(token), Some(tenant_id), Some(client_id), Some(authority_host)) => { + (token, tenant_id, client_id, authority_host) + } _ => return Ok(None), }; - let tenant_id = if let Some(tenant_id) = &config.azure_tenant_id_env_key { - tenant_id - } else { - return Ok(None); - }; - let client_id = if let Some(client_id) = &config.client_id { - client_id - } else { - return Ok(None); - }; - let endpoint = config.endpoint.as_deref().unwrap_or(MSI_ENDPOINT); + let url = Url::parse(authority_host)?.join(&format!("/{tenant_id}/oauth2/v2.0/token"))?; - let mut query_items = vec![("api-version", MSI_API_VERSION), ("resource", resource)]; - query_items.push(("token", &token)); - query_items.push(("tenant_id", &tenant_id)); - query_items.push(("client_id", &client_id)); + let scopes: &[&str] = &[&scope_from_url(&url)]; + let encoded_body: String = form_urlencoded::Serializer::new(String::new()) + .append_pair("client_id", client_id) + .append_pair("scope", &scopes.join(" ")) + .append_pair( + "client_assertion_type", + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + ) + .append_pair("client_assertion", token) + .append_pair("grant_type", "client_credentials") + .finish(); - let url = Url::parse_with_params(endpoint, &query_items)?; let mut req = Request::builder() - .method(Method::GET) + .method(Method::POST) .uri(url.to_string()) - .body("")?; + .body(encoded_body)?; - req.headers_mut() - .insert("metadata", HeaderValue::from_static("true")); - - if let Some(secret) = &config.msi_secret { - req.headers_mut() - .insert("x-identity-header", HeaderValue::from_str(secret)?); - }; + req.headers_mut().insert( + http::header::CONTENT_TYPE.as_str(), + HeaderValue::from_static("application/x-www-form-urlencoded"), + ); let res = Client::new().execute(req.try_into()?).await?; let rsp_status = res.status(); @@ -66,17 +55,29 @@ pub async fn get_workload_identity_token( if !rsp_status.is_success() { return Err(anyhow::anyhow!( - "Failed to get token from working identity credential" + "Failed to get token from working identity credential." )); } - let token: AccessToken = serde_json::from_str(&rsp_body)?; - Ok(Some(token)) + let resp: LoginResponse = serde_json::from_str(&rsp_body)?; + Ok(Some(resp)) } #[derive(Debug, Clone, Deserialize)] -#[allow(unused)] -pub struct AccessToken { +pub struct LoginResponse { + pub token_type: String, + pub expires_in: u64, + pub ext_expires_in: u64, + pub expires_on: Option, + pub not_before: Option, + pub resource: Option, pub access_token: String, - pub expires_on: String, +} + +/// This function generates the scope string from the passed url. The scope string is used to +/// request the AAD token. +fn scope_from_url(url: &Url) -> String { + let scheme = url.scheme(); + let hostname = url.host_str().unwrap(); + format!("{scheme}://{hostname}") } From d2489adddc8f3f4566fe7a786fe26467927549d8 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Wed, 20 Mar 2024 16:03:56 +0800 Subject: [PATCH 05/30] minor --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 52d346ab..d1c39726 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,6 @@ serde = { version = "1", features = ["derive"], optional = true } serde_json = { version = "1", optional = true } sha1 = "0.10" sha2 = { version = "0.10", features = ["oid"] } -time = { version = "0.3.10" } toml = { version = "0.8.9", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] From d293b59197f35ac5fc834a32063a40c1d24b3c75 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Mon, 25 Mar 2024 15:13:00 +0800 Subject: [PATCH 06/30] minor rename --- src/azure/storage/config.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/azure/storage/config.rs b/src/azure/storage/config.rs index e233526f..b1b42bf0 100644 --- a/src/azure/storage/config.rs +++ b/src/azure/storage/config.rs @@ -62,15 +62,15 @@ pub struct Config { /// `authority_host` value will be loaded from: /// /// - this field if it's `is_some` - /// - env value: [`AZURE_AUTHORITY_HOST_ENV_KEY`] + /// - env value: [`AZURE_AUTHORITY_HOST`] /// - profile config: `authority_host` pub authority_host: Option, } pub const AZURE_FEDERATED_TOKEN: &str = "AZURE_FEDERATED_TOKEN"; pub const AZURE_FEDERATED_TOKEN_FILE: &str = "AZURE_FEDERATED_TOKEN_FILE"; -pub const AZURE_TENANT_ID: &str = "AZURE_TENANT_ID_ENV_KEY"; -pub const AZURE_AUTHORITY_HOST_ENV_KEY: &str = "AZURE_AUTHORITY_HOST_ENV_KEY"; +pub const AZURE_TENANT_ID: &str = "AZURE_TENANT_ID"; +pub const AZURE_AUTHORITY_HOST: &str = "AZURE_AUTHORITY_HOST"; const AZURE_PUBLIC_CLOUD: &str = "https://login.microsoftonline.com"; impl Config { @@ -91,7 +91,7 @@ impl Config { self.tenant_id = Some(v.to_string()); } - if let Some(v) = envs.get(AZURE_AUTHORITY_HOST_ENV_KEY) { + if let Some(v) = envs.get(AZURE_AUTHORITY_HOST) { self.authority_host = Some(v.to_string()); } else { self.authority_host = Some(AZURE_PUBLIC_CLOUD.to_string()); From 73bfa62b2631ad1a6d72e318c164948ffa656fb6 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Mon, 25 Mar 2024 15:40:44 +0800 Subject: [PATCH 07/30] default load from env --- src/azure/storage/loader.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index 1ec4082a..104fa514 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -19,7 +19,7 @@ impl Loader { /// Create a new loader via config. pub fn new(config: Config) -> Self { Self { - config, + config: Config::default().from_env(), credential: Arc::default(), } @@ -40,7 +40,6 @@ impl Loader { if let Some(cred) = self.credential.lock().expect("lock poisoned").clone() { return Ok(Some(cred)); } - let cred = self.load_inner().await?; let mut lock = self.credential.lock().expect("lock poisoned"); @@ -53,7 +52,7 @@ impl Loader { if let Some(cred) = self.load_via_config().await? { return Ok(Some(cred)); } - + if let Some(cred) = self.load_via_workload_identity().await? { return Ok(Some(cred)); } From 12dd196b49e9f2b855e322e05fb6d7ecf0392209 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Mon, 25 Mar 2024 15:45:55 +0800 Subject: [PATCH 08/30] debug --- src/azure/storage/loader.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index 104fa514..d9276721 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -78,6 +78,7 @@ impl Loader { } async fn load_via_imds(&self) -> Result> { + println!("load via imdbs"); let token = imds_credential::get_access_token("https://storage.azure.com/", &self.config).await?; let cred = Some(Credential::BearerToken(token.access_token)); @@ -86,6 +87,7 @@ impl Loader { } async fn load_via_workload_identity(&self) -> Result> { + println!("load via workload identity"); let workload_identity_token = workload_identity_credential::get_workload_identity_token(&self.config).await?; match workload_identity_token { From f6ff677f769b96e044be77473801a5e05ede2c5b Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Mon, 25 Mar 2024 15:58:06 +0800 Subject: [PATCH 09/30] debug --- src/azure/storage/imds_credential.rs | 2 +- src/azure/storage/loader.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/azure/storage/imds_credential.rs b/src/azure/storage/imds_credential.rs index cec67699..44faf708 100644 --- a/src/azure/storage/imds_credential.rs +++ b/src/azure/storage/imds_credential.rs @@ -50,7 +50,7 @@ pub async fn get_access_token(resource: &str, config: &Config) -> anyhow::Result let rsp_body = res.text().await?; if !rsp_status.is_success() { - return Err(anyhow::anyhow!("Failed to get token from IMDS endpoint")); + return Err(anyhow::anyhow!("Failed to get token from IMDS endpoint xxx")); } let token: AccessToken = serde_json::from_str(&rsp_body)?; diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index d9276721..104fa514 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -78,7 +78,6 @@ impl Loader { } async fn load_via_imds(&self) -> Result> { - println!("load via imdbs"); let token = imds_credential::get_access_token("https://storage.azure.com/", &self.config).await?; let cred = Some(Credential::BearerToken(token.access_token)); @@ -87,7 +86,6 @@ impl Loader { } async fn load_via_workload_identity(&self) -> Result> { - println!("load via workload identity"); let workload_identity_token = workload_identity_credential::get_workload_identity_token(&self.config).await?; match workload_identity_token { From d5d4ea7187c1edb5861f271e93c350483320cedb Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Mon, 25 Mar 2024 16:55:14 +0800 Subject: [PATCH 10/30] debug --- src/azure/storage/loader.rs | 1 + src/azure/storage/workload_identity_credential.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index 104fa514..c734c95b 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -86,6 +86,7 @@ impl Loader { } async fn load_via_workload_identity(&self) -> Result> { + println!("do workload_identity"); let workload_identity_token = workload_identity_credential::get_workload_identity_token(&self.config).await?; match workload_identity_token { diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 56c7fc67..5dce532a 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -24,7 +24,9 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result return Ok(None), }; - + println!("token = {}", token); + println!("client_id = {}", client_id); + println!("tenant_id = {}", tenant_id); let url = Url::parse(authority_host)?.join(&format!("/{tenant_id}/oauth2/v2.0/token"))?; let scopes: &[&str] = &[&scope_from_url(&url)]; From b40b99da415f09923aa2bc4825356f134b387359 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Mon, 25 Mar 2024 17:07:29 +0800 Subject: [PATCH 11/30] add client id --- src/azure/storage/config.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/azure/storage/config.rs b/src/azure/storage/config.rs index b1b42bf0..4cd42b9d 100644 --- a/src/azure/storage/config.rs +++ b/src/azure/storage/config.rs @@ -26,6 +26,7 @@ pub struct Config { /// Specifies the application id (client id) associated with a user assigned managed service identity resource /// /// The values of object_id and msi_res_id are discarded + /// - nv value: [`AZURE_CLIENT_ID`] /// /// This is part of use AAD(Azure Active Directory) authenticate on Azure VM pub client_id: Option, @@ -70,6 +71,7 @@ pub struct Config { pub const AZURE_FEDERATED_TOKEN: &str = "AZURE_FEDERATED_TOKEN"; pub const AZURE_FEDERATED_TOKEN_FILE: &str = "AZURE_FEDERATED_TOKEN_FILE"; pub const AZURE_TENANT_ID: &str = "AZURE_TENANT_ID"; +pub const AZURE_CLIENT_ID: &str = "AZURE_CLIENT_ID"; pub const AZURE_AUTHORITY_HOST: &str = "AZURE_AUTHORITY_HOST"; const AZURE_PUBLIC_CLOUD: &str = "https://login.microsoftonline.com"; @@ -91,6 +93,10 @@ impl Config { self.tenant_id = Some(v.to_string()); } + if let Some(v) = envs.get(AZURE_CLIENT_ID) { + self.client_id = Some(v.to_string()); + } + if let Some(v) = envs.get(AZURE_AUTHORITY_HOST) { self.authority_host = Some(v.to_string()); } else { From c68cfabf7e8186551bf43c51a18958dcccaba43c Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Mon, 25 Mar 2024 17:18:24 +0800 Subject: [PATCH 12/30] more debug info --- src/azure/storage/workload_identity_credential.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 5dce532a..cca0e634 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -57,7 +57,7 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result Date: Tue, 26 Mar 2024 15:04:17 +0800 Subject: [PATCH 13/30] go ahead debug --- src/azure/storage/workload_identity_credential.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index cca0e634..73d1eeb4 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -30,6 +30,7 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result Date: Tue, 26 Mar 2024 15:04:34 +0800 Subject: [PATCH 14/30] go ahead debug --- src/azure/storage/workload_identity_credential.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 73d1eeb4..aaa4acf1 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -28,9 +28,9 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result Date: Tue, 26 Mar 2024 15:21:10 +0800 Subject: [PATCH 15/30] add api-version --- src/azure/storage/workload_identity_credential.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index aaa4acf1..3a5f1804 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -52,6 +52,11 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result Date: Tue, 26 Mar 2024 15:37:50 +0800 Subject: [PATCH 16/30] fix api-version --- src/azure/storage/workload_identity_credential.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 3a5f1804..ad72c1e5 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -9,6 +9,8 @@ use serde::Deserialize; use super::config::Config; +pub const API_VERSION: &str = "api-version"; + /// Gets an access token for the specified resource and configuration. /// /// See @@ -28,9 +30,9 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result anyhow::Result anyhow::Result Date: Tue, 26 Mar 2024 15:45:44 +0800 Subject: [PATCH 17/30] fix api-version --- src/azure/storage/workload_identity_credential.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index ad72c1e5..6149f0c6 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -29,7 +29,7 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result anyhow::Result Date: Tue, 26 Mar 2024 15:46:14 +0800 Subject: [PATCH 18/30] fix api-version --- src/azure/storage/workload_identity_credential.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 6149f0c6..5738c07d 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -42,7 +42,6 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result anyhow::Result Date: Tue, 26 Mar 2024 15:53:20 +0800 Subject: [PATCH 19/30] minor --- src/azure/storage/workload_identity_credential.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 5738c07d..17160ea0 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -9,7 +9,7 @@ use serde::Deserialize; use super::config::Config; -pub const API_VERSION: &str = "api-version"; +pub const API_VERSION: &str = b"api-version"; /// Gets an access token for the specified resource and configuration. /// @@ -29,8 +29,9 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result anyhow::Result Date: Tue, 26 Mar 2024 15:54:40 +0800 Subject: [PATCH 20/30] minor --- src/azure/storage/workload_identity_credential.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 17160ea0..76489f50 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -9,7 +9,7 @@ use serde::Deserialize; use super::config::Config; -pub const API_VERSION: &str = b"api-version"; +pub const API_VERSION: &str = "api-version"; /// Gets an access token for the specified resource and configuration. /// From 65ceb0bc3c8991216a569a90d30bf0a87a69d622 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Tue, 26 Mar 2024 16:04:45 +0800 Subject: [PATCH 21/30] fix authority_host --- src/azure/storage/workload_identity_credential.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 76489f50..c1d1c41f 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -10,7 +10,7 @@ use serde::Deserialize; use super::config::Config; pub const API_VERSION: &str = "api-version"; - +const AZURE_PUBLIC_CLOUD: &str = "https://login.microsoftonline.com"; /// Gets an access token for the specified resource and configuration. /// /// See @@ -29,7 +29,7 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result Date: Tue, 26 Mar 2024 16:09:40 +0800 Subject: [PATCH 22/30] scope = authority_host --- src/azure/storage/workload_identity_credential.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index c1d1c41f..b13f7dcd 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -32,7 +32,7 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result Date: Tue, 26 Mar 2024 16:23:47 +0800 Subject: [PATCH 23/30] fix scope --- src/azure/storage/workload_identity_credential.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index b13f7dcd..3d0b9fb4 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -32,7 +32,7 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result Date: Tue, 26 Mar 2024 16:31:00 +0800 Subject: [PATCH 24/30] more debug info --- src/azure/storage/loader.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index c734c95b..017482e4 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -54,6 +54,7 @@ impl Loader { } if let Some(cred) = self.load_via_workload_identity().await? { + println!("load_via_workload_identity 成功 "); return Ok(Some(cred)); } From 7a7de13db9d74a18eda78c0419dad4fbb52f9506 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Tue, 26 Mar 2024 16:49:16 +0800 Subject: [PATCH 25/30] show token --- src/azure/storage/loader.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index 017482e4..36a993f4 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -55,6 +55,7 @@ impl Loader { if let Some(cred) = self.load_via_workload_identity().await? { println!("load_via_workload_identity 成功 "); + return Ok(Some(cred)); } @@ -91,7 +92,7 @@ impl Loader { let workload_identity_token = workload_identity_credential::get_workload_identity_token(&self.config).await?; match workload_identity_token { - Some(token) => Ok(Some(Credential::BearerToken(token.access_token))), + Some(token) => {println!("return token = {:?}", token.access_token); Ok( Some(Credential::BearerToken(token.access_token)))}, None => Ok(None), } } From 7ca1d72fb87186114c0c5cbce2d6c0b1fd52ef90 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Wed, 27 Mar 2024 10:49:32 +0800 Subject: [PATCH 26/30] change scope --- Cargo.toml | 2 +- src/azure/storage/workload_identity_credential.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1c39726..e6d06073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,7 @@ toml = { version = "0.8.9", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] home = "0.5" tokio = { version = "1", features = ["fs"], optional = true } - + [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2", features = ["js"] } tokio = { version = "1", optional = true } diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index 3d0b9fb4..e33d3bb5 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -32,7 +32,7 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result Date: Wed, 27 Mar 2024 11:11:47 +0800 Subject: [PATCH 27/30] test pass, remove debug info --- src/azure/storage/config.rs | 2 +- src/azure/storage/imds_credential.rs | 2 +- src/azure/storage/loader.rs | 7 ++--- .../storage/workload_identity_credential.rs | 30 +++++-------------- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/azure/storage/config.rs b/src/azure/storage/config.rs index 4cd42b9d..3b8a3094 100644 --- a/src/azure/storage/config.rs +++ b/src/azure/storage/config.rs @@ -26,7 +26,7 @@ pub struct Config { /// Specifies the application id (client id) associated with a user assigned managed service identity resource /// /// The values of object_id and msi_res_id are discarded - /// - nv value: [`AZURE_CLIENT_ID`] + /// - nv value: [`AZURE_CLIENT_ID`] /// /// This is part of use AAD(Azure Active Directory) authenticate on Azure VM pub client_id: Option, diff --git a/src/azure/storage/imds_credential.rs b/src/azure/storage/imds_credential.rs index 44faf708..cec67699 100644 --- a/src/azure/storage/imds_credential.rs +++ b/src/azure/storage/imds_credential.rs @@ -50,7 +50,7 @@ pub async fn get_access_token(resource: &str, config: &Config) -> anyhow::Result let rsp_body = res.text().await?; if !rsp_status.is_success() { - return Err(anyhow::anyhow!("Failed to get token from IMDS endpoint xxx")); + return Err(anyhow::anyhow!("Failed to get token from IMDS endpoint")); } let token: AccessToken = serde_json::from_str(&rsp_body)?; diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index 36a993f4..d17bb2b8 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -52,10 +52,8 @@ impl Loader { if let Some(cred) = self.load_via_config().await? { return Ok(Some(cred)); } - + if let Some(cred) = self.load_via_workload_identity().await? { - println!("load_via_workload_identity 成功 "); - return Ok(Some(cred)); } @@ -88,11 +86,10 @@ impl Loader { } async fn load_via_workload_identity(&self) -> Result> { - println!("do workload_identity"); let workload_identity_token = workload_identity_credential::get_workload_identity_token(&self.config).await?; match workload_identity_token { - Some(token) => {println!("return token = {:?}", token.access_token); Ok( Some(Credential::BearerToken(token.access_token)))}, + Some(token) => Ok(Some(Credential::BearerToken(token.access_token))), None => Ok(None), } } diff --git a/src/azure/storage/workload_identity_credential.rs b/src/azure/storage/workload_identity_credential.rs index e33d3bb5..844690a6 100644 --- a/src/azure/storage/workload_identity_credential.rs +++ b/src/azure/storage/workload_identity_credential.rs @@ -10,7 +10,7 @@ use serde::Deserialize; use super::config::Config; pub const API_VERSION: &str = "api-version"; -const AZURE_PUBLIC_CLOUD: &str = "https://login.microsoftonline.com"; +const STORAGE_TOKEN_SCOPE: &str = "https://storage.azure.com/.default"; /// Gets an access token for the specified resource and configuration. /// /// See @@ -26,14 +26,8 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result return Ok(None), }; - println!("token = {}", token); - println!("client_id = {}", client_id); - println!("tenant_id = {}", tenant_id); - let url = Url::parse(AZURE_PUBLIC_CLOUD)?.join(&format!("/{tenant_id}/oauth2/v2.0/token"))?; - println!("authority_host = {:?}", authority_host); - println!("这里url = {:?}", url); - let scopes: &[&str] = &["https://storage.azure.com/.default"]; - println!("scopes is {:?}", scopes); + let url = Url::parse(authority_host)?.join(&format!("/{tenant_id}/oauth2/v2.0/token"))?; + let scopes: &[&str] = &[STORAGE_TOKEN_SCOPE]; let encoded_body: String = form_urlencoded::Serializer::new(String::new()) .append_pair("client_id", client_id) .append_pair("scope", &scopes.join(" ")) @@ -54,10 +48,8 @@ pub async fn get_workload_identity_token(config: &Config) -> anyhow::Result anyhow::Result, pub access_token: String, } - -/// This function generates the scope string from the passed url. The scope string is used to -/// request the AAD token. -fn scope_from_url(url: &Url) -> String { - let scheme = url.scheme(); - let hostname = url.host_str().unwrap(); - format!("{scheme}://{hostname}") -} From 33e0e346e6a18f4107e60c8bee28adc06253d969 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Thu, 28 Mar 2024 13:06:24 +0800 Subject: [PATCH 28/30] typo --- Cargo.toml | 2 +- src/azure/storage/config.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e6d06073..d1c39726 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,7 @@ toml = { version = "0.8.9", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] home = "0.5" tokio = { version = "1", features = ["fs"], optional = true } - + [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2", features = ["js"] } tokio = { version = "1", optional = true } diff --git a/src/azure/storage/config.rs b/src/azure/storage/config.rs index 3b8a3094..ef6d75cc 100644 --- a/src/azure/storage/config.rs +++ b/src/azure/storage/config.rs @@ -26,7 +26,7 @@ pub struct Config { /// Specifies the application id (client id) associated with a user assigned managed service identity resource /// /// The values of object_id and msi_res_id are discarded - /// - nv value: [`AZURE_CLIENT_ID`] + /// - cnv value: [`AZURE_CLIENT_ID`] /// /// This is part of use AAD(Azure Active Directory) authenticate on Azure VM pub client_id: Option, From 598bd8515f7600e0870c4573a633b53f9ec5c718 Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Thu, 28 Mar 2024 13:10:11 +0800 Subject: [PATCH 29/30] change new() --- src/azure/storage/loader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index d17bb2b8..e9cc8cb2 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -19,7 +19,7 @@ impl Loader { /// Create a new loader via config. pub fn new(config: Config) -> Self { Self { - config: Config::default().from_env(), + config, credential: Arc::default(), } From 39e8247b0fad9a44fb84ac6c8b6c6dd327b7acad Mon Sep 17 00:00:00 2001 From: congyi <15605187270@163.com> Date: Thu, 28 Mar 2024 14:13:05 +0800 Subject: [PATCH 30/30] resolve comment --- src/azure/storage/loader.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/azure/storage/loader.rs b/src/azure/storage/loader.rs index e9cc8cb2..b659b4aa 100644 --- a/src/azure/storage/loader.rs +++ b/src/azure/storage/loader.rs @@ -25,15 +25,6 @@ impl Loader { } } - /// Create a new loader via loading config from env. - pub fn from_env() -> Self { - Self { - config: Config::default().from_env(), - - credential: Arc::default(), - } - } - /// Load credential. pub async fn load(&self) -> Result> { // Return cached credential if it's valid.