From 233254288381fd230ed467946597bf9223f4f706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Mon, 22 Jan 2024 17:03:35 +0000 Subject: [PATCH] Support specifying host key for ssh config --- .editorconfig | 2 +- flow/connectors/postgres/ssh_wrapped_pool.go | 6 +--- flow/connectors/utils/ssh.go | 30 ++++++++++++++------ protos/peers.proto | 3 +- ui/app/peers/create/[peerType]/helpers/pg.ts | 8 ++++++ ui/components/PeerForms/ClickhouseConfig.tsx | 9 ++---- ui/components/PeerForms/PostgresForm.tsx | 9 ++---- 7 files changed, 39 insertions(+), 28 deletions(-) diff --git a/.editorconfig b/.editorconfig index 174af3e952..004d9e96f2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/flow/connectors/postgres/ssh_wrapped_pool.go b/flow/connectors/postgres/ssh_wrapped_pool.go index 9922c41675..1c5511fc49 100644 --- a/flow/connectors/postgres/ssh_wrapped_pool.go +++ b/flow/connectors/postgres/ssh_wrapped_pool.go @@ -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() diff --git a/flow/connectors/utils/ssh.go b/flow/connectors/utils/ssh.go index 511eea672a..d960b20488 100644 --- a/flow/connectors/utils/ssh.go +++ b/flow/connectors/utils/ssh.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" + "github.com/PeerDB-io/peer-flow/generated/protos" "golang.org/x/crypto/ssh" ) @@ -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) } @@ -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 } diff --git a/protos/peers.proto b/protos/peers.proto index b16c35fccd..372c02936b 100644 --- a/protos/peers.proto +++ b/protos/peers.proto @@ -8,6 +8,7 @@ message SSHConfig { string user = 3; string password = 4; string private_key = 5; + string host_key = 6; } message SnowflakeConfig { @@ -115,7 +116,7 @@ enum DBType { S3 = 5; SQLSERVER = 6; EVENTHUB_GROUP = 7; - CLICKHOUSE = 8; + CLICKHOUSE = 8; } message Peer { diff --git a/ui/app/peers/create/[peerType]/helpers/pg.ts b/ui/app/peers/create/[peerType]/helpers/pg.ts index 8e822fb5c4..122b92be2e 100644 --- a/ui/app/peers/create/[peerType]/helpers/pg.ts +++ b/ui/app/peers/create/[peerType]/helpers/pg.ts @@ -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 = { @@ -94,6 +101,7 @@ export const blankSSHConfig: SSHConfig = { user: '', password: '', privateKey: '', + hostKey: '', }; export const blankPostgresSetting: PostgresConfig = { diff --git a/ui/components/PeerForms/ClickhouseConfig.tsx b/ui/components/PeerForms/ClickhouseConfig.tsx index 1819e626df..1d76286db0 100644 --- a/ui/components/PeerForms/ClickhouseConfig.tsx +++ b/ui/components/PeerForms/ClickhouseConfig.tsx @@ -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) ] || '' } /> diff --git a/ui/components/PeerForms/PostgresForm.tsx b/ui/components/PeerForms/PostgresForm.tsx index 1819e626df..1d76286db0 100644 --- a/ui/components/PeerForms/PostgresForm.tsx +++ b/ui/components/PeerForms/PostgresForm.tsx @@ -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) ] || '' } />