Update Azure SDK and support additional authentication schemes
Microsoft has updated the golang Azure SDK significantly. Update the azure storage driver to use the new SDK. Add support for client secret and MSI authentication schemes in addition to shared key authentication. Implement rootDirectory support for the azure storage driver to mirror the S3 driver. Signed-off-by: Kirat Singh <kirat.singh@beacon.io> Co-authored-by: Cory Snider <corhere@gmail.com>
This commit is contained in:
parent
e5d5810851
commit
ba4a6bbe02
365 changed files with 44060 additions and 21016 deletions
152
registry/storage/driver/azure/azure_auth.go
Normal file
152
registry/storage/driver/azure/azure_auth.go
Normal file
|
@ -0,0 +1,152 @@
|
|||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
|
||||
)
|
||||
|
||||
const (
|
||||
UDCGracePeriod = 30.0 * time.Minute
|
||||
UDCExpiryTime = 48.0 * time.Hour
|
||||
)
|
||||
|
||||
// signer abstracts the specifics of a blob SAS and is specialized
|
||||
// for the different authentication credentials
|
||||
type signer interface {
|
||||
Sign(context.Context, *sas.BlobSignatureValues) (sas.QueryParameters, error)
|
||||
}
|
||||
|
||||
type sharedKeySigner struct {
|
||||
cred *azblob.SharedKeyCredential
|
||||
}
|
||||
|
||||
type clientTokenSigner struct {
|
||||
client *azblob.Client
|
||||
cred azcore.TokenCredential
|
||||
udcMutex sync.Mutex
|
||||
udc *service.UserDelegationCredential
|
||||
udcExpiry time.Time
|
||||
}
|
||||
|
||||
// azureClient abstracts signing blob urls for a container since the
|
||||
// azure apis have completely different underlying authentication apis
|
||||
type azureClient struct {
|
||||
container string
|
||||
client *azblob.Client
|
||||
signer signer
|
||||
}
|
||||
|
||||
func newAzureClient(params *Parameters) (*azureClient, error) {
|
||||
if params.AccountKey != "" {
|
||||
cred, err := azblob.NewSharedKeyCredential(params.AccountName, params.AccountKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := azblob.NewClientWithSharedKeyCredential(params.ServiceURL, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer := &sharedKeySigner{
|
||||
cred: cred,
|
||||
}
|
||||
return &azureClient{
|
||||
container: params.Container,
|
||||
client: client,
|
||||
signer: signer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var cred azcore.TokenCredential
|
||||
var err error
|
||||
if params.Credentials.Type == "client_secret" {
|
||||
creds := ¶ms.Credentials
|
||||
if cred, err = azidentity.NewClientSecretCredential(creds.TenantID, creds.ClientID, creds.Secret, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if cred, err = azidentity.NewDefaultAzureCredential(nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := azblob.NewClient(params.ServiceURL, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer := &clientTokenSigner{
|
||||
client: client,
|
||||
cred: cred,
|
||||
}
|
||||
return &azureClient{
|
||||
container: params.Container,
|
||||
client: client,
|
||||
signer: signer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *azureClient) ContainerClient() *container.Client {
|
||||
return a.client.ServiceClient().NewContainerClient(a.container)
|
||||
}
|
||||
|
||||
func (a *azureClient) SignBlobURL(ctx context.Context, blobURL string, expires time.Time) (string, error) {
|
||||
urlParts, err := sas.ParseURL(blobURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
perms := sas.BlobPermissions{Read: true}
|
||||
signatureValues := sas.BlobSignatureValues{
|
||||
Protocol: sas.ProtocolHTTPS,
|
||||
StartTime: time.Now().UTC().Add(-10 * time.Second),
|
||||
ExpiryTime: expires,
|
||||
Permissions: perms.String(),
|
||||
ContainerName: urlParts.ContainerName,
|
||||
BlobName: urlParts.BlobName,
|
||||
}
|
||||
urlParts.SAS, err = a.signer.Sign(ctx, &signatureValues)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return urlParts.String(), nil
|
||||
}
|
||||
|
||||
func (s *sharedKeySigner) Sign(ctx context.Context, signatureValues *sas.BlobSignatureValues) (sas.QueryParameters, error) {
|
||||
return signatureValues.SignWithSharedKey(s.cred)
|
||||
}
|
||||
|
||||
func (s *clientTokenSigner) refreshUDC(ctx context.Context) (*service.UserDelegationCredential, error) {
|
||||
s.udcMutex.Lock()
|
||||
defer s.udcMutex.Unlock()
|
||||
|
||||
now := time.Now().UTC()
|
||||
if s.udc == nil || s.udcExpiry.Sub(now) < UDCGracePeriod {
|
||||
// reissue user delegation credential
|
||||
startTime := now.Add(-10 * time.Second)
|
||||
expiryTime := startTime.Add(UDCExpiryTime)
|
||||
info := service.KeyInfo{
|
||||
Start: to.Ptr(startTime.UTC().Format(sas.TimeFormat)),
|
||||
Expiry: to.Ptr(expiryTime.UTC().Format(sas.TimeFormat)),
|
||||
}
|
||||
udc, err := s.client.ServiceClient().GetUserDelegationCredential(ctx, info, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.udc = udc
|
||||
s.udcExpiry = expiryTime
|
||||
}
|
||||
return s.udc, nil
|
||||
}
|
||||
|
||||
func (s *clientTokenSigner) Sign(ctx context.Context, signatureValues *sas.BlobSignatureValues) (sas.QueryParameters, error) {
|
||||
udc, err := s.refreshUDC(ctx)
|
||||
if err != nil {
|
||||
return sas.QueryParameters{}, err
|
||||
}
|
||||
return signatureValues.SignWithUserDelegation(udc)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue