Skip to content
Merged
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
6 changes: 6 additions & 0 deletions api/v1/gitrepository_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const (

// GitRepositorySpec specifies the required configuration to produce an
// Artifact for a Git repository.
// +kubebuilder:validation:XValidation:rule="!has(self.serviceAccountName) || (has(self.provider) && self.provider == 'azure')",message="serviceAccountName can only be set when provider is 'azure'"
type GitRepositorySpec struct {
// URL specifies the Git repository URL, it can be an HTTP/S or SSH address.
// +kubebuilder:validation:Pattern="^(http|https|ssh)://.*$"
Expand All @@ -98,6 +99,11 @@ type GitRepositorySpec struct {
// +optional
Provider string `json:"provider,omitempty"`

// ServiceAccountName is the name of the Kubernetes ServiceAccount used to
// authenticate to the GitRepository. This field is only supported for 'azure' provider.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`

// Interval at which the GitRepository URL is checked for updates.
// This interval is approximate and may be subject to jitter to ensure
// efficient use of resources.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ spec:
required:
- name
type: object
serviceAccountName:
description: |-
ServiceAccountName is the name of the Kubernetes ServiceAccount used to
authenticate to the GitRepository. This field is only supported for 'azure' provider.
type: string
sparseCheckout:
description: |-
SparseCheckout specifies a list of directories to checkout when cloning
Expand Down Expand Up @@ -235,6 +240,10 @@ spec:
- interval
- url
type: object
x-kubernetes-validations:
- message: serviceAccountName can only be set when provider is 'azure'
rule: '!has(self.serviceAccountName) || (has(self.provider) && self.provider
== ''azure'')'
status:
default:
observedGeneration: -1
Expand Down
26 changes: 26 additions & 0 deletions docs/api/v1/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,19 @@ When not specified, defaults to &lsquo;generic&rsquo;.</p>
</tr>
<tr>
<td>
<code>serviceAccountName</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>ServiceAccountName is the name of the Kubernetes ServiceAccount used to
authenticate to the GitRepository. This field is only supported for &lsquo;azure&rsquo; provider.</p>
</td>
</tr>
<tr>
<td>
<code>interval</code><br>
<em>
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
Expand Down Expand Up @@ -2067,6 +2080,19 @@ When not specified, defaults to &lsquo;generic&rsquo;.</p>
</tr>
<tr>
<td>
<code>serviceAccountName</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>ServiceAccountName is the name of the Kubernetes ServiceAccount used to
authenticate to the GitRepository. This field is only supported for &lsquo;azure&rsquo; provider.</p>
</td>
</tr>
<tr>
<td>
<code>interval</code><br>
<em>
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
Expand Down
18 changes: 18 additions & 0 deletions docs/spec/v1/gitrepositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,24 @@ flux create secret githubapp ghapp-secret \
--app-private-key=~/private-key.pem
```

### Service Account reference

`.spec.serviceAccountName` is an optional field to specify a Service Account
in the same namespace as GitRepository with purpose depending on the value of
the `.spec.provider` field:

- When `.spec.provider` is set to `azure`, the Service Account
will be used for Workload Identity authentication. In this case, the controller
feature gate `ObjectLevelWorkloadIdentity` must be enabled, otherwise the
controller will error out. For Azure DevOps specific setup, see the
[Azure DevOps integration guide](https://fluxcd.io/flux/integrations/azure/#for-azure-devops).

**Note:** that for a publicly accessible git repository, you don't need to
provide a `secretRef` nor `serviceAccountName`.

For a complete guide on how to set up authentication for cloud providers,
see the integration [docs](/flux/integrations/).

### Interval

`.spec.interval` is a required field that specifies the interval at which the
Expand Down
22 changes: 22 additions & 0 deletions internal/controller/gitrepository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,22 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
getCreds = func() (*authutils.GitCredentials, error) {
var opts []auth.Option

if obj.Spec.ServiceAccountName != "" {
// Check object-level workload identity feature gate.
if !auth.IsObjectLevelWorkloadIdentityEnabled() {
const gate = auth.FeatureGateObjectLevelWorkloadIdentity
const msgFmt = "to use spec.serviceAccountName for provider authentication please enable the %s feature gate in the controller"
err := serror.NewStalling(fmt.Errorf(msgFmt, gate), meta.FeatureGateDisabledReason)
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, meta.FeatureGateDisabledReason, "%s", err)
return nil, err
}
serviceAccount := client.ObjectKey{
Name: obj.Spec.ServiceAccountName,
Namespace: obj.GetNamespace(),
}
opts = append(opts, auth.WithServiceAccount(serviceAccount, r.Client))
}

if r.TokenCache != nil {
involvedObject := cache.InvolvedObject{
Kind: sourcev1.GitRepositoryKind,
Expand Down Expand Up @@ -742,6 +758,12 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
if getCreds != nil {
creds, err := getCreds()
if err != nil {
// Check if it's already a structured error and preserve it
switch err.(type) {
case *serror.Stalling, *serror.Generic:
return nil, err
}

e := serror.NewGeneric(
fmt.Errorf("failed to configure authentication options: %w", err),
sourcev1.AuthenticationFailedReason,
Expand Down
10 changes: 10 additions & 0 deletions internal/controller/gitrepository_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import (

kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/auth"
"github.com/fluxcd/pkg/git"
"github.com/fluxcd/pkg/git/github"
"github.com/fluxcd/pkg/gittestserver"
Expand Down Expand Up @@ -919,6 +920,15 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) {
},
wantErr: "ManagedIdentityCredential",
},
{
name: "azure provider with service account and feature gate for object-level identity disabled",
url: "https://dev.azure.com/foo/bar/_git/baz",
beforeFunc: func(obj *sourcev1.GitRepository) {
obj.Spec.Provider = sourcev1.GitProviderAzure
obj.Spec.ServiceAccountName = "azure-sa"
},
wantErr: auth.FeatureGateObjectLevelWorkloadIdentity,
},
{
name: "github provider with no secret ref",
url: "https://github.com/org/repo.git",
Expand Down