Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for defining a custom list of ssh key exchange algorithms. #18007

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions assets/swagger.json
Expand Up @@ -8150,6 +8150,10 @@
"type": "string",
"title": "Repo contains the URL to the remote repository"
},
"sshKexAlgorithms": {
"description": "KexAlgorithms specifies the key exchange algorithms to use for SSH connections. Whitespace seperated list.",
"type": "string"
},
"sshPrivateKey": {
"description": "SSHPrivateKey contains the PEM data for authenticating at the repo server. Only used with Git repos.",
"type": "string"
Expand Down
2 changes: 2 additions & 0 deletions cmd/util/repo.go
Expand Up @@ -24,6 +24,7 @@ type RepoOptions struct {
Proxy string
GCPServiceAccountKeyPath string
ForceHttpBasicAuth bool
SshKexAlgorithms string
}

func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
Expand All @@ -46,4 +47,5 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
command.Flags().StringVar(&opts.Proxy, "proxy", "", "use proxy to access repository")
command.Flags().StringVar(&opts.GCPServiceAccountKeyPath, "gcp-service-account-key-path", "", "service account key for the Google Cloud Platform")
command.Flags().BoolVar(&opts.ForceHttpBasicAuth, "force-http-basic-auth", false, "whether to force use of basic auth when connecting repository via HTTP")
command.Flags().StringVar(&opts.SshKexAlgorithms, "ssh-kex-algorithms", "", "space-separated list of key exchange algorithms to use for SSH connections")
}
12 changes: 11 additions & 1 deletion docs/operator-manual/argocd-repo-creds.yaml
Expand Up @@ -26,6 +26,16 @@ stringData:
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
sshKexAlgorithms: |
curve25519-sha256
curve25519-sha256@libssh.org
ecdh-sha2-nistp256
ecdh-sha2-nistp384
ecdh-sha2-nistp521
diffie-hellman-group-exchange-sha256
diffie-hellman-group14-sha256
diffie-hellman-group14-sha1

---
apiVersion: v1
kind: Secret
Expand Down Expand Up @@ -60,4 +70,4 @@ stringData:
githubAppPrivateKey: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
-----END OPENSSH PRIVATE KEY-----
10 changes: 10 additions & 0 deletions docs/operator-manual/declarative-setup.md
Expand Up @@ -341,6 +341,16 @@ The following keys are valid to refer to credential secrets:
#### SSH repositories

* `sshPrivateKey` refers to the SSH private key for accessing the repositories
* `sshKexAlgorithms` refers to the SSH key exchange algorithms to use when connecting to the repositories. If not specified, the following algorithms are used:
* `curve25519-sha256`
* `curve25519-sha256@libssh.org`
* `ecdh-sha2-nistp256`
* `ecdh-sha2-nistp384`
* `ecdh-sha2-nistp521`
* `diffie-hellman-group-exchange-sha256`
* `diffie-hellman-group14-sha256`
* `diffie-hellman-group14-sha1`


#### HTTPS repositories

Expand Down
Expand Up @@ -54,6 +54,7 @@ argocd admin repo generate-spec REPOURL [flags]
--password string password to the repository
--project string project of the repository
--proxy string use proxy to access repository
--ssh-kex-algorithms string space-separated list of key exchange algorithms to use for SSH connections
--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)
Expand Down
1 change: 1 addition & 0 deletions docs/user-guide/commands/argocd_repo_add.md
Expand Up @@ -67,6 +67,7 @@ argocd repo add REPOURL [flags]
--password string password to the repository
--project string project of the repository
--proxy string use proxy to access repository
--ssh-kex-algorithms string space-separated list of key exchange algorithms to use for SSH connections
--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)
Expand Down
1,405 changes: 724 additions & 681 deletions pkg/apis/application/v1alpha1/generated.pb.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pkg/apis/application/v1alpha1/generated.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/apis/application/v1alpha1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pkg/apis/application/v1alpha1/repository_types.go
Expand Up @@ -93,6 +93,8 @@ type Repository struct {
GCPServiceAccountKey string `json:"gcpServiceAccountKey,omitempty" protobuf:"bytes,21,opt,name=gcpServiceAccountKey"`
// ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections
ForceHttpBasicAuth bool `json:"forceHttpBasicAuth,omitempty" protobuf:"bytes,22,opt,name=forceHttpBasicAuth"`
// KexAlgorithms specifies the key exchange algorithms to use for SSH connections. Whitespace seperated list.
SSHKexAlgorithms string `json:"sshKexAlgorithms,omitempty" protobuf:"bytes,23,opt,name=sshKexAlgorithms"`
}

// IsInsecure returns true if the repository has been configured to skip server verification
Expand Down Expand Up @@ -196,7 +198,7 @@ func (repo *Repository) GetGitCreds(store git.CredsStore) git.Creds {
return git.NewHTTPSCreds(repo.Username, repo.Password, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), repo.Proxy, store, repo.ForceHttpBasicAuth)
}
if repo.SSHPrivateKey != "" {
return git.NewSSHCreds(repo.SSHPrivateKey, getCAPath(repo.Repo), repo.IsInsecure(), store, repo.Proxy)
return git.NewSSHCreds(repo.SSHPrivateKey, getCAPath(repo.Repo), repo.IsInsecure(), store, repo.Proxy, repo.SSHKexAlgorithms)
}
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(), repo.Proxy, store)
Expand Down
12 changes: 11 additions & 1 deletion ui/src/app/settings/components/repos-list/repos-list.tsx
Expand Up @@ -17,6 +17,7 @@ interface NewSSHRepoParams {
name: string;
url: string;
sshPrivateKey: string;
sshKexAlgorithms: string;
insecure: boolean;
enableLfs: boolean;
proxy: string;
Expand Down Expand Up @@ -67,6 +68,7 @@ interface NewGoogleCloudSourceRepoParams {
interface NewSSHRepoCredsParams {
url: string;
sshPrivateKey: string;
sshKexAlgorithms: string;
}

interface NewHTTPSRepoCredsParams {
Expand Down Expand Up @@ -435,6 +437,14 @@ export class ReposList extends React.Component<
<div className='argo-form-row'>
<FormField formApi={formApi} label='SSH private key data' field='sshPrivateKey' component={TextArea} />
</div>
<div className='argo-form-row'>
<FormField
formApi={formApi}
label='SSH key exchange algorithms (white space seperated list)'
field='sshKexAlgorithms'
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.' />
Expand Down Expand Up @@ -674,7 +684,7 @@ export class ReposList extends React.Component<
// Connect a new repository or create a repository credentials for SSH repositories
private async connectSSHRepo(params: NewSSHRepoParams) {
if (this.credsTemplate) {
this.createSSHCreds({url: params.url, sshPrivateKey: params.sshPrivateKey});
this.createSSHCreds({url: params.url, sshPrivateKey: params.sshPrivateKey, sshKexAlgorithms: params.sshKexAlgorithms});
} else {
this.setState({connecting: true});
try {
Expand Down
1 change: 1 addition & 0 deletions util/db/repository_secrets.go
Expand Up @@ -314,6 +314,7 @@ func secretToRepository(secret *corev1.Secret) (*appsv1.Repository, error) {
Proxy: string(secret.Data["proxy"]),
Project: string(secret.Data["project"]),
GCPServiceAccountKey: string(secret.Data["gcpServiceAccountKey"]),
SSHKexAlgorithms: string(secret.Data["sshKexAlgorithms"]),
}

insecureIgnoreHostKey, err := boolOrFalse(secret, "insecureIgnoreHostKey")
Expand Down
1 change: 1 addition & 0 deletions util/git/client.go
Expand Up @@ -267,6 +267,7 @@ func newAuth(repoURL string, creds Creds) (transport.AuthMethod, error) {
auth := &PublicKeysWithOptions{}
auth.User = sshUser
auth.Signer = signer
auth.KexAlgorithms = strings.Fields(creds.kexAlgorithms)
if creds.insecure {
auth.HostKeyCallback = ssh.InsecureIgnoreHostKey()
} else {
Expand Down
5 changes: 3 additions & 2 deletions util/git/creds.go
Expand Up @@ -243,10 +243,11 @@ type SSHCreds struct {
insecure bool
store CredsStore
proxy string
kexAlgorithms string
}

func NewSSHCreds(sshPrivateKey string, caPath string, insecureIgnoreHostKey bool, store CredsStore, proxy string) SSHCreds {
return SSHCreds{sshPrivateKey, caPath, insecureIgnoreHostKey, store, proxy}
func NewSSHCreds(sshPrivateKey string, caPath string, insecureIgnoreHostKey bool, store CredsStore, proxy string, kexAlgorithms string) SSHCreds {
return SSHCreds{sshPrivateKey, caPath, insecureIgnoreHostKey, store, proxy, kexAlgorithms}
}

type sshPrivateKeyFile string
Expand Down
8 changes: 4 additions & 4 deletions util/git/creds_test.go
Expand Up @@ -206,7 +206,7 @@ func Test_SSHCreds_Environ(t *testing.T) {
caFile := path.Join(tempDir, "caFile")
err := os.WriteFile(caFile, []byte(""), os.FileMode(0600))
require.NoError(t, err)
creds := NewSSHCreds("sshPrivateKey", caFile, insecureIgnoreHostKey, &NoopCredsStore{}, "")
creds := NewSSHCreds("sshPrivateKey", caFile, insecureIgnoreHostKey, &NoopCredsStore{}, "", "")
closer, env, err := creds.Environ()
require.NoError(t, err)
require.Len(t, env, 2)
Expand Down Expand Up @@ -239,7 +239,7 @@ func Test_SSHCreds_Environ_WithProxy(t *testing.T) {
caFile := path.Join(tempDir, "caFile")
err := os.WriteFile(caFile, []byte(""), os.FileMode(0600))
require.NoError(t, err)
creds := NewSSHCreds("sshPrivateKey", caFile, insecureIgnoreHostKey, &NoopCredsStore{}, "socks5://127.0.0.1:1080")
creds := NewSSHCreds("sshPrivateKey", caFile, insecureIgnoreHostKey, &NoopCredsStore{}, "socks5://127.0.0.1:1080", "")
closer, env, err := creds.Environ()
require.NoError(t, err)
require.Len(t, env, 2)
Expand Down Expand Up @@ -273,7 +273,7 @@ func Test_SSHCreds_Environ_WithProxyUserNamePassword(t *testing.T) {
caFile := path.Join(tempDir, "caFile")
err := os.WriteFile(caFile, []byte(""), os.FileMode(0600))
require.NoError(t, err)
creds := NewSSHCreds("sshPrivateKey", caFile, insecureIgnoreHostKey, &NoopCredsStore{}, "socks5://user:password@127.0.0.1:1080")
creds := NewSSHCreds("sshPrivateKey", caFile, insecureIgnoreHostKey, &NoopCredsStore{}, "socks5://user:password@127.0.0.1:1080", "")
closer, env, err := creds.Environ()
require.NoError(t, err)
require.Len(t, env, 4)
Expand Down Expand Up @@ -320,7 +320,7 @@ func Test_SSHCreds_Environ_TempFileCleanupOnInvalidProxyURL(t *testing.T) {
caFile := path.Join(tempDir, "caFile")
err := os.WriteFile(caFile, []byte(""), os.FileMode(0600))
require.NoError(t, err)
creds := NewSSHCreds("sshPrivateKey", caFile, insecureIgnoreHostKey, &NoopCredsStore{}, ":invalid-proxy-url")
creds := NewSSHCreds("sshPrivateKey", caFile, insecureIgnoreHostKey, &NoopCredsStore{}, ":invalid-proxy-url", "")

filesInDevShmBeforeInvocation := countFilesInDevShm()

Expand Down