feat: GitHub organization app for git cloning (#4348) (#5355)

* Git GitHub App auth

Signed-off-by: Slava Markeyev <slavamarkeyev@gmail.com>
This commit is contained in:
Slava Markeyev 2021-02-19 22:24:32 +00:00 committed by GitHub
parent 594c827b66
commit 13b9b92c99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1902 additions and 587 deletions

View file

@ -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)

View file

@ -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": {
@ -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"
@ -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"

View file

@ -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 git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
argocd repo add git@git.example.com: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://git@git.example.com: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://git@git.example.com: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
@ -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{
@ -116,6 +122,18 @@ 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
@ -123,6 +141,9 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
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'"))
@ -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)

View file

@ -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
@ -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://git@git.example.com/repos
argocd repocreds add ssh://git@git.example.com/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{
@ -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)
@ -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
}

View file

@ -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) {
@ -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")
}

View file

@ -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
@ -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 (

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -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:
@ -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).
@ -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`.
@ -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)

View file

@ -36,19 +36,23 @@ argocd-util config repo REPOURL [flags]
### Options
```
--enable-lfs enable git-lfs (Large File Support) on this repository
--enable-oci enable helm-oci (Helm OCI-Based Repository)
-h, --help help for repo
--insecure-ignore-host-key disables SSH strict host key checking (deprecated, use --insecure-skip-server-verification instead)
--insecure-skip-server-verification disables server certificate and host key checks
--name string name of the repository, mandatory for repositories of type helm
-o, --output string Output format. One of: json|yaml (default "yaml")
--password string password to the repository
--ssh-private-key-path string path to the private ssh key (e.g. ~/.ssh/id_rsa)
--tls-client-cert-key-path string path to the TLS client cert's key path (must be PEM format)
--tls-client-cert-path string path to the TLS client cert (must be PEM format)
--type string type of the repository, "git" or "helm" (default "git")
--username string username to the repository
--enable-lfs enable git-lfs (Large File Support) on this repository
--enable-oci enable helm-oci (Helm OCI-Based Repository)
--github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3
--github-app-id int id of the GitHub Application
--github-app-installation-id int installation id of the GitHub Application
--github-app-private-key-path string private key of the GitHub Application
-h, --help help for repo
--insecure-ignore-host-key disables SSH strict host key checking (deprecated, use --insecure-skip-server-verification instead)
--insecure-skip-server-verification disables server certificate and host key checks
--name string name of the repository, mandatory for repositories of type helm
-o, --output string Output format. One of: json|yaml (default "yaml")
--password string password to the repository
--ssh-private-key-path string path to the private ssh key (e.g. ~/.ssh/id_rsa)
--tls-client-cert-key-path string path to the TLS client cert's key path (must be PEM format)
--tls-client-cert-path string path to the TLS client cert (must be PEM format)
--type string type of the repository, "git" or "helm" (default "git")
--username string username to the repository
```
### SEE ALSO

View file

@ -10,10 +10,10 @@ argocd repo add REPOURL [flags]
```
# Add a Git repository via SSH using a private key for authentication, ignoring the server's host key:
argocd repo add git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
argocd repo add git@git.example.com: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://git@git.example.com: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://git@git.example.com: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
@ -30,24 +30,34 @@ argocd repo add REPOURL [flags]
# 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
```
### Options
```
--enable-lfs enable git-lfs (Large File Support) on this repository
--enable-oci enable helm-oci (Helm OCI-Based Repository)
-h, --help help for add
--insecure-ignore-host-key disables SSH strict host key checking (deprecated, use --insecure-skip-server-verification instead)
--insecure-skip-server-verification disables server certificate and host key checks
--name string name of the repository, mandatory for repositories of type helm
--password string password to the repository
--ssh-private-key-path string path to the private ssh key (e.g. ~/.ssh/id_rsa)
--tls-client-cert-key-path string path to the TLS client cert's key path (must be PEM format)
--tls-client-cert-path string path to the TLS client cert (must be PEM format)
--type string type of the repository, "git" or "helm" (default "git")
--upsert Override an existing repository with the same name even if the spec differs
--username string username to the repository
--enable-lfs enable git-lfs (Large File Support) on this repository
--enable-oci enable helm-oci (Helm OCI-Based Repository)
--github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3
--github-app-id int id of the GitHub Application
--github-app-installation-id int installation id of the GitHub Application
--github-app-private-key-path string private key of the GitHub Application
-h, --help help for add
--insecure-ignore-host-key disables SSH strict host key checking (deprecated, use --insecure-skip-server-verification instead)
--insecure-skip-server-verification disables server certificate and host key checks
--name string name of the repository, mandatory for repositories of type helm
--password string password to the repository
--ssh-private-key-path string path to the private ssh key (e.g. ~/.ssh/id_rsa)
--tls-client-cert-key-path string path to the TLS client cert's key path (must be PEM format)
--tls-client-cert-path string path to the TLS client cert (must be PEM format)
--type string type of the repository, "git" or "helm" (default "git")
--upsert Override an existing repository with the same name even if the spec differs
--username string username to the repository
```
### Options inherited from parent commands

View file

@ -15,18 +15,28 @@ argocd repocreds add REPOURL [flags]
# Add credentials with SSH private key authentication to use for all repositories under ssh://git@git.example.com/repos
argocd repocreds add ssh://git@git.example.com/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
```
### Options
```
-h, --help help for add
--password string password to the repository
--ssh-private-key-path string path to the private ssh key (e.g. ~/.ssh/id_rsa)
--tls-client-cert-key-path string path to the TLS client cert's key path (must be PEM format)
--tls-client-cert-path string path to the TLS client cert (must be PEM format)
--upsert Override an existing repository with the same name even if the spec differs
--username string username to the repository
--github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3
--github-app-id int id of the GitHub Application
--github-app-installation-id int installation id of the GitHub Application
--github-app-private-key-path string private key of the GitHub Application
-h, --help help for add
--password string password to the repository
--ssh-private-key-path string path to the private ssh key (e.g. ~/.ssh/id_rsa)
--tls-client-cert-key-path string path to the TLS client cert's key path (must be PEM format)
--tls-client-cert-path string path to the TLS client cert (must be PEM format)
--upsert Override an existing repository with the same name even if the spec differs
--username string username to the repository
```
### Options inherited from parent commands

View file

@ -116,6 +116,42 @@ The Argo CD UI don't support configuring SSH credentials. The SSH credentials ca
argocd repo add git@github.com:argoproj/argocd-example-apps.git --ssh-private-key-path ~/.ssh/id_rsa
```
### GitHub App Credential
Private repositories that are hosted on GitHub.com or GitHub Enterprise can be accessed using credentials from a GitHub Application. Consult the [GitHub documentation](https://docs.github.com/en/developers/apps/about-apps#about-github-apps) on how to create an application.
!!!note
Ensure your application has at least `Read-only` permissions to the `Contents` of the repository. This is the minimum requirement.
> previous to v1.9
GitHub App credentials are not supported.
> v1.9 or later
You can configure access to your Git repository hosted by GitHub.com or GitHub Enterprise using the GitHub App method by either using the CLI or the UI.
Using the CLI:
```
argocd repo add https://github.com/argoproj/argocd-example-apps.git --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem
```
Using the UI:
1. Navigate to `Settings/Repositories`
![connect repo overview](../assets/repo-add-overview.png)
1. Click `Connect Repo using GitHub App` button, enter the URL, App Id, Installation Id, and the app's private key.
![connect repo](../assets/repo-add-github-app.png)
1. Click `Connect` to test the connection and have the repository added
!!!note
When pasting GitHub App private key in the UI, make sure there are no unintended line breaks or additional characters in the text area
## Credential templates
> previous to v1.4

1
go.mod
View file

@ -10,6 +10,7 @@ require (
github.com/argoproj/gitops-engine v0.2.1-0.20210218233004-354817a103ee
github.com/argoproj/pkg v0.2.0
github.com/bombsimon/logrusr v1.0.0
github.com/bradleyfalzon/ghinstallation v1.1.1
github.com/casbin/casbin v1.9.1
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
github.com/coreos/go-oidc v2.1.0+incompatible

6
go.sum
View file

@ -118,6 +118,8 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bombsimon/logrusr v1.0.0 h1:CTCkURYAt5nhCCnKH9eLShYayj2/8Kn/4Qg3QfiU+Ro=
github.com/bombsimon/logrusr v1.0.0/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM=
github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog=
@ -352,8 +354,12 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY=
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=

View file

@ -322,10 +322,18 @@ type RepoAccessQuery struct {
// The name of the repo
Name string `protobuf:"bytes,10,opt,name=name,proto3" json:"name,omitempty"`
// Whether helm-oci support should be enabled for this repo
EnableOci bool `protobuf:"varint,11,opt,name=enableOci,proto3" json:"enableOci,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
EnableOci bool `protobuf:"varint,11,opt,name=enableOci,proto3" json:"enableOci,omitempty"`
// Github App Private Key PEM data
GithubAppPrivateKey string `protobuf:"bytes,12,opt,name=githubAppPrivateKey,proto3" json:"githubAppPrivateKey,omitempty"`
// Github App ID of the app used to access the repo
GithubAppID int64 `protobuf:"varint,13,opt,name=githubAppID,proto3" json:"githubAppID,omitempty"`
// Github App Installation ID of the installed GitHub App
GithubAppInstallationID int64 `protobuf:"varint,14,opt,name=githubAppInstallationID,proto3" json:"githubAppInstallationID,omitempty"`
// Github App Enterprise base url if empty will default to https://api.github.com
GithubAppEnterpriseBaseUrl string `protobuf:"bytes,15,opt,name=githubAppEnterpriseBaseUrl,proto3" json:"githubAppEnterpriseBaseUrl,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RepoAccessQuery) Reset() { *m = RepoAccessQuery{} }
@ -431,6 +439,34 @@ func (m *RepoAccessQuery) GetEnableOci() bool {
return false
}
func (m *RepoAccessQuery) GetGithubAppPrivateKey() string {
if m != nil {
return m.GithubAppPrivateKey
}
return ""
}
func (m *RepoAccessQuery) GetGithubAppID() int64 {
if m != nil {
return m.GithubAppID
}
return 0
}
func (m *RepoAccessQuery) GetGithubAppInstallationID() int64 {
if m != nil {
return m.GithubAppInstallationID
}
return 0
}
func (m *RepoAccessQuery) GetGithubAppEnterpriseBaseUrl() string {
if m != nil {
return m.GithubAppEnterpriseBaseUrl
}
return ""
}
type RepoResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@ -601,69 +637,74 @@ func init() {
}
var fileDescriptor_8d38260443475705 = []byte{
// 989 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0x1b, 0xc5,
0x1b, 0xd6, 0x26, 0xa9, 0x9b, 0x4c, 0x9a, 0xfc, 0xdc, 0x69, 0x7f, 0x95, 0x71, 0xdd, 0x34, 0x9a,
0x96, 0x2a, 0x44, 0x65, 0x17, 0x1b, 0x90, 0x50, 0x11, 0x42, 0x89, 0x83, 0x4a, 0x44, 0xa4, 0xc2,
0x56, 0x45, 0x02, 0x09, 0xa1, 0xc9, 0xfa, 0xb5, 0xbd, 0x64, 0xbd, 0x33, 0xcc, 0x8c, 0x8d, 0xac,
0xa8, 0x17, 0x4e, 0xe5, 0x08, 0x88, 0x1b, 0x17, 0x24, 0x0e, 0x7c, 0x0b, 0xae, 0x1c, 0x91, 0xf8,
0x02, 0x28, 0xe2, 0xc2, 0xb7, 0x40, 0x33, 0xb3, 0xff, 0x1c, 0xdb, 0xdb, 0x54, 0x84, 0xdc, 0x66,
0x9e, 0x99, 0x79, 0x9f, 0xe7, 0x7d, 0xe6, 0xdd, 0x77, 0x07, 0x11, 0x09, 0x62, 0x04, 0xc2, 0x13,
0xc0, 0x99, 0x0c, 0x15, 0x13, 0xe3, 0xc2, 0xd0, 0xe5, 0x82, 0x29, 0x86, 0x51, 0x8e, 0xd4, 0xaf,
0xf7, 0x58, 0x8f, 0x19, 0xd8, 0xd3, 0x23, 0xbb, 0xa3, 0xde, 0xe8, 0x31, 0xd6, 0x8b, 0xc0, 0xa3,
0x3c, 0xf4, 0x68, 0x1c, 0x33, 0x45, 0x55, 0xc8, 0x62, 0x99, 0xac, 0x92, 0xa3, 0xb7, 0xa4, 0x1b,
0x32, 0xb3, 0x1a, 0x30, 0x01, 0xde, 0xa8, 0xe9, 0xf5, 0x20, 0x06, 0x41, 0x15, 0x74, 0x92, 0x3d,
0xfb, 0xbd, 0x50, 0xf5, 0x87, 0x87, 0x6e, 0xc0, 0x06, 0x1e, 0x15, 0x86, 0xe2, 0x0b, 0x33, 0x78,
0x35, 0xe8, 0x78, 0xfc, 0xa8, 0xa7, 0x0f, 0x4b, 0x8f, 0x72, 0x1e, 0x85, 0x81, 0x09, 0xee, 0x8d,
0x9a, 0x34, 0xe2, 0x7d, 0x3a, 0x1d, 0x6a, 0xb7, 0x2c, 0x94, 0x49, 0xe5, 0xb9, 0x29, 0x93, 0x77,
0xd1, 0x9a, 0x0f, 0x9c, 0xed, 0x70, 0x2e, 0x3f, 0x1a, 0x82, 0x18, 0x63, 0x8c, 0x96, 0xf4, 0xa6,
0x9a, 0xb3, 0xe9, 0x6c, 0xad, 0xf8, 0x66, 0x8c, 0xeb, 0x68, 0x59, 0xc0, 0x28, 0x94, 0x21, 0x8b,
0x6b, 0x0b, 0x06, 0xcf, 0xe6, 0xa4, 0x89, 0x2e, 0xef, 0x70, 0xbe, 0x1f, 0x77, 0x99, 0x3e, 0xaa,
0xc6, 0x1c, 0xd2, 0xa3, 0x7a, 0xac, 0x31, 0x4e, 0x55, 0x3f, 0x39, 0x66, 0xc6, 0xe4, 0x18, 0x5d,
0x4b, 0x38, 0xf7, 0x40, 0xd1, 0x30, 0x4a, 0x98, 0x3b, 0xa8, 0x22, 0xd9, 0x50, 0x04, 0x36, 0xc0,
0x6a, 0xeb, 0xc0, 0xcd, 0xf3, 0x73, 0xd3, 0xfc, 0xcc, 0xe0, 0xf3, 0xa0, 0xe3, 0xf2, 0xa3, 0x9e,
0xab, 0xad, 0x72, 0x0b, 0x56, 0xb9, 0xa9, 0x55, 0xee, 0x4e, 0x0e, 0x3e, 0x36, 0x31, 0xfd, 0x24,
0x36, 0x79, 0x07, 0x55, 0xd3, 0x84, 0x7d, 0x90, 0x9c, 0xc5, 0x12, 0xf0, 0x2b, 0xe8, 0x52, 0xa8,
0x60, 0x20, 0x6b, 0xce, 0xe6, 0xe2, 0xd6, 0x6a, 0xeb, 0x9a, 0x5b, 0xb0, 0x29, 0x49, 0xce, 0xb7,
0x3b, 0x48, 0x1b, 0xad, 0xe8, 0xe3, 0xf3, 0xbd, 0x22, 0xe8, 0x4a, 0x97, 0x69, 0x42, 0xe8, 0x0a,
0x90, 0x36, 0xf1, 0x65, 0x7f, 0x02, 0x23, 0xbf, 0x2e, 0xa0, 0xff, 0x19, 0x11, 0x41, 0x00, 0xb2,
0xdc, 0xf7, 0xa1, 0x04, 0x11, 0xd3, 0x01, 0xa4, 0xbe, 0xa7, 0x73, 0xbd, 0xc6, 0xa9, 0x94, 0x5f,
0x31, 0xd1, 0xa9, 0x2d, 0xda, 0xb5, 0x74, 0x8e, 0xef, 0xa2, 0x35, 0x29, 0xfb, 0x1f, 0x8a, 0x70,
0x44, 0x15, 0x7c, 0x00, 0xe3, 0xda, 0x92, 0xd9, 0x30, 0x09, 0xea, 0x08, 0x61, 0x2c, 0x21, 0x18,
0x0a, 0xa8, 0x5d, 0x32, 0x2a, 0xb3, 0x39, 0xbe, 0x8f, 0xae, 0xaa, 0x48, 0xb6, 0xa3, 0x10, 0x62,
0xd5, 0x06, 0xa1, 0xf6, 0xa8, 0xa2, 0xb5, 0x8a, 0x89, 0x32, 0xbd, 0x80, 0xb7, 0x51, 0x75, 0x02,
0xd4, 0x94, 0x97, 0xcd, 0xe6, 0x29, 0x3c, 0x2b, 0x92, 0x95, 0xc9, 0x22, 0x31, 0x39, 0x22, 0x8b,
0x99, 0xfc, 0x1a, 0x68, 0x05, 0x62, 0x7a, 0x18, 0xc1, 0xa3, 0x20, 0xac, 0xad, 0x1a, 0x79, 0x39,
0x40, 0xd6, 0xd1, 0x15, 0x6d, 0x60, 0x7a, 0x83, 0xe4, 0x67, 0x07, 0x5d, 0xd5, 0x40, 0x5b, 0x00,
0x55, 0xe0, 0xc3, 0x97, 0x43, 0x90, 0x0a, 0x7f, 0x52, 0xf0, 0x74, 0xb5, 0xf5, 0xde, 0xbf, 0xa8,
0x27, 0x3f, 0x2b, 0x88, 0xe4, 0x6a, 0x6e, 0xa0, 0xca, 0x90, 0x4b, 0x10, 0x2a, 0xb9, 0xe0, 0x64,
0xa6, 0x65, 0x07, 0x02, 0x3a, 0xf2, 0x51, 0x1c, 0x8d, 0xcd, 0xbd, 0x2c, 0xfb, 0x39, 0x40, 0x62,
0xab, 0xf2, 0x09, 0xef, 0x5c, 0x88, 0xca, 0xd6, 0xdf, 0xeb, 0x96, 0xd0, 0x82, 0x8f, 0x41, 0x8c,
0xc2, 0x00, 0xf0, 0x37, 0x0e, 0x5a, 0x3a, 0x08, 0xa5, 0xc2, 0xff, 0x2f, 0x16, 0x7a, 0x56, 0xd6,
0xf5, 0xfd, 0x73, 0x91, 0xa0, 0x19, 0xc8, 0xed, 0xaf, 0xff, 0xf8, 0xeb, 0xfb, 0x85, 0x1b, 0xf8,
0xba, 0xe9, 0x89, 0xa3, 0x66, 0xde, 0x80, 0x42, 0x90, 0xcf, 0x16, 0x1c, 0xfc, 0xcc, 0x41, 0x8b,
0x0f, 0x61, 0xae, 0x94, 0xf3, 0x71, 0x83, 0xdc, 0x31, 0x32, 0x6e, 0xe1, 0x9b, 0xb3, 0x64, 0x78,
0xc7, 0x7a, 0xf6, 0x14, 0x7f, 0xe7, 0xa0, 0xaa, 0x16, 0xed, 0x17, 0xd6, 0x2e, 0xc0, 0xa2, 0x46,
0x99, 0x45, 0xf8, 0x33, 0xb4, 0x6c, 0x35, 0x75, 0xe7, 0x6a, 0xa9, 0x4e, 0xc2, 0x5d, 0x49, 0xb6,
0x4c, 0x48, 0x82, 0x37, 0x4b, 0xd2, 0xf5, 0x84, 0x0e, 0x39, 0xb0, 0xe1, 0x75, 0x37, 0xc4, 0x2f,
0x9d, 0x0e, 0x9f, 0xfd, 0x14, 0xea, 0x8d, 0x59, 0x4b, 0xd9, 0xc7, 0x77, 0x26, 0x3a, 0xaa, 0x29,
0xbe, 0x75, 0xd0, 0xda, 0x43, 0x50, 0x79, 0xe7, 0xc7, 0xb7, 0x67, 0x44, 0x2e, 0xfe, 0x15, 0xea,
0x64, 0xfe, 0x86, 0x4c, 0xc0, 0xdb, 0x46, 0xc0, 0x9b, 0xe4, 0xb5, 0xd9, 0x02, 0x6c, 0xe7, 0x37,
0x71, 0x9e, 0xf8, 0x07, 0x46, 0x4a, 0xc7, 0x46, 0x78, 0xe0, 0x6c, 0xe3, 0x91, 0x91, 0xf4, 0x3e,
0x44, 0x83, 0x76, 0x9f, 0x0a, 0x35, 0xd7, 0xe6, 0x8d, 0x22, 0x9c, 0x6f, 0xcf, 0x44, 0xb8, 0x46,
0xc4, 0x16, 0xbe, 0x57, 0xe6, 0x42, 0x1f, 0xa2, 0x41, 0x60, 0x69, 0x7e, 0x70, 0x50, 0xc5, 0xb6,
0x2b, 0x7c, 0xeb, 0x34, 0xe3, 0x44, 0x1b, 0x3b, 0xaf, 0x8f, 0xe0, 0x65, 0x23, 0xb0, 0x41, 0x66,
0x16, 0xda, 0x03, 0xd3, 0x30, 0xf4, 0x17, 0xf9, 0xa3, 0x83, 0xaa, 0x29, 0x7f, 0x7a, 0xf6, 0x82,
0x14, 0x92, 0xe7, 0x2b, 0xc4, 0x3f, 0x39, 0xa8, 0x62, 0xfb, 0xe7, 0xb4, 0xa8, 0x89, 0xbe, 0x7a,
0x5e, 0xa2, 0x9a, 0xf6, 0x5e, 0xeb, 0x25, 0xd5, 0x6d, 0x74, 0x3c, 0xcd, 0x2d, 0xfc, 0xc5, 0x41,
0xd5, 0x54, 0xcb, 0x7c, 0x0b, 0xff, 0x13, 0xb5, 0xee, 0x8b, 0xa9, 0xc5, 0x14, 0x55, 0xf6, 0x20,
0x02, 0x05, 0xf3, 0xca, 0xbe, 0x76, 0x1a, 0xce, 0x0a, 0xfe, 0x9e, 0x6d, 0xaa, 0xdb, 0x65, 0x4d,
0x55, 0xbb, 0xd1, 0x47, 0x55, 0x4b, 0x51, 0x30, 0xe3, 0x85, 0xc9, 0xee, 0x9c, 0x81, 0x0c, 0x1f,
0xa3, 0xf5, 0x8f, 0x69, 0x14, 0x6a, 0x5b, 0xed, 0xd3, 0x0a, 0xdf, 0x9c, 0xea, 0x1e, 0xf9, 0x93,
0xab, 0x84, 0xad, 0x65, 0xd8, 0xee, 0x93, 0xbb, 0x65, 0xdf, 0xf2, 0x28, 0xa1, 0xb2, 0x4e, 0xee,
0xee, 0xfe, 0x76, 0xb2, 0xe1, 0xfc, 0x7e, 0xb2, 0xe1, 0xfc, 0x79, 0xb2, 0xe1, 0x7c, 0xfa, 0xc6,
0x19, 0x9e, 0xf9, 0x81, 0x79, 0x18, 0x15, 0xde, 0xe4, 0x87, 0x15, 0xf3, 0x28, 0x7f, 0xfd, 0x9f,
0x00, 0x00, 0x00, 0xff, 0xff, 0x11, 0x28, 0x6f, 0x6f, 0xad, 0x0c, 0x00, 0x00,
// 1063 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4d, 0x6f, 0x1c, 0x45,
0x13, 0xd6, 0xd8, 0xce, 0xc6, 0x6e, 0x7f, 0x64, 0xd3, 0xce, 0x9b, 0x77, 0xd8, 0x38, 0x8e, 0xd5,
0x09, 0x91, 0xb1, 0xc2, 0x4c, 0x6c, 0x40, 0x8a, 0x82, 0x00, 0xf9, 0x23, 0x0a, 0x16, 0x96, 0x02,
0x13, 0x19, 0x09, 0x24, 0x84, 0xda, 0xb3, 0xe5, 0xdd, 0xc1, 0xb3, 0xd3, 0x4d, 0x77, 0xef, 0xa2,
0x95, 0x95, 0x0b, 0xa7, 0x70, 0xe0, 0x00, 0x88, 0x1b, 0x17, 0x24, 0x0e, 0xfc, 0x14, 0x8e, 0x48,
0xfc, 0x01, 0x64, 0x71, 0xe1, 0x5f, 0xa0, 0xee, 0x9e, 0xaf, 0xf5, 0xee, 0x4e, 0x1c, 0x61, 0x7c,
0xeb, 0x7e, 0xaa, 0xba, 0xea, 0xa9, 0x67, 0xaa, 0x6b, 0x66, 0x10, 0x91, 0x20, 0x7a, 0x20, 0x7c,
0x01, 0x9c, 0xc9, 0x48, 0x31, 0xd1, 0x2f, 0x2d, 0x3d, 0x2e, 0x98, 0x62, 0x18, 0x15, 0x48, 0xe3,
0x5a, 0x8b, 0xb5, 0x98, 0x81, 0x7d, 0xbd, 0xb2, 0x1e, 0x8d, 0xa5, 0x16, 0x63, 0xad, 0x18, 0x7c,
0xca, 0x23, 0x9f, 0x26, 0x09, 0x53, 0x54, 0x45, 0x2c, 0x91, 0xa9, 0x95, 0x1c, 0x3d, 0x90, 0x5e,
0xc4, 0x8c, 0x35, 0x64, 0x02, 0xfc, 0xde, 0xba, 0xdf, 0x82, 0x04, 0x04, 0x55, 0xd0, 0x4c, 0x7d,
0x76, 0x5b, 0x91, 0x6a, 0x77, 0x0f, 0xbc, 0x90, 0x75, 0x7c, 0x2a, 0x4c, 0x8a, 0x2f, 0xcc, 0xe2,
0xf5, 0xb0, 0xe9, 0xf3, 0xa3, 0x96, 0x3e, 0x2c, 0x7d, 0xca, 0x79, 0x1c, 0x85, 0x26, 0xb8, 0xdf,
0x5b, 0xa7, 0x31, 0x6f, 0xd3, 0xe1, 0x50, 0x5b, 0x55, 0xa1, 0x4c, 0x29, 0x2f, 0x2c, 0x99, 0xbc,
0x87, 0xe6, 0x03, 0xe0, 0x6c, 0x93, 0x73, 0xf9, 0x51, 0x17, 0x44, 0x1f, 0x63, 0x34, 0xa5, 0x9d,
0x5c, 0x67, 0xc5, 0x59, 0x9d, 0x09, 0xcc, 0x1a, 0x37, 0xd0, 0xb4, 0x80, 0x5e, 0x24, 0x23, 0x96,
0xb8, 0x13, 0x06, 0xcf, 0xf7, 0x64, 0x1d, 0x5d, 0xde, 0xe4, 0x7c, 0x37, 0x39, 0x64, 0xfa, 0xa8,
0xea, 0x73, 0xc8, 0x8e, 0xea, 0xb5, 0xc6, 0x38, 0x55, 0xed, 0xf4, 0x98, 0x59, 0x93, 0x63, 0xb4,
0x98, 0xe6, 0xdc, 0x01, 0x45, 0xa3, 0x38, 0xcd, 0xdc, 0x44, 0x35, 0xc9, 0xba, 0x22, 0xb4, 0x01,
0x66, 0x37, 0xf6, 0xbc, 0xa2, 0x3e, 0x2f, 0xab, 0xcf, 0x2c, 0x3e, 0x0f, 0x9b, 0x1e, 0x3f, 0x6a,
0x79, 0x5a, 0x2a, 0xaf, 0x24, 0x95, 0x97, 0x49, 0xe5, 0x6d, 0x16, 0xe0, 0x53, 0x13, 0x33, 0x48,
0x63, 0x93, 0x77, 0x50, 0x3d, 0x2b, 0x38, 0x00, 0xc9, 0x59, 0x22, 0x01, 0xbf, 0x86, 0x2e, 0x45,
0x0a, 0x3a, 0xd2, 0x75, 0x56, 0x26, 0x57, 0x67, 0x37, 0x16, 0xbd, 0x92, 0x4c, 0x69, 0x71, 0x81,
0xf5, 0x20, 0xdb, 0x68, 0x46, 0x1f, 0x1f, 0xaf, 0x15, 0x41, 0x73, 0x87, 0x4c, 0x27, 0x84, 0x43,
0x01, 0xd2, 0x16, 0x3e, 0x1d, 0x0c, 0x60, 0xe4, 0xdb, 0x29, 0x74, 0xc5, 0x90, 0x08, 0x43, 0x90,
0xd5, 0xba, 0x77, 0x25, 0x88, 0x84, 0x76, 0x20, 0xd3, 0x3d, 0xdb, 0x6b, 0x1b, 0xa7, 0x52, 0x7e,
0xc5, 0x44, 0xd3, 0x9d, 0xb4, 0xb6, 0x6c, 0x8f, 0xef, 0xa0, 0x79, 0x29, 0xdb, 0x1f, 0x8a, 0xa8,
0x47, 0x15, 0x7c, 0x00, 0x7d, 0x77, 0xca, 0x38, 0x0c, 0x82, 0x3a, 0x42, 0x94, 0x48, 0x08, 0xbb,
0x02, 0xdc, 0x4b, 0x86, 0x65, 0xbe, 0xc7, 0xf7, 0xd0, 0x55, 0x15, 0xcb, 0xed, 0x38, 0x82, 0x44,
0x6d, 0x83, 0x50, 0x3b, 0x54, 0x51, 0xb7, 0x66, 0xa2, 0x0c, 0x1b, 0xf0, 0x1a, 0xaa, 0x0f, 0x80,
0x3a, 0xe5, 0x65, 0xe3, 0x3c, 0x84, 0xe7, 0x4d, 0x32, 0x33, 0xd8, 0x24, 0xa6, 0x46, 0x64, 0x31,
0x53, 0xdf, 0x12, 0x9a, 0x81, 0x84, 0x1e, 0xc4, 0xf0, 0x24, 0x8c, 0xdc, 0x59, 0x43, 0xaf, 0x00,
0xf0, 0x7d, 0xb4, 0x68, 0x9b, 0x63, 0x93, 0xf3, 0x52, 0x9d, 0x73, 0x26, 0xc0, 0x28, 0x13, 0x5e,
0x41, 0xb3, 0x39, 0xbc, 0xbb, 0xe3, 0xce, 0xaf, 0x38, 0xab, 0x93, 0x41, 0x19, 0xc2, 0x0f, 0xd0,
0xff, 0x8b, 0x6d, 0x22, 0x15, 0x8d, 0x63, 0xd3, 0x40, 0xbb, 0x3b, 0xee, 0x82, 0xf1, 0x1e, 0x67,
0xc6, 0xef, 0xa2, 0x46, 0x6e, 0x7a, 0x94, 0x28, 0x10, 0x5c, 0x44, 0x12, 0xb6, 0xa8, 0x84, 0x7d,
0x11, 0xbb, 0x57, 0x0c, 0xa9, 0x0a, 0x0f, 0xb2, 0x80, 0xe6, 0x74, 0x3b, 0x64, 0xfd, 0x48, 0x7e,
0x71, 0xd0, 0x55, 0x0d, 0x6c, 0x0b, 0xa0, 0x0a, 0x02, 0xf8, 0xb2, 0x0b, 0x52, 0xe1, 0x4f, 0x4a,
0x1d, 0x32, 0xbb, 0xf1, 0xe8, 0x5f, 0xdc, 0x8e, 0x20, 0x6f, 0xef, 0xb4, 0xd1, 0xae, 0xa3, 0x5a,
0x97, 0x4b, 0x10, 0x2a, 0x6d, 0xd7, 0x74, 0xa7, 0x1f, 0x42, 0x28, 0xa0, 0x29, 0x9f, 0x24, 0x71,
0xdf, 0x74, 0xd9, 0x74, 0x50, 0x00, 0x24, 0xb1, 0x2c, 0xf7, 0x79, 0xf3, 0x42, 0x58, 0x6e, 0xfc,
0xbd, 0x60, 0x13, 0x5a, 0xf0, 0x29, 0x88, 0x5e, 0x14, 0x02, 0xfe, 0xc6, 0x41, 0x53, 0x7b, 0x91,
0x54, 0xf8, 0x7f, 0xe5, 0x6b, 0x9b, 0x5f, 0xd2, 0xc6, 0xee, 0xb9, 0x50, 0xd0, 0x19, 0xc8, 0xad,
0xaf, 0xff, 0xf8, 0xeb, 0x87, 0x89, 0xeb, 0xf8, 0x9a, 0x99, 0xf0, 0xbd, 0xf5, 0x62, 0x9c, 0x46,
0x20, 0x9f, 0x4f, 0x38, 0xf8, 0xb9, 0x83, 0x26, 0x1f, 0xc3, 0x58, 0x2a, 0xe7, 0xa3, 0x06, 0xb9,
0x6d, 0x68, 0xdc, 0xc4, 0x37, 0x46, 0xd1, 0xf0, 0x8f, 0xf5, 0xee, 0x19, 0xfe, 0xde, 0x41, 0x75,
0x4d, 0x3a, 0x28, 0xd9, 0x2e, 0x40, 0xa2, 0xa5, 0x2a, 0x89, 0xf0, 0x67, 0x68, 0xda, 0x72, 0x3a,
0x1c, 0xcb, 0xa5, 0x3e, 0x08, 0x1f, 0x4a, 0xb2, 0x6a, 0x42, 0x12, 0xbc, 0x52, 0x51, 0xae, 0x2f,
0x74, 0xc8, 0x8e, 0x0d, 0xaf, 0x67, 0x3b, 0x7e, 0xe5, 0x74, 0xf8, 0xfc, 0x15, 0xd7, 0x58, 0x1a,
0x65, 0xca, 0x2f, 0xdf, 0x99, 0xd2, 0x51, 0x9d, 0xe2, 0x3b, 0x07, 0xcd, 0x3f, 0x06, 0x55, 0xbc,
0xc7, 0xf0, 0xad, 0x11, 0x91, 0xcb, 0xef, 0xb8, 0x06, 0x19, 0xef, 0x90, 0x13, 0x78, 0xdb, 0x10,
0x78, 0x8b, 0xdc, 0x1f, 0x4d, 0xc0, 0xbe, 0xc7, 0x4c, 0x9c, 0xfd, 0x60, 0xcf, 0x50, 0x69, 0xda,
0x08, 0x0f, 0x9d, 0x35, 0xdc, 0x33, 0x94, 0xde, 0x87, 0xb8, 0xb3, 0xdd, 0xa6, 0x42, 0x8d, 0x95,
0x79, 0xb9, 0x0c, 0x17, 0xee, 0x39, 0x09, 0xcf, 0x90, 0x58, 0xc5, 0x77, 0xab, 0x54, 0x68, 0x43,
0xdc, 0x09, 0x6d, 0x9a, 0x1f, 0x1d, 0x54, 0xb3, 0xe3, 0x0a, 0xdf, 0x3c, 0x9d, 0x71, 0x60, 0x8c,
0x9d, 0xd7, 0x25, 0x78, 0xd5, 0x10, 0x5c, 0x22, 0x23, 0x1b, 0xed, 0xa1, 0x19, 0x18, 0xfa, 0x46,
0xfe, 0xe4, 0xa0, 0x7a, 0x96, 0x3f, 0x3b, 0x7b, 0x41, 0x0c, 0xc9, 0x8b, 0x19, 0xe2, 0x9f, 0x1d,
0x54, 0xb3, 0xf3, 0x73, 0x98, 0xd4, 0xc0, 0x5c, 0x3d, 0x2f, 0x52, 0xeb, 0xf6, 0xb9, 0x36, 0x2a,
0xba, 0xdb, 0xf0, 0x78, 0x56, 0x48, 0xf8, 0xab, 0x83, 0xea, 0x19, 0x97, 0xf1, 0x12, 0xfe, 0x27,
0x6c, 0xbd, 0x97, 0x63, 0x8b, 0x29, 0xaa, 0xed, 0x40, 0x0c, 0x0a, 0xc6, 0xb5, 0xbd, 0x7b, 0x1a,
0xce, 0x1b, 0xfe, 0xae, 0x1d, 0xaa, 0x6b, 0x55, 0x43, 0x55, 0xab, 0xd1, 0x46, 0x75, 0x9b, 0xa2,
0x24, 0xc6, 0x4b, 0x27, 0xbb, 0x7d, 0x86, 0x64, 0xf8, 0x18, 0x2d, 0x7c, 0x4c, 0xe3, 0x48, 0xcb,
0x6a, 0x3f, 0x14, 0xf1, 0x8d, 0xa1, 0xe9, 0x51, 0x7c, 0x40, 0x56, 0x64, 0xdb, 0x30, 0xd9, 0xee,
0x91, 0x3b, 0x55, 0x77, 0xb9, 0x97, 0xa6, 0xb2, 0x4a, 0x6e, 0x6d, 0xfd, 0x76, 0xb2, 0xec, 0xfc,
0x7e, 0xb2, 0xec, 0xfc, 0x79, 0xb2, 0xec, 0x7c, 0xfa, 0xe6, 0x19, 0x7e, 0x5a, 0x42, 0xf3, 0x99,
0x57, 0xfa, 0xc3, 0x38, 0xa8, 0x99, 0x5f, 0x8c, 0x37, 0xfe, 0x09, 0x00, 0x00, 0xff, 0xff, 0x2b,
0xf1, 0xe2, 0xa9, 0x7b, 0x0d, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -1474,6 +1515,30 @@ func (m *RepoAccessQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.GithubAppEnterpriseBaseUrl) > 0 {
i -= len(m.GithubAppEnterpriseBaseUrl)
copy(dAtA[i:], m.GithubAppEnterpriseBaseUrl)
i = encodeVarintRepository(dAtA, i, uint64(len(m.GithubAppEnterpriseBaseUrl)))
i--
dAtA[i] = 0x7a
}
if m.GithubAppInstallationID != 0 {
i = encodeVarintRepository(dAtA, i, uint64(m.GithubAppInstallationID))
i--
dAtA[i] = 0x70
}
if m.GithubAppID != 0 {
i = encodeVarintRepository(dAtA, i, uint64(m.GithubAppID))
i--
dAtA[i] = 0x68
}
if len(m.GithubAppPrivateKey) > 0 {
i -= len(m.GithubAppPrivateKey)
copy(dAtA[i:], m.GithubAppPrivateKey)
i = encodeVarintRepository(dAtA, i, uint64(len(m.GithubAppPrivateKey)))
i--
dAtA[i] = 0x62
}
if m.EnableOci {
i--
if m.EnableOci {
@ -1826,6 +1891,20 @@ func (m *RepoAccessQuery) Size() (n int) {
if m.EnableOci {
n += 2
}
l = len(m.GithubAppPrivateKey)
if l > 0 {
n += 1 + l + sovRepository(uint64(l))
}
if m.GithubAppID != 0 {
n += 1 + sovRepository(uint64(m.GithubAppID))
}
if m.GithubAppInstallationID != 0 {
n += 1 + sovRepository(uint64(m.GithubAppInstallationID))
}
l = len(m.GithubAppEnterpriseBaseUrl)
if l > 0 {
n += 1 + l + sovRepository(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
@ -2733,6 +2812,108 @@ func (m *RepoAccessQuery) Unmarshal(dAtA []byte) error {
}
}
m.EnableOci = bool(v != 0)
case 12:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field GithubAppPrivateKey", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRepository
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRepository
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.GithubAppPrivateKey = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 13:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field GithubAppID", wireType)
}
m.GithubAppID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.GithubAppID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 14:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field GithubAppInstallationID", wireType)
}
m.GithubAppInstallationID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.GithubAppInstallationID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 15:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field GithubAppEnterpriseBaseUrl", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRepository
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRepository
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.GithubAppEnterpriseBaseUrl = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRepository(dAtA[iNdEx:])

View file

@ -62,7 +62,13 @@ API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,JWTToken,IssuedAt
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,KustomizeOptions,BinaryPath
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,KustomizeOptions,BuildOptions
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,RepoCreds,GitHubAppEnterpriseBaseURL
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,RepoCreds,GithubAppId
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,RepoCreds,GithubAppInstallationId
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,Repository,EnableLFS
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,Repository,GitHubAppEnterpriseBaseURL
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,Repository,GithubAppId
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,Repository,GithubAppInstallationId
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,ResourceActionDefinition,ActionLua
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,ResourceActions,ActionDiscoveryLua
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,ResourceOverride,Actions

File diff suppressed because it is too large Load diff

View file

@ -737,6 +737,18 @@ message RepoCreds {
// TLS client cert key for authenticating at the repo server
optional string tlsClientCertKey = 6;
// Github App Private Key PEM data
optional string githubAppPrivateKey = 7;
// Github App ID of the app used to access the repo
optional int64 githubAppID = 8;
// Github App Installation ID of the installed GitHub App
optional int64 githubAppInstallationID = 9;
// Github App Enterprise base url if empty will default to https://api.github.com
optional string githubAppEnterpriseBaseUrl = 10;
}
// RepositoryList is a collection of Repositories.
@ -791,6 +803,18 @@ message Repository {
// Whether helm-oci support should be enabled for this repo
optional bool enableOCI = 14;
// Github App Private Key PEM data
optional string githubAppPrivateKey = 15;
// Github App ID of the app used to access the repo
optional int64 githubAppID = 16;
// Github App Installation ID of the installed GitHub App
optional int64 githubAppInstallationID = 17;
// Github App Enterprise base url if empty will default to https://api.github.com
optional string githubAppEnterpriseBaseUrl = 18;
}
// A RepositoryCertificate is either SSH known hosts entry or TLS certificate

View file

@ -2686,6 +2686,34 @@ func schema_pkg_apis_application_v1alpha1_RepoCreds(ref common.ReferenceCallback
Format: "",
},
},
"githubAppPrivateKey": {
SchemaProps: spec.SchemaProps{
Description: "Github App Private Key PEM data",
Type: []string{"string"},
Format: "",
},
},
"githubAppID": {
SchemaProps: spec.SchemaProps{
Description: "Github App ID of the app used to access the repo",
Type: []string{"integer"},
Format: "int64",
},
},
"githubAppInstallationID": {
SchemaProps: spec.SchemaProps{
Description: "Github App Installation ID of the installed GitHub App",
Type: []string{"integer"},
Format: "int64",
},
},
"githubAppEnterpriseBaseUrl": {
SchemaProps: spec.SchemaProps{
Description: "Github App Enterprise base url if empty will default to https://api.github.com",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"url"},
},
@ -2834,6 +2862,34 @@ func schema_pkg_apis_application_v1alpha1_Repository(ref common.ReferenceCallbac
Format: "",
},
},
"githubAppPrivateKey": {
SchemaProps: spec.SchemaProps{
Description: "Github App Private Key PEM data",
Type: []string{"string"},
Format: "",
},
},
"githubAppID": {
SchemaProps: spec.SchemaProps{
Description: "Github App ID of the app used to access the repo",
Type: []string{"integer"},
Format: "int64",
},
},
"githubAppInstallationID": {
SchemaProps: spec.SchemaProps{
Description: "Github App Installation ID of the installed GitHub App",
Type: []string{"integer"},
Format: "int64",
},
},
"githubAppEnterpriseBaseUrl": {
SchemaProps: spec.SchemaProps{
Description: "Github App Enterprise base url if empty will default to https://api.github.com",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"repo"},
},

View file

@ -1372,6 +1372,14 @@ type RepoCreds struct {
TLSClientCertData string `json:"tlsClientCertData,omitempty" protobuf:"bytes,5,opt,name=tlsClientCertData"`
// TLS client cert key for authenticating at the repo server
TLSClientCertKey string `json:"tlsClientCertKey,omitempty" protobuf:"bytes,6,opt,name=tlsClientCertKey"`
// Github App Private Key PEM data
GithubAppPrivateKey string `json:"githubAppPrivateKey,omitempty" protobuf:"bytes,7,opt,name=githubAppPrivateKey"`
// Github App ID of the app used to access the repo
GithubAppId int64 `json:"githubAppID,omitempty" protobuf:"bytes,8,opt,name=githubAppID"`
// Github App Installation ID of the installed GitHub App
GithubAppInstallationId int64 `json:"githubAppInstallationID,omitempty" protobuf:"bytes,9,opt,name=githubAppInstallationID"`
// Github App Enterprise base url if empty will default to https://api.github.com
GitHubAppEnterpriseBaseURL string `json:"githubAppEnterpriseBaseUrl,omitempty" protobuf:"bytes,10,opt,name=githubAppEnterpriseBaseUrl"`
}
// Repository is a repository holding application configurations
@ -1406,6 +1414,14 @@ type Repository struct {
InheritedCreds bool `json:"inheritedCreds,omitempty" protobuf:"bytes,13,opt,name=inheritedCreds"`
// Whether helm-oci support should be enabled for this repo
EnableOCI bool `json:"enableOCI,omitempty" protobuf:"bytes,14,opt,name=enableOCI"`
// Github App Private Key PEM data
GithubAppPrivateKey string `json:"githubAppPrivateKey,omitempty" protobuf:"bytes,15,opt,name=githubAppPrivateKey"`
// Github App ID of the app used to access the repo
GithubAppId int64 `json:"githubAppID,omitempty" protobuf:"bytes,16,opt,name=githubAppID"`
// Github App Installation ID of the installed GitHub App
GithubAppInstallationId int64 `json:"githubAppInstallationID,omitempty" protobuf:"bytes,17,opt,name=githubAppInstallationID"`
// Github App Enterprise base url if empty will default to https://api.github.com
GitHubAppEnterpriseBaseURL string `json:"githubAppEnterpriseBaseUrl,omitempty" protobuf:"bytes,18,opt,name=githubAppEnterpriseBaseUrl"`
}
// IsInsecure returns true if receiver has been configured to skip server verification
@ -1420,7 +1436,7 @@ func (repo *Repository) IsLFSEnabled() bool {
// HasCredentials returns true when the receiver has been configured any credentials
func (m *Repository) HasCredentials() bool {
return m.Username != "" || m.Password != "" || m.SSHPrivateKey != "" || m.TLSClientCertData != ""
return m.Username != "" || m.Password != "" || m.SSHPrivateKey != "" || m.TLSClientCertData != "" || m.GithubAppPrivateKey != ""
}
func (repo *Repository) CopyCredentialsFromRepo(source *Repository) {
@ -1440,6 +1456,18 @@ func (repo *Repository) CopyCredentialsFromRepo(source *Repository) {
if repo.TLSClientCertKey == "" {
repo.TLSClientCertKey = source.TLSClientCertKey
}
if repo.GithubAppPrivateKey == "" {
repo.GithubAppPrivateKey = source.GithubAppPrivateKey
}
if repo.GithubAppId == 0 {
repo.GithubAppId = source.GithubAppId
}
if repo.GithubAppInstallationId == 0 {
repo.GithubAppInstallationId = source.GithubAppInstallationId
}
if repo.GitHubAppEnterpriseBaseURL == "" {
repo.GitHubAppEnterpriseBaseURL = source.GitHubAppEnterpriseBaseURL
}
}
}
@ -1461,6 +1489,18 @@ func (repo *Repository) CopyCredentialsFrom(source *RepoCreds) {
if repo.TLSClientCertKey == "" {
repo.TLSClientCertKey = source.TLSClientCertKey
}
if repo.GithubAppPrivateKey == "" {
repo.GithubAppPrivateKey = source.GithubAppPrivateKey
}
if repo.GithubAppId == 0 {
repo.GithubAppId = source.GithubAppId
}
if repo.GithubAppInstallationId == 0 {
repo.GithubAppInstallationId = source.GithubAppInstallationId
}
if repo.GitHubAppEnterpriseBaseURL == "" {
repo.GitHubAppEnterpriseBaseURL = source.GitHubAppEnterpriseBaseURL
}
}
}
@ -1474,6 +1514,9 @@ func (repo *Repository) GetGitCreds() git.Creds {
if repo.SSHPrivateKey != "" {
return git.NewSSHCreds(repo.SSHPrivateKey, getCAPath(repo.Repo), repo.IsInsecure())
}
if repo.GithubAppPrivateKey != "" && repo.GithubAppId != 0 && repo.GithubAppInstallationId != 0 {
return git.NewGitHubAppCreds(repo.GithubAppId, repo.GithubAppInstallationId, repo.GithubAppPrivateKey, repo.GitHubAppEnterpriseBaseURL, repo.Repo, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure())
}
return git.NopCreds{}
}

View file

@ -110,12 +110,15 @@ func (s *Server) Get(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.R
}
// remove secrets
item := appsv1.Repository{
Repo: repo.Repo,
Type: rType,
Name: repo.Name,
Username: repo.Username,
Insecure: repo.IsInsecure(),
EnableLFS: repo.EnableLFS,
Repo: repo.Repo,
Type: rType,
Name: repo.Name,
Username: repo.Username,
Insecure: repo.IsInsecure(),
EnableLFS: repo.EnableLFS,
GithubAppId: repo.GithubAppId,
GithubAppInstallationId: repo.GithubAppInstallationId,
GitHubAppEnterpriseBaseURL: repo.GitHubAppEnterpriseBaseURL,
}
item.ConnectionState = s.getConnectionState(ctx, item.Repo, q.ForceRefresh)
@ -371,16 +374,20 @@ func (s *Server) ValidateAccess(ctx context.Context, q *repositorypkg.RepoAccess
}
repo := &appsv1.Repository{
Repo: q.Repo,
Type: q.Type,
Name: q.Name,
Username: q.Username,
Password: q.Password,
SSHPrivateKey: q.SshPrivateKey,
Insecure: q.Insecure,
TLSClientCertData: q.TlsClientCertData,
TLSClientCertKey: q.TlsClientCertKey,
EnableOCI: q.EnableOci,
Repo: q.Repo,
Type: q.Type,
Name: q.Name,
Username: q.Username,
Password: q.Password,
SSHPrivateKey: q.SshPrivateKey,
Insecure: q.Insecure,
TLSClientCertData: q.TlsClientCertData,
TLSClientCertKey: q.TlsClientCertKey,
EnableOCI: q.EnableOci,
GithubAppPrivateKey: q.GithubAppPrivateKey,
GithubAppId: q.GithubAppID,
GithubAppInstallationId: q.GithubAppInstallationID,
GitHubAppEnterpriseBaseURL: q.GithubAppEnterpriseBaseUrl,
}
var repoCreds *appsv1.RepoCreds

View file

@ -65,6 +65,14 @@ message RepoAccessQuery {
string name = 10;
// Whether helm-oci support should be enabled for this repo
bool enableOci = 11;
// Github App Private Key PEM data
string githubAppPrivateKey = 12;
// Github App ID of the app used to access the repo
int64 githubAppID = 13;
// Github App Installation ID of the installed GitHub App
int64 githubAppInstallationID = 14;
// Github App Enterprise base url if empty will default to https://api.github.com
string githubAppEnterpriseBaseUrl = 15;
}
message RepoResponse {}

View file

@ -4,7 +4,7 @@ import * as React from 'react';
import {Form, FormApi, Text, TextArea} from 'react-form';
import {RouteComponentProps} from 'react-router';
import {CheckboxField, ConnectionStateIcon, DataLoader, EmptyState, ErrorNotification, Page, Repo} from '../../../shared/components';
import {CheckboxField, ConnectionStateIcon, DataLoader, EmptyState, ErrorNotification, NumberField, Page, Repo} from '../../../shared/components';
import {Spinner} from '../../../shared/components';
import {AppContext} from '../../../shared/context';
import * as models from '../../../shared/models';
@ -33,6 +33,20 @@ interface NewHTTPSRepoParams {
enableLfs: boolean;
}
interface NewGitHubAppRepoParams {
type: string;
name: string;
url: string;
githubAppPrivateKey: string;
githubAppId: bigint;
githubAppInstallationId: bigint;
githubAppEnterpriseBaseURL: string;
tlsClientCertData: string;
tlsClientCertKey: string;
insecure: boolean;
enableLfs: boolean;
}
interface NewSSHRepoCredsParams {
url: string;
sshPrivateKey: string;
@ -46,6 +60,16 @@ interface NewHTTPSRepoCredsParams {
tlsClientCertKey: string;
}
interface NewGitHubAppRepoCredsParams {
url: string;
githubAppPrivateKey: string;
githubAppId: bigint;
githubAppInstallationId: bigint;
githubAppEnterpriseBaseURL: string;
tlsClientCertData: string;
tlsClientCertKey: string;
}
export class ReposList extends React.Component<RouteComponentProps<any>, {connecting: boolean}> {
public static contextTypes = {
router: PropTypes.object,
@ -55,6 +79,7 @@ export class ReposList extends React.Component<RouteComponentProps<any>, {connec
private formApiSSH: FormApi;
private formApiHTTPS: FormApi;
private formApiGitHubApp: FormApi;
private credsTemplate: boolean;
private repoLoader: DataLoader;
private credsLoader: DataLoader;
@ -82,6 +107,11 @@ export class ReposList extends React.Component<RouteComponentProps<any>, {connec
title: 'Connect Repo using HTTPS',
action: () => (this.showConnectHTTPSRepo = true)
},
{
iconClassName: 'fa fa-plus',
title: 'Connect Repo using GitHub App',
action: () => (this.showConnectGitHubAppRepo = true)
},
{
iconClassName: 'fa fa-redo',
title: 'Refresh list',
@ -155,6 +185,9 @@ export class ReposList extends React.Component<RouteComponentProps<any>, {connec
<button className='argo-button argo-button--base' onClick={() => (this.showConnectHTTPSRepo = true)}>
Connect Repo using HTTPS
</button>
<button className='argo-button argo-button--base' onClick={() => (this.showConnectGitHubAppRepo = true)}>
Connect Repo using GitHub App
</button>{' '}
</EmptyState>
)
}
@ -334,6 +367,95 @@ export class ReposList extends React.Component<RouteComponentProps<any>, {connec
)}
</Form>
</SlidingPanel>
<SlidingPanel
isShown={this.showConnectGitHubAppRepo}
onClose={() => (this.showConnectGitHubAppRepo = false)}
header={
<div>
<button
className='argo-button argo-button--base'
onClick={() => {
this.credsTemplate = false;
this.formApiGitHubApp.submitForm(null);
}}>
<Spinner show={this.state.connecting} style={{marginRight: '5px'}} />
Connect
</button>{' '}
<button
className='argo-button argo-button--base'
onClick={() => {
this.credsTemplate = true;
this.formApiGitHubApp.submitForm(null);
}}>
Save as credentials template
</button>{' '}
<button onClick={() => (this.showConnectGitHubAppRepo = false)} className='argo-button argo-button--base-o'>
Cancel
</button>
</div>
}>
<h4>Connect repo using GitHub App</h4>
<Form
onSubmit={params => this.connectGitHubAppRepo(params as NewGitHubAppRepoParams)}
getApi={api => (this.formApiGitHubApp = api)}
defaultValues={{type: 'git', ghType: 'GitHub'}}
validateError={(params: NewGitHubAppRepoParams) => ({
url: (!params.url && 'Repo URL is required') || (this.credsTemplate && !this.isHTTPSUrl(params.url) && 'Not a valid HTTPS URL'),
githubAppId: !params.githubAppId && 'GitHub App ID is required',
githubAppInstallationId: !params.githubAppInstallationId && 'GitHub App installation ID is required',
githubAppPrivateKey: !params.githubAppPrivateKey && 'GitHub App private Key is required'
})}>
{formApi => (
<form onSubmit={formApi.submitForm} role='form' className='repos-list width-control'>
<div className='argo-form-row'>
<FormField formApi={formApi} label='Type' field='ghType' component={FormSelect} componentProps={{options: ['GitHub', 'GitHub Enterprise']}} />
</div>
{formApi.getFormState().values.type === 'GitHub Enterprise' && (
<React.Fragment>
<div className='argo-form-row'>
<FormField
formApi={formApi}
label='GitHub Enterprise Base URL (e.g. https://ghe.example.com/api/v3)'
field='githubAppEnterpriseBaseURL'
component={Text}
/>
</div>
</React.Fragment>
)}
<div className='argo-form-row'>
<FormField formApi={formApi} label='Repository URL' field='url' component={Text} />
</div>
<div className='argo-form-row'>
<FormField formApi={formApi} label='GitHub App ID' field='githubAppId' component={NumberField} />
</div>
<div className='argo-form-row'>
<FormField formApi={formApi} label='GitHub App Installation ID' field='githubAppInstallationId' component={NumberField} />
</div>
<div className='argo-form-row'>
<FormField formApi={formApi} label='GitHub App private key' field='githubAppPrivateKey' component={TextArea} />
</div>
<div className='argo-form-row'>
<FormField formApi={formApi} label='Skip server verification' field='insecure' component={CheckboxField} />
<HelpIcon title='This setting is ignored when creating as credential template.' />
</div>
<div className='argo-form-row'>
<FormField formApi={formApi} label='Enable LFS support (Git only)' field='enableLfs' component={CheckboxField} />
<HelpIcon title='This setting is ignored when creating as credential template.' />
</div>
{formApi.getFormState().values.ghType === 'GitHub Enterprise' && (
<React.Fragment>
<div className='argo-form-row'>
<FormField formApi={formApi} label='TLS client certificate (optional)' field='tlsClientCertData' component={TextArea} />
</div>
<div className='argo-form-row'>
<FormField formApi={formApi} label='TLS client certificate key (optional)' field='tlsClientCertKey' component={TextArea} />
</div>
</React.Fragment>
)}
</form>
)}
</Form>
</SlidingPanel>
</Page>
);
}
@ -377,6 +499,12 @@ export class ReposList extends React.Component<RouteComponentProps<any>, {connec
this.formApiHTTPS.resetAll();
}
// Empty all fields in SSH repository form
private clearConnectGitHubAppForm() {
this.credsTemplate = false;
this.formApiGitHubApp.resetAll();
}
// Connect a new repository or create a repository credentials for SSH repositories
private async connectSSHRepo(params: NewSSHRepoParams) {
if (this.credsTemplate) {
@ -425,6 +553,35 @@ export class ReposList extends React.Component<RouteComponentProps<any>, {connec
}
}
// Connect a new repository or create a repository credentials for GitHub App repositories
private async connectGitHubAppRepo(params: NewGitHubAppRepoParams) {
if (this.credsTemplate) {
this.createGitHubAppCreds({
url: params.url,
githubAppPrivateKey: params.githubAppPrivateKey,
githubAppId: params.githubAppId,
githubAppInstallationId: params.githubAppInstallationId,
githubAppEnterpriseBaseURL: params.githubAppEnterpriseBaseURL,
tlsClientCertData: params.tlsClientCertData,
tlsClientCertKey: params.tlsClientCertKey
});
} else {
this.setState({connecting: true});
try {
await services.repos.createGitHubApp(params);
this.repoLoader.reload();
this.showConnectGitHubAppRepo = false;
} catch (e) {
this.appContext.apis.notifications.show({
content: <ErrorNotification title='Unable to connect GitHub app repository' e={e} />,
type: NotificationType.Error
});
} finally {
this.setState({connecting: false});
}
}
}
private async createHTTPSCreds(params: NewHTTPSRepoCredsParams) {
try {
await services.repocreds.createHTTPS(params);
@ -451,6 +608,19 @@ export class ReposList extends React.Component<RouteComponentProps<any>, {connec
}
}
private async createGitHubAppCreds(params: NewGitHubAppRepoCredsParams) {
try {
await services.repocreds.createGitHubApp(params);
this.credsLoader.reload();
this.showConnectGitHubAppRepo = false;
} catch (e) {
this.appContext.apis.notifications.show({
content: <ErrorNotification title='Unable to create GitHub App credentials' e={e} />,
type: NotificationType.Error
});
}
}
// Remove a repository from the configuration
private async disconnectRepo(repo: string) {
const confirmed = await this.appContext.apis.popup.confirm('Disconnect repository', `Are you sure you want to disconnect '${repo}'?`);
@ -489,6 +659,16 @@ export class ReposList extends React.Component<RouteComponentProps<any>, {connec
this.appContext.router.history.push(`${this.props.match.url}?addSSHRepo=${val}`);
}
// Whether to show the GitHub App repository connection dialogue on the page
private get showConnectGitHubAppRepo() {
return new URLSearchParams(this.props.location.search).get('addGitHubAppRepo') === 'true';
}
private set showConnectGitHubAppRepo(val: boolean) {
this.clearConnectGitHubAppForm();
this.appContext.router.history.push(`${this.props.match.url}?addGitHubAppRepo=${val}`);
}
private get appContext(): AppContext {
return this.context as AppContext;
}

View file

@ -64,6 +64,49 @@ export class RepositoriesService {
.then(res => res.body as models.Repository);
}
public createGitHubApp({
type,
name,
url,
githubAppPrivateKey,
githubAppId,
githubAppInstallationId,
githubAppEnterpriseBaseURL,
tlsClientCertData,
tlsClientCertKey,
insecure,
enableLfs
}: {
type: string;
name: string;
url: string;
githubAppPrivateKey: string;
githubAppId: bigint;
githubAppInstallationId: bigint;
githubAppEnterpriseBaseURL: string;
tlsClientCertData: string;
tlsClientCertKey: string;
insecure: boolean;
enableLfs: boolean;
}): Promise<models.Repository> {
return requests
.post('/repositories')
.send({
type,
name,
repo: url,
githubAppPrivateKey,
githubAppId,
githubAppInstallationId,
githubAppEnterpriseBaseURL,
tlsClientCertData,
tlsClientCertKey,
insecure,
enableLfs
})
.then(res => res.body as models.Repository);
}
public delete(url: string): Promise<models.Repository> {
return requests
.delete(`/repositories/${encodeURIComponent(url)}`)

View file

@ -35,6 +35,29 @@ export class RepoCredsService {
.then(res => res.body as models.RepoCreds);
}
public createGitHubApp({
url,
githubAppPrivateKey,
githubAppId,
githubAppInstallationId,
githubAppEnterpriseBaseURL,
tlsClientCertData,
tlsClientCertKey
}: {
url: string;
githubAppPrivateKey: string;
githubAppId: bigint;
githubAppInstallationId: bigint;
githubAppEnterpriseBaseURL: string;
tlsClientCertData: string;
tlsClientCertKey: string;
}): Promise<models.RepoCreds> {
return requests
.post('/repocreds')
.send({url, githubAppPrivateKey, githubAppId, githubAppInstallationId, githubAppEnterpriseBaseURL, tlsClientCertData, tlsClientCertKey})
.then(res => res.body as models.RepoCreds);
}
public delete(url: string): Promise<models.RepoCreds> {
return requests
.delete(`/repocreds/${encodeURIComponent(url)}`)

View file

@ -36,6 +36,8 @@ const (
tlsClientCertData = "tlsClientCertData"
// The name of the key storing the TLS client cert key in the secret
tlsClientCertKey = "tlsClientCertKey"
// The name of the key storing the GitHub App private key in the secret
githubAppPrivateKey = "githubAppPrivateKey"
)
func (db *db) CreateRepository(ctx context.Context, r *appsv1.Repository) (*appsv1.Repository, error) {
@ -59,15 +61,21 @@ func (db *db) CreateRepository(ctx context.Context, r *appsv1.Repository) (*apps
if r.SSHPrivateKey != "" {
data[sshPrivateKey] = []byte(r.SSHPrivateKey)
}
if r.GithubAppPrivateKey != "" {
data[githubAppPrivateKey] = []byte(r.GithubAppPrivateKey)
}
repoInfo := settings.Repository{
URL: r.Repo,
Type: r.Type,
Name: r.Name,
InsecureIgnoreHostKey: r.IsInsecure(),
Insecure: r.IsInsecure(),
EnableLFS: r.EnableLFS,
EnableOci: r.EnableOCI,
URL: r.Repo,
Type: r.Type,
Name: r.Name,
InsecureIgnoreHostKey: r.IsInsecure(),
Insecure: r.IsInsecure(),
EnableLFS: r.EnableLFS,
EnableOci: r.EnableOCI,
GithubAppId: r.GithubAppId,
GithubAppInstallationId: r.GithubAppInstallationId,
GithubAppEnterpriseBaseURL: r.GitHubAppEnterpriseBaseURL,
}
err = db.updateRepositorySecrets(&repoInfo, r)
if err != nil {
@ -166,34 +174,42 @@ func (db *db) listRepositories(ctx context.Context, repoType *string) ([]*appsv1
func (db *db) credentialsToRepository(repoInfo settings.Repository) (*appsv1.Repository, error) {
repo := &appsv1.Repository{
Repo: repoInfo.URL,
Type: repoInfo.Type,
Name: repoInfo.Name,
InsecureIgnoreHostKey: repoInfo.InsecureIgnoreHostKey,
Insecure: repoInfo.Insecure,
EnableLFS: repoInfo.EnableLFS,
EnableOCI: repoInfo.EnableOci,
Repo: repoInfo.URL,
Type: repoInfo.Type,
Name: repoInfo.Name,
InsecureIgnoreHostKey: repoInfo.InsecureIgnoreHostKey,
Insecure: repoInfo.Insecure,
EnableLFS: repoInfo.EnableLFS,
EnableOCI: repoInfo.EnableOci,
GithubAppId: repoInfo.GithubAppId,
GithubAppInstallationId: repoInfo.GithubAppInstallationId,
GitHubAppEnterpriseBaseURL: repoInfo.GithubAppEnterpriseBaseURL,
}
err := db.unmarshalFromSecretsStr(map[*string]*apiv1.SecretKeySelector{
&repo.Username: repoInfo.UsernameSecret,
&repo.Password: repoInfo.PasswordSecret,
&repo.SSHPrivateKey: repoInfo.SSHPrivateKeySecret,
&repo.TLSClientCertData: repoInfo.TLSClientCertDataSecret,
&repo.TLSClientCertKey: repoInfo.TLSClientCertKeySecret,
&repo.Username: repoInfo.UsernameSecret,
&repo.Password: repoInfo.PasswordSecret,
&repo.SSHPrivateKey: repoInfo.SSHPrivateKeySecret,
&repo.TLSClientCertData: repoInfo.TLSClientCertDataSecret,
&repo.TLSClientCertKey: repoInfo.TLSClientCertKeySecret,
&repo.GithubAppPrivateKey: repoInfo.GithubAppPrivateKeySecret,
}, make(map[string]*apiv1.Secret))
return repo, err
}
func (db *db) credentialsToRepositoryCredentials(repoInfo settings.RepositoryCredentials) (*appsv1.RepoCreds, error) {
creds := &appsv1.RepoCreds{
URL: repoInfo.URL,
URL: repoInfo.URL,
GithubAppId: repoInfo.GithubAppId,
GithubAppInstallationId: repoInfo.GithubAppInstallationId,
GitHubAppEnterpriseBaseURL: repoInfo.GithubAppEnterpriseBaseURL,
}
err := db.unmarshalFromSecretsStr(map[*string]*apiv1.SecretKeySelector{
&creds.Username: repoInfo.UsernameSecret,
&creds.Password: repoInfo.PasswordSecret,
&creds.SSHPrivateKey: repoInfo.SSHPrivateKeySecret,
&creds.TLSClientCertData: repoInfo.TLSClientCertDataSecret,
&creds.TLSClientCertKey: repoInfo.TLSClientCertKeySecret,
&creds.Username: repoInfo.UsernameSecret,
&creds.Password: repoInfo.PasswordSecret,
&creds.SSHPrivateKey: repoInfo.SSHPrivateKeySecret,
&creds.TLSClientCertData: repoInfo.TLSClientCertDataSecret,
&creds.TLSClientCertKey: repoInfo.TLSClientCertKeySecret,
&creds.GithubAppPrivateKey: repoInfo.GithubAppPrivateKeySecret,
}, make(map[string]*apiv1.Secret))
return creds, err
}
@ -241,11 +257,12 @@ func (db *db) DeleteRepository(ctx context.Context, repoURL string) error {
return status.Errorf(codes.NotFound, "repo '%s' not found", repoURL)
}
err = db.updateRepositorySecrets(&repos[index], &appsv1.Repository{
SSHPrivateKey: "",
Password: "",
Username: "",
TLSClientCertData: "",
TLSClientCertKey: "",
SSHPrivateKey: "",
Password: "",
Username: "",
TLSClientCertData: "",
TLSClientCertKey: "",
GithubAppPrivateKey: "",
})
if err != nil {
return err
@ -301,7 +318,10 @@ func (db *db) CreateRepositoryCredentials(ctx context.Context, r *appsv1.RepoCre
}
repoInfo := settings.RepositoryCredentials{
URL: r.URL,
URL: r.URL,
GithubAppId: r.GithubAppId,
GithubAppInstallationId: r.GithubAppInstallationId,
GithubAppEnterpriseBaseURL: r.GitHubAppEnterpriseBaseURL,
}
err = db.updateCredentialsSecret(&repoInfo, r)
@ -357,11 +377,12 @@ func (db *db) DeleteRepositoryCredentials(ctx context.Context, name string) erro
return status.Errorf(codes.NotFound, "repository credentials '%s' not found", name)
}
err = db.updateCredentialsSecret(&repos[index], &appsv1.RepoCreds{
SSHPrivateKey: "",
Password: "",
Username: "",
TLSClientCertData: "",
TLSClientCertKey: "",
SSHPrivateKey: "",
Password: "",
Username: "",
TLSClientCertData: "",
TLSClientCertKey: "",
GithubAppPrivateKey: "",
})
if err != nil {
return err
@ -372,12 +393,16 @@ func (db *db) DeleteRepositoryCredentials(ctx context.Context, name string) erro
func (db *db) updateCredentialsSecret(credsInfo *settings.RepositoryCredentials, c *appsv1.RepoCreds) error {
r := &appsv1.Repository{
Repo: c.URL,
Username: c.Username,
Password: c.Password,
SSHPrivateKey: c.SSHPrivateKey,
TLSClientCertData: c.TLSClientCertData,
TLSClientCertKey: c.TLSClientCertKey,
Repo: c.URL,
Username: c.Username,
Password: c.Password,
SSHPrivateKey: c.SSHPrivateKey,
TLSClientCertData: c.TLSClientCertData,
TLSClientCertKey: c.TLSClientCertKey,
GithubAppPrivateKey: c.GithubAppPrivateKey,
GithubAppId: c.GithubAppId,
GithubAppInstallationId: c.GithubAppInstallationId,
GitHubAppEnterpriseBaseURL: c.GitHubAppEnterpriseBaseURL,
}
secretsData := make(map[string]map[string][]byte)
@ -386,6 +411,7 @@ func (db *db) updateCredentialsSecret(credsInfo *settings.RepositoryCredentials,
credsInfo.SSHPrivateKeySecret = setSecretData(credSecretPrefix, r.Repo, secretsData, credsInfo.SSHPrivateKeySecret, r.SSHPrivateKey, sshPrivateKey)
credsInfo.TLSClientCertDataSecret = setSecretData(credSecretPrefix, r.Repo, secretsData, credsInfo.TLSClientCertDataSecret, r.TLSClientCertData, tlsClientCertData)
credsInfo.TLSClientCertKeySecret = setSecretData(credSecretPrefix, r.Repo, secretsData, credsInfo.TLSClientCertKeySecret, r.TLSClientCertKey, tlsClientCertKey)
credsInfo.GithubAppPrivateKeySecret = setSecretData(repoSecretPrefix, r.Repo, secretsData, credsInfo.GithubAppPrivateKeySecret, r.GithubAppPrivateKey, githubAppPrivateKey)
for k, v := range secretsData {
err := db.upsertSecret(k, v)
if err != nil {
@ -403,6 +429,7 @@ func (db *db) updateRepositorySecrets(repoInfo *settings.Repository, r *appsv1.R
repoInfo.SSHPrivateKeySecret = setSecretData(repoSecretPrefix, r.Repo, secretsData, repoInfo.SSHPrivateKeySecret, r.SSHPrivateKey, sshPrivateKey)
repoInfo.TLSClientCertDataSecret = setSecretData(repoSecretPrefix, r.Repo, secretsData, repoInfo.TLSClientCertDataSecret, r.TLSClientCertData, tlsClientCertData)
repoInfo.TLSClientCertKeySecret = setSecretData(repoSecretPrefix, r.Repo, secretsData, repoInfo.TLSClientCertKeySecret, r.TLSClientCertKey, tlsClientCertKey)
repoInfo.GithubAppPrivateKeySecret = setSecretData(repoSecretPrefix, r.Repo, secretsData, repoInfo.GithubAppPrivateKeySecret, r.GithubAppPrivateKey, githubAppPrivateKey)
for k, v := range secretsData {
err := db.upsertSecret(k, v)
if err != nil {
@ -462,7 +489,7 @@ func (db *db) upsertSecret(name string, data map[string][]byte) error {
}
}
} else {
for _, key := range []string{username, password, sshPrivateKey, tlsClientCertData, tlsClientCertKey} {
for _, key := range []string{username, password, sshPrivateKey, tlsClientCertData, tlsClientCertKey, githubAppPrivateKey} {
if secret.Data == nil {
secret.Data = make(map[string][]byte)
}

View file

@ -131,16 +131,16 @@ func GetRepoHTTPClient(repoURL string, insecure bool, creds Creds) *http.Client
var err error
cert := tls.Certificate{}
// If we aren't called with HTTPSCreds, then we just return an empty cert
httpsCreds, ok := creds.(HTTPSCreds)
// If we aren't called with GenericHTTPSCreds, then we just return an empty cert
httpsCreds, ok := creds.(GenericHTTPSCreds)
if !ok {
return &cert, nil
}
// If the creds contain client certificate data, we return a TLS.Certificate
// populated with the cert and its key.
if httpsCreds.clientCertData != "" && httpsCreds.clientCertKey != "" {
cert, err = tls.X509KeyPair([]byte(httpsCreds.clientCertData), []byte(httpsCreds.clientCertKey))
if httpsCreds.HasClientCert() {
cert, err = tls.X509KeyPair([]byte(httpsCreds.GetClientCertData()), []byte(httpsCreds.GetClientCertKey()))
if err != nil {
log.Errorf("Could not load Client Certificate: %v", err)
return &cert, nil
@ -218,6 +218,13 @@ func newAuth(repoURL string, creds Creds) (transport.AuthMethod, error) {
case HTTPSCreds:
auth := githttp.BasicAuth{Username: creds.username, Password: creds.password}
return &auth, nil
case GitHubAppCreds:
token, err := creds.getAccessToken()
if err != nil {
return nil, err
}
auth := githttp.BasicAuth{Username: "x-access-token", Password: token}
return &auth, nil
}
return nil, nil
}

View file

@ -1,18 +1,43 @@
package git
import (
"context"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"os"
"strconv"
"strings"
"time"
gocache "github.com/patrickmn/go-cache"
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/bradleyfalzon/ghinstallation"
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/common"
certutil "github.com/argoproj/argo-cd/util/cert"
)
// In memory cache for storing github APP api token credentials
var (
githubAppTokenCache *gocache.Cache
)
func init() {
githubAppCredsExp := common.GithubAppCredsExpirationDuration
if exp := os.Getenv(common.EnvGithubAppCredsExpirationDuration); exp != "" {
if qps, err := strconv.Atoi(exp); err != nil {
githubAppCredsExp = time.Duration(qps) * time.Minute
}
}
githubAppTokenCache = gocache.New(githubAppCredsExp, 1*time.Minute)
}
type Creds interface {
Environ() (io.Closer, []string, error)
}
@ -32,6 +57,13 @@ func (c NopCreds) Environ() (io.Closer, []string, error) {
return NopCloser{}, nil, nil
}
type GenericHTTPSCreds interface {
HasClientCert() bool
GetClientCertData() string
GetClientCertKey() string
Environ() (io.Closer, []string, error)
}
// HTTPS creds implementation
type HTTPSCreds struct {
// Username for authentication
@ -46,7 +78,7 @@ type HTTPSCreds struct {
clientCertKey string
}
func NewHTTPSCreds(username string, password string, clientCertData string, clientCertKey string, insecure bool) HTTPSCreds {
func NewHTTPSCreds(username string, password string, clientCertData string, clientCertKey string, insecure bool) GenericHTTPSCreds {
return HTTPSCreds{
username,
password,
@ -71,7 +103,7 @@ func (c HTTPSCreds) Environ() (io.Closer, []string, error) {
// In case the repo is configured for using a TLS client cert, we need to make
// sure git client will use it. The certificate's key must not be password
// protected.
if c.clientCertData != "" && c.clientCertKey != "" {
if c.HasClientCert() {
var certFile, keyFile *os.File
// We need to actually create two temp files, one for storing cert data and
@ -116,6 +148,18 @@ func (c HTTPSCreds) Environ() (io.Closer, []string, error) {
return httpCloser, env, nil
}
func (g HTTPSCreds) HasClientCert() bool {
return g.clientCertData != "" && g.clientCertKey != ""
}
func (c HTTPSCreds) GetClientCertData() string {
return c.clientCertData
}
func (c HTTPSCreds) GetClientCertKey() string {
return c.clientCertKey
}
// SSH implementation
type SSHCreds struct {
sshPrivateKey string
@ -179,3 +223,143 @@ func (c SSHCreds) Environ() (io.Closer, []string, error) {
env = append(env, []string{fmt.Sprintf("GIT_SSH_COMMAND=%s", strings.Join(args, " "))}...)
return sshPrivateKeyFile(file.Name()), env, nil
}
// GitHubAppCreds to authenticate as GitHub application
type GitHubAppCreds struct {
appID int64
appInstallId int64
privateKey string
baseURL string
repoURL string
clientCertData string
clientCertKey string
insecure bool
}
// NewGitHubAppCreds provide github app credentials
func NewGitHubAppCreds(appID int64, appInstallId int64, privateKey string, baseURL string, repoURL string, clientCertData string, clientCertKey string, insecure bool) GenericHTTPSCreds {
return GitHubAppCreds{appID: appID, appInstallId: appInstallId, privateKey: privateKey, baseURL: baseURL, repoURL: repoURL, clientCertData: clientCertData, clientCertKey: clientCertKey, insecure: insecure}
}
func (g GitHubAppCreds) Environ() (io.Closer, []string, error) {
token, err := g.getAccessToken()
if err != nil {
return NopCloser{}, nil, err
}
env := []string{fmt.Sprintf("GIT_ASKPASS=%s", "git-ask-pass.sh"), "GIT_USERNAME=x-access-token", fmt.Sprintf("GIT_PASSWORD=%s", token)}
httpCloser := authFilePaths(make([]string, 0))
// GIT_SSL_NO_VERIFY is used to tell git not to validate the server's cert at
// all.
if g.insecure {
env = append(env, "GIT_SSL_NO_VERIFY=true")
}
// In case the repo is configured for using a TLS client cert, we need to make
// sure git client will use it. The certificate's key must not be password
// protected.
if g.HasClientCert() {
var certFile, keyFile *os.File
// We need to actually create two temp files, one for storing cert data and
// another for storing the key. If we fail to create second fail, the first
// must be removed.
certFile, err := ioutil.TempFile(argoio.TempDir, "")
if err == nil {
defer certFile.Close()
keyFile, err = ioutil.TempFile(argoio.TempDir, "")
if err != nil {
removeErr := os.Remove(certFile.Name())
if removeErr != nil {
log.Errorf("Could not remove previously created tempfile %s: %v", certFile.Name(), removeErr)
}
return NopCloser{}, nil, err
}
defer keyFile.Close()
} else {
return NopCloser{}, nil, err
}
// We should have both temp files by now
httpCloser = authFilePaths([]string{certFile.Name(), keyFile.Name()})
_, err = certFile.WriteString(g.clientCertData)
if err != nil {
httpCloser.Close()
return NopCloser{}, nil, err
}
// GIT_SSL_CERT is the full path to a client certificate to be used
env = append(env, fmt.Sprintf("GIT_SSL_CERT=%s", certFile.Name()))
_, err = keyFile.WriteString(g.clientCertKey)
if err != nil {
httpCloser.Close()
return NopCloser{}, nil, err
}
// GIT_SSL_KEY is the full path to a client certificate's key to be used
env = append(env, fmt.Sprintf("GIT_SSL_KEY=%s", keyFile.Name()))
}
return httpCloser, env, nil
}
// getAccessToken fetches GitHub token using the app id, install id, and private key.
// the token is then cached for re-use.
func (g GitHubAppCreds) getAccessToken() (string, error) {
// Timeout
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
// Compute hash of creds for lookup in cache
h := sha256.New()
_, err := h.Write([]byte(fmt.Sprintf("%s %d %d %s", g.privateKey, g.appID, g.appInstallId, g.baseURL)))
if err != nil {
return "", err
}
key := fmt.Sprintf("%x", h.Sum(nil))
// Check cache for GitHub transport which helps fetch an API token
t, found := githubAppTokenCache.Get(key)
if found {
itr := t.(*ghinstallation.Transport)
// This method caches the token and if it's expired retrieves a new one
return itr.Token(ctx)
}
// GitHub API url
baseUrl := "https://api.github.com"
if g.baseURL != "" {
baseUrl = strings.TrimSuffix(g.baseURL, "/")
}
// Create a new GitHub transport
c := GetRepoHTTPClient(baseUrl, g.insecure, g)
itr, err := ghinstallation.New(c.Transport,
g.appID,
g.appInstallId,
[]byte(g.privateKey),
)
if err != nil {
return "", err
}
itr.BaseURL = baseUrl
// Add transport to cache
githubAppTokenCache.Set(key, itr, time.Minute*60)
return itr.Token(ctx)
}
func (g GitHubAppCreds) HasClientCert() bool {
return g.clientCertData != "" && g.clientCertKey != ""
}
func (g GitHubAppCreds) GetClientCertData() string {
return g.clientCertData
}
func (g GitHubAppCreds) GetClientCertKey() string {
return g.clientCertKey
}

View file

@ -177,6 +177,14 @@ type Repository struct {
TLSClientCertKeySecret *apiv1.SecretKeySelector `json:"tlsClientCertKeySecret,omitempty"`
// Whether the repo is helm-oci enabled. Git only.
EnableOci bool `json:"enableOci,omitempty"`
// Github App Private Key PEM data
GithubAppPrivateKeySecret *apiv1.SecretKeySelector `json:"githubAppPrivateKeySecret,omitempty"`
// Github App ID of the app used to access the repo
GithubAppId int64 `json:"githubAppID,omitempty"`
// Github App Installation ID of the installed GitHub App
GithubAppInstallationId int64 `json:"githubAppInstallationID,omitempty"`
// Github App Enterprise base url if empty will default to https://api.github.com
GithubAppEnterpriseBaseURL string `json:"githubAppEnterpriseBaseUrl,omitempty"`
}
// Credential template for accessing repositories
@ -193,6 +201,14 @@ type RepositoryCredentials struct {
TLSClientCertDataSecret *apiv1.SecretKeySelector `json:"tlsClientCertDataSecret,omitempty"`
// Name of the secret storing the TLS client cert's key data
TLSClientCertKeySecret *apiv1.SecretKeySelector `json:"tlsClientCertKeySecret,omitempty"`
// Github App Private Key PEM data
GithubAppPrivateKeySecret *apiv1.SecretKeySelector `json:"githubAppPrivateKeySecret,omitempty"`
// Github App ID of the app used to access the repo
GithubAppId int64 `json:"githubAppID,omitempty"`
// Github App Installation ID of the installed GitHub App
GithubAppInstallationId int64 `json:"githubAppInstallationID,omitempty"`
// Github App Enterprise base url if empty will default to https://api.github.com
GithubAppEnterpriseBaseURL string `json:"githubAppEnterpriseBaseUrl,omitempty"`
}
const (