This repository has been archived by the owner on Nov 30, 2023. It is now read-only.
/
credential.go
124 lines (104 loc) · 4.68 KB
/
credential.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
package credential
import (
"context"
"github.com/Azure/go-autorest/autorest/azure/auth"
"github.com/giantswarm/k8sclient/v7/pkg/k8sclient"
"github.com/giantswarm/microerror"
"github.com/giantswarm/micrologger"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/giantswarm/azure-operator/v5/pkg/label"
)
const (
clientIDKey = "azure.azureoperator.clientid"
clientSecretKey = "azure.azureoperator.clientsecret"
defaultAzureGUID = "37f13270-5c7a-56ff-9211-8426baaeaabd"
partnerIDKey = "azure.azureoperator.partnerid"
subscriptionIDKey = "azure.azureoperator.subscriptionid"
tenantIDKey = "azure.azureoperator.tenantid"
)
type K8SCredential struct {
k8sclient k8sclient.Interface
gsTenantID string
logger micrologger.Logger
}
func NewK8SCredentialProvider(k8sclient k8sclient.Interface, gsTenantID string, logger micrologger.Logger) Provider {
return K8SCredential{
k8sclient: k8sclient,
gsTenantID: gsTenantID,
logger: logger,
}
}
// GetOrganizationAzureCredentials returns the organization's credentials.
// This means a configured `ClientCredentialsConfig` together with the subscription ID and the partner ID.
// The Service Principals in the organizations' secrets will always belong the the GiantSwarm Tenant ID in `gsTenantID`.
func (k K8SCredential) GetOrganizationAzureCredentials(ctx context.Context, credentialNamespace, credentialName string) (auth.ClientCredentialsConfig, string, string, error) {
secret := &v1.Secret{}
err := k.k8sclient.CtrlClient().Get(ctx, client.ObjectKey{Namespace: credentialNamespace, Name: credentialName}, secret)
if err != nil {
return auth.ClientCredentialsConfig{}, "", "", microerror.Mask(err)
}
clientID, err := valueFromSecret(secret, clientIDKey)
if err != nil {
return auth.ClientCredentialsConfig{}, "", "", microerror.Mask(err)
}
clientSecret, err := valueFromSecret(secret, clientSecretKey)
if err != nil {
return auth.ClientCredentialsConfig{}, "", "", microerror.Mask(err)
}
tenantID, err := valueFromSecret(secret, tenantIDKey)
if err != nil {
return auth.ClientCredentialsConfig{}, "", "", microerror.Mask(err)
}
subscriptionID, err := valueFromSecret(secret, subscriptionIDKey)
if err != nil {
return auth.ClientCredentialsConfig{}, "", "", microerror.Mask(err)
}
partnerID, err := valueFromSecret(secret, partnerIDKey)
if err != nil {
// No having Partner ID in the secret means that customer has not
// upgraded yet to use the Azure Partner Program. In that case we set a
// constant random generated GUID that we haven't registered with Azure.
// When all customers have migrated, we should error out instead.
partnerID = defaultAzureGUID
}
if _, exists := secret.GetLabels()[label.SingleTenantSP]; exists {
return auth.ClientCredentialsConfig{}, "", "", microerror.Maskf(oldStyleCredentialsError, "This version of azure operator requires multi tenant service principal setup (secret %q has label %q)", secret.Name, label.SingleTenantSP)
}
credentials := auth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
if tenantID == k.gsTenantID {
k.logger.Debugf(ctx, "Azure subscription %#q belongs to the same tenant ID %#q that owns the service principal. Using single tenant authentication", subscriptionID, tenantID)
} else {
k.logger.Debugf(ctx, "Azure subscription %#q belongs to the tenant ID %#q which is different than the Tenant ID %#q that owns the Service Principal. Using multi tenant authentication", subscriptionID, tenantID, k.gsTenantID)
credentials.AuxTenants = append(credentials.AuxTenants, k.gsTenantID)
}
return credentials, subscriptionID, partnerID, nil
}
func valueFromSecret(secret *v1.Secret, key string) (string, error) {
v, ok := secret.Data[key]
if !ok {
return "", microerror.Maskf(missingValueError, key)
}
return string(v), nil
}
// NewAzureCredentials returns a `ClientCredentialsConfig` configured taking values from Environment, but parameters
// have precedence over environment variables.
func NewAzureCredentials(clientID, clientSecret, tenantID string) (auth.ClientCredentialsConfig, error) {
settings, err := auth.GetSettingsFromEnvironment()
if err != nil {
return auth.ClientCredentialsConfig{}, microerror.Mask(err)
}
if clientID != "" {
settings.Values[auth.ClientID] = clientID
}
if clientSecret != "" {
settings.Values[auth.ClientSecret] = clientSecret
}
if tenantID != "" {
settings.Values[auth.TenantID] = tenantID
}
if settings.Values[auth.ClientID] == "" || settings.Values[auth.ClientSecret] == "" || settings.Values[auth.TenantID] == "" {
return auth.ClientCredentialsConfig{}, microerror.Maskf(invalidConfigError, "credentials must not be empty")
}
return settings.GetClientCredentials()
}