Skip to content
Closed
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
32 changes: 21 additions & 11 deletions azure/azure_kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const (
AzureClientID = "AZURE_CLIENT_ID"
// AzureClientSecret of service principal account
AzureClientSecret = "AZURE_CLIENT_SECRET"
// AzureClientCertPath is path of a client certificate of service principal account
AzureClientCertPath = "AZURE_CLIENT_CERT_PATH"
// AzureClientCertPassword is the password of the client certificate of service principal account
AzureClientCertPassword = "AZURE_CIENT_CERT_PASSWORD"
// AzureEnviornment to connect
AzureEnviornment = "AZURE_ENVIRONMENT"
// AzureVaultURI of azure key vault
Expand All @@ -34,14 +38,15 @@ const (
)

var (
ErrAzureTenantIDNotSet = errors.New("AZURE_TENANT_ID not set.")
ErrAzureClientIDNotSet = errors.New("AZURE_CLIENT_ID not set.")
ErrAzureSecretIDNotSet = errors.New("AZURE_SECRET_ID not set.")
ErrAzureVaultURLNotSet = errors.New("AZURE_VAULT_URL not set.")
ErrAzureEnvironmentNotset = errors.New("AZURE_ENVIRONMENT not set.")
ErrAzureConfigMissing = errors.New("AzureConfig is not provided")
ErrAzureAuthentication = errors.New("Azure authentication failed")
ErrInvalidSecretResp = errors.New("Secret Data received from secrets provider is either empty/invalid")
ErrAzureTenantIDNotSet = errors.New("AZURE_TENANT_ID not set.")
ErrAzureClientIDNotSet = errors.New("AZURE_CLIENT_ID not set.")
ErrAzureSecretIDNotSet = errors.New("AZURE_SECRET_ID not set.")
ErrAzureClientAuthMethodNotSet = errors.New("AZURE_SECRET_ID or AZURE_CLIENT_CERT_PATH not set.")
ErrAzureVaultURLNotSet = errors.New("AZURE_VAULT_URL not set.")
ErrAzureEnvironmentNotset = errors.New("AZURE_ENVIRONMENT not set.")
ErrAzureConfigMissing = errors.New("AzureConfig is not provided")
ErrAzureAuthentication = errors.New("Azure authentication failed")
ErrInvalidSecretResp = errors.New("Secret Data received from secrets provider is either empty/invalid")
)

type azureSecrets struct {
Expand All @@ -61,10 +66,15 @@ func New(
if clientID == "" {
return nil, ErrAzureClientIDNotSet
}

secretID := getAzureKVParams(secretConfig, AzureClientSecret)
if secretID == "" {
return nil, ErrAzureSecretIDNotSet
clientCertPath := getAzureKVParams(secretConfig, AzureClientCertPath)

if secretID == "" && clientCertPath == "" {
return nil, ErrAzureClientAuthMethodNotSet
}
clientCertPassword := getAzureKVParams(secretConfig, AzureClientCertPassword)

envName := getAzureKVParams(secretConfig, AzureEnviornment)
if envName == "" {
// we set back to default AzurePublicCloud
Expand All @@ -75,7 +85,7 @@ func New(
return nil, ErrAzureVaultURLNotSet
}

client, err := getAzureVaultClient(clientID, secretID, tenantID, envName)
client, err := getAzureVaultClient(clientID, secretID, clientCertPath, clientCertPassword, tenantID, envName)
if err != nil {
return nil, err
}
Expand Down
47 changes: 42 additions & 5 deletions azure/azure_kv_helper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package azure

import (
"crypto/rsa"
"crypto/x509"
"fmt"
"net/url"
"os"
"strings"
Expand All @@ -9,6 +12,7 @@ import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
"golang.org/x/crypto/pkcs12"
)

func getAzureKVParams(secretConfig map[string]interface{}, name string) string {
Expand All @@ -19,7 +23,7 @@ func getAzureKVParams(secretConfig map[string]interface{}, name string) string {
}
}

func getAzureVaultClient(clientID, secretID, tenantID, envName string) (keyvault.BaseClient, error) {
func getAzureVaultClient(clientID, secretID, certPath, certPassword, tenantID, envName string) (keyvault.BaseClient, error) {
var environment *azure.Environment
alternateEndpoint, _ := url.Parse(
"https://login.windows.net/" + tenantID + "/oauth2/token")
Expand All @@ -37,11 +41,44 @@ func getAzureVaultClient(clientID, secretID, tenantID, envName string) (keyvault
}
oauthconfig.AuthorizeEndpoint = *alternateEndpoint

token, err := adal.NewServicePrincipalToken(
*oauthconfig, clientID, secretID, strings.TrimSuffix(environment.KeyVaultEndpoint, "/"))
if err != nil {
return keyClient, err
var token *adal.ServicePrincipalToken
if secretID != "" {
token, err = adal.NewServicePrincipalToken(
*oauthconfig, clientID, secretID, strings.TrimSuffix(environment.KeyVaultEndpoint, "/"))
if err != nil {
return keyClient, err
}
} else if certPath != "" {
certData, err := os.ReadFile(certPath)
if err != nil {
return keyClient, fmt.Errorf("reading the client certificate from file %s: %v", certPath, err)
}
certificate, privateKey, err := decodePkcs12(certData, certPassword)
if err != nil {
return keyClient, fmt.Errorf("failed to decode the client certificate: %v", err)
}
token, err = adal.NewServicePrincipalTokenFromCertificate(
*oauthconfig, clientID, certificate, privateKey, strings.TrimSuffix(environment.KeyVaultEndpoint, "/"))
if err != nil {
return keyClient, err
}
}

keyClient.Authorizer = autorest.NewBearerAuthorizer(token)
return keyClient, nil
}

// decodePkcs12 decodes a PKCS#12 client certificate by extracting the public certificate and
// the private RSA key
func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
privateKey, certificate, err := pkcs12.Decode(pkcs, password)
if err != nil {
return nil, nil, fmt.Errorf("decoding the PKCS#12 client certificate: %v", err)
}
rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey)
if !isRsaKey {
return nil, nil, fmt.Errorf("PKCS#12 certificate must contain a RSA private key")
}

return certificate, rsaPrivateKey, nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ require (
github.com/onsi/gomega v1.24.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.8.1 // indirect
golang.org/x/crypto v0.6.0
golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401
golang.org/x/time v0.3.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
Expand Down