Implement initial support for passphrase-protected SSH keyfiles

This commit is contained in:
2024-05-17 11:42:44 -04:00
parent b9688d29f1
commit 2bdfb43484
2 changed files with 25 additions and 14 deletions
+2 -1
View File
@@ -48,5 +48,6 @@ func ReadConfig(readKeys []string, missingValueError string) []string {
// sshUser = <remote user> // sshUser = <remote user>
// sshIP = <remote ip> // sshIP = <remote ip>
// sshPort = <remote ssh port> // sshPort = <remote ssh port>
// sshIdentity = <ssh private key identity file path> // sshKey = <ssh private key identity file path>
// sshKeyProtected = <true/false>
// netPinEnabled = <true/false> // netPinEnabled = <true/false>
+23 -13
View File
@@ -3,6 +3,7 @@ package sync
import ( import (
"fmt" "fmt"
"github.com/rwinkhart/MUTN/src/backend" "github.com/rwinkhart/MUTN/src/backend"
"github.com/rwinkhart/MUTN/src/cli"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/knownhosts" "golang.org/x/crypto/ssh/knownhosts"
"os" "os"
@@ -18,18 +19,18 @@ const (
) )
// GetSSHOutput runs a command over SSH and returns the output // GetSSHOutput runs a command over SSH and returns the output
// currently only supports password-less key-based authentication TODO add password support, still require key // only supports key-based authentication (passphrase-protected keys are supported in a CLI environment) TODO create a more generic interface for passphrase input
func GetSSHOutput(cmd string, manualSync bool) string { func GetSSHOutput(cmd string, manualSync bool) string {
// get SSH config info, exit if not configured (displaying an error if the sync job was called manually) // get SSH config info, exit if not configured (displaying an error if the sync job was called manually)
var sshUserIPPortIdentity []string var sshUserConfig []string
if manualSync { if manualSync {
sshUserIPPortIdentity = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshIdentity"}, "SSH settings not configured - run \"mutn init\" to configure") sshUserConfig = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshKey", "sshKeyProtected"}, "SSH settings not configured - run \"mutn init\" to configure")
} else { } else {
sshUserIPPortIdentity = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshIdentity"}, "0") sshUserConfig = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshKey", "sshKeyProtected"}, "0")
} }
var user, ip, port, identity string var user, ip, port, keyFile, keyFileProtected string
for i, key := range sshUserIPPortIdentity { for i, key := range sshUserConfig {
switch i { switch i {
case 0: case 0:
user = key user = key
@@ -38,19 +39,28 @@ func GetSSHOutput(cmd string, manualSync bool) string {
case 2: case 2:
port = key port = key
case 3: case 3:
identity = key keyFile = key
case 4:
keyFileProtected = key
} }
} }
// read and parse private key // read private key
key, err := os.ReadFile(identity) key, err := os.ReadFile(keyFile)
if err != nil { if err != nil {
fmt.Println(backend.AnsiError+"Sync failed - unable to read private key file:", identity+backend.AnsiReset) fmt.Println(backend.AnsiError+"Sync failed - unable to read private key file:", keyFile+backend.AnsiReset)
os.Exit(1) os.Exit(1)
} }
parsedKey, err := ssh.ParsePrivateKey(key)
// parse private key
var parsedKey ssh.Signer
if keyFileProtected != "true" {
parsedKey, err = ssh.ParsePrivateKey(key)
} else {
parsedKey, err = ssh.ParsePrivateKeyWithPassphrase(key, []byte(cli.InputHidden("Enter passphrase for \""+keyFile+"\":"))) // TODO test passphrase-protected keys
}
if err != nil { if err != nil {
fmt.Println(backend.AnsiError+"Sync failed - Unable to parse private key:", identity+backend.AnsiReset) fmt.Println(backend.AnsiError+"Sync failed - Unable to parse private key:", keyFile+backend.AnsiReset)
os.Exit(1) os.Exit(1)
} }
@@ -67,7 +77,7 @@ func GetSSHOutput(cmd string, manualSync bool) string {
Auth: []ssh.AuthMethod{ Auth: []ssh.AuthMethod{
ssh.PublicKeys(parsedKey), ssh.PublicKeys(parsedKey),
}, },
HostKeyCallback: hostKeyCallback, // TODO notify user that the server must already be in known_hosts HostKeyCallback: hostKeyCallback,
} }
// connect to SSH server // connect to SSH server