Skip to content

Commit

Permalink
Merge pull request #8 from cice/support-encrypted-ssh
Browse files Browse the repository at this point in the history
Support encrypted ssh (passhrase protected keys)
  • Loading branch information
xetys authored Feb 18, 2018
2 parents b07d16b + bfd6d8e commit c0cd715
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 10 deletions.
6 changes: 6 additions & 0 deletions cmd/clusterAddWorker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions cmd/clusterCreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ to quickly create a Cobra application.`,
masterServerType, _ := cmd.Flags().GetString("master-server-type")
workerServerType, _ := cmd.Flags().GetString("worker-server-type")

err := capturePassphrase(sshKeyName)

if err != nil {
log.Fatal(err)
}

cluster := Cluster{Name: clusterName, wait: false}

if err := cluster.CreateMasterNodes(sshKeyName, masterServerType, 1); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions cmd/clusterKubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 0 additions & 1 deletion cmd/contextAdd.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"fmt"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
"log"
"os"
"strings"
)
Expand Down
2 changes: 0 additions & 2 deletions cmd/contextUse.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ package cmd

import (
"fmt"

"github.com/spf13/cobra"
"os"
)

// useCmd represents the use command
Expand Down
104 changes: 97 additions & 7 deletions cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,112 @@ 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'", node.SSHKeyName))
return errors.New(fmt.Sprintf("could 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))

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
}

return nil, errors.New("passphrase not found")
}

func isEncrypted(privateKey *SSHKey) (bool, error) {
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")

}

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))
}
signer, err := ssh.ParsePrivateKey(pemBytes)

encrypted, err := isEncrypted(privateKey)

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)},
Expand Down Expand Up @@ -142,4 +232,4 @@ func FatalOnError(err error) {
if err != nil {
log.Fatal(err)
}
}
}

0 comments on commit c0cd715

Please sign in to comment.