Skip to content

Commit

Permalink
Support specifying host key for ssh config (#1125)
Browse files Browse the repository at this point in the history
Empty host key interpreted as accept-all

Fixes #804

UX would be greatly improved by implementing a "load host key" button
which has server request host key from remote server & fill in ui field

Host keys can be acquired with
```sh
ssh-keyscan $HOST
```
  • Loading branch information
serprex authored Jan 22, 2024
1 parent 6997d5e commit ddca548
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ trim_trailing_whitespace = true
indent_style = space
indent_size = 4

[{package.json,*.yml,*.yaml}]
[{package.json,*.yml,*.yaml,*.proto}]
indent_style = space
indent_size = 2
6 changes: 1 addition & 5 deletions flow/connectors/postgres/ssh_wrapped_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ func NewSSHWrappedPostgresPool(
if sshConfig != nil {
sshServer = fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port)
var err error
clientConfig, err = utils.GetSSHClientConfig(
sshConfig.User,
sshConfig.Password,
sshConfig.PrivateKey,
)
clientConfig, err = utils.GetSSHClientConfig(sshConfig)
if err != nil {
slog.Error("Failed to get SSH client config", slog.Any("error", err))
cancel()
Expand Down
30 changes: 21 additions & 9 deletions flow/connectors/utils/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"fmt"

"github.com/PeerDB-io/peer-flow/generated/protos"
"golang.org/x/crypto/ssh"
)

Expand All @@ -13,17 +14,17 @@ import (
// user: SSH username
// password: SSH password (can be empty if using a private key)
// privateKeyString: Private key as a string (can be empty if using a password)
func GetSSHClientConfig(user, password, privateKeyString string) (*ssh.ClientConfig, error) {
func GetSSHClientConfig(config *protos.SSHConfig) (*ssh.ClientConfig, error) {
var authMethods []ssh.AuthMethod

// Password-based authentication
if password != "" {
authMethods = append(authMethods, ssh.Password(password))
if config.Password != "" {
authMethods = append(authMethods, ssh.Password(config.Password))
}

// Private key-based authentication
if privateKeyString != "" {
pkey, err := base64.StdEncoding.DecodeString(privateKeyString)
if config.PrivateKey != "" {
pkey, err := base64.StdEncoding.DecodeString(config.PrivateKey)
if err != nil {
return nil, fmt.Errorf("failed to base64 decode private key: %w", err)
}
Expand All @@ -40,10 +41,21 @@ func GetSSHClientConfig(user, password, privateKeyString string) (*ssh.ClientCon
return nil, fmt.Errorf("no authentication methods provided")
}

return &ssh.ClientConfig{
User: user,
Auth: authMethods,
var hostKeyCallback ssh.HostKeyCallback
if config.HostKey != "" {
pubKey, err := ssh.ParsePublicKey([]byte(config.HostKey))
if err != nil {
return nil, fmt.Errorf("failed to parse host key: %w", err)
}
hostKeyCallback = ssh.FixedHostKey(pubKey)
} else {
//nolint:gosec
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
hostKeyCallback = ssh.InsecureIgnoreHostKey()
}

return &ssh.ClientConfig{
User: config.User,
Auth: authMethods,
HostKeyCallback: hostKeyCallback,
}, nil
}
3 changes: 2 additions & 1 deletion protos/peers.proto
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ message SSHConfig {
string user = 3;
string password = 4;
string private_key = 5;
string host_key = 6;
}

message SnowflakeConfig {
Expand Down Expand Up @@ -115,7 +116,7 @@ enum DBType {
S3 = 5;
SQLSERVER = 6;
EVENTHUB_GROUP = 7;
CLICKHOUSE = 8;
CLICKHOUSE = 8;
}

message Peer {
Expand Down
8 changes: 8 additions & 0 deletions ui/app/peers/create/[peerType]/helpers/pg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ export const sshSetting = [
optional: true,
tips: 'Private key as a BASE64 string for authentication in order to SSH into your machine.',
},
{
label: 'Host Key',
stateHandler: (value: string, setter: sshSetter) =>
setter((curr) => ({ ...curr, hostKey: value })),
optional: true,
tips: 'Public key of host to mitigate MITM attacks when SSHing into your machine.',
},
];

export const blankSSHConfig: SSHConfig = {
Expand All @@ -94,6 +101,7 @@ export const blankSSHConfig: SSHConfig = {
user: '',
password: '',
privateKey: '',
hostKey: '',
};

export const blankPostgresSetting: PostgresConfig = {
Expand Down
9 changes: 3 additions & 6 deletions ui/components/PeerForms/ClickhouseConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,9 @@ export default function PostgresForm({ settings, setter }: ConfigProps) {
(sshConfig as SSHConfig)[
sshParam.label === 'BASE64 Private Key'
? 'privateKey'
: (sshParam.label.toLowerCase() as
| 'host'
| 'port'
| 'user'
| 'password'
| 'privateKey')
: sshParam.label === 'Host Key'
? 'hostKey'
: (sshParam.label.toLowerCase() as keyof SSHConfig)
] || ''
}
/>
Expand Down
9 changes: 3 additions & 6 deletions ui/components/PeerForms/PostgresForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,9 @@ export default function PostgresForm({ settings, setter }: ConfigProps) {
(sshConfig as SSHConfig)[
sshParam.label === 'BASE64 Private Key'
? 'privateKey'
: (sshParam.label.toLowerCase() as
| 'host'
| 'port'
| 'user'
| 'password'
| 'privateKey')
: sshParam.label === 'Host Key'
? 'hostKey'
: (sshParam.label.toLowerCase() as keyof SSHConfig)
] || ''
}
/>
Expand Down

0 comments on commit ddca548

Please sign in to comment.