From 1840cfaf10d2585cdda6b2f25f538c12e5193643 Mon Sep 17 00:00:00 2001 From: Marian Theisen Date: Fri, 16 Feb 2018 23:49:11 +0100 Subject: [PATCH 1/4] Proof of concept, ssh key with passphrase --- cmd/clusterCreate.go | 10 ++++ cmd/contextAdd.go | 1 - cmd/contextUse.go | 2 - cmd/util.go | 107 ++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 111 insertions(+), 9 deletions(-) diff --git a/cmd/clusterCreate.go b/cmd/clusterCreate.go index 89eafc33..ed4f89ef 100644 --- a/cmd/clusterCreate.go +++ b/cmd/clusterCreate.go @@ -48,6 +48,16 @@ to quickly create a Cobra application.`, masterServerType, _ := cmd.Flags().GetString("master-server-type") workerServerType, _ := cmd.Flags().GetString("worker-server-type") + encrypted, err := isEncrypted(sshKeyName) + + if err != nil { + log.Fatal(err) + } + + if encrypted { + capturePassphrase(sshKeyName) + } + cluster := Cluster{Name: clusterName, wait: false} if err := cluster.CreateMasterNodes(sshKeyName, masterServerType, 1); err != nil { diff --git a/cmd/contextAdd.go b/cmd/contextAdd.go index a69dd668..eed02a8a 100644 --- a/cmd/contextAdd.go +++ b/cmd/contextAdd.go @@ -19,7 +19,6 @@ import ( "fmt" "github.com/hetznercloud/hcloud-go/hcloud" "github.com/spf13/cobra" - "log" "os" "strings" ) diff --git a/cmd/contextUse.go b/cmd/contextUse.go index 4ceb0d1b..8df13062 100644 --- a/cmd/contextUse.go +++ b/cmd/contextUse.go @@ -16,9 +16,7 @@ package cmd import ( "fmt" - "github.com/spf13/cobra" - "os" ) // useCmd represents the use command diff --git a/cmd/util.go b/cmd/util.go index 2145f0eb..c5277ed2 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -11,22 +11,117 @@ import ( "log" "time" "github.com/Pallinder/go-randomdata" + "encoding/pem" + "strings" + "golang.org/x/crypto/ssh/terminal" + "syscall" ) -func runCmd(node Node, command string) (output string, err error) { - index, privateKey := AppConf.Config.FindSSHKeyByName(node.SSHKeyName) +var sshPassPhrases = make(map[string][]byte) + +func capturePassphrase(sshKeyName string) (error) { + index, privateKey := AppConf.Config.FindSSHKeyByName(sshKeyName) + if index < 0 { + return errors.New(fmt.Sprintf("cound not find SSH key '%s'", sshKeyName)) + } + + fmt.Print("Enter passphrase for ssh key " + privateKey.PrivateKeyPath + ": ") + text, err := terminal.ReadPassword(int(syscall.Stdin)) + + if err != nil { + return err + } + + fmt.Print("\n") + sshPassPhrases[privateKey.PrivateKeyPath] = text + + return nil +} + +func getPassphrase(privateKeyPath string) ([]byte, error) { + if phrase, ok := sshPassPhrases[privateKeyPath]; ok { + return phrase, nil + } + + fmt.Print("Enter passphrase for ssh key " + privateKeyPath + ": ") + text, err := terminal.ReadPassword(int(syscall.Stdin)) + + if err != nil { + return nil, err + } + + fmt.Print("\n") + sshPassPhrases[privateKeyPath] = text + + return sshPassPhrases[privateKeyPath], nil +} + +func isEncrypted(sshKeyName string) (bool, error) { + index, privateKey := AppConf.Config.FindSSHKeyByName(sshKeyName) if index < 0 { - return "", errors.New(fmt.Sprintf("cound not find SSH key '%s'", node.SSHKeyName)) + return false, errors.New(fmt.Sprintf("cound not find SSH key '%s'", sshKeyName)) } pemBytes, err := ioutil.ReadFile(privateKey.PrivateKeyPath) if err != nil { - return "", err + return false, err + } + + block, _ := pem.Decode(pemBytes) + if block == nil { + return false, errors.New("ssh: no key found") + } - signer, err := ssh.ParsePrivateKey(pemBytes) + + return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED"), nil +} + +func getPrivateSshKey(sshKeyName string) (ssh.Signer, error) { + index, privateKey := AppConf.Config.FindSSHKeyByName(sshKeyName) + if index < 0 { + return nil, errors.New(fmt.Sprintf("cound not find SSH key '%s'", sshKeyName)) + } + + encrypted, err := isEncrypted(sshKeyName) + + if err != nil { + return nil, err + } + + pemBytes, err := ioutil.ReadFile(privateKey.PrivateKeyPath) if err != nil { - return "", errors.New(fmt.Sprintf("parse key failed:%v", err)) + return nil, err } + + if encrypted { + passPhrase, err := getPassphrase(privateKey.PrivateKeyPath) + if err != nil { + return nil, errors.New(fmt.Sprintf("parse key failed:%v", err)) + } + + signer, err := ssh.ParsePrivateKeyWithPassphrase(pemBytes, passPhrase) + if err != nil { + return nil, errors.New(fmt.Sprintf("parse key failed:%v", err)) + } + + return signer, err + } else { + signer, err := ssh.ParsePrivateKey(pemBytes) + if err != nil { + return nil, errors.New(fmt.Sprintf("parse key failed:%v", err)) + } + + return signer, err + } +} + +func runCmd(node Node, command string) (output string, err error) { + signer, err := getPrivateSshKey(node.SSHKeyName) + + if err != nil { + return "", err + } + config := &ssh.ClientConfig{ User: "root", Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, From 0705dd142db7b0f9674b5fb46710590e8bcc467e Mon Sep 17 00:00:00 2001 From: Marian Theisen Date: Fri, 16 Feb 2018 23:55:04 +0100 Subject: [PATCH 2/4] refactor --- cmd/clusterAddWorker.go | 6 ++++++ cmd/clusterCreate.go | 6 +----- cmd/util.go | 31 +++++++++++++------------------ 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/cmd/clusterAddWorker.go b/cmd/clusterAddWorker.go index c409f89d..68441f89 100644 --- a/cmd/clusterAddWorker.go +++ b/cmd/clusterAddWorker.go @@ -75,6 +75,12 @@ You can specify the worker server type as in cluster create.`, log.Fatal("master not found") } + err := capturePassphrase(sshKeyName) + + if err != nil { + log.Fatal(err) + } + maxNo := 0 for _, node := range cluster.Nodes { if !node.IsMaster { diff --git a/cmd/clusterCreate.go b/cmd/clusterCreate.go index ed4f89ef..85d1cf89 100644 --- a/cmd/clusterCreate.go +++ b/cmd/clusterCreate.go @@ -48,16 +48,12 @@ to quickly create a Cobra application.`, masterServerType, _ := cmd.Flags().GetString("master-server-type") workerServerType, _ := cmd.Flags().GetString("worker-server-type") - encrypted, err := isEncrypted(sshKeyName) + err := capturePassphrase(sshKeyName) if err != nil { log.Fatal(err) } - if encrypted { - capturePassphrase(sshKeyName) - } - cluster := Cluster{Name: clusterName, wait: false} if err := cluster.CreateMasterNodes(sshKeyName, masterServerType, 1); err != nil { diff --git a/cmd/util.go b/cmd/util.go index c5277ed2..55550c31 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -25,6 +25,16 @@ func capturePassphrase(sshKeyName string) (error) { return errors.New(fmt.Sprintf("cound not find SSH key '%s'", sshKeyName)) } + encrypted, err := isEncrypted(privateKey) + + if err != nil { + return err + } + + if !encrypted { + return nil + } + fmt.Print("Enter passphrase for ssh key " + privateKey.PrivateKeyPath + ": ") text, err := terminal.ReadPassword(int(syscall.Stdin)) @@ -43,25 +53,10 @@ func getPassphrase(privateKeyPath string) ([]byte, error) { return phrase, nil } - fmt.Print("Enter passphrase for ssh key " + privateKeyPath + ": ") - text, err := terminal.ReadPassword(int(syscall.Stdin)) - - if err != nil { - return nil, err - } - - fmt.Print("\n") - sshPassPhrases[privateKeyPath] = text - - return sshPassPhrases[privateKeyPath], nil + return nil, errors.New("passphrase not found") } -func isEncrypted(sshKeyName string) (bool, error) { - index, privateKey := AppConf.Config.FindSSHKeyByName(sshKeyName) - if index < 0 { - return false, errors.New(fmt.Sprintf("cound not find SSH key '%s'", sshKeyName)) - } - +func isEncrypted(privateKey *SSHKey) (bool, error) { pemBytes, err := ioutil.ReadFile(privateKey.PrivateKeyPath) if err != nil { return false, err @@ -82,7 +77,7 @@ func getPrivateSshKey(sshKeyName string) (ssh.Signer, error) { return nil, errors.New(fmt.Sprintf("cound not find SSH key '%s'", sshKeyName)) } - encrypted, err := isEncrypted(sshKeyName) + encrypted, err := isEncrypted(privateKey) if err != nil { return nil, err From 171df34da6e1dfa7d3a2ce0239e2fd5e549e815e Mon Sep 17 00:00:00 2001 From: David Steiman Date: Sun, 18 Feb 2018 18:53:44 +0100 Subject: [PATCH 3/4] fix typo --- cmd/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/util.go b/cmd/util.go index 55550c31..579000eb 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -22,7 +22,7 @@ var sshPassPhrases = make(map[string][]byte) func capturePassphrase(sshKeyName string) (error) { index, privateKey := AppConf.Config.FindSSHKeyByName(sshKeyName) if index < 0 { - return errors.New(fmt.Sprintf("cound not find SSH key '%s'", sshKeyName)) + return errors.New(fmt.Sprintf("could not find SSH key '%s'", sshKeyName)) } encrypted, err := isEncrypted(privateKey) @@ -232,4 +232,4 @@ func FatalOnError(err error) { if err != nil { log.Fatal(err) } -} \ No newline at end of file +} From bfd6d8ea19138b6e8b05ca59842773661fa5a9b1 Mon Sep 17 00:00:00 2001 From: David Steiman Date: Sun, 18 Feb 2018 18:55:04 +0100 Subject: [PATCH 4/4] add capture to kubeconfig --- cmd/clusterKubeconfig.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/clusterKubeconfig.go b/cmd/clusterKubeconfig.go index d6c872fa..dbc34e8b 100644 --- a/cmd/clusterKubeconfig.go +++ b/cmd/clusterKubeconfig.go @@ -39,7 +39,9 @@ Example 4: hetzner-kube cluster kubeconfig -n my-cluster -p > my-conf.yaml # pri name, _ := cmd.Flags().GetString("name") _, cluster := AppConf.Config.FindClusterByName(name) masterNode, err := cluster.GetMasterNode() + FatalOnError(err) + err = capturePassphrase(masterNode.SSHKeyName) FatalOnError(err) kubeConfigContent, err := runCmd(*masterNode, "cat /etc/kubernetes/admin.conf")