feat: Azure Key Vault support
This commit is contained in:
@@ -13,6 +13,7 @@ use std::panic::PanicHookInfo;
|
||||
|
||||
use crate::cli::wrap_and_run_command;
|
||||
use std::panic;
|
||||
use std::process::exit;
|
||||
|
||||
mod cli;
|
||||
mod command;
|
||||
@@ -133,6 +134,11 @@ async fn main() -> Result<()> {
|
||||
println!("{}", get_config_file_path()?.display());
|
||||
return Ok(());
|
||||
}
|
||||
if cli.command.is_none() {
|
||||
Cli::command().print_help()?;
|
||||
println!();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let config = load_config()?;
|
||||
let mut provider_config = config.extract_provider_config(cli.provider.clone())?;
|
||||
|
||||
+8
-4
@@ -150,10 +150,14 @@ impl ProviderConfig {
|
||||
debug!("Using AWS Secrets Manager provider");
|
||||
provider_def
|
||||
}
|
||||
SupportedProvider::GcpSecretManager { provider_def } => {
|
||||
debug!("Using GCP Secret Manager provider");
|
||||
provider_def
|
||||
}
|
||||
SupportedProvider::GcpSecretManager { provider_def } => {
|
||||
debug!("Using GCP Secret Manager provider");
|
||||
provider_def
|
||||
}
|
||||
SupportedProvider::AzureKeyVault { provider_def } => {
|
||||
debug!("Using Azure Key Vault provider");
|
||||
provider_def
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use validator::Validate;
|
||||
/// Example
|
||||
/// ```no_run
|
||||
/// use gman::providers::{SecretProvider, SupportedProvider};
|
||||
/// use gman::config::{Config, ProviderConfig};
|
||||
/// use gman::config::Config;
|
||||
/// use gman::providers::aws_secrets_manager::AwsSecretsManagerProvider;
|
||||
///
|
||||
/// let provider = AwsSecretsManagerProvider {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
use crate::providers::SecretProvider;
|
||||
use anyhow::{Context, Result};
|
||||
use azure_identity::DefaultAzureCredential;
|
||||
use azure_security_keyvault_secrets::models::SetSecretParameters;
|
||||
use azure_security_keyvault_secrets::{ResourceExt, SecretClient};
|
||||
use futures::TryStreamExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use validator::Validate;
|
||||
|
||||
#[skip_serializing_none]
|
||||
/// Configuration for Azure Key Vault provider
|
||||
/// See [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/)
|
||||
/// for more information.
|
||||
///
|
||||
/// This provider stores secrets in Azure Key Vault. It requires
|
||||
/// a vault name to be specified.
|
||||
///
|
||||
/// Example
|
||||
/// ```no_run
|
||||
/// use gman::providers::{SecretProvider, SupportedProvider};
|
||||
/// use gman::config::{Config, ProviderConfig};
|
||||
/// use gman::providers::azure_key_vault::AzureKeyVaultProvider;
|
||||
///
|
||||
/// let provider = AzureKeyVaultProvider {
|
||||
/// vault_name: Some("my-vault-name".to_string()),
|
||||
/// };
|
||||
/// let _ = provider.set_secret("MY_SECRET", "value");
|
||||
#[derive(Debug, Clone, Validate, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct AzureKeyVaultProvider {
|
||||
#[validate(required)]
|
||||
pub vault_name: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl SecretProvider for AzureKeyVaultProvider {
|
||||
fn name(&self) -> &'static str {
|
||||
"AzureKeyVaultProvider"
|
||||
}
|
||||
|
||||
async fn get_secret(&self, key: &str) -> Result<String> {
|
||||
let body = self
|
||||
.get_client()?
|
||||
.get_secret(key, "", None)
|
||||
.await?
|
||||
.into_body()
|
||||
.await?;
|
||||
|
||||
body.value
|
||||
.with_context(|| format!("Secret '{}' not found", key))
|
||||
}
|
||||
|
||||
async fn set_secret(&self, key: &str, value: &str) -> Result<()> {
|
||||
let params = SetSecretParameters {
|
||||
value: Some(value.to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self.get_client()?
|
||||
.set_secret(key, params.try_into()?, None)
|
||||
.await?
|
||||
.into_body()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_secret(&self, key: &str, value: &str) -> Result<()> {
|
||||
self.set_secret(key, value).await
|
||||
}
|
||||
|
||||
async fn delete_secret(&self, key: &str) -> Result<()> {
|
||||
self.get_client()?.delete_secret(key, None).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_secrets(&self) -> Result<Vec<String>> {
|
||||
let mut pager = self
|
||||
.get_client()?
|
||||
.list_secret_properties(None)?
|
||||
.into_stream();
|
||||
let mut secrets = Vec::new();
|
||||
while let Some(props) = pager.try_next().await? {
|
||||
let name = props.resource_id()?.name;
|
||||
secrets.push(name);
|
||||
}
|
||||
|
||||
Ok(secrets)
|
||||
}
|
||||
}
|
||||
|
||||
impl AzureKeyVaultProvider {
|
||||
fn get_client(&self) -> Result<SecretClient> {
|
||||
let credential = DefaultAzureCredential::new()?;
|
||||
let client = SecretClient::new(
|
||||
format!(
|
||||
"https://{}.vault.azure.net",
|
||||
self.vault_name.as_ref().unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
credential,
|
||||
None,
|
||||
)?;
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ use validator::Validate;
|
||||
/// ```no_run
|
||||
/// use gman::providers::local::LocalProvider;
|
||||
/// use gman::providers::{SecretProvider, SupportedProvider};
|
||||
/// use gman::config::{Config, ProviderConfig};
|
||||
/// use gman::config::Config;
|
||||
///
|
||||
/// let provider = LocalProvider::default();
|
||||
/// // Will prompt for a password when reading/writing secrets unless a
|
||||
|
||||
+13
-5
@@ -3,17 +3,19 @@
|
||||
//! Implementations provide storage/backends for secrets and a common
|
||||
//! interface used by the CLI.
|
||||
pub mod aws_secrets_manager;
|
||||
pub mod azure_key_vault;
|
||||
pub mod gcp_secret_manager;
|
||||
mod git_sync;
|
||||
pub mod local;
|
||||
|
||||
use std::fmt;
|
||||
use crate::providers::local::LocalProvider;
|
||||
use anyhow::{Result, anyhow};
|
||||
use aws_secrets_manager::AwsSecretsManagerProvider;
|
||||
use gcp_secret_manager::GcpSecretManagerProvider;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use validator::{Validate, ValidationErrors};
|
||||
use aws_secrets_manager::AwsSecretsManagerProvider;
|
||||
use gcp_secret_manager::GcpSecretManagerProvider;
|
||||
|
||||
/// A secret storage backend capable of CRUD, with optional
|
||||
/// update, listing, and sync support.
|
||||
@@ -59,6 +61,10 @@ pub enum SupportedProvider {
|
||||
#[serde(flatten)]
|
||||
provider_def: GcpSecretManagerProvider,
|
||||
},
|
||||
AzureKeyVault {
|
||||
#[serde(flatten)]
|
||||
provider_def: azure_key_vault::AzureKeyVaultProvider,
|
||||
},
|
||||
}
|
||||
|
||||
impl Validate for SupportedProvider {
|
||||
@@ -66,7 +72,8 @@ impl Validate for SupportedProvider {
|
||||
match self {
|
||||
SupportedProvider::Local { provider_def } => provider_def.validate(),
|
||||
SupportedProvider::AwsSecretsManager { provider_def } => provider_def.validate(),
|
||||
SupportedProvider::GcpSecretManager { provider_def } => provider_def.validate(),
|
||||
SupportedProvider::GcpSecretManager { provider_def } => provider_def.validate(),
|
||||
SupportedProvider::AzureKeyVault { provider_def } => provider_def.validate(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,11 +87,12 @@ impl Default for SupportedProvider {
|
||||
}
|
||||
|
||||
impl Display for SupportedProvider {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SupportedProvider::Local { .. } => write!(f, "local"),
|
||||
SupportedProvider::AwsSecretsManager { .. } => write!(f, "aws_secrets_manager"),
|
||||
SupportedProvider::GcpSecretManager { .. } => write!(f, "gcp_secret_manager"),
|
||||
SupportedProvider::GcpSecretManager { .. } => write!(f, "gcp_secret_manager"),
|
||||
SupportedProvider::AzureKeyVault { .. } => write!(f, "azure_key_vault"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user