Skip to content

Commit

Permalink
enable environmental authentication for Azure Key Vault var provider
Browse files Browse the repository at this point in the history
Signed-off-by: David Justice <[email protected]>
  • Loading branch information
devigned committed Jul 24, 2024
1 parent 08eac60 commit a467a52
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 173 deletions.
63 changes: 7 additions & 56 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/trigger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl<Executor: TriggerExecutor> TriggerExecutorBuilder<Executor> {
self.loader.add_dynamic_host_component(
&mut builder,
spin_variables::VariablesHostComponent::new(
runtime_config.variables_providers(),
runtime_config.variables_providers()?,
),
)?;
}
Expand All @@ -225,7 +225,7 @@ impl<Executor: TriggerExecutor> TriggerExecutorBuilder<Executor> {
let app_name = app.borrowed().require_metadata(APP_NAME_KEY)?;

let resolver =
spin_variables::make_resolver(app.borrowed(), runtime_config.variables_providers())?;
spin_variables::make_resolver(app.borrowed(), runtime_config.variables_providers()?)?;
let prepared_resolver = std::sync::Arc::new(resolver.prepare().await?);
resolver_cell
.set(prepared_resolver.clone())
Expand Down
26 changes: 14 additions & 12 deletions crates/trigger/src/runtime_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,17 @@ impl RuntimeConfig {
}

/// Return a Vec of configured [`VariablesProvider`]s.
pub fn variables_providers(&self) -> Vec<VariablesProvider> {
let default_provider = VariablesProviderOpts::default_provider_opts(self).build_provider();
pub fn variables_providers(&self) -> Result<Vec<VariablesProvider>> {
let default_provider =
VariablesProviderOpts::default_provider_opts(self).build_provider()?;
let mut providers: Vec<VariablesProvider> = vec![default_provider];
providers.extend(self.opts_layers().flat_map(|opts| {
opts.variables_providers
.iter()
.map(|opts| opts.build_provider())
}));
providers
for opts in self.opts_layers() {
for var_provider in &opts.variables_providers {
let provider = var_provider.build_provider()?;
providers.push(provider);
}
}
Ok(providers)
}

/// Return an iterator of named configured [`KeyValueStore`]s.
Expand Down Expand Up @@ -473,7 +475,7 @@ mod tests {
let mut config = RuntimeConfig::new(None);

// One default provider
assert_eq!(config.variables_providers().len(), 1);
assert_eq!(config.variables_providers()?.len(), 1);

merge_config_toml(
&mut config,
Expand All @@ -485,7 +487,7 @@ mod tests {
mount = "root"
},
);
assert_eq!(config.variables_providers().len(), 2);
assert_eq!(config.variables_providers()?.len(), 2);

Ok(())
}
Expand All @@ -495,7 +497,7 @@ mod tests {
let mut config = RuntimeConfig::new(None);

// One default provider
assert_eq!(config.variables_providers().len(), 1);
assert_eq!(config.variables_providers()?.len(), 1);

merge_config_toml(
&mut config,
Expand All @@ -507,7 +509,7 @@ mod tests {
mount = "root"
},
);
assert_eq!(config.variables_providers().len(), 2);
assert_eq!(config.variables_providers()?.len(), 2);

Ok(())
}
Expand Down
59 changes: 40 additions & 19 deletions crates/trigger/src/runtime_config/variables_provider.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use std::path::PathBuf;

use anyhow::{anyhow, Result};
use serde::Deserialize;
use spin_variables::provider::azure_key_vault::{
AzureKeyVaultAuthOptions, AzureKeyVaultRuntimeConfigOptions,
};
use spin_variables::provider::{
azure_key_vault::{AzureAuthorityHost, AzureKeyVaultProvider},
env::EnvProvider,
Expand All @@ -27,7 +31,7 @@ impl VariablesProviderOpts {
))
}

pub fn build_provider(&self) -> VariablesProvider {
pub fn build_provider(&self) -> Result<VariablesProvider> {
match self {
Self::Env(opts) => opts.build_provider(),
Self::Vault(opts) => opts.build_provider(),
Expand Down Expand Up @@ -60,11 +64,11 @@ impl EnvVariablesProviderOpts {
}
}

pub fn build_provider(&self) -> VariablesProvider {
Box::new(EnvProvider::new(
pub fn build_provider(&self) -> Result<VariablesProvider> {
Ok(Box::new(EnvProvider::new(
self.prefix.clone(),
self.dotenv_path.clone(),
))
)))
}
}

Expand All @@ -79,35 +83,52 @@ pub struct VaultVariablesProviderOpts {
}

impl VaultVariablesProviderOpts {
pub fn build_provider(&self) -> VariablesProvider {
Box::new(VaultProvider::new(
pub fn build_provider(&self) -> Result<VariablesProvider> {
Ok(Box::new(VaultProvider::new(
&self.url,
&self.token,
&self.mount,
self.prefix.as_deref(),
))
)))
}
}

#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AzureKeyVaultVariablesProviderOpts {
pub client_id: String,
pub client_secret: String,
pub tenant_id: String,
pub vault_url: String,
#[serde(default)]
pub authority_host: AzureAuthorityHost,
pub client_id: Option<String>,
pub client_secret: Option<String>,
pub tenant_id: Option<String>,
pub authority_host: Option<AzureAuthorityHost>,
}

impl AzureKeyVaultVariablesProviderOpts {
pub fn build_provider(&self) -> VariablesProvider {
Box::new(AzureKeyVaultProvider::new(
&self.client_id,
&self.client_secret,
&self.tenant_id,
pub fn build_provider(&self) -> Result<VariablesProvider> {
let auth_config_runtime_vars = [&self.client_id, &self.tenant_id, &self.client_secret];
let any_some = auth_config_runtime_vars.iter().any(|&var| var.is_some());
let any_none = auth_config_runtime_vars.iter().any(|&var| var.is_none());

if any_none && any_some {
// some of the service principal auth options were specified, but not enough to authenticate.
return Err(anyhow!("The current runtime config specifies some but not all of the Azure KeyVault 'client_id', 'client_secret', and 'tenant_id' values. Provide the missing values to authenticate to Azure KeyVault with the given service principal, or remove all these values to authenticate using ambient authentication (e.g. env vars, Azure CLI, Managed Identity, Workload Identity)."));
}

let auth_options = if any_some {
// all the service principal auth options were specified in the runtime config
AzureKeyVaultAuthOptions::RuntimeConfigValues(AzureKeyVaultRuntimeConfigOptions::new(
self.client_id.clone().unwrap(),
self.client_secret.clone().unwrap(),
self.tenant_id.clone().unwrap(),
self.authority_host,
))
} else {
AzureKeyVaultAuthOptions::Environmental
};

Ok(Box::new(AzureKeyVaultProvider::new(
&self.vault_url,
self.authority_host,
))
auth_options,
)?))
}
}
6 changes: 3 additions & 3 deletions crates/variables/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ tokio = { version = "1", features = ["rt-multi-thread"] }
vaultrs = "0.6.2"
serde = "1.0.188"
tracing = { workspace = true }
azure_security_keyvault = "0.20.0"
azure_core = "0.20.0"
azure_identity = "0.20.0"
azure_security_keyvault = { git = "https://github.com/azure/azure-sdk-for-rust.git", rev = "8c4caa251c3903d5eae848b41bb1d02a4d65231c" }
azure_core = { git = "https://github.com/azure/azure-sdk-for-rust.git", rev = "8c4caa251c3903d5eae848b41bb1d02a4d65231c" }
azure_identity = { git = "https://github.com/azure/azure-sdk-for-rust.git", rev = "8c4caa251c3903d5eae848b41bb1d02a4d65231c" }

[dev-dependencies]
toml = "0.5"
Expand Down
Loading

0 comments on commit a467a52

Please sign in to comment.