Skip to content

Commit 698ba84

Browse files
committed
support using certs for authenticating azure kv
Signed-off-by: sp98 <sapillai@redhat.com>
1 parent 5f4b25c commit 698ba84

File tree

3 files changed

+52
-9
lines changed

3 files changed

+52
-9
lines changed

azure/azure_kv.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const (
2121
AzureClientID = "AZURE_CLIENT_ID"
2222
// AzureClientSecret of service principal account
2323
AzureClientSecret = "AZURE_CLIENT_SECRET"
24+
// AzureClientCertPath is path of a client certificate of service principal account
25+
AzureClientCertPath = "AZURE_CLIENT_CERT_PATH"
26+
// AzureClientCertPassword is the password of the client certificate of service principal account
27+
AzureClientCertPassword = "AZURE_CIENT_CERT_PASSWORD"
2428
// AzureEnviornment to connect
2529
AzureEnviornment = "AZURE_ENVIRONMENT"
2630
// AzureVaultURI of azure key vault
@@ -61,10 +65,11 @@ func New(
6165
if clientID == "" {
6266
return nil, ErrAzureClientIDNotSet
6367
}
68+
6469
secretID := getAzureKVParams(secretConfig, AzureClientSecret)
65-
if secretID == "" {
66-
return nil, ErrAzureSecretIDNotSet
67-
}
70+
clientCertPath := getAzureKVParams(secretConfig, AzureClientCertPath)
71+
clientCertPassword := getAzureKVParams(secretConfig, AzureClientCertPassword)
72+
6873
envName := getAzureKVParams(secretConfig, AzureEnviornment)
6974
if envName == "" {
7075
// we set back to default AzurePublicCloud
@@ -75,7 +80,7 @@ func New(
7580
return nil, ErrAzureVaultURLNotSet
7681
}
7782

78-
client, err := getAzureVaultClient(clientID, secretID, tenantID, envName)
83+
client, err := getAzureVaultClient(clientID, secretID, clientCertPath, clientCertPassword, tenantID, envName)
7984
if err != nil {
8085
return nil, err
8186
}

azure/azure_kv_helper.go

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package azure
22

33
import (
4+
"crypto/rsa"
5+
"crypto/x509"
6+
"fmt"
47
"net/url"
58
"os"
69
"strings"
@@ -9,6 +12,7 @@ import (
912
"github.com/Azure/go-autorest/autorest"
1013
"github.com/Azure/go-autorest/autorest/adal"
1114
"github.com/Azure/go-autorest/autorest/azure"
15+
"golang.org/x/crypto/pkcs12"
1216
)
1317

1418
func getAzureKVParams(secretConfig map[string]interface{}, name string) string {
@@ -19,7 +23,7 @@ func getAzureKVParams(secretConfig map[string]interface{}, name string) string {
1923
}
2024
}
2125

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

40-
token, err := adal.NewServicePrincipalToken(
41-
*oauthconfig, clientID, secretID, strings.TrimSuffix(environment.KeyVaultEndpoint, "/"))
42-
if err != nil {
43-
return keyClient, err
44+
var token *adal.ServicePrincipalToken
45+
if secretID != "" {
46+
token, err = adal.NewServicePrincipalToken(
47+
*oauthconfig, clientID, secretID, strings.TrimSuffix(environment.KeyVaultEndpoint, "/"))
48+
if err != nil {
49+
return keyClient, err
50+
}
51+
} else if certPath != "" && certPassword != "" {
52+
certData, err := os.ReadFile(certPath)
53+
if err != nil {
54+
return keyClient, fmt.Errorf("reading the client certificate from file %s: %v", certPath, err)
55+
}
56+
certificate, privateKey, err := decodePkcs12(certData, certPassword)
57+
if err != nil {
58+
return keyClient, fmt.Errorf("decoding the client certificate: %v", err)
59+
}
60+
token, err = adal.NewServicePrincipalTokenFromCertificate(
61+
*oauthconfig, clientID, certificate, privateKey, strings.TrimSuffix(environment.KeyVaultEndpoint, "/"))
62+
if err != nil {
63+
return keyClient, err
64+
}
4465
}
66+
4567
keyClient.Authorizer = autorest.NewBearerAuthorizer(token)
4668
return keyClient, nil
4769
}
70+
71+
// decodePkcs12 decodes a PKCS#12 client certificate by extracting the public certificate and
72+
// the private RSA key
73+
func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
74+
privateKey, certificate, err := pkcs12.Decode(pkcs, password)
75+
if err != nil {
76+
return nil, nil, fmt.Errorf("decoding the PKCS#12 client certificate: %v", err)
77+
}
78+
rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey)
79+
if !isRsaKey {
80+
return nil, nil, fmt.Errorf("PKCS#12 certificate must contain a RSA private key")
81+
}
82+
83+
return certificate, rsaPrivateKey, nil
84+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ require (
4242
github.com/onsi/gomega v1.24.1 // indirect
4343
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
4444
github.com/rogpeppe/go-internal v1.8.1 // indirect
45+
golang.org/x/crypto v0.6.0
4546
golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401
4647
golang.org/x/time v0.3.0 // indirect
4748
google.golang.org/protobuf v1.28.1 // indirect

0 commit comments

Comments
 (0)