Skip to content

Prefer native parser for SSH public key parsing #23798

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

Merged
merged 5 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ RUN_MODE = ; prod
;; default is the system temporary directory.
;SSH_KEY_TEST_PATH =
;;
;; Path to ssh-keygen, default is 'ssh-keygen' which means the shell is responsible for finding out which one to call.
;SSH_KEYGEN_PATH = ssh-keygen
;; Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Gitea does the parsing itself.
;SSH_KEYGEN_PATH =
;;
;; Enable SSH Authorized Key Backup when rewriting all keys, default is true
;SSH_AUTHORIZED_KEYS_BACKUP = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `SSH_SERVER_MACS`: **[email protected], hmac-sha2-256, hmac-sha1**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect
- `SSH_SERVER_HOST_KEYS`: **ssh/gitea.rsa, ssh/gogs.rsa**: For the built-in SSH server, choose the keypairs to offer as the host key. The private key should be at `SSH_SERVER_HOST_KEY` and the public `SSH_SERVER_HOST_KEY.pub`. Relative paths are made absolute relative to the `APP_DATA_PATH`. If no key exists a 4096 bit RSA key will be created for you.
- `SSH_KEY_TEST_PATH`: **/tmp**: Directory to create temporary files in when testing public keys using ssh-keygen, default is the system temporary directory.
- `SSH_KEYGEN_PATH`: **ssh-keygen**: Path to ssh-keygen, default is 'ssh-keygen' which means the shell is responsible for finding out which one to call.
- `SSH_KEYGEN_PATH`: **\<empty\>**: Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Gitea does the parsing itself.
- `SSH_EXPOSE_ANONYMOUS`: **false**: Enable exposure of SSH clone URL to anonymous visitors, default is false.
- `SSH_PER_WRITE_TIMEOUT`: **30s**: Timeout for any write to the SSH connections. (Set to
-1 to disable all timeouts.)
Expand Down
9 changes: 7 additions & 2 deletions models/asymkey/ssh_key_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func CheckPublicKeyString(content string) (_ string, err error) {
keyType string
length int
)
if setting.SSH.StartBuiltinServer {
if len(setting.SSH.KeygenPath) == 0 {
fnName = "SSHNativeParsePublicKey"
keyType, length, err = SSHNativeParsePublicKey(content)
} else {
Expand Down Expand Up @@ -285,7 +285,12 @@ func SSHKeyGenParsePublicKey(key string) (string, int, error) {
}
}()

stdout, stderr, err := process.GetManager().Exec("SSHKeyGenParsePublicKey", setting.SSH.KeygenPath, "-lf", tmpName)
keygenPath := setting.SSH.KeygenPath
if len(keygenPath) == 0 {
keygenPath = "ssh-keygen"
}

stdout, stderr, err := process.GetManager().Exec("SSHKeyGenParsePublicKey", keygenPath, "-lf", tmpName)
if err != nil {
return "", 0, fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
}
Expand Down
8 changes: 8 additions & 0 deletions models/asymkey/ssh_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ func Test_SSHParsePublicKey(t *testing.T) {
assert.Equal(t, tc.keyType, keyTypeK)
assert.EqualValues(t, tc.length, lengthK)
})
t.Run("SSHParseKeyNative", func(t *testing.T) {
keyTypeK, lengthK, err := SSHNativeParsePublicKey(tc.content)
if err != nil {
assert.Fail(t, "%v", err)
}
assert.Equal(t, tc.keyType, keyTypeK)
assert.EqualValues(t, tc.length, lengthK)
})
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions modules/setting/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var SSH = struct {
ServerCiphers: []string{"[email protected]", "aes128-ctr", "aes192-ctr", "aes256-ctr", "[email protected]", "[email protected]"},
ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"},
ServerMACs: []string{"[email protected]", "hmac-sha2-256", "hmac-sha1"},
KeygenPath: "ssh-keygen",
KeygenPath: "",
MinimumKeySizeCheck: true,
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047},
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
Expand Down Expand Up @@ -134,7 +134,7 @@ func loadSSHFrom(rootCfg ConfigProvider) {
}
}

SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").String()
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)
Expand Down