Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adjust the provider to use the available service Account annotations instead of requiring it again in the SPC parameters #1443

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ require (
golang.org/x/net v0.26.0
google.golang.org/grpc v1.59.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.25.3
k8s.io/client-go v0.25.3
k8s.io/component-base v0.25.3
k8s.io/klog/v2 v2.80.1
sigs.k8s.io/secrets-store-csi-driver v1.3.4
Expand All @@ -35,19 +37,33 @@ require (
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/zapr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.12.2 // indirect
Expand All @@ -63,10 +79,20 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/apimachinery v0.25.3 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.25.3 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
65 changes: 65 additions & 0 deletions go.sum

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,13 @@ rules:
verbs: ["get", "watch", "list"]
{{- end }}
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ template "sscdpa.fullname" . }}-cluster-role
{{ include "sscdpa.labels" . | indent 2 }}
rules:
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["get"]
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,17 @@ subjects:
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ template "sscdpa.fullname" . }}-cluster-role-binding
{{ include "sscdpa.labels" . | indent 2 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ template "sscdpa.fullname" . }}-cluster-role
subjects:
- kind: ServiceAccount
name: csi-secrets-store-provider-azure
namespace: {{ .Release.Namespace }}
57 changes: 57 additions & 0 deletions pkg/provider/kuberneteshelper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package provider

import (
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sv1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
)

const (
// ServiceAccountClientID annotation
ServiceAccountClientID = "azure.workload.identity/client-id"
)

type KubernetesHelper struct {
nameSpace, svcAcc string
k8sClient k8sv1.CoreV1Interface
}

func NewKubernetesHelper(nameSpace, svcAcc string) KubernetesHelper {
k8sClient, err := getK8sClient()
if err != nil {
panic(err)
}

return KubernetesHelper{
nameSpace: nameSpace,
svcAcc: svcAcc,
k8sClient: k8sClient,
}
}

func (k KubernetesHelper) GetServiceAccountClientID() (string, error) {
serviceAccount, err := k.k8sClient.ServiceAccounts(k.nameSpace).Get(context.Background(), k.svcAcc, metav1.GetOptions{})
if err != nil {
return "", fmt.Errorf("failed to get service account %s in namespace %s, error: %w", k.svcAcc, k.nameSpace, err)
}
clientID, ok := serviceAccount.Annotations[ServiceAccountClientID]
if !ok {
return "", fmt.Errorf("clientID not found in service account %s in namespace %s", k.svcAcc, k.nameSpace)
}
return clientID, nil
}

func getK8sClient() (k8sv1.CoreV1Interface, error) {
config, err := rest.InClusterConfig()
if err != nil {
return nil, fmt.Errorf("failed to get in cluster config, error: %w", err)
}
k8sClient, err := k8sv1.NewForConfig(config)
if err != nil {
panic(err)
}
return k8sClient, nil
}
16 changes: 15 additions & 1 deletion pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func (p *provider) GetSecretsStoreObjectContent(ctx context.Context, attrib, sec
cloudEnvFileName := types.GetCloudEnvFileName(attrib)
podName := types.GetPodName(attrib)
podNamespace := types.GetPodNamespace(attrib)
saName := types.GetServiceAccountName(attrib)

usePodIdentity, err := types.GetUsePodIdentity(attrib)
if err != nil {
Expand All @@ -131,9 +132,22 @@ func (p *provider) GetSecretsStoreObjectContent(ctx context.Context, attrib, sec
if err != nil {
return nil, fmt.Errorf("failed to parse useVMManagedIdentity flag, error: %w", err)
}
usePodServiceAccountAnnotation, err := types.GetUsePodServiceAccountAnnotation(attrib)
if err != nil {
return nil, fmt.Errorf("failed to parse usePodServiceAccountAnnotation flag, error: %w", err)
}

// attributes for workload identity
workloadIdentityClientID := types.GetClientID(attrib)
var workloadIdentityClientID string
if usePodServiceAccountAnnotation {
kubernetesHelper := NewKubernetesHelper(podNamespace, saName)
workloadIdentityClientID, err = kubernetesHelper.GetServiceAccountClientID()
if err != nil {
return nil, fmt.Errorf("failed to get service account client id, error: %w", err)
}
} else {
workloadIdentityClientID = types.GetClientID(attrib)
}
saTokens := types.GetServiceAccountTokens(attrib)

if keyvaultName == "" {
Expand Down
14 changes: 14 additions & 0 deletions pkg/provider/types/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ func GetUsePodIdentity(parameters map[string]string) (bool, error) {
return strconv.ParseBool(str)
}

// GetUsePodServiceAccountAnnotation returns if pod service account annotation is enabled
func GetUsePodServiceAccountAnnotation(parameters map[string]string) (bool, error) {
str := strings.TrimSpace(parameters[UsePodServiceAccountAnnotationParameter])
if str == "" {
return false, nil
}
return strconv.ParseBool(str)
}

// GetUseVMManagedIdentity returns if VM managed identity is enabled
func GetUseVMManagedIdentity(parameters map[string]string) (bool, error) {
str := strings.TrimSpace(parameters[UseVMManagedIdentityParameter])
Expand Down Expand Up @@ -80,6 +89,11 @@ func GetServiceAccountTokens(parameters map[string]string) string {
return strings.TrimSpace(parameters[CSIAttributeServiceAccountTokens])
}

// GetServiceAccountName returns the service account name
func GetServiceAccountName(parameters map[string]string) string {
return strings.TrimSpace(parameters[CSIAttributeServiceAccountName])
}

// GetObjects returns the key vault objects
func GetObjects(parameters map[string]string) string {
return strings.TrimSpace(parameters[ObjectsParameter])
Expand Down
102 changes: 102 additions & 0 deletions pkg/provider/types/parameters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,69 @@ func TestGetUsePodIdentity(t *testing.T) {
}
}

func TestGetUsePodServiceAccountAnnotation(t *testing.T) {
tests := []struct {
name string
parameters map[string]string
expected bool
}{
{
name: "empty",
parameters: map[string]string{
UsePodServiceAccountAnnotationParameter: "",
},
expected: false,
},
{
name: "set to true",
parameters: map[string]string{
UsePodServiceAccountAnnotationParameter: "true",
},
expected: true,
},
{
name: "set to false",
parameters: map[string]string{
UsePodServiceAccountAnnotationParameter: "false",
},
expected: false,
},
{
name: "set to True",
parameters: map[string]string{
UsePodServiceAccountAnnotationParameter: "True",
},
expected: true,
},
{
name: "set to False",
parameters: map[string]string{
UsePodServiceAccountAnnotationParameter: "False",
},
expected: false,
},
{
name: "trim spaces",
parameters: map[string]string{
UsePodServiceAccountAnnotationParameter: " true ",
},
expected: true,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual, err := GetUsePodServiceAccountAnnotation(test.parameters)
if err != nil {
t.Errorf("GetUsePodServiceAccountAnnotation() error = %v, expected nil", err)
}
if actual != test.expected {
t.Errorf("GetUsePodServiceAccountAnnotation() = %v, expected %v", actual, test.expected)
}
})
}
}

func TestGetUsePodIdentityError(t *testing.T) {
parameters := map[string]string{
UsePodIdentityParameter: "test",
Expand Down Expand Up @@ -516,6 +579,45 @@ func TestGetServiceAccountTokens(t *testing.T) {
}
}

func TestGetServiceAccountName(t *testing.T) {
tests := []struct {
name string
parameters map[string]string
expected string
}{
{
name: "empty",
parameters: map[string]string{
CSIAttributeServiceAccountName: "",
},
expected: "",
},
{
name: "not empty",
parameters: map[string]string{
CSIAttributeServiceAccountName: "test",
},
expected: "test",
},
{
name: "trim spaces",
parameters: map[string]string{
CSIAttributeServiceAccountName: " test ",
},
expected: "test",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := GetServiceAccountName(test.parameters)
if actual != test.expected {
t.Errorf("GetServiceAccountName() = %v, expected %v", actual, test.expected)
}
})
}
}

func TestGetObjects(t *testing.T) {
tests := []struct {
name string
Expand Down
3 changes: 3 additions & 0 deletions pkg/provider/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@ const (
CSIAttributePodName = "csi.storage.k8s.io/pod.name"
CSIAttributePodNamespace = "csi.storage.k8s.io/pod.namespace"
CSIAttributeServiceAccountTokens = "csi.storage.k8s.io/serviceAccount.tokens" // nolint
CSIAttributeServiceAccountName = "csi.storage.k8s.io/serviceAccount.name"

// KeyVaultNameParameter is the name of the key vault name parameter
KeyVaultNameParameter = "keyvaultName"
// CloudNameParameter is the name of the cloud name parameter
CloudNameParameter = "cloudName"
// UsePodIdentityParameter is the name of the use pod identity parameter
UsePodIdentityParameter = "usePodIdentity"
// UsePodServiceAccountAnnotationParameter is the name of the use pod service account annotation parameter
UsePodServiceAccountAnnotationParameter = "usePodServiceAccountAnnotation"
// UseVMManagedIdentityParameter is the name of the use VM managed identity parameter
UseVMManagedIdentityParameter = "useVMManagedIdentity"
// UserAssignedIdentityIDParameter is the name of the user assigned identity ID parameter
Expand Down
12 changes: 7 additions & 5 deletions test/e2e/framework/serviceaccount/serviceaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import (

// CreateInput is the input for Create.
type CreateInput struct {
Creator framework.Creator
Name string
Namespace string
Creator framework.Creator
Name string
Namespace string
Annotations map[string]string
}

// Create creates a ServiceAccount resource.
Expand All @@ -28,8 +29,9 @@ func Create(input CreateInput) *corev1.ServiceAccount {
By(fmt.Sprintf("Creating ServiceAccount \"%s/%s\"", input.Namespace, input.Name))
sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: input.Name,
Namespace: input.Namespace,
Name: input.Name,
Namespace: input.Namespace,
Annotations: input.Annotations,
},
}

Expand Down
Loading