Skip to content

Commit ba27bba

Browse files
authored
Merge pull request #210 from patst/feat/workload-identity
Allow Azure Workload identity authentication
2 parents c56290b + f6ffdb8 commit ba27bba

File tree

5 files changed

+70
-19
lines changed

5 files changed

+70
-19
lines changed

apis/v1alpha1/types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ const (
5151
IdentityTypeGoogleApplicationCredentials = "GoogleApplicationCredentials"
5252

5353
IdentityTypeAzureServicePrincipalCredentials = "AzureServicePrincipalCredentials"
54+
55+
IdentityTypeAzureWorkloadIdentityCredentials = "AzureWorkloadIdentityCredentials"
5456
)
5557

5658
// Identity used to authenticate.
5759
type Identity struct {
5860
// Type of identity.
59-
// +kubebuilder:validation:Enum=GoogleApplicationCredentials;AzureServicePrincipalCredentials
61+
// +kubebuilder:validation:Enum=GoogleApplicationCredentials;AzureServicePrincipalCredentials;AzureWorkloadIdentityCredentials
6062
Type IdentityType `json:"type"`
6163

6264
ProviderCredentials `json:",inline"`
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
apiVersion: kubernetes.crossplane.io/v1alpha1
2+
kind: ProviderConfig
3+
metadata:
4+
name: kubernetes-provider
5+
spec:
6+
credentials:
7+
source: Secret
8+
secretRef:
9+
namespace: crossplane-system
10+
name: cluster-config
11+
key: kubeconfig
12+
identity:
13+
type: AzureWorkloadIdentityCredentials
14+
source: Secret
15+
secretRef:
16+
name: azure-credentials
17+
namespace: crossplane-system
18+
key: credentials.json
19+
---
20+
apiVersion: v1
21+
kind: Secret
22+
metadata:
23+
name: azure-credentials
24+
namespace: crossplane-system
25+
stringData:
26+
# serverId hardcoded to AKS ID, see https://azure.github.io/kubelogin/concepts/aks.html#azure-kubernetes-service-aad-server
27+
credentials.json: |
28+
{
29+
"tenantId": "<aad-tenant-id>",
30+
"serverId": "6dae42f8-4368-4678-94ff-3960e28e3630",
31+
"clientId": "<client-id>",
32+
"federatedTokenFile": "/var/run/secrets/azure/tokens/azure-identity-token",
33+
"authorityHost": "https://login.microsoftonline.com/"
34+
}

internal/clients/azure/azure.go

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,25 @@ import (
99
"github.com/pkg/errors"
1010
"github.com/spf13/pflag"
1111
"k8s.io/client-go/rest"
12+
13+
apisv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1"
1214
)
1315

1416
// Credentials Secret content is a json whose keys are below.
1517
const (
16-
CredentialsKeyClientID = "clientId"
17-
CredentialsKeyClientSecret = "clientSecret"
18-
CredentialsKeyTenantID = "tenantId"
19-
CredentialsKeyClientCert = "clientCertificate"
20-
CredentialsKeyClientCertPass = "clientCertificatePassword"
18+
CredentialsKeyClientID = "clientId"
19+
CredentialsKeyClientSecret = "clientSecret"
20+
CredentialsKeyTenantID = "tenantId"
21+
CredentialsKeyClientCert = "clientCertificate"
22+
CredentialsKeyClientCertPass = "clientCertificatePassword"
23+
CredentialsKeyFederatedTokenFile = "federatedTokenFile"
24+
CredentialsKeyAuthorityHost = "authorityHost"
25+
CredentialsKeyServerID = "serverId"
2126
)
2227

2328
// WrapRESTConfig configures the supplied REST config to use OAuth2 bearer
2429
// tokens fetched using the supplied Azure Credentials.
25-
func WrapRESTConfig(_ context.Context, rc *rest.Config, credentials []byte, _ ...string) error {
30+
func WrapRESTConfig(_ context.Context, rc *rest.Config, credentials []byte, identityType apisv1alpha1.IdentityType, _ ...string) error { // nolint:gocyclo // todo: refactor
2631
m := map[string]string{}
2732
if err := json.Unmarshal(credentials, &m); err != nil {
2833
return err
@@ -44,21 +49,30 @@ func WrapRESTConfig(_ context.Context, rc *rest.Config, credentials []byte, _ ..
4449
return errors.Wrap(err, "could not parse execProvider arguments in kubeconfig")
4550
}
4651
rc.ExecProvider = nil
47-
// TODO: support other login methods like MSI, Workload Identity in the future
48-
opts.LoginMethod = token.ServicePrincipalLogin
49-
opts.ClientID = m[CredentialsKeyClientID]
50-
opts.ClientSecret = m[CredentialsKeyClientSecret]
51-
opts.TenantID = m[CredentialsKeyTenantID]
52-
if cert, ok := m[CredentialsKeyClientCert]; ok {
53-
opts.ClientCert = cert
54-
if certpass, ok2 := m[CredentialsKeyClientCertPass]; ok2 {
55-
opts.ClientCertPassword = certpass
52+
switch identityType {
53+
case apisv1alpha1.IdentityTypeAzureServicePrincipalCredentials:
54+
opts.LoginMethod = token.ServicePrincipalLogin
55+
opts.ClientID = m[CredentialsKeyClientID]
56+
opts.ClientSecret = m[CredentialsKeyClientSecret]
57+
opts.TenantID = m[CredentialsKeyTenantID]
58+
if cert, ok := m[CredentialsKeyClientCert]; ok {
59+
opts.ClientCert = cert
60+
if certpass, ok2 := m[CredentialsKeyClientCertPass]; ok2 {
61+
opts.ClientCertPassword = certpass
62+
}
5663
}
64+
case apisv1alpha1.IdentityTypeAzureWorkloadIdentityCredentials:
65+
opts.LoginMethod = token.WorkloadIdentityLogin
66+
opts.ClientID = m[CredentialsKeyClientID]
67+
opts.TenantID = m[CredentialsKeyTenantID]
68+
opts.ServerID = m[CredentialsKeyServerID]
69+
opts.FederatedTokenFile = m[CredentialsKeyFederatedTokenFile]
70+
opts.AuthorityHost = m[CredentialsKeyAuthorityHost]
5771
}
5872

5973
p, err := token.NewTokenProvider(&opts)
6074
if err != nil {
61-
return errors.New("cannot build azure token provider")
75+
return errors.Wrap(err, "cannot build azure token provider")
6276
}
6377

6478
rc.Wrap(func(rt http.RoundTripper) http.RoundTripper {

internal/clients/kube/kube.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func configForProvider(ctx context.Context, local client.Client, providerConfigN
105105
return nil, errors.Wrap(err, errInjectGoogleCredentials)
106106
}
107107
}
108-
case v1alpha1.IdentityTypeAzureServicePrincipalCredentials:
108+
case v1alpha1.IdentityTypeAzureServicePrincipalCredentials, v1alpha1.IdentityTypeAzureWorkloadIdentityCredentials:
109109
switch id.Source { //nolint:exhaustive
110110
case xpv1.CredentialsSourceInjectedIdentity:
111111
return nil, errors.Errorf("%s is not supported as identity source for identity type %s",
@@ -116,7 +116,7 @@ func configForProvider(ctx context.Context, local client.Client, providerConfigN
116116
return nil, errors.Wrap(err, errExtractAzureCredentials)
117117
}
118118

119-
if err := azure.WrapRESTConfig(ctx, rc, creds); err != nil {
119+
if err := azure.WrapRESTConfig(ctx, rc, creds, id.Type); err != nil {
120120
return nil, errors.Wrap(err, errInjectAzureCredentials)
121121
}
122122
}

package/crds/kubernetes.crossplane.io_providerconfigs.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ spec:
170170
enum:
171171
- GoogleApplicationCredentials
172172
- AzureServicePrincipalCredentials
173+
- AzureWorkloadIdentityCredentials
173174
type: string
174175
required:
175176
- source

0 commit comments

Comments
 (0)