Skip to content

Commit 8f7772b

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 8f7772b

File tree

6 files changed

+181
-85
lines changed

6 files changed

+181
-85
lines changed

azure/azure_kv.go

Lines changed: 60 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,13 @@ 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 Secret is not present
122+
if strings.Contains(err.Error(), "SecretNotFound") {
123+
return nil, false, secrets.ErrSecretNotFound
124+
}
103125
return nil, true, err
104126
}
105127
return secretResp, false, nil
@@ -109,7 +131,7 @@ func (az *azureSecrets) GetSecret(
109131
return nil, secrets.NoVersion, err
110132
}
111133

112-
secretResp, ok := resp.(keyvault.SecretBundle)
134+
secretResp, ok := resp.(azsecrets.GetSecretResponse)
113135
if !ok || secretResp.Value == nil {
114136
return nil, secrets.NoVersion, ErrInvalidSecretResp
115137
}
@@ -133,7 +155,7 @@ func (az *azureSecrets) PutSecret(
133155
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
134156
defer cancel()
135157

136-
var secretResp keyvault.SecretBundle
158+
var secretResp azsecrets.SecretBundle
137159
if secretName == "" {
138160
return secrets.NoVersion, secrets.ErrEmptySecretId
139161
}
@@ -146,10 +168,10 @@ func (az *azureSecrets) PutSecret(
146168
return secrets.NoVersion, err
147169
}
148170

171+
valueStr := string(value)
149172
t := func() (interface{}, bool, error) {
150-
secretResp, err = az.kv.SetSecret(ctx, az.baseURL, secretName, keyvault.SecretSetParameters{
151-
Value: to.StringPtr(string(value)),
152-
})
173+
params := azsecrets.SetSecretParameters{Value: &valueStr}
174+
az.kv.SetSecret(ctx, secretName, params, nil)
153175
if err != nil {
154176
return nil, true, err
155177
}
@@ -169,7 +191,7 @@ func (az *azureSecrets) DeleteSecret(
169191
if secretName == "" {
170192
return secrets.ErrEmptySecretId
171193
}
172-
_, err := az.kv.DeleteSecret(ctx, az.baseURL, secretName)
194+
_, err := az.kv.DeleteSecret(ctx, secretName, nil)
173195

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

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)