From 4f2836ee2e25625dae4015ac6365d5f65655d570 Mon Sep 17 00:00:00 2001 From: Dipti Pai Date: Wed, 11 Sep 2024 11:24:17 -0700 Subject: [PATCH] Enable Azure OIDC for Azure DevOps Repository in IAC - Controller changes to set the provider options in git authOptions to fetch provider credentials while building git config. - API docs for setting up workload identity in IAC - Unit tests for testing provider settings in git config Signed-off-by: Dipti Pai --- docs/spec/v1beta2/imageupdateautomations.md | 40 ++++++++++++++++ go.mod | 19 +++++++- go.sum | 31 +++++++++---- internal/source/git.go | 12 +++++ internal/source/git_test.go | 51 +++++++++++++++++++++ 5 files changed, 143 insertions(+), 10 deletions(-) diff --git a/docs/spec/v1beta2/imageupdateautomations.md b/docs/spec/v1beta2/imageupdateautomations.md index 4ae62fa2..eca566cd 100644 --- a/docs/spec/v1beta2/imageupdateautomations.md +++ b/docs/spec/v1beta2/imageupdateautomations.md @@ -192,6 +192,46 @@ tuned to adjust the Git operation timeout. The proxy configurations are also derived from the referenced GitRepository source. `GitRepository.spec.proxySecretRef` can be used to configure proxy use. +`GitRepository` can be configured to specify an OIDC +[provider](https://github.com/dipti-pai/source-controller/blob/git-azure-oidc-auth/docs/spec/v1/gitrepositories.md#provider) +for authentication using `.spec.provider` field. + +If the provider is set to `azure`, make sure the +[pre-requisites](https://github.com/dipti-pai/source-controller/blob/git-azure-oidc-auth/docs/spec/v1/gitrepositories.md#azure) +are met. Add the following patch in flux-system/kustomization.yaml file. + +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - gotk-components.yaml + - gotk-sync.yaml +patches: + - patch: |- + apiVersion: v1 + kind: ServiceAccount + metadata: + name: image-automation-controller + namespace: flux-system + annotations: + azure.workload.identity/client-id: + labels: + azure.workload.identity/use: "true" + - patch: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: image-automation-controller + namespace: flux-system + labels: + azure.workload.identity/use: "true" + spec: + template: + metadata: + labels: + azure.workload.identity/use: "true" +``` + ### Git specification `.spec.git` is a required field to specify Git configurations related to source diff --git a/go.mod b/go.mod index 34edc399..437438fd 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/fluxcd/image-automation-controller -go 1.22.0 +go 1.22.4 replace github.com/fluxcd/image-automation-controller/api => ./api @@ -20,6 +20,7 @@ require ( github.com/fluxcd/pkg/apis/acl v0.3.0 github.com/fluxcd/pkg/apis/event v0.10.0 github.com/fluxcd/pkg/apis/meta v1.6.0 + github.com/fluxcd/pkg/auth v0.0.0-00010101000000-000000000000 github.com/fluxcd/pkg/git v0.20.0 github.com/fluxcd/pkg/git/gogit v0.20.0 github.com/fluxcd/pkg/gittestserver v0.13.0 @@ -44,7 +45,11 @@ require ( require ( dario.cat/mergo v1.0.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect @@ -72,6 +77,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.2 // indirect @@ -108,6 +114,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.20.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect @@ -131,7 +138,7 @@ require ( golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect @@ -153,3 +160,11 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +replace github.com/fluxcd/pkg/auth => github.com/dipti-pai/pkg/auth v0.0.0-20240911054527-6cd938515ffc + +replace github.com/fluxcd/pkg/git => github.com/dipti-pai/pkg/git v0.0.0-20240911054527-6cd938515ffc + +replace github.com/fluxcd/pkg/git/gogit => github.com/dipti-pai/pkg/git/gogit v0.0.0-20240911054527-6cd938515ffc + +replace github.com/fluxcd/source-controller/api => github.com/dipti-pai/source-controller/api v0.0.0-20240910233436-8e52ad3d618f diff --git a/go.sum b/go.sum index ee328c4a..35f41469 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,16 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 h1:1nGuui+4POelzDwI7RG56yfQJHCnKvwfMoU7VsEp+Zg= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0/go.mod h1:99EvauvlcJ1U06amZiksfYz/3aFGyIhWGHVyiZXtBAI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 h1:H+U3Gk9zY56G3u872L82bk4thcsy2Gghb9ExT4Zvm1o= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0/go.mod h1:mgrmMSgaLp9hmax62XQTd0N4aAqSE5E0DulSpVYK7vc= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -40,6 +48,14 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dipti-pai/pkg/auth v0.0.0-20240911054527-6cd938515ffc h1:UIX9r0pBI/slFtIYYBBNZ1r8hfdkzFzzjszxkE+9L88= +github.com/dipti-pai/pkg/auth v0.0.0-20240911054527-6cd938515ffc/go.mod h1:0VS8EHPXNoB9q84OJg+t2LlkdIvWzttUPXhSxMKavGk= +github.com/dipti-pai/pkg/git v0.0.0-20240911054527-6cd938515ffc h1:GwDwv82ofB8LU5yVBb9ydIxiI0VVJXcsLOdOh1/1G8I= +github.com/dipti-pai/pkg/git v0.0.0-20240911054527-6cd938515ffc/go.mod h1:XTZfxHFy96sbGzbhN68u8+L6IKjqAxLax/dCq9gaUk4= +github.com/dipti-pai/pkg/git/gogit v0.0.0-20240911054527-6cd938515ffc h1:Dt9u14xQKTxRi6MtNjoKoq8zP61zIUEY9fanbcHIB3M= +github.com/dipti-pai/pkg/git/gogit v0.0.0-20240911054527-6cd938515ffc/go.mod h1:pX0wDKVhNINddJ3vtUS6ripizHTqjc+kk93CLO0UDmM= +github.com/dipti-pai/source-controller/api v0.0.0-20240910233436-8e52ad3d618f h1:UDVuRf2zplDxLA6EfUspWAdD2c049yJ1z5gb+c8NKmM= +github.com/dipti-pai/source-controller/api v0.0.0-20240910233436-8e52ad3d618f/go.mod h1:tBdalWxrxxHAJRB9HiXXYRBQQGM8wDe+ngK7wL+BN6A= github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 h1:1NyRx2f4W4WBRyg0Kys0ZbaNmDDzZ2R/C7DTi+bbsJ0= github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380/go.mod h1:thX175TtLTzLj3p7N/Q9IiKZ7NF+p72cvL91emV0hzo= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= @@ -66,10 +82,6 @@ github.com/fluxcd/pkg/apis/event v0.10.0 h1:eMYXjMnLQ9jctPkTauuiBmEI127RjCKDf1zf github.com/fluxcd/pkg/apis/event v0.10.0/go.mod h1:pG/3gbSBLNy6YGZP2eajiyVgkEQDvva789t46PY6NFE= github.com/fluxcd/pkg/apis/meta v1.6.0 h1:93TcRpiph0OCoQh+cI+PM7E35kBW9dScuas9tWc90Dw= github.com/fluxcd/pkg/apis/meta v1.6.0/go.mod h1:ZOeHcvyVdZDC5ZOGV7YuwplIvAx6LvmpeyhfTcNZCnc= -github.com/fluxcd/pkg/git v0.20.0 h1:byUbxLLZ9AyVYmK16mvxY/iA/ZhNwA30GHKPKNh7pik= -github.com/fluxcd/pkg/git v0.20.0/go.mod h1:YnBOFhX7zzyVjg/u1Et1xBqXs30kb2sWWesIl3/glhw= -github.com/fluxcd/pkg/git/gogit v0.20.0 h1:ZlWq//I465lv9aEEWaJhjJaTiTtnjcH+Td0fg1rPXWU= -github.com/fluxcd/pkg/git/gogit v0.20.0/go.mod h1:ZA4WsKr28cj1yuplxOw9vHgCL4OCNJJLib1cJ77Tp9o= github.com/fluxcd/pkg/gittestserver v0.13.0 h1:6rvD9Z7+4zBcNT+LK0z4H0z6mDaw1Zd8ZaLh/dw8dzI= github.com/fluxcd/pkg/gittestserver v0.13.0/go.mod h1:LDw32Wo9mTmKNmJq4g7LRVBqPXlpMIWFBDOrRRh/+As= github.com/fluxcd/pkg/runtime v0.49.0 h1:XldsD4C2TsfuIgku3NEQYCXFLZWDau22YqClTGUihVo= @@ -78,8 +90,6 @@ github.com/fluxcd/pkg/ssh v0.14.0 h1:rkcUwEZiwNoHq8oGOf/THV5sf9LBbXOoJgOt+6+bU34 github.com/fluxcd/pkg/ssh v0.14.0/go.mod h1:1USgRvaaayJfzybQaCIAUn2e8LPsLe601Rec7Y8KQQE= github.com/fluxcd/pkg/version v0.4.0 h1:3F6oeIZ+ug/f7pALIBhcUhfURel37EPPOn7nsGfsnOg= github.com/fluxcd/pkg/version v0.4.0/go.mod h1:izVsSDxac81qWRmpOL9qcxZYx+zAN1ajoP5SidGP6PA= -github.com/fluxcd/source-controller/api v1.3.0 h1:Z5Lq0aJY87yg0cQDEuwGLKS60GhdErCHtsi546HUt10= -github.com/fluxcd/source-controller/api v1.3.0/go.mod h1:+tfd0vltjcVs/bbnq9AlYR9AAHSVfM/Z4v4TpQmdJf4= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -114,6 +124,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1 github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -216,6 +228,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -308,8 +322,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= diff --git a/internal/source/git.go b/internal/source/git.go index cd8ed725..3a87f0d6 100644 --- a/internal/source/git.go +++ b/internal/source/git.go @@ -31,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/fluxcd/pkg/auth/azure" "github.com/fluxcd/pkg/git" "github.com/fluxcd/pkg/git/gogit" sourcev1 "github.com/fluxcd/source-controller/api/v1" @@ -180,6 +181,17 @@ func getAuthOpts(ctx context.Context, c client.Client, repo *sourcev1.GitReposit return nil, fmt.Errorf("failed to configure authentication options: %w", err) } + if repo.Spec.Provider != "" && repo.Spec.Provider != sourcev1.GitProviderGeneric { + if repo.Spec.Provider == sourcev1.GitProviderAzure { + opts.ProviderOpts = &git.ProviderOptions{ + Name: sourcev1.GitProviderAzure, + AzureOpts: []azure.OptFunc{ + azure.WithAzureDevOpsScope(), + }, + } + } + } + return opts, nil } diff --git a/internal/source/git_test.go b/internal/source/git_test.go index 7921d766..2802d68e 100644 --- a/internal/source/git_test.go +++ b/internal/source/git_test.go @@ -138,6 +138,57 @@ func Test_getAuthOpts(t *testing.T) { } } +func Test_getAuthOpts_providerAuth(t *testing.T) { + tests := []struct { + name string + beforeFunc func(obj *sourcev1.GitRepository) + wantProviderOptsName string + }{ + { + name: "azure provider", + beforeFunc: func(obj *sourcev1.GitRepository) { + obj.Spec.Provider = sourcev1.GitProviderAzure + }, + wantProviderOptsName: sourcev1.GitProviderAzure, + }, + { + name: "generic provider", + beforeFunc: func(obj *sourcev1.GitRepository) { + obj.Spec.Provider = sourcev1.GitProviderGeneric + }, + }, + { + name: "no provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + obj := &sourcev1.GitRepository{ + Spec: sourcev1.GitRepositorySpec{ + URL: "https://dev.azure.com/foo/bar/_git/baz", + }, + } + + if tt.beforeFunc != nil { + tt.beforeFunc(obj) + } + opts, err := getAuthOpts(context.TODO(), nil, obj) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(opts).ToNot(BeNil()) + if tt.wantProviderOptsName != "" { + g.Expect(opts.ProviderOpts).ToNot(BeNil()) + g.Expect(opts.ProviderOpts.Name).To(Equal(tt.wantProviderOptsName)) + } else { + g.Expect(opts.ProviderOpts).To(BeNil()) + } + }) + } +} + func Test_getProxyOpts(t *testing.T) { namespace := "default" invalidProxy := &corev1.Secret{