Skip to content

Commit

Permalink
Refactor the azure credential, make it explicit
Browse files Browse the repository at this point in the history
Signed-off-by: mabhi <abhijit.mukherjee@infracloud.io>
  • Loading branch information
mabhi committed Oct 13, 2023
1 parent 1da9d83 commit 72c7a41
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 30 deletions.
74 changes: 61 additions & 13 deletions pkg/blockstorage/azure/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package azure

import (
"context"
_ "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"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"
Expand All @@ -16,16 +16,23 @@ const ActiveDirectory = "activeDirectory"
// to be available with azidentity: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#readme-credential-types
// determine if the combination of creds are client secret creds
func isClientCredsAvailable(config map[string]string) bool {
return (config[blockstorage.AzureTenantID] != "" &&
return config[blockstorage.AzureTenantID] != "" &&
config[blockstorage.AzureClientID] != "" &&
config[blockstorage.AzureClientSecret] != "")
config[blockstorage.AzureClientSecret] != ""
}

// determine if the combination of creds are MSI creds
func isMSICredsAvailable(config map[string]string) bool {
_, clientIDok := config[blockstorage.AzureClientID]
return (clientIDok && config[blockstorage.AzureTenantID] == "" &&
config[blockstorage.AzureClientSecret] == "")
return clientIDok && config[blockstorage.AzureTenantID] == "" &&
config[blockstorage.AzureClientSecret] == ""
}

func isDefaultCredsAvailable(config map[string]string) bool {
_, clientIDok := config[blockstorage.AzureClientID]
_, tenantIDok := config[blockstorage.AzureTenantID]
_, clientSecretOk := config[blockstorage.AzureClientSecret]
return !clientIDok && !tenantIDok && !clientSecretOk
}

type ClientCredentialsConfig struct {
Expand Down Expand Up @@ -53,22 +60,58 @@ func NewClientCredentialsConfig(clientID string, clientSecret string, tenantID s
// Public interface to authenticate with different Azure credentials type
type AzureAuthenticator interface {
Authenticate(creds map[string]string) error
GetTokenCredential() azcore.TokenCredential
}

func NewAzureAuthenticator(config map[string]string) (AzureAuthenticator, error) {
// NewAzureAuthenticator opens up the possibility to Auth with:
//1. Env variables
//2. Managed Identity
//3. Workload Identity
//4. AzureCli
switch {
case isMSICredsAvailable(config):
return &MsiAuthenticator{}, nil
case isClientCredsAvailable(config):
return &ClientSecretAuthenticator{}, nil
case isDefaultCredsAvailable(config):
return &DefaultAuthenticator{}, nil
default:
return nil, errors.New("Fail to get an authenticator for provided creds combination")
}
}

// authenticate with default credential
type DefaultAuthenticator struct {
azcore.TokenCredential
}

func (d *DefaultAuthenticator) GetTokenCredential() azcore.TokenCredential {
return d.TokenCredential
}

func (d *DefaultAuthenticator) Authenticate(creds map[string]string) error {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return errors.Wrap(err, "Failed to create an Azure Default Identity credential")
}
_, err = cred.GetToken(context.Background(), policy.TokenRequestOptions{})
if err != nil {
return errors.Wrap(err, "Failed to create an access token")
}
d.TokenCredential = cred
// creds passed authentication
return nil
}

// authenticate with MSI creds
type MsiAuthenticator struct{}
type MsiAuthenticator struct {
azcore.TokenCredential
}

func (m *MsiAuthenticator) GetTokenCredential() azcore.TokenCredential {
return m.TokenCredential
}
func (m *MsiAuthenticator) Authenticate(creds map[string]string) error {
// check if MSI endpoint is available

Expand All @@ -80,34 +123,39 @@ func (m *MsiAuthenticator) Authenticate(creds map[string]string) error {
opts := azidentity.ManagedIdentityCredentialOptions{ID: azClientID}
cred, err := azidentity.NewManagedIdentityCredential(&opts)
if err != nil {
return errors.Wrap(err, "Failed to create a identity credential")
return errors.Wrap(err, "Failed to create an Azure Managed Identity credential")
}
_, err = cred.GetToken(context.Background(), policy.TokenRequestOptions{})

if err != nil {
return errors.Wrap(err, "Failed to create a service principal token")
return errors.Wrap(err, "Failed to create an access token")
}
m.TokenCredential = cred
// creds passed authentication
return nil
}

// authenticate with client secret creds
type ClientSecretAuthenticator struct{}
type ClientSecretAuthenticator struct {
azcore.TokenCredential
}

func (c *ClientSecretAuthenticator) GetTokenCredential() azcore.TokenCredential {
return c.TokenCredential
}
func (c *ClientSecretAuthenticator) Authenticate(creds map[string]string) error {
credConfig, err := getCredConfigForAuth(creds)
if err != nil {
return errors.Wrap(err, "Failed to get Client Secret config")
}
cred, err := azidentity.NewClientSecretCredential(credConfig.TenantID, credConfig.ClientID, credConfig.ClientSecret, nil)
if err != nil {
return errors.Wrap(err, "Failed to create a identity credential")
return errors.Wrap(err, "Failed to create an Azure Client Secret credential")
}
_, err = cred.GetToken(context.Background(), policy.TokenRequestOptions{})

if err != nil {
return errors.Wrap(err, "Failed to create a service principal token")
return errors.Wrap(err, "Failed to create an access token")
}
c.TokenCredential = cred
// creds passed authentication
return nil
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/blockstorage/azure/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (s *AuthSuite) TestIsMSICredsAvailable(c *C) {
c.Assert(isMSICredsAvailable(config), Equals, false)
}

func (s *AuthSuite) TestNewAzureAutheticator(c *C) {
func (s *AuthSuite) TestNewAzureAuthenticator(c *C) {
// successful with client secret creds
config := map[string]string{
blockstorage.AzureTenantID: "some-tenant-id",
Expand Down Expand Up @@ -101,11 +101,11 @@ func (s *AuthSuite) TestNewAzureAutheticator(c *C) {
c.Assert(err, IsNil)
c.Assert(authenticator, NotNil)

// unsuccessful with no creds
// successful with no creds, but uses azure default credential
config = map[string]string{}
authenticator, err = NewAzureAuthenticator(config)
c.Assert(err, NotNil)
c.Assert(authenticator, IsNil)
c.Assert(err, IsNil)
c.Assert(authenticator, NotNil)

// unsuccessful with an undefined combo of credss
config = map[string]string{
Expand Down
25 changes: 13 additions & 12 deletions pkg/blockstorage/azure/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,22 @@ package azure

import (
"context"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions"
"github.com/kanisterio/kanister/pkg/blockstorage"
"github.com/kanisterio/kanister/pkg/log"
"github.com/pkg/errors"
)

// Client is a wrapper for Client client
// Client is a wrapper
type Client struct {
Cred *azidentity.DefaultAzureCredential
SubscriptionID string
ResourceGroup string
BaseURI string
//https://github.com/Azure-Samples/azure-sdk-for-go-samples/blob/main/sdk/resourcemanager/compute/disk/main.go
//https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/arm/compute#DisksClient
DisksClient *armcompute.DisksClient
//https://github.com/Azure-Samples/azure-sdk-for-go-samples/blob/main/sdk/resourcemanager/compute/snapshot/main.go
//https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/arm/compute#SnapshotsClient
Cred azcore.TokenCredential
SubscriptionID string
ResourceGroup string
BaseURI string
DisksClient *armcompute.DisksClient
SnapshotsClient *armcompute.SnapshotsClient
SKUsClient *armcompute.ResourceSKUsClient
SubscriptionsClient *armsubscriptions.Client
Expand Down Expand Up @@ -75,10 +71,15 @@ func NewClient(ctx context.Context, config map[string]string) (*Client, error) {
}
}

cred, err := azidentity.NewDefaultAzureCredential(nil)
authenticator, err := NewAzureAuthenticator(config)
if err != nil {
return nil, err
}
err = authenticator.Authenticate(config)
if err != nil {
return nil, err
}
cred := authenticator.GetTokenCredential()
computeClientFactory, err = armcompute.NewClientFactory(subscriptionID, cred, nil)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/blockstorage/azure/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (s *ClientSuite) TestClient(c *C) {
config[blockstorage.AzureCloudEnvironmentID] = envconfig.GetEnvOrSkip(c, blockstorage.AzureCloudEnvironmentID)
azCli, err := NewClient(context.Background(), config)
c.Assert(err, IsNil)

c.Assert(azCli.Cred, NotNil)
c.Assert(azCli.SubscriptionID, NotNil)
c.Assert(azCli.DisksClient, NotNil)
c.Assert(azCli.SnapshotsClient, NotNil)
Expand Down

0 comments on commit 72c7a41

Please sign in to comment.