Skip to content

Commit b812767

Browse files
enxebrebryan-cox
authored andcommitted
Add the ability to auth via certs without storing them in etcd secret
Signed-off-by: Bryan Cox <[email protected]>
1 parent 85def75 commit b812767

File tree

6 files changed

+112
-12
lines changed

6 files changed

+112
-12
lines changed

api/v1beta1/azureclusteridentity_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type AzureClusterIdentitySpec struct {
5959
// ClientSecret is a secret reference which should contain either a Service Principal password or certificate secret.
6060
// +optional
6161
ClientSecret corev1.SecretReference `json:"clientSecret,omitempty"`
62-
// certPath is the path where certificates exist. When set, it takes precedence over ClientSecret for types that uses certs like ServicePrincipalCertificate.
62+
// CertPath is the path where certificates exist. When set, it takes precedence over ClientSecret for types that use certs like ServicePrincipalCertificate.
6363
// +optional
6464
CertPath string `json:"certPath,omitempty"`
6565
// TenantID is the service principal primary tenant id.

azure/scope/identity.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package scope
1818

1919
import (
2020
"context"
21+
"os"
2122
"reflect"
2223

2324
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
@@ -127,11 +128,20 @@ func (p *AzureCredentialsProvider) GetTokenCredential(ctx context.Context, resou
127128
cred, authErr = azidentity.NewClientSecretCredential(p.GetTenantID(), p.Identity.Spec.ClientID, clientSecret, &options)
128129

129130
case infrav1.ServicePrincipalCertificate:
130-
clientSecret, err := p.GetClientSecret(ctx)
131-
if err != nil {
132-
return nil, errors.Wrap(err, "failed to get client secret")
131+
var certsContent []byte
132+
if p.Identity.Spec.CertPath != "" {
133+
certsContent, err = os.ReadFile(p.Identity.Spec.CertPath)
134+
if err != nil {
135+
return nil, errors.Wrap(err, "failed to read certificate file")
136+
}
137+
} else {
138+
clientSecret, err := p.GetClientSecret(ctx)
139+
if err != nil {
140+
return nil, errors.Wrap(err, "failed to get client secret")
141+
}
142+
certsContent = []byte(clientSecret)
133143
}
134-
certs, key, err := azidentity.ParseCertificates([]byte(clientSecret), nil)
144+
certs, key, err := azidentity.ParseCertificates(certsContent, nil)
135145
if err != nil {
136146
return nil, errors.Wrap(err, "failed to parse certificate data")
137147
}
@@ -200,7 +210,7 @@ func (p *AzureCredentialsProvider) Type() infrav1.IdentityType {
200210
// This does not include managed identities.
201211
func (p *AzureCredentialsProvider) hasClientSecret() bool {
202212
switch p.Identity.Spec.Type {
203-
case infrav1.ServicePrincipal, infrav1.ManualServicePrincipal, infrav1.ServicePrincipalCertificate:
213+
case infrav1.ServicePrincipal, infrav1.ManualServicePrincipal:
204214
return true
205215
default:
206216
return false

azure/scope/identity_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,10 @@ func TestHasClientSecret(t *testing.T) {
163163
name: "service principal with certificate",
164164
identity: &infrav1.AzureClusterIdentity{
165165
Spec: infrav1.AzureClusterIdentitySpec{
166-
Type: infrav1.ServicePrincipalCertificate,
167-
ClientSecret: corev1.SecretReference{Name: "my-client-secret"},
166+
Type: infrav1.ServicePrincipalCertificate,
168167
},
169168
},
170-
want: true,
169+
want: false,
171170
},
172171
{
173172
name: "manual service principal",
@@ -316,6 +315,25 @@ func TestGetTokenCredential(t *testing.T) {
316315
},
317316
},
318317
},
318+
{
319+
name: "service principal certificate with certificate filepath",
320+
cluster: &infrav1.AzureCluster{
321+
Spec: infrav1.AzureClusterSpec{
322+
AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
323+
IdentityRef: &corev1.ObjectReference{
324+
Kind: infrav1.AzureClusterIdentityKind,
325+
},
326+
},
327+
},
328+
},
329+
identity: &infrav1.AzureClusterIdentity{
330+
Spec: infrav1.AzureClusterIdentitySpec{
331+
Type: infrav1.ServicePrincipalCertificate,
332+
TenantID: fakeTenantID,
333+
CertPath: "../../test/setup/certificate",
334+
},
335+
},
336+
},
319337
{
320338
name: "user-assigned identity",
321339
cluster: &infrav1.AzureCluster{

config/crd/bases/infrastructure.cluster.x-k8s.io_azureclusteridentities.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ spec:
124124
x-kubernetes-map-type: atomic
125125
type: object
126126
certPath:
127-
description: certPath is the path where certificates exist. When set,
128-
it takes precedence over ClientSecret for types that uses certs
129-
like ServicePrincipalCertificate.
127+
description: CertPath is the path where certificates exist. When set,
128+
it takes precedence over ClientSecret for types that use certs like
129+
ServicePrincipalCertificate.
130130
type: string
131131
clientID:
132132
description: |-

controllers/asosecret_controller_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,31 @@ func TestASOSecretReconcile(t *testing.T) {
135135
}
136136
}),
137137
},
138+
"should reconcile normally for AzureManagedControlPlane with IdentityRef configured of type Service Principal with Certificate": {
139+
clusterName: defaultAzureManagedControlPlane.Name,
140+
objects: []runtime.Object{
141+
getASOAzureManagedControlPlane(func(c *infrav1.AzureManagedControlPlane) {
142+
c.Spec.IdentityRef = &corev1.ObjectReference{
143+
Name: "my-azure-cluster-identity",
144+
Namespace: "default",
145+
}
146+
}),
147+
getASOAzureClusterIdentity(func(identity *infrav1.AzureClusterIdentity) {
148+
identity.Spec.Type = infrav1.ServicePrincipalCertificate
149+
identity.Spec.CertPath = "../test/setup/certificate"
150+
}),
151+
defaultCluster,
152+
},
153+
asoSecret: getASOSecret(defaultAzureManagedControlPlane, func(s *corev1.Secret) {
154+
s.Data = map[string][]byte{
155+
"AZURE_SUBSCRIPTION_ID": []byte("fooSubscription"),
156+
"AZURE_TENANT_ID": []byte("fooTenant"),
157+
"AZURE_CLIENT_ID": []byte("fooClient"),
158+
"AZURE_CLIENT_CERTIFICATE_PASSWORD": []byte(""),
159+
"AZURE_CLIENT_CERTIFICATE": []byte("-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDjrdEr9P0TaUES\ndspE6cyo22NU8yhRrbYlV9VH2vWvnPsThXcxhnd+cUqdNEBswhwgFlUQcg/eSVxw\nrr+3nh+bFTZWPcY+1LQYxfpKGsrCXQfB82LDJIZDX4gHYrWf3Z272jXN1XeFAKti\nwDKgDXXuPH7r5lH7vC3RXeAffqLwQJhZf+NoHNtv9MH9IdUkQfmDFZtI/CQzCrb6\n+vOS6EmUD/Q2FNHBzgxCguGqgNyBcQbxJ9Qng+ZjIFuhGYXJlsyRUtexyzTR5/v0\nVNK8UsZgRBFhXqrBv/RoCCG+xVJYtmd0QsrvNzDqG6QnjUB21zVXqzKEkW2gRtjX\ncw4vYQehAgMBAAECggEAS6xtjg0nAokk0jS+ZOpKlkMZAFaza3ZvyHipkHDz4PMt\ntl7Rb5oQZGvWT2rbEOrxey7BBi7LHGhIu8ExQp/hRGPoBAETP7XlyCghWPkPtEtE\ndU/mXxLoN0NszHuf/2si7pmH8YqGZ6QB0tgr22ut60mbK+AJFsEEf4aSpBUspepJ\n2800sQHsqPE6L6kYkfZ2GRRY1V9vUrYEODKZpWzMhN3UA9nAKH9PB6xvP2OdyMNh\nhKgmUUMNIFtwr8pZlJn60cf0UrWrc5CvqQLuaGYlzDgUQGV4JEVjqm9F6lMfEPUw\neN70MVe1pcLeLq2rGCVWU3gakh/HvJqlR/sa546HgwKBgQDyf1vkyX4w5sboi6DJ\ncl5dMULtMMRpB1OaMFVOJjI9gZJ8mCdRjqXdYo5aS2KIqxie8tGG9+SohxDAWl4t\nlSUtDsE44fSmILqC5zIawNRQnnkv0X8LwmYu0Qd7YAjJMlLTWyDRsjD9XRq4nsR+\nmJVwrt85iSpS5UFyryEzPbFj0wKBgQDwWzraeN0Eccf1iIYmQsYy+yMEAlHNR5yi\ngPXuAhSybv2JReRhdUb39hLr/LvKw0ZeXiLWXmYUGpbyzPyXIm0s+PL3LWl65GTF\nl+cfV5wfAdDkk6rAdEPEE2pxN85ChyaPYPoYr0ohmV97VQcYc5FqY+j1tM6R1RDt\n/fWBSa8iOwKBgQCpa1dtWWTDj4gqUdrswu2wmEkU47xlUIwVLm164u64z/zi9X6K\n2WmCaWfhJ8fYigjyi9zdOfXT1EFc0gX4PLozZ5qRPjQpmLYV3KbB0DTFemJaiTgE\npDW1wa5DgQ3CW1lIduNP/fmCGfkgQTQw6jOF/XbRgMZEEg2OrVI5tYFopwKBgER9\niqjEth5VGejCjY+LiZTvcUvsKUk4tc6stueqmiE6dW7PhsOqup1f9oZej1i5Cm1L\nn9u8LJRf+1GWzgd3HOsqyXlb7GnDeV/A6HBK88b2KoNn/Mk4mDLgYX1/rHvSrU9A\nECRGlvY6ETZAxXPXQsGxVKnnatGtiFR5AKNlzs0PAoGAa5+X+DUqGh9aE5ID3wrv\njkjxQ2KLFJCNSq8f9GSuvpvgXstHh6wKoM6vMwIShjgXuURH8Ub4uhRsWnxMildF\n7EE+QaWU9jnCm2HQYArfXrAWw6DBudiSkBqgKc6HjDHun5fXlYUo8UesNMQOrg7b\nbydQZ5/4V/1oSWPETk7jSr0=\n-----END PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIDCTCCAfGgAwIBAgIUFSntEn+Tv6HM2xJReECJpJcC7iUwDQYJKoZIhvcNAQEL\nBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDEwODE5NTQxNFoXDTM0MDEw\nNTE5NTQxNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEA463RK/T9E2lBEnbKROnMqNtjVPMoUa22JVfVR9r1r5z7\nE4V3MYZ3fnFKnTRAbMIcIBZVEHIP3klccK6/t54fmxU2Vj3GPtS0GMX6ShrKwl0H\nwfNiwySGQ1+IB2K1n92du9o1zdV3hQCrYsAyoA117jx+6+ZR+7wt0V3gH36i8ECY\nWX/jaBzbb/TB/SHVJEH5gxWbSPwkMwq2+vrzkuhJlA/0NhTRwc4MQoLhqoDcgXEG\n8SfUJ4PmYyBboRmFyZbMkVLXscs00ef79FTSvFLGYEQRYV6qwb/0aAghvsVSWLZn\ndELK7zcw6hukJ41Adtc1V6syhJFtoEbY13MOL2EHoQIDAQABo1MwUTAdBgNVHQ4E\nFgQUfry/KDtamwMlRQsFPbBhzdv2U5cwHwYDVR0jBBgwFoAUfry/KDtamwMlRQsF\nPbBhzdv2U5cwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAyYst\nVvewKRRpuYRWc4XG6WnYphUdyZLMoIlq0syZ1aj6YbqoK9NMHAYEnCvSov6zIZOa\ntrhuUcf9GFz5e0iJ2zIlDc312Iwsv41xiC/bs16kEn8Yf/SujEXasj7vmA3HrFWf\nwZTH/yFL5azo/f+lA1Q28YwqFpHmle0y0O53Uth4p0tmwlnu+CrO9fHp3kTlb7fD\n6mqfk9Nrt8tOC4aHYDoqtYUgZhx58xsHMOTetKeRlp8HMF9oROtriz4nYm6IhTwo\n5k1A13S3BjaxkZCyPXCgXssuXagNLasrr5Qq+Vgdb/nDhVehV8+Z4J0Ynzy9MZsE\nH1N1NfMtsA+PEqtPXA==\n-----END CERTIFICATE-----\n"),
160+
}
161+
}),
162+
},
138163
"should reconcile normally for AzureCluster with an IdentityRef of type WorkloadIdentity": {
139164
clusterName: defaultAzureCluster.Name,
140165
objects: []runtime.Object{

test/setup/certificate

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDjrdEr9P0TaUES
3+
dspE6cyo22NU8yhRrbYlV9VH2vWvnPsThXcxhnd+cUqdNEBswhwgFlUQcg/eSVxw
4+
rr+3nh+bFTZWPcY+1LQYxfpKGsrCXQfB82LDJIZDX4gHYrWf3Z272jXN1XeFAKti
5+
wDKgDXXuPH7r5lH7vC3RXeAffqLwQJhZf+NoHNtv9MH9IdUkQfmDFZtI/CQzCrb6
6+
+vOS6EmUD/Q2FNHBzgxCguGqgNyBcQbxJ9Qng+ZjIFuhGYXJlsyRUtexyzTR5/v0
7+
VNK8UsZgRBFhXqrBv/RoCCG+xVJYtmd0QsrvNzDqG6QnjUB21zVXqzKEkW2gRtjX
8+
cw4vYQehAgMBAAECggEAS6xtjg0nAokk0jS+ZOpKlkMZAFaza3ZvyHipkHDz4PMt
9+
tl7Rb5oQZGvWT2rbEOrxey7BBi7LHGhIu8ExQp/hRGPoBAETP7XlyCghWPkPtEtE
10+
dU/mXxLoN0NszHuf/2si7pmH8YqGZ6QB0tgr22ut60mbK+AJFsEEf4aSpBUspepJ
11+
2800sQHsqPE6L6kYkfZ2GRRY1V9vUrYEODKZpWzMhN3UA9nAKH9PB6xvP2OdyMNh
12+
hKgmUUMNIFtwr8pZlJn60cf0UrWrc5CvqQLuaGYlzDgUQGV4JEVjqm9F6lMfEPUw
13+
eN70MVe1pcLeLq2rGCVWU3gakh/HvJqlR/sa546HgwKBgQDyf1vkyX4w5sboi6DJ
14+
cl5dMULtMMRpB1OaMFVOJjI9gZJ8mCdRjqXdYo5aS2KIqxie8tGG9+SohxDAWl4t
15+
lSUtDsE44fSmILqC5zIawNRQnnkv0X8LwmYu0Qd7YAjJMlLTWyDRsjD9XRq4nsR+
16+
mJVwrt85iSpS5UFyryEzPbFj0wKBgQDwWzraeN0Eccf1iIYmQsYy+yMEAlHNR5yi
17+
gPXuAhSybv2JReRhdUb39hLr/LvKw0ZeXiLWXmYUGpbyzPyXIm0s+PL3LWl65GTF
18+
l+cfV5wfAdDkk6rAdEPEE2pxN85ChyaPYPoYr0ohmV97VQcYc5FqY+j1tM6R1RDt
19+
/fWBSa8iOwKBgQCpa1dtWWTDj4gqUdrswu2wmEkU47xlUIwVLm164u64z/zi9X6K
20+
2WmCaWfhJ8fYigjyi9zdOfXT1EFc0gX4PLozZ5qRPjQpmLYV3KbB0DTFemJaiTgE
21+
pDW1wa5DgQ3CW1lIduNP/fmCGfkgQTQw6jOF/XbRgMZEEg2OrVI5tYFopwKBgER9
22+
iqjEth5VGejCjY+LiZTvcUvsKUk4tc6stueqmiE6dW7PhsOqup1f9oZej1i5Cm1L
23+
n9u8LJRf+1GWzgd3HOsqyXlb7GnDeV/A6HBK88b2KoNn/Mk4mDLgYX1/rHvSrU9A
24+
ECRGlvY6ETZAxXPXQsGxVKnnatGtiFR5AKNlzs0PAoGAa5+X+DUqGh9aE5ID3wrv
25+
jkjxQ2KLFJCNSq8f9GSuvpvgXstHh6wKoM6vMwIShjgXuURH8Ub4uhRsWnxMildF
26+
7EE+QaWU9jnCm2HQYArfXrAWw6DBudiSkBqgKc6HjDHun5fXlYUo8UesNMQOrg7b
27+
bydQZ5/4V/1oSWPETk7jSr0=
28+
-----END PRIVATE KEY-----
29+
-----BEGIN CERTIFICATE-----
30+
MIIDCTCCAfGgAwIBAgIUFSntEn+Tv6HM2xJReECJpJcC7iUwDQYJKoZIhvcNAQEL
31+
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDEwODE5NTQxNFoXDTM0MDEw
32+
NTE5NTQxNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
33+
AAOCAQ8AMIIBCgKCAQEA463RK/T9E2lBEnbKROnMqNtjVPMoUa22JVfVR9r1r5z7
34+
E4V3MYZ3fnFKnTRAbMIcIBZVEHIP3klccK6/t54fmxU2Vj3GPtS0GMX6ShrKwl0H
35+
wfNiwySGQ1+IB2K1n92du9o1zdV3hQCrYsAyoA117jx+6+ZR+7wt0V3gH36i8ECY
36+
WX/jaBzbb/TB/SHVJEH5gxWbSPwkMwq2+vrzkuhJlA/0NhTRwc4MQoLhqoDcgXEG
37+
8SfUJ4PmYyBboRmFyZbMkVLXscs00ef79FTSvFLGYEQRYV6qwb/0aAghvsVSWLZn
38+
dELK7zcw6hukJ41Adtc1V6syhJFtoEbY13MOL2EHoQIDAQABo1MwUTAdBgNVHQ4E
39+
FgQUfry/KDtamwMlRQsFPbBhzdv2U5cwHwYDVR0jBBgwFoAUfry/KDtamwMlRQsF
40+
PbBhzdv2U5cwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAyYst
41+
VvewKRRpuYRWc4XG6WnYphUdyZLMoIlq0syZ1aj6YbqoK9NMHAYEnCvSov6zIZOa
42+
trhuUcf9GFz5e0iJ2zIlDc312Iwsv41xiC/bs16kEn8Yf/SujEXasj7vmA3HrFWf
43+
wZTH/yFL5azo/f+lA1Q28YwqFpHmle0y0O53Uth4p0tmwlnu+CrO9fHp3kTlb7fD
44+
6mqfk9Nrt8tOC4aHYDoqtYUgZhx58xsHMOTetKeRlp8HMF9oROtriz4nYm6IhTwo
45+
5k1A13S3BjaxkZCyPXCgXssuXagNLasrr5Qq+Vgdb/nDhVehV8+Z4J0Ynzy9MZsE
46+
H1N1NfMtsA+PEqtPXA==
47+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)