Skip to content

Commit

Permalink
[RFC-007] Implement GitHub app authentication for git repositories.
Browse files Browse the repository at this point in the history
- API change to add new `github` provider field in `GitRepository` spec.
- Controller change to use the GitHub authentication information specified in `.spec.secretRef` to create the auth options to authenticate to git repositories when the `provider` field is set to `github`,
- Tests for new `github` provider field
- Updated docs to use GitHub Apps for authentication in source-controller.

Signed-off-by: Dipti Pai <[email protected]>
  • Loading branch information
dipti-pai committed Nov 12, 2024
1 parent 53868f7 commit 0e437a3
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 14 deletions.
8 changes: 6 additions & 2 deletions api/v1/gitrepository_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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"`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: |-
Expand Down
4 changes: 2 additions & 2 deletions docs/api/v1/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ string
</td>
<td>
<em>(Optional)</em>
<p>Provider used for authentication, can be &lsquo;azure&rsquo;, &lsquo;generic&rsquo;.
<p>Provider used for authentication, can be &lsquo;azure&rsquo;, &lsquo;github&rsquo;, &lsquo;generic&rsquo;.
When not specified, defaults to &lsquo;generic&rsquo;.</p>
</td>
</tr>
Expand Down Expand Up @@ -1730,7 +1730,7 @@ string
</td>
<td>
<em>(Optional)</em>
<p>Provider used for authentication, can be &lsquo;azure&rsquo;, &lsquo;generic&rsquo;.
<p>Provider used for authentication, can be &lsquo;azure&rsquo;, &lsquo;github&rsquo;, &lsquo;generic&rsquo;.
When not specified, defaults to &lsquo;generic&rsquo;.</p>
</td>
</tr>
Expand Down
46 changes: 46 additions & 0 deletions docs/spec/v1/gitrepositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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/<app-name>`.
* 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/<installation-id>`. 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: <app-id>
githubAppInstallationID: <app-installation-id>
githubAppPrivateKey: |
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
```

### Interval

Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
16 changes: 10 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
13 changes: 11 additions & 2 deletions internal/controller/gitrepository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion internal/controller/gitrepository_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
}
Expand All @@ -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)
Expand Down

0 comments on commit 0e437a3

Please sign in to comment.