diff --git a/api/v1/gitrepository_types.go b/api/v1/gitrepository_types.go index 2ed4df258..20ef37d0c 100644 --- a/api/v1/gitrepository_types.go +++ b/api/v1/gitrepository_types.go @@ -35,6 +35,10 @@ const ( // GitProviderAzure provides support for authentication to azure // repositories using Managed Identity. GitProviderAzure string = "azure" + + // GitProviderGitHub provides support for authentication to git + // repositories using GitHub App authentication + GitProviderGitHub string = "github" ) const ( @@ -88,9 +92,9 @@ type GitRepositorySpec struct { // +optional SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"` - // Provider used for authentication, can be 'azure', 'generic'. + // Provider used for authentication, can be 'azure', 'github', 'generic'. // When not specified, defaults to 'generic'. - // +kubebuilder:validation:Enum=generic;azure + // +kubebuilder:validation:Enum=generic;azure;github // +optional Provider string `json:"provider,omitempty"` diff --git a/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml b/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml index 9d01fbd54..0e37a7b49 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml @@ -105,11 +105,12 @@ spec: type: string provider: description: |- - Provider used for authentication, can be 'azure', 'generic'. + Provider used for authentication, can be 'azure', 'github', 'generic'. When not specified, defaults to 'generic'. enum: - generic - azure + - github type: string proxySecretRef: description: |- diff --git a/docs/api/v1/source.md b/docs/api/v1/source.md index 521dddc14..121a056cd 100644 --- a/docs/api/v1/source.md +++ b/docs/api/v1/source.md @@ -390,7 +390,7 @@ string (Optional) -

Provider used for authentication, can be ‘azure’, ‘generic’. +

Provider used for authentication, can be ‘azure’, ‘github’, ‘generic’. When not specified, defaults to ‘generic’.

@@ -1730,7 +1730,7 @@ string (Optional) -

Provider used for authentication, can be ‘azure’, ‘generic’. +

Provider used for authentication, can be ‘azure’, ‘github’, ‘generic’. When not specified, defaults to ‘generic’.

diff --git a/docs/spec/v1/gitrepositories.md b/docs/spec/v1/gitrepositories.md index e78aee74a..20dc9f12f 100644 --- a/docs/spec/v1/gitrepositories.md +++ b/docs/spec/v1/gitrepositories.md @@ -221,6 +221,7 @@ Supported options are: - `generic` - `azure` +- `github` When provider is not specified, it defaults to `generic` indicating that mechanisms using `spec.secretRef` are used for authentication. @@ -296,6 +297,51 @@ must follow this format: ``` https://dev.azure.com/{your-organization}/{your-project}/_git/{your-repository} ``` +#### GitHub + +The `github` provider can be used to authenticate to git repositories using +[GitHub Apps](https://docs.github.com/en/apps/overview). + +##### Pre-requisites + +- [Register]((https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app)) + the GitHub App with the necessary permissions and [generate a private + key](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps) + for the app. + +- [Install](https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app) + the app in the organization/account configuring access to the necessary + repositories. + +##### Configure GitHub App secret + +The GitHub App information is specified in `.spec.secretRef` in the format +specified below: + +* Get the App ID from the app settings page at + `https://github.com/settings/apps/`. +* Get the App Installation ID from the app installations page at +`https://github.com/settings/installations`. Click the installed app, the URL +will contain the installation ID +`https://github.com/settings/installations/`. For +organizations, the first part of the URL may be different, but it follows the +same pattern. +* The private key that was generated in the pre-requisites. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: github-sa +type: Opaque +stringData: + githubAppID: + githubAppInstallationID: + githubAppPrivateKey: | + -----BEGIN RSA PRIVATE KEY----- + ... + -----END RSA PRIVATE KEY----- +``` ### Interval diff --git a/go.mod b/go.mod index 21e67dc84..0eca304cb 100644 --- a/go.mod +++ b/go.mod @@ -137,6 +137,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver/v4 v4.0.0 // indirect + github.com/bradleyfalzon/ghinstallation/v2 v2.11.0 // indirect github.com/bshuster-repo/logrus-logstash-hook v1.0.0 // indirect github.com/buildkite/agent/v3 v3.76.2 // indirect github.com/buildkite/go-pipeline v0.10.0 // indirect @@ -221,6 +222,7 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230516205744-dbecb1de8cfa // indirect github.com/google/go-github/v55 v55.0.0 // indirect + github.com/google/go-github/v62 v62.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.8 // indirect @@ -407,3 +409,9 @@ require ( ) retract v0.32.0 // Refers to incorrect ./api version. + +replace github.com/fluxcd/pkg/auth => github.com/dipti-pai/pkg/auth v0.0.0-20241112210126-09c286242cc8 + +replace github.com/fluxcd/pkg/git/gogit => github.com/dipti-pai/pkg/git/gogit v0.0.0-20241112210126-09c286242cc8 + +replace github.com/fluxcd/pkg/git => github.com/dipti-pai/pkg/git v0.0.0-20241112210126-09c286242cc8 diff --git a/go.sum b/go.sum index 486054569..fb0ef8fad 100644 --- a/go.sum +++ b/go.sum @@ -202,6 +202,8 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bradleyfalzon/ghinstallation/v2 v2.11.0 h1:R9d0v+iobRHSaE4wKUnXFiZp53AL4ED5MzgEMwGTZag= +github.com/bradleyfalzon/ghinstallation/v2 v2.11.0/go.mod h1:0LWKQwOHewXO/1acI6TtyE0Xc4ObDb2rFN7eHBAG71M= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= @@ -290,6 +292,12 @@ github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1G github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/dipti-pai/pkg/auth v0.0.0-20241112210126-09c286242cc8 h1:FlhPrNBTBXmrqkauOIEa2jyZD4lEzX6/dN6E8XxaJEE= +github.com/dipti-pai/pkg/auth v0.0.0-20241112210126-09c286242cc8/go.mod h1:hBf1c8e7+5utvTIB38NDRpaciDIf9A63okkIEtH3zhA= +github.com/dipti-pai/pkg/git v0.0.0-20241112210126-09c286242cc8 h1:XeByD9Qq8X0CmzKNnPtr0lSTSV//Yl0WfKs6i6Bz7QQ= +github.com/dipti-pai/pkg/git v0.0.0-20241112210126-09c286242cc8/go.mod h1:LUbu8ljvEk+ACKSRUM3Jeb9dpME63+Qjn0mgbQS5xBI= +github.com/dipti-pai/pkg/git/gogit v0.0.0-20241112210126-09c286242cc8 h1:ltsT6WuqvNudJtMClCVRwAVYWBb/uG5xCQJeaQQgtjs= +github.com/dipti-pai/pkg/git/gogit v0.0.0-20241112210126-09c286242cc8/go.mod h1:olbXMpeNBlL2IL665+X9WpdIKDaVgUKuBbnBGbPN3K8= github.com/distribution/distribution/v3 v3.0.0-beta.1 h1:X+ELTxPuZ1Xe5MsD3kp2wfGUhc8I+MPfRis8dZ818Ic= github.com/distribution/distribution/v3 v3.0.0-beta.1/go.mod h1:O9O8uamhHzWWQVTjuQpyYUVm/ShPHPUDgvQMpHGVBDs= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -348,14 +356,8 @@ github.com/fluxcd/pkg/apis/event v0.10.1 h1:3PIAWOtEWblw7R2NUiEMFahRLs3cyYWXQpsM github.com/fluxcd/pkg/apis/event v0.10.1/go.mod h1:MuOoVHW27i0KOoEEerSOg49VdUy4etKc3thbQIeXAmg= github.com/fluxcd/pkg/apis/meta v1.6.1 h1:maLhcRJ3P/70ArLCY/LF/YovkxXbX+6sTWZwZQBeNq0= github.com/fluxcd/pkg/apis/meta v1.6.1/go.mod h1:YndB/gxgGZmKfqpAfFxyCDNFJFP0ikpeJzs66jwq280= -github.com/fluxcd/pkg/auth v0.0.1 h1:3fMg1EdkQdY2Rv1qHbiPPWCBa27xsNeu09y9SuZk6Co= -github.com/fluxcd/pkg/auth v0.0.1/go.mod h1:tdCkiB3/LBg7CcxX1fhVmM5ZjDIaOduK0XX88pBXie0= github.com/fluxcd/pkg/cache v0.0.4 h1:TM733caGoj58GFCOKQN3GajdSVmFx8yNx0HY6l5wV+M= github.com/fluxcd/pkg/cache v0.0.4/go.mod h1:jE7QdMvS9SZcdQaDhUYUm2/fV/KORA362iCNMLdH4pw= -github.com/fluxcd/pkg/git v0.21.0 h1:5FfcKj9bDVz8KwoOQUOSJABLMeSdhvLBf7yctwwuMzc= -github.com/fluxcd/pkg/git v0.21.0/go.mod h1:iCCmUCunoFLgntySJfIDxsHGYfS97ky990gEKIDZ9lo= -github.com/fluxcd/pkg/git/gogit v0.21.0 h1:iR2kzW1XrcBDYuC8zVIAdC/2/aeXuRkZ9jupdd54E6I= -github.com/fluxcd/pkg/git/gogit v0.21.0/go.mod h1:gyoSlEIqzsOiTwSL0iFuEiJat+W0uGgc+WEiCVC1xk8= github.com/fluxcd/pkg/gittestserver v0.13.1 h1:5rXF8ANlk6wtAsvqH7tI7gaO2zhMySftf7ALh0AhfU4= github.com/fluxcd/pkg/gittestserver v0.13.1/go.mod h1:nPO7ibtBRgLWFHTSvxI63zZubJXU82cVMH6nViVnHsY= github.com/fluxcd/pkg/helmtestserver v0.20.0 h1:eNeon7D92DYkTnBShGBS1l5blpjW7IQ21U0gTpArbiE= @@ -526,6 +528,8 @@ github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-2023051620574 github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230516205744-dbecb1de8cfa/go.mod h1:KdL98/Va8Dy1irB6lTxIRIQ7bQj4lbrlvqUzKEQ+ZBU= github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg= github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA= +github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4= +github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff --git a/internal/controller/gitrepository_controller.go b/internal/controller/gitrepository_controller.go index 9ecaf2866..2db594144 100644 --- a/internal/controller/gitrepository_controller.go +++ b/internal/controller/gitrepository_controller.go @@ -28,6 +28,7 @@ import ( securejoin "github.com/cyphar/filepath-securejoin" "github.com/fluxcd/pkg/auth/azure" + "github.com/fluxcd/pkg/auth/github" "github.com/fluxcd/pkg/runtime/logger" "github.com/go-git/go-git/v5/plumbing/transport" corev1 "k8s.io/api/core/v1" @@ -650,13 +651,21 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1 } // Configure provider authentication if specified in spec - if obj.GetProvider() == sourcev1.GitProviderAzure { + switch obj.GetProvider() { + case sourcev1.GitProviderAzure: authOpts.ProviderOpts = &git.ProviderOptions{ - Name: obj.GetProvider(), + Name: sourcev1.GitProviderAzure, AzureOpts: []azure.OptFunc{ azure.WithAzureDevOpsScope(), }, } + case sourcev1.GitProviderGitHub: + authOpts.ProviderOpts = &git.ProviderOptions{ + Name: sourcev1.GitProviderGitHub, + GitHubOpts: []github.OptFunc{ + github.WithAppData(authData), + }, + } } return authOpts, nil diff --git a/internal/controller/gitrepository_controller_test.go b/internal/controller/gitrepository_controller_test.go index a81235553..14cac8f8b 100644 --- a/internal/controller/gitrepository_controller_test.go +++ b/internal/controller/gitrepository_controller_test.go @@ -686,23 +686,35 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { tests := []struct { name string + url string beforeFunc func(obj *sourcev1.GitRepository) wantProviderOptsName string }{ { name: "azure provider", + url: "https://dev.azure.com/foo/bar/_git/baz", beforeFunc: func(obj *sourcev1.GitRepository) { obj.Spec.Provider = sourcev1.GitProviderAzure }, wantProviderOptsName: sourcev1.GitProviderAzure, }, + { + name: "github provider", + url: "https://github.com/org/repo.git", + beforeFunc: func(obj *sourcev1.GitRepository) { + obj.Spec.Provider = sourcev1.GitProviderGitHub + }, + wantProviderOptsName: sourcev1.GitProviderGitHub, + }, { name: "generic provider", + url: "https://example.com/org/repo", beforeFunc: func(obj *sourcev1.GitRepository) { obj.Spec.Provider = sourcev1.GitProviderGeneric }, }, { + url: "https://example.com/org/repo", name: "no provider", }, } @@ -712,7 +724,8 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { g := NewWithT(t) obj := &sourcev1.GitRepository{} r := &GitRepositoryReconciler{} - url, _ := url.Parse("https://dev.azure.com/foo/bar/_git/baz") + url, err := url.Parse(tt.url) + g.Expect(err).ToNot(HaveOccurred()) if tt.beforeFunc != nil { tt.beforeFunc(obj)