[RFC-0010] Add multi-tenant workload identity support for Azure GitRepository
Signed-off-by: Dipti Pai <diptipai89@outlook.com>
This commit is contained in:
parent
5f9702bb01
commit
4fe3434ee8
|
@ -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)://.*$"
|
||||
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -413,6 +413,19 @@ When not specified, defaults to ‘generic’.</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 ‘azure’ 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">
|
||||
|
@ -2067,6 +2080,19 @@ When not specified, defaults to ‘generic’.</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 ‘azure’ 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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue