Skip to content

Commit

Permalink
feat: GitHub organization app for git cloning (argoproj#4348) (argopr…
Browse files Browse the repository at this point in the history
…oj#5355)

* Git GitHub App auth

Signed-off-by: Slava Markeyev <[email protected]>
  • Loading branch information
stlava authored Feb 19, 2021
1 parent 594c827 commit 13b9b92
Show file tree
Hide file tree
Showing 30 changed files with 1,902 additions and 587 deletions.
1 change: 1 addition & 0 deletions USERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [MOO Print](https://www.moo.com/)
1. [MTN Group](https://www.mtn.com/)
1. [New Relic](https://newrelic.com/)
1. [Nextdoor](https://nextdoor.com/)
1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
1. [Octadesk](https://octadesk.com)
1. [openEuler](https://openeuler.org)
Expand Down
62 changes: 62 additions & 0 deletions assets/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -2809,6 +2809,32 @@
"description": "Whether helm-oci support should be enabled for this repo.",
"name": "enableOci",
"in": "query"
},
{
"type": "string",
"description": "Github App Private Key PEM data.",
"name": "githubAppPrivateKey",
"in": "query"
},
{
"type": "string",
"format": "int64",
"description": "Github App ID of the app used to access the repo.",
"name": "githubAppID",
"in": "query"
},
{
"type": "string",
"format": "int64",
"description": "Github App Installation ID of the installed GitHub App.",
"name": "githubAppInstallationID",
"in": "query"
},
{
"type": "string",
"description": "Github App Enterprise base url if empty will default to https://api.github.com.",
"name": "githubAppEnterpriseBaseUrl",
"in": "query"
}
],
"responses": {
Expand Down Expand Up @@ -5612,6 +5638,24 @@
"type": "object",
"title": "RepoCreds holds a repository credentials definition",
"properties": {
"githubAppEnterpriseBaseUrl": {
"type": "string",
"title": "Github App Enterprise base url if empty will default to https://api.github.com"
},
"githubAppID": {
"type": "string",
"format": "int64",
"title": "Github App ID of the app used to access the repo"
},
"githubAppInstallationID": {
"type": "string",
"format": "int64",
"title": "Github App Installation ID of the installed GitHub App"
},
"githubAppPrivateKey": {
"type": "string",
"title": "Github App Private Key PEM data"
},
"password": {
"type": "string",
"title": "Password for authenticating at the repo server"
Expand Down Expand Up @@ -5668,6 +5712,24 @@
"type": "boolean",
"title": "Whether helm-oci support should be enabled for this repo"
},
"githubAppEnterpriseBaseUrl": {
"type": "string",
"title": "Github App Enterprise base url if empty will default to https://api.github.com"
},
"githubAppID": {
"type": "string",
"format": "int64",
"title": "Github App ID of the app used to access the repo"
},
"githubAppInstallationID": {
"type": "string",
"format": "int64",
"title": "Github App Installation ID of the installed GitHub App"
},
"githubAppPrivateKey": {
"type": "string",
"title": "Github App Private Key PEM data"
},
"inheritedCreds": {
"type": "boolean",
"title": "Whether credentials were inherited from a credential set"
Expand Down
51 changes: 38 additions & 13 deletions cmd/argocd/commands/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {

// For better readability and easier formatting
var repoAddExamples = ` # Add a Git repository via SSH using a private key for authentication, ignoring the server's host key:
argocd repo add [email protected]:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
argocd repo add [email protected]:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
# Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here
argocd repo add ssh://[email protected]:2222/repos/repo --ssh-private-key-path ~/id_rsa
# Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here
argocd repo add ssh://[email protected]:2222/repos/repo --ssh-private-key-path ~/id_rsa
# Add a private Git repository via HTTPS using username/password and TLS client certificates:
argocd repo add https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key
Expand All @@ -65,6 +65,12 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
# Add a private Helm OCI-based repository named 'stable' via HTTPS
argocd repo add helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
# Add a private Git repository on GitHub.com via GitHub App
argocd repo add https://git.example.com/repos/repo --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem
# Add a private Git repository on GitHub Enterprise via GitHub App
argocd repo add https://ghe.example.com/repos/repo --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem --github-app-enterprise-base-url https://ghe.example.com/api/v3
`

var command = &cobra.Command{
Expand Down Expand Up @@ -116,13 +122,28 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
}
}

// Specifying github-app-private-key-path is only valid for HTTPS repositories
if repoOpts.GithubAppPrivateKeyPath != "" {
if git.IsHTTPSURL(repoOpts.Repo.Repo) {
githubAppPrivateKey, err := ioutil.ReadFile(repoOpts.GithubAppPrivateKeyPath)
errors.CheckError(err)
repoOpts.Repo.GithubAppPrivateKey = string(githubAppPrivateKey)
} else {
err := fmt.Errorf("--github-app-private-key-path is only supported for HTTPS repositories")
errors.CheckError(err)
}
}

// Set repository connection properties only when creating repository, not
// when creating repository credentials.
// InsecureIgnoreHostKey is deprecated and only here for backwards compat
repoOpts.Repo.InsecureIgnoreHostKey = repoOpts.InsecureIgnoreHostKey
repoOpts.Repo.Insecure = repoOpts.InsecureSkipServerVerification
repoOpts.Repo.EnableLFS = repoOpts.EnableLfs
repoOpts.Repo.EnableOCI = repoOpts.EnableOci
repoOpts.Repo.GithubAppId = repoOpts.GithubAppId
repoOpts.Repo.GithubAppInstallationId = repoOpts.GithubAppInstallationId
repoOpts.Repo.GitHubAppEnterpriseBaseURL = repoOpts.GitHubAppEnterpriseBaseURL

if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
errors.CheckError(fmt.Errorf("Must specify --name for repos of type 'helm'"))
Expand All @@ -145,16 +166,20 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
// are high that we do not have the given URL pointing to a valid Git
// repo anyway.
repoAccessReq := repositorypkg.RepoAccessQuery{
Repo: repoOpts.Repo.Repo,
Type: repoOpts.Repo.Type,
Name: repoOpts.Repo.Name,
Username: repoOpts.Repo.Username,
Password: repoOpts.Repo.Password,
SshPrivateKey: repoOpts.Repo.SSHPrivateKey,
TlsClientCertData: repoOpts.Repo.TLSClientCertData,
TlsClientCertKey: repoOpts.Repo.TLSClientCertKey,
Insecure: repoOpts.Repo.IsInsecure(),
EnableOci: repoOpts.Repo.EnableOCI,
Repo: repoOpts.Repo.Repo,
Type: repoOpts.Repo.Type,
Name: repoOpts.Repo.Name,
Username: repoOpts.Repo.Username,
Password: repoOpts.Repo.Password,
SshPrivateKey: repoOpts.Repo.SSHPrivateKey,
TlsClientCertData: repoOpts.Repo.TLSClientCertData,
TlsClientCertKey: repoOpts.Repo.TLSClientCertKey,
Insecure: repoOpts.Repo.IsInsecure(),
EnableOci: repoOpts.Repo.EnableOCI,
GithubAppPrivateKey: repoOpts.Repo.GithubAppPrivateKey,
GithubAppID: repoOpts.Repo.GithubAppId,
GithubAppInstallationID: repoOpts.Repo.GithubAppInstallationId,
GithubAppEnterpriseBaseUrl: repoOpts.Repo.GitHubAppEnterpriseBaseURL,
}
_, err := repoIf.ValidateAccess(context.Background(), &repoAccessReq)
errors.CheckError(err)
Expand Down
33 changes: 28 additions & 5 deletions cmd/argocd/commands/repocreds.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ func NewRepoCredsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
// NewRepoCredsAddCommand returns a new instance of an `argocd repocreds add` command
func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
repo appsv1.RepoCreds
upsert bool
sshPrivateKeyPath string
tlsClientCertPath string
tlsClientCertKeyPath string
repo appsv1.RepoCreds
upsert bool
sshPrivateKeyPath string
tlsClientCertPath string
tlsClientCertKeyPath string
githubAppPrivateKeyPath string
)

// For better readability and easier formatting
Expand All @@ -52,6 +53,12 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
# Add credentials with SSH private key authentication to use for all repositories under ssh://[email protected]/repos
argocd repocreds add ssh://[email protected]/repos/ --ssh-private-key-path ~/.ssh/id_rsa
# Add credentials with GitHub App authentication to use for all repositories under https://github.com/repos
argocd repocreds add https://github.com/repos/ --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem
# Add credentials with GitHub App authentication to use for all repositories under https://ghe.example.com/repos
argocd repocreds add https://ghe.example.com/repos/ --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem --github-app-enterprise-base-url https://ghe.example.com/api/v3
`

var command = &cobra.Command{
Expand Down Expand Up @@ -103,6 +110,18 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
}
}

// Specifying github-app-private-key-path is only valid for HTTPS repositories
if githubAppPrivateKeyPath != "" {
if git.IsHTTPSURL(repo.URL) {
githubAppPrivateKey, err := ioutil.ReadFile(githubAppPrivateKeyPath)
errors.CheckError(err)
repo.GithubAppPrivateKey = string(githubAppPrivateKey)
} else {
err := fmt.Errorf("--github-app-private-key-path is only supported for HTTPS repositories")
errors.CheckError(err)
}
}

conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoCredsClientOrDie()
defer io.Close(conn)

Expand All @@ -127,6 +146,10 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
command.Flags().StringVar(&sshPrivateKeyPath, "ssh-private-key-path", "", "path to the private ssh key (e.g. ~/.ssh/id_rsa)")
command.Flags().StringVar(&tlsClientCertPath, "tls-client-cert-path", "", "path to the TLS client cert (must be PEM format)")
command.Flags().StringVar(&tlsClientCertKeyPath, "tls-client-cert-key-path", "", "path to the TLS client cert's key path (must be PEM format)")
command.Flags().Int64Var(&repo.GithubAppId, "github-app-id", 0, "id of the GitHub Application")
command.Flags().Int64Var(&repo.GithubAppInstallationId, "github-app-installation-id", 0, "installation id of the GitHub Application")
command.Flags().StringVar(&githubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
command.Flags().StringVar(&repo.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
command.Flags().BoolVar(&upsert, "upsert", false, "Override an existing repository with the same name even if the spec differs")
return command
}
Expand Down
8 changes: 8 additions & 0 deletions cmd/util/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ type RepoOptions struct {
TlsClientCertKeyPath string
EnableLfs bool
EnableOci bool
GithubAppId int64
GithubAppInstallationId int64
GithubAppPrivateKeyPath string
GitHubAppEnterpriseBaseURL string
}

func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
Expand All @@ -31,4 +35,8 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
command.Flags().BoolVar(&opts.InsecureSkipServerVerification, "insecure-skip-server-verification", false, "disables server certificate and host key checks")
command.Flags().BoolVar(&opts.EnableLfs, "enable-lfs", false, "enable git-lfs (Large File Support) on this repository")
command.Flags().BoolVar(&opts.EnableOci, "enable-oci", false, "enable helm-oci (Helm OCI-Based Repository)")
command.Flags().Int64Var(&opts.GithubAppId, "github-app-id", 0, "id of the GitHub Application")
command.Flags().Int64Var(&opts.GithubAppInstallationId, "github-app-installation-id", 0, "installation id of the GitHub Application")
command.Flags().StringVar(&opts.GithubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
command.Flags().StringVar(&opts.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
}
4 changes: 4 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ const (
RevisionHistoryLimit = 10
// ChangePasswordSSOTokenMaxAge is the max token age for password change operation
ChangePasswordSSOTokenMaxAge = time.Minute * 5
// GithubAppCredsExpirationDuration is the default time used to cache the GitHub app credentials
GithubAppCredsExpirationDuration = time.Minute * 60
)

// Dex related constants
Expand Down Expand Up @@ -184,6 +186,8 @@ const (
EnvControllerShard = "ARGOCD_CONTROLLER_SHARD"
// EnvEnableGRPCTimeHistogramEnv enables gRPC metrics collection
EnvEnableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM"
// EnvGithubAppCredsExpirationDuration controls the caching of Github app credentials. This value is in minutes (default: 60)
EnvGithubAppCredsExpirationDuration = "ARGOCD_GITHUB_APP_CREDS_EXPIRATION_DURATION"
)

const (
Expand Down
Binary file added docs/assets/repo-add-github-app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/repo-add-overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 52 additions & 1 deletion docs/operator-manual/declarative-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ Repository credentials are stored in secret. Use following steps to configure a

1. Create secret which contains repository credentials. Consider using [bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) to store encrypted secret
definition as a Kubernetes manifest.
2. Register repository in the `argocd-cm` config map. Each repository must have `url` field and, depending on whether you connect using HTTPS or SSH, `usernameSecret` and `passwordSecret` (for HTTPS) or `sshPrivateKeySecret` (for SSH).
2. Register repository in the `argocd-cm` config map. Each repository must have `url` field and, depending on whether you connect using HTTPS, SSH, or GitHub App, `usernameSecret` and `passwordSecret` (for HTTPS), `sshPrivateKeySecret` (for SSH), `githubAppPrivateKeySecret` (for GitHub App).

Example for HTTPS:

Expand Down Expand Up @@ -199,6 +199,37 @@ data:
key: sshPrivateKey
```

> v1.9 or later

Example for GitHub App:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
labels:
app.kubernetes.io/name: argocd-cm
app.kubernetes.io/part-of: argocd
data:
repositories: |
- url: https://github.com/argoproj/my-private-repository
githubAppID: 1
githubAppInstallationID: 2
githubAppPrivateKeySecret:
name: my-secret
key: githubAppPrivateKey
- url: https://ghe.example.com/argoproj/my-private-repository
githubAppID: 1
githubAppInstallationID: 2
githubAppEnterpriseBaseUrl: https://ghe.example.com/api/v3
githubAppPrivateKeySecret:
name: my-secret
key: githubAppPrivateKey
```

!!! tip
The Kubernetes documentation has [instructions for creating a secret containing a private key](https://kubernetes.io/docs/concepts/configuration/secret/#use-case-pod-with-ssh-keys).

Expand Down Expand Up @@ -233,6 +264,19 @@ data:
sshPrivateKeySecret:
name: my-secret
key: sshPrivateKey
- url: https://github.com/argoproj
githubAppID: 1
githubAppInstallationID: 2
githubAppPrivateKeySecret:
name: my-secret
key: githubAppPrivateKey
- url: https://ghe.example.com/argoproj
githubAppID: 1
githubAppInstallationID: 2
githubAppEnterpriseBaseUrl: https://ghe.example.com/api/v3
githubAppPrivateKeySecret:
name: my-secret
key: githubAppPrivateKey
```

Argo CD will only use the credentials if you omit `usernameSecret`, `passwordSecret`, and `sshPrivateKeySecret` fields (`insecureIgnoreHostKey` is ignored) or if your repository is not listed in `repositories`.
Expand Down Expand Up @@ -341,6 +385,13 @@ The following keys are valid to refer to credential secrets:
* `usernameSecret` and `passwordSecret` refer to secrets where username and/or password are stored for accessing the repositories
* `tlsClientCertData` and `tlsClientCertKey` refer to secrets where a TLS client certificate (`tlsClientCertData`) and the corresponding private key `tlsClientCertKey` are stored for accessing the repositories

#### GitHub App repositories

* `githubAppPrivateKeySecret` refers to the secret where the GitHub App private key is stored for accessing the repositories
* `githubAppID` refers to the GitHub Application ID for the application you created.
* `githubAppInstallationID` refers to the Installation ID of the GitHub app you created and installed.
* `githubAppEnterpriseBaseUrl` refers to the base api URL for GitHub Enterprise (e.g. `https://ghe.example.com/api/v3`)
* `tlsClientCertData` and `tlsClientCertKey` refer to secrets where a TLS client certificate (`tlsClientCertData`) and the corresponding private key `tlsClientCertKey` are stored for accessing GitHub Enterprise if custom certificates are used.

### Repositories using self-signed TLS certificates (or are signed by custom CA)

Expand Down
Loading

0 comments on commit 13b9b92

Please sign in to comment.