-
Notifications
You must be signed in to change notification settings - Fork 189
/
cred.go
171 lines (157 loc) · 6.43 KB
/
cred.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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)