Skip to content

Commit 945912f

Browse files
committed
[RFC-0010] Add multi-tenant workload identity support for Azure GitRepository
Signed-off-by: Dipti Pai <diptipai89@outlook.com>
1 parent 48da00d commit 945912f

File tree

6 files changed

+86
-0
lines changed

6 files changed

+86
-0
lines changed

api/v1/gitrepository_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ type GitRepositorySpec struct {
9898
// +optional
9999
Provider string `json:"provider,omitempty"`
100100

101+
// ServiceAccountName is the name of the Kubernetes ServiceAccount used to
102+
// authenticate to the GitRepository. This field is only supported for 'azure' provider.
103+
// +optional
104+
ServiceAccountName string `json:"serviceAccountName,omitempty"`
105+
101106
// Interval at which the GitRepository URL is checked for updates.
102107
// This interval is approximate and may be subject to jitter to ensure
103108
// efficient use of resources.

config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ spec:
174174
required:
175175
- name
176176
type: object
177+
serviceAccountName:
178+
description: |-
179+
ServiceAccountName is the name of the Kubernetes ServiceAccount used to
180+
authenticate to the GitRepository. This field is only supported for 'azure' provider.
181+
type: string
177182
sparseCheckout:
178183
description: |-
179184
SparseCheckout specifies a list of directories to checkout when cloning

docs/api/v1/source.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,19 @@ When not specified, defaults to &lsquo;generic&rsquo;.</p>
412412
</tr>
413413
<tr>
414414
<td>
415+
<code>serviceAccountName</code><br>
416+
<em>
417+
string
418+
</em>
419+
</td>
420+
<td>
421+
<em>(Optional)</em>
422+
<p>ServiceAccountName is the name of the Kubernetes ServiceAccount used to
423+
authenticate to the GitRepository. This field is only supported for &lsquo;azure&rsquo; provider.</p>
424+
</td>
425+
</tr>
426+
<tr>
427+
<td>
415428
<code>interval</code><br>
416429
<em>
417430
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
@@ -2065,6 +2078,19 @@ When not specified, defaults to &lsquo;generic&rsquo;.</p>
20652078
</tr>
20662079
<tr>
20672080
<td>
2081+
<code>serviceAccountName</code><br>
2082+
<em>
2083+
string
2084+
</em>
2085+
</td>
2086+
<td>
2087+
<em>(Optional)</em>
2088+
<p>ServiceAccountName is the name of the Kubernetes ServiceAccount used to
2089+
authenticate to the GitRepository. This field is only supported for &lsquo;azure&rsquo; provider.</p>
2090+
</td>
2091+
</tr>
2092+
<tr>
2093+
<td>
20682094
<code>interval</code><br>
20692095
<em>
20702096
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">

docs/spec/v1/gitrepositories.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,23 @@ flux create secret githubapp ghapp-secret \
393393
--app-private-key=~/private-key.pem
394394
```
395395

396+
### Service Account reference
397+
398+
`.spec.serviceAccountName` is an optional field to specify a Service Account
399+
in the same namespace as GitRepository with purpose depending on the value of
400+
the `.spec.provider` field:
401+
402+
- When `.spec.provider` is set to `azure`, the Service Account
403+
will be used for Workload Identity authentication. In this case, the controller
404+
feature gate `ObjectLevelWorkloadIdentity` must be enabled, otherwise the
405+
controller will error out.
406+
407+
**Note:** that for a publicly accessible git repository, you don't need to
408+
provide a `secretRef` nor `serviceAccountName`.
409+
410+
For a complete guide on how to set up authentication for cloud providers,
411+
see the integration [docs](/flux/integrations/).
412+
396413
### Interval
397414

398415
`.spec.interval` is a required field that specifies the interval at which the

internal/controller/gitrepository_controller.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,21 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
663663
getCreds = func() (*authutils.GitCredentials, error) {
664664
var opts []auth.Option
665665

666+
if obj.Spec.ServiceAccountName != "" {
667+
// Check object-level workload identity feature gate.
668+
if !auth.IsObjectLevelWorkloadIdentityEnabled() {
669+
const gate = auth.FeatureGateObjectLevelWorkloadIdentity
670+
const msgFmt = "to use spec.serviceAccountName for provider authentication please enable the %s feature gate in the controller"
671+
err := serror.NewStalling(fmt.Errorf(msgFmt, gate), meta.FeatureGateDisabledReason)
672+
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, meta.FeatureGateDisabledReason, "%s", err)
673+
return nil, err
674+
}
675+
serviceAccount := client.ObjectKey{
676+
Name: obj.Spec.ServiceAccountName,
677+
Namespace: obj.GetNamespace(),
678+
}
679+
opts = append(opts, auth.WithServiceAccount(serviceAccount, r.Client))
680+
}
666681
if r.TokenCache != nil {
667682
involvedObject := cache.InvolvedObject{
668683
Kind: sourcev1.GitRepositoryKind,
@@ -742,6 +757,14 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
742757
if getCreds != nil {
743758
creds, err := getCreds()
744759
if err != nil {
760+
// Check if it's already a structured error and preserve it
761+
if se, ok := err.(*serror.Stalling); ok {
762+
return nil, se
763+
}
764+
if ge, ok := err.(*serror.Generic); ok {
765+
return nil, ge
766+
}
767+
745768
e := serror.NewGeneric(
746769
fmt.Errorf("failed to configure authentication options: %w", err),
747770
sourcev1.AuthenticationFailedReason,

internal/controller/gitrepository_controller_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import (
4848

4949
kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status"
5050
"github.com/fluxcd/pkg/apis/meta"
51+
"github.com/fluxcd/pkg/auth"
5152
"github.com/fluxcd/pkg/git"
5253
"github.com/fluxcd/pkg/git/github"
5354
"github.com/fluxcd/pkg/gittestserver"
@@ -919,6 +920,15 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) {
919920
},
920921
wantErr: "ManagedIdentityCredential",
921922
},
923+
{
924+
name: "azure provider with service account and feature gate for object-level identity disabled",
925+
url: "https://dev.azure.com/foo/bar/_git/baz",
926+
beforeFunc: func(obj *sourcev1.GitRepository) {
927+
obj.Spec.Provider = sourcev1.GitProviderAzure
928+
obj.Spec.ServiceAccountName = "azure-sa"
929+
},
930+
wantErr: auth.FeatureGateObjectLevelWorkloadIdentity,
931+
},
922932
{
923933
name: "github provider with no secret ref",
924934
url: "https://github.com/org/repo.git",

0 commit comments

Comments
 (0)