зеркало из https://github.com/Azure/aztfexport.git
172 строки
6.4 KiB
Go
172 строки
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"log/slog"
|
|
|
|
"github.com/Azure/aztfexport/pkg/config"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
|
"github.com/hashicorp/go-multierror"
|
|
"software.sslmate.com/src/go-pkcs12"
|
|
)
|
|
|
|
type DefaultAzureCredentialOptions struct {
|
|
AuthConfig config.AuthConfig
|
|
ClientOptions azcore.ClientOptions
|
|
DisableInstanceDiscovery bool
|
|
// Only applies to certificate credential
|
|
SendCertificateChain bool
|
|
}
|
|
|
|
// DefaultAzureCredential is a default credential chain for applications that will deploy to Azure.
|
|
// It attempts to authenticate with each of these credential types, in the following order, stopping
|
|
// when one provides a token:
|
|
// - [ClientSecretCredential]
|
|
// - [ClientCertificateCredential]
|
|
// - [OIDCCredential]
|
|
// - [ManagedIdentityCredential]
|
|
// - [AzureCLICredential]
|
|
type DefaultAzureCredential struct {
|
|
chain *azidentity.ChainedTokenCredential
|
|
}
|
|
|
|
// NewDefaultAzureCredential creates a DefaultAzureCredential. Pass nil for options to accept defaults.
|
|
func NewDefaultAzureCredential(logger slog.Logger, opt *DefaultAzureCredentialOptions) (*DefaultAzureCredential, error) {
|
|
var creds []azcore.TokenCredential
|
|
var errors error
|
|
|
|
if opt == nil {
|
|
opt = &DefaultAzureCredentialOptions{}
|
|
}
|
|
|
|
logger.Info("Building credential via client secret")
|
|
if cred, err := azidentity.NewClientSecretCredential(
|
|
opt.AuthConfig.TenantID,
|
|
opt.AuthConfig.ClientID,
|
|
opt.AuthConfig.ClientSecret,
|
|
&azidentity.ClientSecretCredentialOptions{
|
|
ClientOptions: opt.ClientOptions,
|
|
AdditionallyAllowedTenants: opt.AuthConfig.AuxiliaryTenantIDs,
|
|
DisableInstanceDiscovery: opt.DisableInstanceDiscovery,
|
|
},
|
|
); err == nil {
|
|
logger.Info("Successfully built credential via client secret")
|
|
creds = append(creds, cred)
|
|
} else {
|
|
thisErr := fmt.Errorf("Building credential via client secret failed: %v", err)
|
|
logger.Warn(thisErr.Error())
|
|
errors = multierror.Append(errors, thisErr)
|
|
}
|
|
|
|
logger.Info("Building credential via client certificaite")
|
|
if cert, err := base64.StdEncoding.DecodeString(opt.AuthConfig.ClientCertificateEncoded); err != nil {
|
|
thisErr := fmt.Errorf("Building credential via client certificate failed: base64 decoidng certificate: %v", err)
|
|
logger.Warn(thisErr.Error())
|
|
errors = multierror.Append(errors, thisErr)
|
|
} else {
|
|
// We are using a 3rd party module for parsing the certificate (the same one as is used by go-azure-sdk/sdk/auth/client_certificate_authorizer.go)
|
|
// Reason can be found at: https://github.com/Azure/azure-sdk-for-go/issues/22906
|
|
//certs, key, err := azidentity.ParseCertificates(cert, []byte(opt.AuthConfig.ClientCertificatePassword))
|
|
key, cert, _, err := pkcs12.DecodeChain(cert, opt.AuthConfig.ClientCertificatePassword)
|
|
if err != nil {
|
|
thisErr := fmt.Errorf("Building credential via client certificate failed: failed to parse certificate: %v", err)
|
|
logger.Warn(thisErr.Error())
|
|
errors = multierror.Append(errors, thisErr)
|
|
} else {
|
|
if cred, err := azidentity.NewClientCertificateCredential(
|
|
opt.AuthConfig.TenantID,
|
|
opt.AuthConfig.ClientID,
|
|
[]*x509.Certificate{cert},
|
|
key,
|
|
&azidentity.ClientCertificateCredentialOptions{
|
|
ClientOptions: opt.ClientOptions,
|
|
AdditionallyAllowedTenants: opt.AuthConfig.AuxiliaryTenantIDs,
|
|
DisableInstanceDiscovery: opt.DisableInstanceDiscovery,
|
|
SendCertificateChain: opt.SendCertificateChain,
|
|
},
|
|
); err != nil {
|
|
thisErr := fmt.Errorf("Building credential via client certificate failed: %v", err)
|
|
logger.Warn(thisErr.Error())
|
|
errors = multierror.Append(errors, thisErr)
|
|
} else {
|
|
logger.Info("Successfully built credential via client certificate")
|
|
creds = append(creds, cred)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !opt.AuthConfig.UseOIDC {
|
|
logger.Info("OIDC credential skipped")
|
|
} else {
|
|
logger.Info("Building credential via OIDC")
|
|
if cred, err := NewOidcCredential(&OidcCredentialOptions{
|
|
ClientOptions: opt.ClientOptions,
|
|
TenantID: opt.AuthConfig.TenantID,
|
|
ClientID: opt.AuthConfig.ClientID,
|
|
RequestToken: opt.AuthConfig.OIDCTokenRequestToken,
|
|
RequestUrl: opt.AuthConfig.OIDCTokenRequestURL,
|
|
Token: opt.AuthConfig.OIDCAssertionToken,
|
|
}); err != nil {
|
|
thisErr := fmt.Errorf("Building credential via OIDC failed: %v", err)
|
|
logger.Warn(thisErr.Error())
|
|
errors = multierror.Append(errors, thisErr)
|
|
} else {
|
|
logger.Info("Successfully built credential via OIDC")
|
|
creds = append(creds, cred)
|
|
}
|
|
}
|
|
|
|
if !opt.AuthConfig.UseManagedIdentity {
|
|
logger.Info("managed identity credential skipped")
|
|
} else {
|
|
logger.Info("Building credential via managed identity")
|
|
if cred, err := azidentity.NewManagedIdentityCredential(&azidentity.ManagedIdentityCredentialOptions{
|
|
ClientOptions: opt.ClientOptions,
|
|
ID: azidentity.ClientID(opt.AuthConfig.ClientID),
|
|
}); err != nil {
|
|
thisErr := fmt.Errorf("Building credential via managed identity failed: %v", err)
|
|
logger.Warn(thisErr.Error())
|
|
errors = multierror.Append(errors, thisErr)
|
|
} else {
|
|
logger.Info("Successfully built credential via managed identity")
|
|
creds = append(creds, cred)
|
|
}
|
|
}
|
|
|
|
if !opt.AuthConfig.UseAzureCLI {
|
|
logger.Info("Azure CLI credential skipped")
|
|
} else {
|
|
logger.Info("Building credential via Azure CLI")
|
|
if cred, err := azidentity.NewAzureCLICredential(&azidentity.AzureCLICredentialOptions{
|
|
AdditionallyAllowedTenants: opt.AuthConfig.AuxiliaryTenantIDs,
|
|
TenantID: opt.AuthConfig.TenantID,
|
|
}); err != nil {
|
|
thisErr := fmt.Errorf("Building credential via Azure CLI failed: %v", err)
|
|
logger.Warn(thisErr.Error())
|
|
errors = multierror.Append(errors, thisErr)
|
|
} else {
|
|
logger.Info("Successfully built credential via Azure CLI")
|
|
creds = append(creds, cred)
|
|
}
|
|
}
|
|
|
|
chain, err := azidentity.NewChainedTokenCredential(creds, nil)
|
|
if err != nil {
|
|
err = multierror.Append(err, fmt.Errorf("Errors from credential build tries: %v", errors))
|
|
return nil, err
|
|
}
|
|
return &DefaultAzureCredential{chain: chain}, nil
|
|
}
|
|
|
|
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
|
func (c *DefaultAzureCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
|
return c.chain.GetToken(ctx, opts)
|
|
}
|
|
|
|
var _ azcore.TokenCredential = (*DefaultAzureCredential)(nil)
|