Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions docs/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,11 @@ data:
password-old: <path:projects/12345678987/secrets/test-secret#password#another-version-id>
```

### AZURE Key Vault
### Azure Key Vault

##### Azure Authentication
Refer to the [Use environment-based authentication](https://docs.microsoft.com/en-us/azure/developer/go/azure-sdk-authorization#use-environment-based-authentication) in the Azure SDK for Go.

The Azure SDK for Go leverages a credential chain that supports many authentication methods. See [DefaultAzureCredential overview](https://learn.microsoft.com/en-us/azure/developer/go/sdk/authentication/credential-chains#defaultazurecredential-overview) for the supported options.

Any secrets that are disabled in the key vault will be skipped if found.

Expand All @@ -446,10 +447,17 @@ For Azure, `path` is the unique name of your key vault.
(fewer HTTP calls and therefore lower chance of hitting rate limit) than generic placeholders.

These are the parameters for Azure:

```
AVP_TYPE: azurekeyvault
```

Optionally, you may specify the cloud name. Valid values are `AzureCloud` (default), `AzureChinaCloud`, and `AzureUSGovernment`:

```
AVP_AZURE_CLOUD_NAME: AzureCloud
```

##### Examples

###### Path Annotation
Expand Down
24 changes: 14 additions & 10 deletions pkg/backends/azurekeyvault.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ package backends
import (
"context"
"fmt"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets"
"github.com/argoproj-labs/argocd-vault-plugin/pkg/utils"
"time"
)

// AzureKeyVault is a struct for working with an Azure Key Vault backend
type AzureKeyVault struct {
Credential azcore.TokenCredential
ClientBuilder func(vaultURL string, credential azcore.TokenCredential, options *azsecrets.ClientOptions) (AzSecretsClient, error)
Credential azcore.TokenCredential
KeyVaultDNSSuffix string
SecretClientOptions azsecrets.ClientOptions
ClientBuilder func(vaultURL string, credential azcore.TokenCredential, options *azsecrets.ClientOptions) (AzSecretsClient, error)
}

type AzSecretsClient interface {
Expand All @@ -22,9 +25,11 @@ type AzSecretsClient interface {
}

// NewAzureKeyVaultBackend initializes a new Azure Key Vault backend
func NewAzureKeyVaultBackend(credential azcore.TokenCredential, clientBuilder func(vaultURL string, credential azcore.TokenCredential, options *azsecrets.ClientOptions) (*azsecrets.Client, error)) *AzureKeyVault {
func NewAzureKeyVaultBackend(credential azcore.TokenCredential, clientBuilder func(vaultURL string, credential azcore.TokenCredential, options *azsecrets.ClientOptions) (*azsecrets.Client, error), secretClientOptions azsecrets.ClientOptions, dnsSuffix string) *AzureKeyVault {
return &AzureKeyVault{
Credential: credential,
Credential: credential,
KeyVaultDNSSuffix: dnsSuffix,
SecretClientOptions: secretClientOptions,
ClientBuilder: func(vaultURL string, credential azcore.TokenCredential, options *azsecrets.ClientOptions) (AzSecretsClient, error) {
return clientBuilder(vaultURL, credential, options)
},
Expand All @@ -40,14 +45,14 @@ func (a *AzureKeyVault) Login() error {
// For Azure Key Vault, `kvpath` is the unique name of your vault
// For Azure use the version here not make really sens as each secret have a different version but let support it
func (a *AzureKeyVault) GetSecrets(kvpath string, version string, _ map[string]string) (map[string]interface{}, error) {
kvpath = fmt.Sprintf("https://%s.vault.azure.net", kvpath)
kvpath = fmt.Sprintf("https://%s.%s", kvpath, a.KeyVaultDNSSuffix)

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

verboseOptionalVersion("Azure Key Vault list all secrets from vault %s", version, kvpath)

client, err := a.ClientBuilder(kvpath, a.Credential, nil)
client, err := a.ClientBuilder(kvpath, a.Credential, &a.SecretClientOptions)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -97,10 +102,9 @@ func (a *AzureKeyVault) GetIndividualSecret(kvpath, secret, version string, anno
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

kvpath = fmt.Sprintf("https://%s.%s", kvpath, a.KeyVaultDNSSuffix)
verboseOptionalVersion("Azure Key Vault getting individual secret %s from vault %s", version, secret, kvpath)

kvpath = fmt.Sprintf("https://%s.vault.azure.net", kvpath)
client, err := a.ClientBuilder(kvpath, a.Credential, nil)
client, err := a.ClientBuilder(kvpath, a.Credential, &a.SecretClientOptions)
if err != nil {
return nil, err
}
Expand Down
14 changes: 9 additions & 5 deletions pkg/backends/azurekeyvault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package backends_test
import (
"context"
"errors"
"reflect"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets"
"github.com/argoproj-labs/argocd-vault-plugin/pkg/backends"
"reflect"
"testing"
)

const secretNamePrefix = "https://myvaultname.vault.azure.net/keys/"
Expand Down Expand Up @@ -37,7 +38,8 @@ func makeResponse(id azsecrets.ID, value string, err error) (azsecrets.GetSecret

func newAzureKeyVaultBackendMock(simulateError string) *backends.AzureKeyVault {
return &backends.AzureKeyVault{
Credential: nil,
Credential: nil,
KeyVaultDNSSuffix: "vault.azure.net",
ClientBuilder: func(vaultURL string, credential azcore.TokenCredential, options *azsecrets.ClientOptions) (backends.AzSecretsClient, error) {
return &mockClientProxy{
simulateError: simulateError,
Expand Down Expand Up @@ -142,7 +144,8 @@ func TestAzGetSecretNotExist(t *testing.T) {

func TestAzGetSecretBuilderError(t *testing.T) {
var keyVault = &backends.AzureKeyVault{
Credential: nil,
Credential: nil,
KeyVaultDNSSuffix: "vault.azure.net",
ClientBuilder: func(vaultURL string, credential azcore.TokenCredential, options *azsecrets.ClientOptions) (backends.AzSecretsClient, error) {
return nil, errors.New("boom")
},
Expand Down Expand Up @@ -199,7 +202,8 @@ func TestAzGetSecretsWithErrorOnGetSecret(t *testing.T) {

func TestAzGetSecretsBuilderError(t *testing.T) {
var keyVault = &backends.AzureKeyVault{
Credential: nil,
Credential: nil,
KeyVaultDNSSuffix: "vault.azure.net",
ClientBuilder: func(vaultURL string, credential azcore.TokenCredential, options *azsecrets.ClientOptions) (backends.AzSecretsClient, error) {
return nil, errors.New("boom")
},
Expand Down
32 changes: 29 additions & 3 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import (
"bytes"
"context"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets"
"os"
"strconv"
"strings"
"time"

gcpsm "cloud.google.com/go/secretmanager/apiv1"
"github.com/1Password/connect-sdk-go/connect"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets"
delineasecretserver "github.com/DelineaXPM/tss-sdk-go/v2/server"
"github.com/IBM/go-sdk-core/v5/core"
ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv2"
Expand Down Expand Up @@ -190,12 +192,36 @@ func New(v *viper.Viper, co *Options) (*Config, error) {
}
case types.AzureKeyVaultbackend:
{
cred, err := azidentity.NewDefaultAzureCredential(nil)
cloudName := v.GetString(types.EnvAvpAzureCloud)
var cloudCfg cloud.Configuration
var keyVaultDnsSuffix string
switch cloudName {
case "", types.AzurePublicCloudName:
cloudCfg = cloud.AzurePublic
keyVaultDnsSuffix = types.AzurePublicCloudKeyVaultDnsSuffix
utils.VerboseToStdErr("using Azure Public Cloud for Key Vault")
case types.AzureChinaCloudName:
cloudCfg = cloud.AzureChina
keyVaultDnsSuffix = types.AzureChinaCloudKeyVaultDnsSuffix
utils.VerboseToStdErr("using Azure China Cloud for Key Vault")
case types.AzureUSGovernmentCloudName:
cloudCfg = cloud.AzureGovernment
keyVaultDnsSuffix = types.AzureUSGovernmentKeyVaultDnsSuffix
utils.VerboseToStdErr("using Azure US Government Cloud for Key Vault")
default:
return nil, fmt.Errorf("unsupported Azure cloud name %s for Key Vault", cloudName)
}
coreClientOptions := azcore.ClientOptions{
Cloud: cloudCfg,
}
cred, err := azidentity.NewDefaultAzureCredential(&azidentity.DefaultAzureCredentialOptions{
ClientOptions: coreClientOptions,
})
if err != nil {
return nil, err
}

backend = backends.NewAzureKeyVaultBackend(cred, azsecrets.NewClient)
backend = backends.NewAzureKeyVaultBackend(cred, azsecrets.NewClient, azsecrets.ClientOptions{ClientOptions: coreClientOptions}, keyVaultDnsSuffix)
}
case types.Sopsbackend:
{
Expand Down
30 changes: 30 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,36 @@ func TestNewConfig(t *testing.T) {
},
"*backends.AzureKeyVault",
},
{
map[string]interface{}{
"AVP_TYPE": "azurekeyvault",
"AVP_AZURE_CLOUD_NAME": "AzureCloud",
"AZURE_TENANT_ID": "test",
"AZURE_CLIENT_ID": "test",
"AZURE_CLIENT_SECRET": "test",
},
"*backends.AzureKeyVault",
},
{
map[string]interface{}{
"AVP_TYPE": "azurekeyvault",
"AVP_AZURE_CLOUD_NAME": "AzureUSGovernment",
"AZURE_TENANT_ID": "test",
"AZURE_CLIENT_ID": "test",
"AZURE_CLIENT_SECRET": "test",
},
"*backends.AzureKeyVault",
},
{
map[string]interface{}{
"AVP_TYPE": "azurekeyvault",
"AVP_AZURE_CLOUD_NAME": "AzureChinaCloud",
"AZURE_TENANT_ID": "test",
"AZURE_CLIENT_ID": "test",
"AZURE_CLIENT_SECRET": "test",
},
"*backends.AzureKeyVault",
},
{
map[string]interface{}{
"AVP_TYPE": "yandexcloudlockbox",
Expand Down
57 changes: 32 additions & 25 deletions pkg/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const (
EnvArgoCDPrefix = "ARGOCD_ENV"

// Environment Variable Constants
EnvAvpAzureCloud = "AVP_AZURE_CLOUD_NAME"
EnvAvpType = "AVP_TYPE"
EnvAvpRoleID = "AVP_ROLE_ID"
EnvAvpSecretID = "AVP_SECRET_ID"
Expand Down Expand Up @@ -33,31 +34,37 @@ const (
EnvAvpDelineaDomain = "AVP_DELINEA_DOMAIN"

// Backend and Auth Constants
VaultBackend = "vault"
IBMSecretsManagerbackend = "ibmsecretsmanager"
AWSSecretsManagerbackend = "awssecretsmanager"
GCPSecretManagerbackend = "gcpsecretmanager"
AzureKeyVaultbackend = "azurekeyvault"
Sopsbackend = "sops"
YandexCloudLockboxbackend = "yandexcloudlockbox"
DelineaSecretServerbackend = "delineasecretserver"
OnePasswordConnect = "1passwordconnect"
KeeperSecretsManagerBackend = "keepersecretsmanager"
KubernetesSecretBackend = "kubernetessecret"
K8sAuth = "k8s"
ApproleAuth = "approle"
GithubAuth = "github"
TokenAuth = "token"
UserPass = "userpass"
IAMAuth = "iam"
AwsDefaultRegion = "us-east-2"
GCPCurrentSecretVersion = "latest"
IBMMaxRetries = 3
IBMRetryIntervalSeconds = 20
IBMMaxPerPage = 200
IBMIAMCredentialsType = "iam_credentials"
IBMImportedCertType = "imported_cert"
IBMPublicCertType = "public_cert"
VaultBackend = "vault"
IBMSecretsManagerbackend = "ibmsecretsmanager"
AWSSecretsManagerbackend = "awssecretsmanager"
GCPSecretManagerbackend = "gcpsecretmanager"
AzureKeyVaultbackend = "azurekeyvault"
AzurePublicCloudName = "AzureCloud"
AzurePublicCloudKeyVaultDnsSuffix = "vault.azure.net"
AzureChinaCloudName = "AzureChinaCloud"
AzureChinaCloudKeyVaultDnsSuffix = "vault.azure.cn"
AzureUSGovernmentCloudName = "AzureUSGovernment"
AzureUSGovernmentKeyVaultDnsSuffix = "vault.usgovcloudapi.net"
Sopsbackend = "sops"
YandexCloudLockboxbackend = "yandexcloudlockbox"
DelineaSecretServerbackend = "delineasecretserver"
OnePasswordConnect = "1passwordconnect"
KeeperSecretsManagerBackend = "keepersecretsmanager"
KubernetesSecretBackend = "kubernetessecret"
K8sAuth = "k8s"
ApproleAuth = "approle"
GithubAuth = "github"
TokenAuth = "token"
UserPass = "userpass"
IAMAuth = "iam"
AwsDefaultRegion = "us-east-2"
GCPCurrentSecretVersion = "latest"
IBMMaxRetries = 3
IBMRetryIntervalSeconds = 20
IBMMaxPerPage = 200
IBMIAMCredentialsType = "iam_credentials"
IBMImportedCertType = "imported_cert"
IBMPublicCertType = "public_cert"

// Supported annotations
AVPPathAnnotation = "avp.kubernetes.io/path"
Expand Down