Skip to content

Commit 8c472e0

Browse files
committed
adjust the provider to use the available service Account annotations instead of requiring it again in the SPC parameters
1 parent 2fc9b5f commit 8c472e0

File tree

10 files changed

+199
-23
lines changed

10 files changed

+199
-23
lines changed

charts/csi-secrets-store-provider-azure/templates/role.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,27 @@ rules:
4747
verbs: ["get", "watch", "list"]
4848
{{- end }}
4949
{{- end }}
50+
---
51+
apiVersion: rbac.authorization.k8s.io/v1
52+
kind: ClusterRoleBinding
53+
metadata:
54+
name: {{ template "sscdpa.fullname" . }}-cluster-role-binding
55+
{{ include "sscdpa.labels" . | indent 2 }}
56+
roleRef:
57+
apiGroup: rbac.authorization.k8s.io
58+
kind: ClusterRole
59+
name: {{ template "sscdpa.fullname" . }}-cluster-role
60+
subjects:
61+
- kind: ServiceAccount
62+
name: csi-secrets-store-provider-azure
63+
namespace: {{ .Release.Namespace }}
64+
---
65+
apiVersion: rbac.authorization.k8s.io/v1
66+
kind: ClusterRole
67+
metadata:
68+
name: {{ template "sscdpa.fullname" . }}-cluster-role
69+
{{ include "sscdpa.labels" . | indent 2 }}
70+
rules:
71+
- apiGroups: [""]
72+
resources: ["serviceaccounts"]
73+
verbs: ["get"]

go.mod

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ require (
2323
golang.org/x/net v0.17.0
2424
google.golang.org/grpc v1.59.0
2525
gopkg.in/yaml.v3 v3.0.1
26+
k8s.io/client-go v0.25.3
2627
k8s.io/component-base v0.25.3
2728
k8s.io/klog/v2 v2.80.1
2829
sigs.k8s.io/secrets-store-csi-driver v1.3.4
@@ -35,18 +36,32 @@ require (
3536
github.com/Azure/go-autorest/logger v0.2.1 // indirect
3637
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
3738
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
39+
github.com/PuerkitoBio/purell v1.1.1 // indirect
40+
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
3841
github.com/beorn7/perks v1.0.1 // indirect
3942
github.com/cespare/xxhash/v2 v2.2.0 // indirect
4043
github.com/davecgh/go-spew v1.1.1 // indirect
44+
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
4145
github.com/go-logr/logr v1.2.3 // indirect
4246
github.com/go-logr/zapr v1.2.3 // indirect
47+
github.com/go-openapi/jsonpointer v0.19.5 // indirect
48+
github.com/go-openapi/jsonreference v0.19.5 // indirect
49+
github.com/go-openapi/swag v0.19.14 // indirect
4350
github.com/gogo/protobuf v1.3.2 // indirect
4451
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
4552
github.com/golang/protobuf v1.5.3 // indirect
53+
github.com/google/gnostic v0.5.7-v3refs // indirect
54+
github.com/google/gofuzz v1.2.0 // indirect
4655
github.com/google/uuid v1.3.1 // indirect
4756
github.com/inconshreveable/mousetrap v1.0.1 // indirect
57+
github.com/josharian/intern v1.0.0 // indirect
58+
github.com/json-iterator/go v1.1.12 // indirect
4859
github.com/kylelemons/godebug v1.1.0 // indirect
60+
github.com/mailru/easyjson v0.7.6 // indirect
4961
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
62+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
63+
github.com/modern-go/reflect2 v1.0.2 // indirect
64+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
5065
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
5166
github.com/pmezard/go-difflib v1.0.0 // indirect
5267
github.com/prometheus/client_golang v1.12.2 // indirect
@@ -62,10 +77,21 @@ require (
6277
go.uber.org/atomic v1.10.0 // indirect
6378
go.uber.org/multierr v1.8.0 // indirect
6479
go.uber.org/zap v1.24.0 // indirect
80+
golang.org/x/oauth2 v0.11.0 // indirect
6581
golang.org/x/sys v0.16.0 // indirect
82+
golang.org/x/term v0.16.0 // indirect
6683
golang.org/x/text v0.14.0 // indirect
84+
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
85+
google.golang.org/appengine v1.6.7 // indirect
6786
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
6887
google.golang.org/protobuf v1.31.0 // indirect
6988
gopkg.in/inf.v0 v0.9.1 // indirect
89+
gopkg.in/yaml.v2 v2.4.0 // indirect
90+
k8s.io/api v0.25.3 // indirect
7091
k8s.io/apimachinery v0.25.3 // indirect
92+
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
93+
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect
94+
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
95+
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
96+
sigs.k8s.io/yaml v1.3.0 // indirect
7197
)

go.sum

Lines changed: 64 additions & 0 deletions
Large diffs are not rendered by default.

pkg/provider/kuberneteshelper.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
k8sv1 "k8s.io/client-go/kubernetes/typed/core/v1"
9+
"k8s.io/client-go/rest"
10+
)
11+
12+
const (
13+
// ServiceAccountClientID annotation
14+
ServiceAccountClientID = "azure.workload.identity/client-id"
15+
)
16+
17+
type KubernetesHelper struct {
18+
nameSpace, svcAcc string
19+
k8sClient k8sv1.CoreV1Interface
20+
}
21+
22+
func NewKubernetesHelper(nameSpace, svcAcc string) KubernetesHelper {
23+
k8sClient, err := getK8sClient()
24+
if err != nil {
25+
panic(err)
26+
}
27+
28+
return KubernetesHelper{
29+
nameSpace: nameSpace,
30+
svcAcc: svcAcc,
31+
k8sClient: k8sClient,
32+
}
33+
}
34+
35+
func (k KubernetesHelper) GetServiceAccountClientID() (string, error) {
36+
serviceAccount, err := k.k8sClient.ServiceAccounts(k.nameSpace).Get(context.Background(), k.svcAcc, metav1.GetOptions{})
37+
if err != nil {
38+
return "", fmt.Errorf("failed to get service account %s in namespace %s, error: %w", k.svcAcc, k.nameSpace, err)
39+
}
40+
clientID, ok := serviceAccount.Annotations[ServiceAccountClientID]
41+
if !ok {
42+
return "", fmt.Errorf("clientID not found in service account %s in namespace %s", k.svcAcc, k.nameSpace)
43+
}
44+
return clientID, nil
45+
}
46+
47+
func getK8sClient() (k8sv1.CoreV1Interface, error) {
48+
config, err := rest.InClusterConfig()
49+
if err != nil {
50+
return nil, fmt.Errorf("failed to get in cluster config, error: %w", err)
51+
}
52+
k8sClient, err := k8sv1.NewForConfig(config)
53+
if err != nil {
54+
panic(err)
55+
}
56+
return k8sClient, nil
57+
}

pkg/provider/provider.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ func (p *provider) GetSecretsStoreObjectContent(ctx context.Context, attrib, sec
123123
cloudEnvFileName := types.GetCloudEnvFileName(attrib)
124124
podName := types.GetPodName(attrib)
125125
podNamespace := types.GetPodNamespace(attrib)
126+
saName := types.GetServiceAccountName(attrib)
126127

127128
usePodIdentity, err := types.GetUsePodIdentity(attrib)
128129
if err != nil {
@@ -134,7 +135,11 @@ func (p *provider) GetSecretsStoreObjectContent(ctx context.Context, attrib, sec
134135
}
135136

136137
// attributes for workload identity
137-
workloadIdentityClientID := types.GetClientID(attrib)
138+
kubernetesHelper := NewKubernetesHelper(podNamespace, saName)
139+
workloadIdentityClientID, err := kubernetesHelper.GetServiceAccountClientID()
140+
if err != nil {
141+
return nil, fmt.Errorf("failed to get service account client id, error: %w", err)
142+
}
138143
saTokens := types.GetServiceAccountTokens(attrib)
139144

140145
if keyvaultName == "" {

pkg/provider/types/parameters.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,16 @@ func GetPodNamespace(parameters map[string]string) string {
7070
return strings.TrimSpace(parameters[CSIAttributePodNamespace])
7171
}
7272

73-
// GetClientID returns the client ID
74-
func GetClientID(parameters map[string]string) string {
75-
return strings.TrimSpace(parameters[ClientIDParameter])
76-
}
77-
7873
// GetServiceAccountTokens returns the service account tokens
7974
func GetServiceAccountTokens(parameters map[string]string) string {
8075
return strings.TrimSpace(parameters[CSIAttributeServiceAccountTokens])
8176
}
8277

78+
// GetServiceAccountName returns the service account name
79+
func GetServiceAccountName(parameters map[string]string) string {
80+
return strings.TrimSpace(parameters[CSIAttributeServiceAccountName])
81+
}
82+
8383
// GetObjects returns the key vault objects
8484
func GetObjects(parameters map[string]string) string {
8585
return strings.TrimSpace(parameters[ObjectsParameter])

pkg/provider/types/parameters_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ func TestGetPodNamespace(t *testing.T) {
438438
}
439439
}
440440

441-
func TestGetClientID(t *testing.T) {
441+
func TestGetServiceAccountTokens(t *testing.T) {
442442
tests := []struct {
443443
name string
444444
parameters map[string]string
@@ -447,37 +447,37 @@ func TestGetClientID(t *testing.T) {
447447
{
448448
name: "empty",
449449
parameters: map[string]string{
450-
"clientID": "",
450+
CSIAttributeServiceAccountTokens: "",
451451
},
452452
expected: "",
453453
},
454454
{
455455
name: "not empty",
456456
parameters: map[string]string{
457-
"clientID": "test",
457+
CSIAttributeServiceAccountTokens: "test",
458458
},
459459
expected: "test",
460460
},
461461
{
462462
name: "trim spaces",
463463
parameters: map[string]string{
464-
"clientID": " test ",
464+
CSIAttributeServiceAccountTokens: " test ",
465465
},
466466
expected: "test",
467467
},
468468
}
469469

470470
for _, test := range tests {
471471
t.Run(test.name, func(t *testing.T) {
472-
actual := GetClientID(test.parameters)
472+
actual := GetServiceAccountTokens(test.parameters)
473473
if actual != test.expected {
474-
t.Errorf("GetClientID() = %v, expected %v", actual, test.expected)
474+
t.Errorf("GetServiceAccountTokens() = %v, expected %v", actual, test.expected)
475475
}
476476
})
477477
}
478478
}
479479

480-
func TestGetServiceAccountTokens(t *testing.T) {
480+
func TestGetServiceAccountName(t *testing.T) {
481481
tests := []struct {
482482
name string
483483
parameters map[string]string
@@ -486,31 +486,31 @@ func TestGetServiceAccountTokens(t *testing.T) {
486486
{
487487
name: "empty",
488488
parameters: map[string]string{
489-
CSIAttributeServiceAccountTokens: "",
489+
CSIAttributeServiceAccountName: "",
490490
},
491491
expected: "",
492492
},
493493
{
494494
name: "not empty",
495495
parameters: map[string]string{
496-
CSIAttributeServiceAccountTokens: "test",
496+
CSIAttributeServiceAccountName: "test",
497497
},
498498
expected: "test",
499499
},
500500
{
501501
name: "trim spaces",
502502
parameters: map[string]string{
503-
CSIAttributeServiceAccountTokens: " test ",
503+
CSIAttributeServiceAccountName: " test ",
504504
},
505505
expected: "test",
506506
},
507507
}
508508

509509
for _, test := range tests {
510510
t.Run(test.name, func(t *testing.T) {
511-
actual := GetServiceAccountTokens(test.parameters)
511+
actual := GetServiceAccountName(test.parameters)
512512
if actual != test.expected {
513-
t.Errorf("GetServiceAccountTokens() = %v, expected %v", actual, test.expected)
513+
t.Errorf("GetServiceAccountName() = %v, expected %v", actual, test.expected)
514514
}
515515
})
516516
}

pkg/provider/types/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const (
2828
CSIAttributePodName = "csi.storage.k8s.io/pod.name"
2929
CSIAttributePodNamespace = "csi.storage.k8s.io/pod.namespace"
3030
CSIAttributeServiceAccountTokens = "csi.storage.k8s.io/serviceAccount.tokens" // nolint
31+
CSIAttributeServiceAccountName = "csi.storage.k8s.io/serviceAccount.name"
3132

3233
// KeyVaultNameParameter is the name of the key vault name parameter
3334
KeyVaultNameParameter = "keyvaultName"

website/content/en/configurations/identity-access-modes/workload-identity-mode.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ spec:
2121
provider: azure
2222
parameters:
2323
usePodIdentity: "false" # set to true for pod identity access mode
24-
clientID: "<client id of the Azure AD Application or user-assigned managed identity to use for workload identity>"
2524
keyvaultName: "kvname"
2625
cloudName: "" # [OPTIONAL for Azure] if not provided, azure environment will default to AzurePublicCloud
2726
objects: |
@@ -172,10 +171,12 @@ az identity federated-credential create \
172171

173172
### 4. Deploy your secretproviderclass and application
174173

175-
Set the `clientID` in the `SecretProviderClass` to the client ID of the AAD application or user-assigned managed identity.
174+
Set the `azure.workload.identity/client-id` annotation in the `ServiceAccount` of your pod to the client ID of the AAD application or user-assigned managed identity.
176175

177176
```yaml
178-
clientID: "${APPLICATION_OR_MANAGED_IDENTITY_CLIENT_ID}"
177+
metadata:
178+
annoations:
179+
azure.workload.identity/client-id: "${APPLICATION_OR_MANAGED_IDENTITY_CLIENT_ID}"
179180
```
180181

181182
## Pros

website/content/en/getting-started/usage/_index.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ To provide identity to access Key Vault, refer to the following [section](#provi
4141
usePodIdentity: "false" # [OPTIONAL] if not provided, will default to "false"
4242
useVMManagedIdentity: "false" # [OPTIONAL available for version > 0.0.4] if not provided, will default to "false"
4343
userAssignedIdentityID: "client_id" # [OPTIONAL available for version > 0.0.4] use the client id to specify which user assigned managed identity to use. If using a user assigned identity as the VM's managed identity, then specify the identity's client id. If empty, then defaults to use the system assigned identity on the VM
44-
clientID: "client_id" # [OPTIONAL available for version > 1.1.0] client id of the Azure AD Application or managed identity to use for workload identity
4544
keyvaultName: "kvname" # the name of the KeyVault
4645
cloudName: "" # [OPTIONAL available for version > 0.0.4] if not provided, azure environment will default to AzurePublicCloud
4746
cloudEnvFileName: "" # [OPTIONAL available for version > 0.0.7] use to define path to file for populating azure environment
@@ -69,7 +68,6 @@ To provide identity to access Key Vault, refer to the following [section](#provi
6968
| usePodIdentity | no | set to true for using aad-pod-identity to access keyvault | "false" |
7069
| useVMManagedIdentity | no | [__*available for version > 0.0.4*__] specify access mode to enable use of User-assigned managed identity | "false" |
7170
| userAssignedIdentityID | no | [__*available for version > 0.0.4*__] the user assigned identity ID is required for User-assigned Managed Identity mode | "" |
72-
| clientID | no | [__*available for version > 1.1.0*__] client id of the Azure AD Application or managed identity to use for workload identity | "" |
7371
| keyvaultName | yes | name of a Key Vault instance | "" |
7472
| cloudName | no | [__*available for version > 0.0.4*__] name of the azure cloud based on azure go sdk (AzurePublicCloud, AzureUSGovernmentCloud, AzureChinaCloud, AzureGermanCloud, AzureStackCloud) | "" |
7573
| cloudEnvFileName | no | [__*available for version > 0.0.7*__] path to the file to be used while populating the Azure Environment (required if target cloud is AzureStackCloud). More details [here](../../configurations/custom-environments). | "" |

0 commit comments

Comments
 (0)