pkg/armhelpers/credentials.go (139 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. package armhelpers import ( "crypto/rsa" "crypto/x509" "encoding/pem" "os" "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/pkg/errors" ) // NewDefaultCredential returns an AzureClient func NewDefaultCredential(cloud cloud.Configuration, subscriptionID string) (*azidentity.DefaultAzureCredential, error) { options := &azidentity.DefaultAzureCredentialOptions{ ClientOptions: policy.ClientOptions{ Cloud: cloud, }, } cred, err := azidentity.NewDefaultAzureCredential(options) if err != nil { return nil, err } return cred, nil } // NewClientSecretCredential returns an AzureClient via client_id and client_secret func NewClientSecretCredential(cloud cloud.Configuration, subscriptionID, clientID, clientSecret string) (*azidentity.ClientSecretCredential, error) { tenantID, err := getOAuthConfig(subscriptionID, cloud) if err != nil { return nil, err } options := &azidentity.ClientSecretCredentialOptions{ DisableInstanceDiscovery: true, ClientOptions: policy.ClientOptions{ Cloud: cloud, }, } cred, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, options) if err != nil { return nil, err } return cred, nil } // NewClientSecretCredentialExternalTenant returns an AzureClient via client_id and client_secret from a tenant func NewClientSecretCredentialExternalTenant(cloud cloud.Configuration, subscriptionID, clientID, clientSecret string) (*azidentity.ClientSecretCredential, error) { options := &azidentity.ClientSecretCredentialOptions{ DisableInstanceDiscovery: true, ClientOptions: policy.ClientOptions{ Cloud: cloud, }, } cred, err := azidentity.NewClientSecretCredential("adfs", clientID, clientSecret, options) if err != nil { return nil, err } return cred, nil } // NewClientCertificateCredential returns an AzureClient via client_id and jwt certificate assertion func NewClientCertificateCredential(cloud cloud.Configuration, subscriptionID, clientID, certificatePath, privateKeyPath string) (*azidentity.ClientCertificateCredential, error) { certificateData, err := os.ReadFile(certificatePath) if err != nil { return nil, errors.Wrap(err, "Failed to read certificate") } block, _ := pem.Decode(certificateData) if block == nil { return nil, errors.New("Failed to decode pem block from certificate") } certificate, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, errors.Wrap(err, "Failed to parse certificate") } privateKey, err := parseRsaPrivateKey(privateKeyPath) if err != nil { return nil, errors.Wrap(err, "Failed to parse rsa private key") } tenantID, err := getOAuthConfig(subscriptionID, cloud) if err != nil { return nil, err } options := &azidentity.ClientCertificateCredentialOptions{ SendCertificateChain: true, DisableInstanceDiscovery: true, ClientOptions: policy.ClientOptions{ Cloud: cloud, }, } return azidentity.NewClientCertificateCredential(tenantID, clientID, []*x509.Certificate{certificate}, privateKey, options) } // NewClientCertificateCredentialExternalTenant returns an AzureClient via client_id and jwt certificate assertion a 3rd party tenant func NewClientCertificateCredentialExternalTenant(cloud cloud.Configuration, subscriptionID, clientID, certificatePath, privateKeyPath string) (*azidentity.ClientCertificateCredential, error) { certificateData, err := os.ReadFile(certificatePath) if err != nil { return nil, errors.Wrap(err, "Failed to read certificate") } block, _ := pem.Decode(certificateData) if block == nil { return nil, errors.New("Failed to decode pem block from certificate") } certificate, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, errors.Wrap(err, "Failed to parse certificate") } privateKey, err := parseRsaPrivateKey(privateKeyPath) if err != nil { return nil, errors.Wrap(err, "Failed to parse rsa private key") } options := &azidentity.ClientCertificateCredentialOptions{ SendCertificateChain: true, DisableInstanceDiscovery: true, ClientOptions: policy.ClientOptions{ Cloud: cloud, }, } return azidentity.NewClientCertificateCredential("adfs", clientID, []*x509.Certificate{certificate}, privateKey, options) } func getOAuthConfig(subscriptionID string, cloud cloud.Configuration) (string, error) { tenantID, err := GetTenantID(subscriptionID, cloud) if err != nil { return "", err } return tenantID, nil } func parseRsaPrivateKey(path string) (*rsa.PrivateKey, error) { privateKeyData, err := os.ReadFile(path) if err != nil { return nil, err } block, _ := pem.Decode(privateKeyData) if block == nil { return nil, errors.New("Failed to decode a pem block from private key") } privatePkcs1Key, errPkcs1 := x509.ParsePKCS1PrivateKey(block.Bytes) if errPkcs1 == nil { return privatePkcs1Key, nil } privatePkcs8Key, errPkcs8 := x509.ParsePKCS8PrivateKey(block.Bytes) if errPkcs8 == nil { privatePkcs8RsaKey, ok := privatePkcs8Key.(*rsa.PrivateKey) if !ok { return nil, errors.New("pkcs8 contained non-RSA key. Expected RSA key") } return privatePkcs8RsaKey, nil } return nil, errors.Errorf("failed to parse private key as Pkcs#1 or Pkcs#8. (%s). (%s)", errPkcs1, errPkcs8) }