Skip to content

Commit 5a08606

Browse files
committed
migrate from adal to azidentity
adal is is out of support since March 31, 2023. This PR migrates from adal to azidentity for azure key vault Signed-off-by: sp98 <sapillai@redhat.com>
1 parent 5f4b25c commit 5a08606

6 files changed

Lines changed: 182 additions & 85 deletions

File tree

azure/azure_kv.go

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,31 @@ import (
44
"context"
55
"encoding/json"
66
"errors"
7+
"strings"
78
"time"
89

9-
"github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
10-
"github.com/Azure/go-autorest/autorest/to"
10+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
11+
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
12+
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
1113
"github.com/libopenstorage/secrets"
1214
"github.com/portworx/sched-ops/task"
1315
)
1416

1517
const (
16-
Name = secrets.TypeAzure
17-
AzureCloud = "AzurePublicCloud"
18+
Name = secrets.TypeAzure
19+
AzureCloud = "AzurePublicCloud"
20+
AzureGovernment = "AzureUSGovernment"
21+
AzureChina = "AzureChinaCloud"
1822
// AzureTenantID for Azure Active Directory
1923
AzureTenantID = "AZURE_TENANT_ID"
2024
// AzureClientID of service principal account
2125
AzureClientID = "AZURE_CLIENT_ID"
2226
// AzureClientSecret of service principal account
2327
AzureClientSecret = "AZURE_CLIENT_SECRET"
28+
// AzureClientCertPath is path of the client certificate
29+
AzureClientCertPath = "AZURE_CLIENT_CERT_PATH"
30+
// AzureClientCertPassword is the password of the private key
31+
AzureClientCertPassword = "AZURE_CIENT_CERT_PASSWORD"
2432
// AzureEnviornment to connect
2533
AzureEnviornment = "AZURE_ENVIRONMENT"
2634
// AzureVaultURI of azure key vault
@@ -37,6 +45,7 @@ var (
3745
ErrAzureTenantIDNotSet = errors.New("AZURE_TENANT_ID not set.")
3846
ErrAzureClientIDNotSet = errors.New("AZURE_CLIENT_ID not set.")
3947
ErrAzureSecretIDNotSet = errors.New("AZURE_SECRET_ID not set.")
48+
ErrAzureAuthMedhodNotSet = errors.New("AZURE_SECRET_ID or AZURE_CLIENT_CERT_PATH not set")
4049
ErrAzureVaultURLNotSet = errors.New("AZURE_VAULT_URL not set.")
4150
ErrAzureEnvironmentNotset = errors.New("AZURE_ENVIRONMENT not set.")
4251
ErrAzureConfigMissing = errors.New("AzureConfig is not provided")
@@ -45,7 +54,7 @@ var (
4554
)
4655

4756
type azureSecrets struct {
48-
kv keyvault.BaseClient
57+
kv azsecrets.Client
4958
baseURL string
5059
}
5160

@@ -62,26 +71,34 @@ func New(
6271
return nil, ErrAzureClientIDNotSet
6372
}
6473
secretID := getAzureKVParams(secretConfig, AzureClientSecret)
65-
if secretID == "" {
66-
return nil, ErrAzureSecretIDNotSet
67-
}
68-
envName := getAzureKVParams(secretConfig, AzureEnviornment)
69-
if envName == "" {
70-
// we set back to default AzurePublicCloud
71-
envName = AzureCloud
72-
}
74+
clientCertPath := getAzureKVParams(secretConfig, AzureClientCertPath)
75+
clientCertPassword := getAzureKVParams(secretConfig, AzureClientCertPassword)
76+
7377
vaultURL := getAzureKVParams(secretConfig, AzureVaultURL)
7478
if vaultURL == "" {
7579
return nil, ErrAzureVaultURLNotSet
7680
}
7781

78-
client, err := getAzureVaultClient(clientID, secretID, tenantID, envName)
79-
if err != nil {
80-
return nil, err
82+
clientOpts := getAzureClientOptions(secretConfig)
83+
84+
var client *azsecrets.Client
85+
var err error
86+
if secretID != "" {
87+
client, err = getAzureVaultClient(clientID, secretID, tenantID, vaultURL, clientOpts)
88+
if err != nil {
89+
return nil, err
90+
}
91+
} else if clientCertPath != "" {
92+
client, err = getAzureVaultClientWithCert(clientID, tenantID, vaultURL, clientCertPath, clientCertPassword, clientOpts)
93+
if err != nil {
94+
return nil, err
95+
}
96+
} else {
97+
return nil, ErrAzureAuthMedhodNotSet
8198
}
8299

83100
return &azureSecrets{
84-
kv: client,
101+
kv: *client,
85102
baseURL: vaultURL,
86103
}, nil
87104
}
@@ -98,8 +115,14 @@ func (az *azureSecrets) GetSecret(
98115
}
99116

100117
t := func() (interface{}, bool, error) {
101-
secretResp, err := az.kv.GetSecret(ctx, az.baseURL, secretID, "")
118+
// passing empty version to always get the latest version of the secret.
119+
secretResp, err := az.kv.GetSecret(ctx, secretID, "", nil)
102120
if err != nil {
121+
// don't retry if the secret key was not found
122+
responseError, ok := err.(*azcore.ResponseError)
123+
if ok && responseError.StatusCode == 404 {
124+
return nil, false, secrets.ErrSecretNotFound
125+
}
103126
return nil, true, err
104127
}
105128
return secretResp, false, nil
@@ -109,7 +132,7 @@ func (az *azureSecrets) GetSecret(
109132
return nil, secrets.NoVersion, err
110133
}
111134

112-
secretResp, ok := resp.(keyvault.SecretBundle)
135+
secretResp, ok := resp.(azsecrets.GetSecretResponse)
113136
if !ok || secretResp.Value == nil {
114137
return nil, secrets.NoVersion, ErrInvalidSecretResp
115138
}
@@ -133,7 +156,7 @@ func (az *azureSecrets) PutSecret(
133156
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
134157
defer cancel()
135158

136-
var secretResp keyvault.SecretBundle
159+
var secretResp azsecrets.SecretBundle
137160
if secretName == "" {
138161
return secrets.NoVersion, secrets.ErrEmptySecretId
139162
}
@@ -146,10 +169,10 @@ func (az *azureSecrets) PutSecret(
146169
return secrets.NoVersion, err
147170
}
148171

172+
valueStr := string(value)
149173
t := func() (interface{}, bool, error) {
150-
secretResp, err = az.kv.SetSecret(ctx, az.baseURL, secretName, keyvault.SecretSetParameters{
151-
Value: to.StringPtr(string(value)),
152-
})
174+
params := azsecrets.SetSecretParameters{Value: &valueStr}
175+
az.kv.SetSecret(ctx, secretName, params, nil)
153176
if err != nil {
154177
return nil, true, err
155178
}
@@ -169,7 +192,7 @@ func (az *azureSecrets) DeleteSecret(
169192
if secretName == "" {
170193
return secrets.ErrEmptySecretId
171194
}
172-
_, err := az.kv.DeleteSecret(ctx, az.baseURL, secretName)
195+
_, err := az.kv.DeleteSecret(ctx, secretName, nil)
173196

174197
return err
175198
}
@@ -213,3 +236,17 @@ func init() {
213236
panic(err.Error())
214237
}
215238
}
239+
240+
func getAzureClientOptions(secretConfig map[string]interface{}) azcore.ClientOptions {
241+
opts := azcore.ClientOptions{}
242+
cloudEnv := getAzureKVParams(secretConfig, AzureEnviornment)
243+
if strings.EqualFold(cloudEnv, AzureGovernment) {
244+
opts.Cloud = cloud.AzureGovernment
245+
} else if strings.EqualFold(cloudEnv, AzureChina) {
246+
opts.Cloud = cloud.AzureChina
247+
} else if cloudEnv == AzureCloud || cloudEnv == "" {
248+
opts.Cloud = cloud.AzurePublic
249+
}
250+
return opts
251+
252+
}

azure/azure_kv_helper.go

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package azure
22

33
import (
4-
"net/url"
4+
"fmt"
55
"os"
6-
"strings"
76

8-
"github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
9-
"github.com/Azure/go-autorest/autorest"
10-
"github.com/Azure/go-autorest/autorest/adal"
11-
"github.com/Azure/go-autorest/autorest/azure"
7+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
8+
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
9+
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
1210
)
1311

1412
func getAzureKVParams(secretConfig map[string]interface{}, name string) string {
@@ -19,29 +17,46 @@ func getAzureKVParams(secretConfig map[string]interface{}, name string) string {
1917
}
2018
}
2119

22-
func getAzureVaultClient(clientID, secretID, tenantID, envName string) (keyvault.BaseClient, error) {
23-
var environment *azure.Environment
24-
alternateEndpoint, _ := url.Parse(
25-
"https://login.windows.net/" + tenantID + "/oauth2/token")
20+
func getAzureVaultClient(clientID, secretID, tenantID, vaultURL string, opts azcore.ClientOptions) (*azsecrets.Client, error) {
21+
cred, err := azidentity.NewClientSecretCredential(tenantID, clientID, secretID, &azidentity.ClientSecretCredentialOptions{ClientOptions: opts})
22+
if err != nil {
23+
return nil, fmt.Errorf("failed to get client secret credentials. %v", err)
24+
}
25+
client, err := azsecrets.NewClient(vaultURL, cred, &azsecrets.ClientOptions{ClientOptions: opts})
26+
if err != nil {
27+
return nil, fmt.Errorf("failed to get client to access azure kv secrets. %v", err)
28+
}
29+
30+
return client, nil
31+
}
32+
33+
func getAzureVaultClientWithCert(clientID, tenantID, vaultURL, certPath, certPassword string, opts azcore.ClientOptions) (*azsecrets.Client, error) {
34+
certData, err := os.ReadFile(certPath)
35+
if err != nil {
36+
return nil, fmt.Errorf("failed read certificate from path %q. %v", certPath, err)
37+
}
2638

27-
keyClient := keyvault.New()
28-
env, err := azure.EnvironmentFromName(envName)
39+
var passphrase []byte
40+
if certPassword == "" {
41+
passphrase = nil
42+
} else {
43+
passphrase = []byte(certPassword)
44+
}
45+
46+
certs, key, err := azidentity.ParseCertificates(certData, passphrase)
2947
if err != nil {
30-
return keyClient, err
48+
return nil, fmt.Errorf("failed load certificate and private key. %v", err)
3149
}
32-
environment = &env
33-
oauthconfig, err := adal.NewOAuthConfig(
34-
environment.ActiveDirectoryEndpoint, tenantID)
50+
51+
cred, err := azidentity.NewClientCertificateCredential(tenantID, clientID, certs, key, &azidentity.ClientCertificateCredentialOptions{ClientOptions: opts})
3552
if err != nil {
36-
return keyClient, err
53+
return nil, fmt.Errorf("failed to construct client certificate credentials. %v", err)
3754
}
38-
oauthconfig.AuthorizeEndpoint = *alternateEndpoint
3955

40-
token, err := adal.NewServicePrincipalToken(
41-
*oauthconfig, clientID, secretID, strings.TrimSuffix(environment.KeyVaultEndpoint, "/"))
56+
client, err := azsecrets.NewClient(vaultURL, cred, &azsecrets.ClientOptions{ClientOptions: opts})
4257
if err != nil {
43-
return keyClient, err
58+
return nil, fmt.Errorf("failed to get client to access azure kv secrets. %v", err)
4459
}
45-
keyClient.Authorizer = autorest.NewBearerAuthorizer(token)
46-
return keyClient, nil
60+
61+
return client, nil
4762
}

azure/azure_kv_test.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,24 @@ func TestNew(t *testing.T) {
1111
os.Unsetenv("AZURE_TENANT_ID")
1212
os.Unsetenv("AZURE_CLIENT_ID")
1313
os.Unsetenv("AZURE_CLIENT_SECRET")
14+
os.Unsetenv("AZURE_CLIENT_CERT_PATH")
1415
os.Unsetenv("AZURE_ENVIRONMENT")
1516
os.Unsetenv("AZURE_VAULT_URL")
1617

1718
// nil secret config
1819
_, err := New(nil)
19-
assert.Equal(t, err, ErrAzureTenantIDNotSet)
20+
assert.Equal(t, ErrAzureTenantIDNotSet, err)
2021
os.Setenv("AZURE_TENANT_ID", "invalid_tenant_id")
2122

2223
_, err = New(nil)
23-
assert.Equal(t, err, ErrAzureClientIDNotSet)
24+
assert.Equal(t, ErrAzureClientIDNotSet, err)
2425
os.Setenv("AZURE_CLIENT_ID", "invalid-client-id")
2526

2627
_, err = New(nil)
27-
assert.Equal(t, err, ErrAzureSecretIDNotSet)
28-
os.Setenv("AZURE_CLIENT_SECRET", "invalid-secret-id")
29-
30-
_, err = New(nil)
31-
assert.Equal(t, err, ErrAzureVaultURLNotSet)
28+
assert.Equal(t, ErrAzureVaultURLNotSet, err)
3229
os.Setenv("AZURE_VAULT_URL", "invalid-vault-url")
3330

3431
_, err = New(nil)
35-
assert.NoError(t, err, "Unepxected error on New")
32+
assert.Equal(t, ErrAzureAuthMedhodNotSet, err)
33+
os.Setenv("AZURE_CLIENT_SECRET", "invalid-secret-id")
3634
}

go.mod

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ module github.com/libopenstorage/secrets
33
go 1.13
44

55
require (
6-
github.com/Azure/azure-sdk-for-go v62.0.0+incompatible
7-
github.com/Azure/go-autorest/autorest v0.11.27
8-
github.com/Azure/go-autorest/autorest/adal v0.9.20
9-
github.com/Azure/go-autorest/autorest/to v0.4.0
106
github.com/IBM/keyprotect-go-client v0.5.1
117
github.com/aws/aws-sdk-go v1.44.164
128
github.com/golang/mock v1.6.0
@@ -18,21 +14,21 @@ require (
1814
github.com/portworx/kvdb v0.0.0-20200929023115-b312c7519467
1915
github.com/portworx/sched-ops v1.20.4-rc1
2016
github.com/sirupsen/logrus v1.9.0
21-
github.com/stretchr/testify v1.8.0
17+
github.com/stretchr/testify v1.8.4
2218
google.golang.org/api v0.83.0
2319
k8s.io/client-go v12.0.0+incompatible
2420
)
2521

2622
require (
27-
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
23+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1
24+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1
25+
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0
2826
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
2927
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
3028
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
3129
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
3230
github.com/go-openapi/jsonreference v0.20.0 // indirect
3331
github.com/go-test/deep v1.0.8 // indirect
34-
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
35-
github.com/google/uuid v1.3.0 // indirect
3632
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
3733
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
3834
github.com/imdario/mergo v0.3.13 // indirect

0 commit comments

Comments
 (0)