Skip to content

Commit

Permalink
feat: use the azsecrets, azkeys, azcertificates keyvault sdk (#…
Browse files Browse the repository at this point in the history
…1109)

* add keyvault interface
* update auth to remove adapter and return token cred
* implement keyvault interface and cleanup url usage
* update e2e tests to use azsecrets
* add mock_keyvault and initial unit test
* remove getObjectVersion helper and use id.Version()
* add unit test for getsecret
* add unit test for getcerts

Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>
  • Loading branch information
aramase committed Apr 25, 2023
1 parent 43632c8 commit 9f974df
Show file tree
Hide file tree
Showing 11 changed files with 791 additions and 313 deletions.
9 changes: 5 additions & 4 deletions go.mod
Expand Up @@ -3,15 +3,17 @@ module github.com/Azure/secrets-store-csi-driver-provider-azure
go 1.19

require (
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.8.0
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0
github.com/Azure/go-autorest/autorest v0.11.28
github.com/Azure/go-autorest/autorest/adal v0.9.22
github.com/Azure/go-autorest/autorest/date v0.3.0
github.com/Azure/go-autorest/autorest/to v0.4.0
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.8
github.com/jongio/azidext/go/azidext v0.4.0
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.1
go.opentelemetry.io/otel v0.20.0
Expand All @@ -28,9 +30,8 @@ require (

require (
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 // indirect
Expand Down
15 changes: 8 additions & 7 deletions go.sum
Expand Up @@ -31,14 +31,20 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1 h1:gVXuXcWd1i4C2Ruxe321aU+IKGaStvGB/S90PUPB/W8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1/go.mod h1:DffdKW9RFqa5VgmsjUOsS7UE7eiA5iAvYUs63bhKQ0M=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 h1:T8quHYlUGyb/oqtSTwqlCr1ilJHrDv+ZtpSfo+hm1BU=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M3yopJ/p0iq5DdY6Yv5ZUt9MTRZOQM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.8.0 h1:edn/e2qs1fEkPHlZqbESJWhFai9Pk/UA5eiwFUA1nwI=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.8.0/go.mod h1:8eUJPoEz7doIqSwW2pAvLGhEy3mDC9o/ToCa8OZy7go=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0 h1:TOFrNxfjslms5nLLIMjW7N0+zSALX4KiGsptmpb16AA=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0/go.mod h1:EAyXOW1F6BTJPiK2pDvmnvxOHPxoTYWoqBeIlql+QhI=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0 h1:82w8tzLcOwDP/Q35j/wEBPt0n0kVC3cjtPdD62G8UAk=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0/go.mod h1:S78i9yTr4o/nXlH76bKjGUye9Z2wSxO5Tz7GoDr4vfI=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 h1:Lg6BW0VPmCwcMlvOviL3ruHFO+H9tZNqscK0AeuFjGM=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
Expand All @@ -53,8 +59,6 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9A
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
Expand Down Expand Up @@ -266,10 +270,7 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jongio/azidext/go/azidext v0.4.0 h1:TOYyVFMeWGgXNhURSgrEtUCu7JAAKgsy+5C4+AEfYlw=
github.com/jongio/azidext/go/azidext v0.4.0/go.mod h1:VrlpGde5B+pPbTUxnThE5UIQQkcebdr3jrC2MmlMVSI=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
Expand Down
31 changes: 10 additions & 21 deletions pkg/auth/auth.go
Expand Up @@ -15,9 +15,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/jongio/azidext/go/azidext"
"github.com/pkg/errors"
"k8s.io/klog/v2"
)
Expand Down Expand Up @@ -137,30 +135,21 @@ func NewConfig(
return config, nil
}

// GetAuthorizer returns an Azure authorizer based on the provided azure identity
func (c Config) GetAuthorizer(podName, podNamespace, resource, aadEndpoint, tenantID, nmiPort string) (autorest.Authorizer, error) {
var cred azcore.TokenCredential
var err error

// GetCredential returns the azure credential to use based on the auth config
func (c Config) GetCredential(podName, podNamespace, resource, aadEndpoint, tenantID, nmiPort string) (azcore.TokenCredential, error) {
// use switch case to ensure only one of the identity modes is enabled
switch {
case c.UsePodIdentity:
cred, err = getAuthorizerForPodIdentity(podName, podNamespace, resource, tenantID, nmiPort)
return getPodIdentityTokenCredential(podName, podNamespace, resource, tenantID, nmiPort)
case c.UseVMManagedIdentity:
cred, err = getAuthorizerForManagedIdentity(c.UserAssignedIdentityID)
return getManagedIdentityTokenCredential(c.UserAssignedIdentityID)
case len(c.AADClientSecret) > 0 && len(c.AADClientID) > 0:
cred, err = getAuthorizerForServicePrincipal(c.AADClientID, c.AADClientSecret, aadEndpoint, tenantID)
return getServicePrincipalTokenCredential(c.AADClientID, c.AADClientSecret, aadEndpoint, tenantID)
case len(c.WorkloadIdentityClientID) > 0 && len(c.WorkloadIdentityToken) > 0:
cred, err = getAuthorizerForWorkloadIdentity(c.WorkloadIdentityClientID, c.WorkloadIdentityToken, aadEndpoint, tenantID)
return getWorkloadIdentityTokenCredential(c.WorkloadIdentityClientID, c.WorkloadIdentityToken, aadEndpoint, tenantID)
default:
return nil, fmt.Errorf("no identity mode is enabled")
}

if err != nil {
return nil, err
}

return azidext.NewTokenCredentialAdapter(cred, []string{getScope(resource)}), nil
}

func newWorkloadIdentityCredential(tenantID, clientID, assertion string, options *workloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
Expand All @@ -181,7 +170,7 @@ func (w *workloadIdentityCredential) getAssertion(context.Context) (string, erro
return w.assertion, nil
}

func getAuthorizerForWorkloadIdentity(clientID, signedAssertion, aadEndpoint, tenantID string) (azcore.TokenCredential, error) {
func getWorkloadIdentityTokenCredential(clientID, signedAssertion, aadEndpoint, tenantID string) (azcore.TokenCredential, error) {
opts := &workloadIdentityCredentialOptions{
ClientOptions: azcore.ClientOptions{
Cloud: cloud.Configuration{
Expand All @@ -192,7 +181,7 @@ func getAuthorizerForWorkloadIdentity(clientID, signedAssertion, aadEndpoint, te
return newWorkloadIdentityCredential(tenantID, clientID, signedAssertion, opts)
}

func getAuthorizerForServicePrincipal(clientID, secret, aadEndpoint, tenantID string) (azcore.TokenCredential, error) {
func getServicePrincipalTokenCredential(clientID, secret, aadEndpoint, tenantID string) (azcore.TokenCredential, error) {
opts := &azidentity.ClientSecretCredentialOptions{
ClientOptions: azcore.ClientOptions{
Cloud: cloud.Configuration{
Expand All @@ -203,7 +192,7 @@ func getAuthorizerForServicePrincipal(clientID, secret, aadEndpoint, tenantID st
return azidentity.NewClientSecretCredential(tenantID, clientID, secret, opts)
}

func getAuthorizerForManagedIdentity(identityClientID string) (azcore.TokenCredential, error) {
func getManagedIdentityTokenCredential(identityClientID string) (azcore.TokenCredential, error) {
opts := &azidentity.ManagedIdentityCredentialOptions{
ID: azidentity.ClientID(identityClientID),
}
Expand Down Expand Up @@ -258,7 +247,7 @@ func (c *podIdentityCredential) GetToken(ctx context.Context, _ policy.TokenRequ
}, nil
}

func getAuthorizerForPodIdentity(podName, podNamespace, resource, tenantID, nmiPort string) (azcore.TokenCredential, error) {
func getPodIdentityTokenCredential(podName, podNamespace, resource, tenantID, nmiPort string) (azcore.TokenCredential, error) {
if len(podName) == 0 || len(podNamespace) == 0 {
return nil, fmt.Errorf("pod information is not available. deploy a CSIDriver object to set podInfoOnMount: true")
}
Expand Down
174 changes: 174 additions & 0 deletions pkg/provider/keyvault.go
@@ -0,0 +1,174 @@
package provider

import (
"context"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
"github.com/Azure/go-autorest/autorest/date"

"github.com/Azure/secrets-store-csi-driver-provider-azure/pkg/provider/types"
)

type KeyVault interface {
GetSecret(ctx context.Context, name, version string) (*azsecrets.SecretBundle, error)
GetSecretVersions(ctx context.Context, name string) ([]types.KeyVaultObjectVersion, error)
GetKey(ctx context.Context, name, version string) (*azkeys.KeyBundle, error)
GetKeyVersions(ctx context.Context, name string) ([]types.KeyVaultObjectVersion, error)
GetCertificate(ctx context.Context, name, version string) (*azcertificates.CertificateBundle, error)
GetCertificateVersions(ctx context.Context, name string) ([]types.KeyVaultObjectVersion, error)
}

// TODO(aramase): add user agent
type client struct {
secrets *azsecrets.Client
keys *azkeys.Client
certs *azcertificates.Client
}

// NewClient creates a new KeyVault client
func NewClient(cred azcore.TokenCredential, vaultURI string) (KeyVault, error) {
secrets, err := azsecrets.NewClient(vaultURI, cred, nil)
if err != nil {
return nil, err
}
keys, err := azkeys.NewClient(vaultURI, cred, nil)
if err != nil {
return nil, err
}
certs, err := azcertificates.NewClient(vaultURI, cred, nil)
if err != nil {
return nil, err
}

return &client{
secrets: secrets,
keys: keys,
certs: certs,
}, nil
}

func (c *client) GetSecret(ctx context.Context, name, version string) (*azsecrets.SecretBundle, error) {
resp, err := c.secrets.GetSecret(ctx, name, version, &azsecrets.GetSecretOptions{})
if err != nil {
return nil, err
}
return &resp.SecretBundle, nil
}

func (c *client) GetKey(ctx context.Context, name, version string) (*azkeys.KeyBundle, error) {
resp, err := c.keys.GetKey(ctx, name, version, &azkeys.GetKeyOptions{})
if err != nil {
return nil, err
}
return &resp.KeyBundle, nil
}

func (c *client) GetCertificate(ctx context.Context, name, version string) (*azcertificates.CertificateBundle, error) {
resp, err := c.certs.GetCertificate(ctx, name, version, &azcertificates.GetCertificateOptions{})
if err != nil {
return nil, err
}
return &resp.CertificateBundle, nil
}

func (c *client) GetSecretVersions(ctx context.Context, name string) ([]types.KeyVaultObjectVersion, error) {
pager := c.secrets.NewListSecretVersionsPager(name, &azsecrets.ListSecretVersionsOptions{})
var versions []types.KeyVaultObjectVersion

for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return nil, err
}
for _, secret := range page.SecretListResult.Value {
if secret.Attributes == nil {
continue
}
if secret.Attributes.Enabled != nil && !*secret.Attributes.Enabled {
continue
}

id := *secret.ID
created := date.UnixEpoch()
if secret.Attributes.Created != nil {
created = *secret.Attributes.Created
}

versions = append(versions, types.KeyVaultObjectVersion{
Version: id.Version(),
Created: created,
})
}
}

return versions, nil
}

func (c *client) GetKeyVersions(ctx context.Context, name string) ([]types.KeyVaultObjectVersion, error) {
pager := c.keys.NewListKeyVersionsPager(name, &azkeys.ListKeyVersionsOptions{})
var versions []types.KeyVaultObjectVersion

for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return nil, err
}
for _, key := range page.KeyListResult.Value {
if key.Attributes == nil {
continue
}
if key.Attributes.Enabled != nil && !*key.Attributes.Enabled {
continue
}

id := *key.KID
created := date.UnixEpoch()
if key.Attributes.Created != nil {
created = *key.Attributes.Created
}

versions = append(versions, types.KeyVaultObjectVersion{
Version: id.Version(),
Created: created,
})
}
}

return versions, nil
}

func (c *client) GetCertificateVersions(ctx context.Context, name string) ([]types.KeyVaultObjectVersion, error) {
pager := c.certs.NewListCertificateVersionsPager(name, &azcertificates.ListCertificateVersionsOptions{})
var versions []types.KeyVaultObjectVersion

for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return nil, err
}
for _, cert := range page.CertificateListResult.Value {
if cert.Attributes == nil {
continue
}
if cert.Attributes.Enabled != nil && !*cert.Attributes.Enabled {
continue
}

id := *cert.ID
created := date.UnixEpoch()
if cert.Attributes.Created != nil {
created = *cert.Attributes.Created
}

versions = append(versions, types.KeyVaultObjectVersion{
Version: id.Version(),
Created: created,
})
}
}

return versions, nil
}
4 changes: 4 additions & 0 deletions pkg/provider/mock_keyvault/doc.go
@@ -0,0 +1,4 @@
// Run go generate to regenerate this mock.
//
//go:generate ../../../.tools/mockgen -destination keyvault_mock.go -package mock_keyvault -source ../keyvault.go
package mock_keyvault //nolint

0 comments on commit 9f974df

Please sign in to comment.