@@ -22,9 +22,6 @@ import (
2222 "strings"
2323
2424 authnv1 "k8s.io/api/authentication/v1"
25- corev1 "k8s.io/api/core/v1"
26- "k8s.io/apimachinery/pkg/api/errors"
27- "sigs.k8s.io/controller-runtime/pkg/client"
2825
2926 "github.com/fluxcd/pkg/cache"
3027)
@@ -45,49 +42,57 @@ func GetAccessToken(ctx context.Context, provider Provider, opts ...Option) (Tok
4542 }
4643
4744 // Update access token fetcher for a service account if specified.
48- var serviceAccount * corev1.ServiceAccount
49- var providerIdentity string
50- var audiences []string
51- if o .ShouldGetServiceAccountToken () {
45+ var saInfo * serviceAccountInfo
46+ if o .ShouldGetServiceAccount () {
47+ // Check the feature gate for object-level workload identity.
48+ if ! IsObjectLevelWorkloadIdentityEnabled () {
49+ return nil , ErrObjectLevelWorkloadIdentityNotEnabled
50+ }
51+
5252 // Fetch service account details.
5353 var err error
54- saRef := client.ObjectKey {
55- Name : o .ServiceAccountName ,
56- Namespace : o .ServiceAccountNamespace ,
57- }
58- serviceAccount , audiences , providerIdentity , err =
59- getServiceAccountAndProviderInfo (ctx , provider , o .Client , saRef , opts ... )
54+ saInfo , err = getServiceAccountInfo (ctx , provider , o .Client , opts ... )
6055 if err != nil {
6156 return nil , err
6257 }
6358
6459 // Update the function to create an access token using the service account.
65- newAccessToken = func () (Token , error ) {
66- // Check the feature gate for object-level workload identity.
67- if ! IsObjectLevelWorkloadIdentityEnabled () {
68- return nil , ErrObjectLevelWorkloadIdentityNotEnabled
60+ if saInfo .useServiceAccount {
61+ newAccessToken = func () (Token , error ) {
62+ // Issue Kubernetes OIDC token for the service account.
63+ tokenReq := & authnv1.TokenRequest {
64+ Spec : authnv1.TokenRequestSpec {
65+ Audiences : saInfo .audiences ,
66+ },
67+ }
68+ if err := o .Client .SubResource ("token" ).Create (ctx , saInfo .obj , tokenReq ); err != nil {
69+ return nil , fmt .Errorf ("failed to create kubernetes token for service account '%s/%s': %w" ,
70+ saInfo .obj .Namespace , saInfo .obj .Name , err )
71+ }
72+ oidcToken := tokenReq .Status .Token
73+
74+ // Exchange the Kubernetes OIDC token for a provider access token.
75+ token , err := provider .NewTokenForServiceAccount (ctx , oidcToken , * saInfo .obj , opts ... )
76+ if err != nil {
77+ return nil , fmt .Errorf ("failed to create provider access token for service account '%s/%s': %w" ,
78+ saInfo .obj .Namespace , saInfo .obj .Name , err )
79+ }
80+
81+ return token , nil
6982 }
83+ }
84+ }
7085
71- // Issue Kubernetes OIDC token for the service account.
72- tokenReq := & authnv1.TokenRequest {
73- Spec : authnv1.TokenRequestSpec {
74- Audiences : audiences ,
75- },
76- }
77- if err := o .Client .SubResource ("token" ).Create (ctx , serviceAccount , tokenReq ); err != nil {
78- return nil , fmt .Errorf ("failed to create kubernetes token for service account '%s/%s': %w" ,
79- serviceAccount .Namespace , serviceAccount .Name , err )
80- }
81- oidcToken := tokenReq .Status .Token
82-
83- // Exchange the Kubernetes OIDC token for a provider access token.
84- token , err := provider .NewTokenForServiceAccount (ctx , oidcToken , * serviceAccount , opts ... )
86+ // Update access token fetcher for impersonation if supported by the provider.
87+ if saInfo != nil && saInfo .providerIdentityForImpersonation != nil {
88+ newNonImpersonatedToken := newAccessToken
89+ newAccessToken = func () (Token , error ) {
90+ token , err := newNonImpersonatedToken ()
8591 if err != nil {
86- return nil , fmt .Errorf ("failed to create provider access token for service account '%s/%s': %w" ,
87- serviceAccount .Namespace , serviceAccount .Name , err )
92+ return nil , err
8893 }
89-
90- return token , nil
94+ p := provider .( ProviderWithImpersonation )
95+ return p . NewTokenForIdentity ( ctx , token , saInfo . providerIdentityForImpersonation , opts ... )
9196 }
9297 }
9398
@@ -97,7 +102,7 @@ func GetAccessToken(ctx context.Context, provider Provider, opts ...Option) (Tok
97102 }
98103
99104 // Build cache key.
100- cacheKey := buildAccessTokenCacheKey (provider , audiences , providerIdentity , serviceAccount , opts ... )
105+ cacheKey := buildAccessTokenCacheKey (provider , saInfo , opts ... )
101106
102107 // Build involved object details.
103108 kind := o .InvolvedObject .Kind
@@ -116,55 +121,7 @@ func GetAccessToken(ctx context.Context, provider Provider, opts ...Option) (Tok
116121 return token , nil
117122}
118123
119- func getServiceAccountAndProviderInfo (ctx context.Context , provider Provider , client client.Client ,
120- key client.ObjectKey , opts ... Option ) (* corev1.ServiceAccount , []string , string , error ) {
121-
122- var o Options
123- o .Apply (opts ... )
124-
125- defaultSA := getDefaultServiceAccount ()
126- var setDefaultSA bool
127-
128- // Apply multi-tenancy lockdown: use default service account when .serviceAccountName
129- // is not explicitly specified in the object. This results in Object-Level Workload Identity.
130- if key .Name == "" && defaultSA != "" {
131- key .Name = defaultSA
132- setDefaultSA = true
133- }
134-
135- // Get service account.
136- var serviceAccount corev1.ServiceAccount
137- if err := client .Get (ctx , key , & serviceAccount ); err != nil {
138- if errors .IsNotFound (err ) && setDefaultSA {
139- return nil , nil , "" , fmt .Errorf ("failed to get service account '%s': %w" ,
140- key , ErrDefaultServiceAccountNotFound )
141- }
142- return nil , nil , "" , fmt .Errorf ("failed to get service account '%s': %w" ,
143- key , err )
144- }
145-
146- // Get provider audience.
147- audiences := o .Audiences
148- if len (audiences ) == 0 {
149- var err error
150- audiences , err = provider .GetAudiences (ctx , serviceAccount )
151- if err != nil {
152- return nil , nil , "" , fmt .Errorf ("failed to get provider audience: %w" , err )
153- }
154- }
155-
156- // Get provider identity.
157- providerIdentity , err := provider .GetIdentity (serviceAccount )
158- if err != nil {
159- return nil , nil , "" , fmt .Errorf ("failed to get provider identity from service account '%s/%s' annotations: %w" ,
160- key .Namespace , key .Name , err )
161- }
162-
163- return & serviceAccount , audiences , providerIdentity , nil
164- }
165-
166- func buildAccessTokenCacheKey (provider Provider , audiences []string , providerIdentity string ,
167- serviceAccount * corev1.ServiceAccount , opts ... Option ) string {
124+ func buildAccessTokenCacheKey (provider Provider , saInfo * serviceAccountInfo , opts ... Option ) string {
168125
169126 var o Options
170127 o .Apply (opts ... )
@@ -173,11 +130,16 @@ func buildAccessTokenCacheKey(provider Provider, audiences []string, providerIde
173130
174131 parts = append (parts , fmt .Sprintf ("provider=%s" , provider .GetName ()))
175132
176- if serviceAccount != nil {
177- parts = append (parts , fmt .Sprintf ("providerIdentity=%s" , providerIdentity ))
178- parts = append (parts , fmt .Sprintf ("serviceAccountName=%s" , serviceAccount .Name ))
179- parts = append (parts , fmt .Sprintf ("serviceAccountNamespace=%s" , serviceAccount .Namespace ))
180- parts = append (parts , fmt .Sprintf ("serviceAccountTokenAudiences=%s" , strings .Join (audiences , "," )))
133+ if saInfo != nil {
134+ if saInfo .useServiceAccount {
135+ parts = append (parts , fmt .Sprintf ("serviceAccountName=%s" , saInfo .obj .Name ))
136+ parts = append (parts , fmt .Sprintf ("serviceAccountNamespace=%s" , saInfo .obj .Namespace ))
137+ parts = append (parts , fmt .Sprintf ("serviceAccountTokenAudiences=%s" , strings .Join (saInfo .audiences , "," )))
138+ parts = append (parts , fmt .Sprintf ("providerIdentity=%s" , saInfo .providerIdentity ))
139+ }
140+ if saInfo .providerIdentityForImpersonation != nil {
141+ parts = append (parts , fmt .Sprintf ("providerIdentityForImpersonation=%s" , saInfo .providerIdentityForImpersonation ))
142+ }
181143 }
182144
183145 if len (o .Scopes ) > 0 {
0 commit comments