Merge pull request #294 from justinsb/use_ssh_key

SSH key improvements
This commit is contained in:
Justin Santa Barbara 2016-08-11 22:28:41 -04:00 committed by GitHub
commit 7699dc8fd2
13 changed files with 157 additions and 56 deletions

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"io/ioutil"
"k8s.io/kops/upup/pkg/api" "k8s.io/kops/upup/pkg/api"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup" "k8s.io/kops/upup/pkg/fi/cloudup"
@ -297,8 +298,14 @@ func (c *CreateClusterCmd) Run(args []string) error {
} }
} }
sshPublicKeys := make(map[string][]byte)
if c.SSHPublicKey != "" { if c.SSHPublicKey != "" {
c.SSHPublicKey = utils.ExpandPath(c.SSHPublicKey) c.SSHPublicKey = utils.ExpandPath(c.SSHPublicKey)
authorized, err := ioutil.ReadFile(c.SSHPublicKey)
if err != nil {
return fmt.Errorf("error reading SSH key file %q: %v", c.SSHPublicKey, err)
}
sshPublicKeys[fi.SecretNameSSHPrimary] = authorized
} }
if c.AdminAccess != "" { if c.AdminAccess != "" {
@ -350,6 +357,14 @@ func (c *CreateClusterCmd) Run(args []string) error {
return fmt.Errorf("error writing completed cluster spec: %v", err) return fmt.Errorf("error writing completed cluster spec: %v", err)
} }
for k, data := range sshPublicKeys {
keyStore := clusterRegistry.KeyStore(cluster.Name)
err = keyStore.AddSSHPublicKey(k, data)
if err != nil {
return fmt.Errorf("error addding SSH public key: %v", err)
}
}
if isDryrun { if isDryrun {
fmt.Println("Previewing changes that will be made:\n") fmt.Println("Previewing changes that will be made:\n")
} }
@ -360,7 +375,6 @@ func (c *CreateClusterCmd) Run(args []string) error {
Models: strings.Split(c.Models, ","), Models: strings.Split(c.Models, ","),
ClusterRegistry: clusterRegistry, ClusterRegistry: clusterRegistry,
Target: c.Target, Target: c.Target,
SSHPublicKey: c.SSHPublicKey,
OutDir: c.OutDir, OutDir: c.OutDir,
DryRun: isDryrun, DryRun: isDryrun,
} }

View File

@ -186,7 +186,7 @@ func (c *RootCmd) Cluster() (*api.ClusterRegistry, *api.Cluster, error) {
} }
func (c *RootCmd) InstanceGroupRegistry() (*api.InstanceGroupRegistry, error) { func (c *RootCmd) InstanceGroupRegistry() (*api.InstanceGroupRegistry, error) {
clusterStore, err := c.ClusterRegistry() clusterRegistry, err := c.ClusterRegistry()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -196,11 +196,11 @@ func (c *RootCmd) InstanceGroupRegistry() (*api.InstanceGroupRegistry, error) {
return nil, fmt.Errorf("--name is required") return nil, fmt.Errorf("--name is required")
} }
return clusterStore.InstanceGroups(clusterName) return clusterRegistry.InstanceGroups(clusterName)
} }
func (c *RootCmd) SecretStore() (fi.SecretStore, error) { func (c *RootCmd) SecretStore() (fi.SecretStore, error) {
clusterStore, err := c.ClusterRegistry() clusterRegistry, err := c.ClusterRegistry()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -210,11 +210,11 @@ func (c *RootCmd) SecretStore() (fi.SecretStore, error) {
return nil, fmt.Errorf("--name is required") return nil, fmt.Errorf("--name is required")
} }
return clusterStore.SecretStore(clusterName), nil return clusterRegistry.SecretStore(clusterName), nil
} }
func (c *RootCmd) KeyStore() (fi.CAStore, error) { func (c *RootCmd) KeyStore() (fi.CAStore, error) {
clusterStore, err := c.ClusterRegistry() clusterRegistry, err := c.ClusterRegistry()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -224,5 +224,5 @@ func (c *RootCmd) KeyStore() (fi.CAStore, error) {
return nil, fmt.Errorf("--name is required") return nil, fmt.Errorf("--name is required")
} }
return clusterStore.KeyStore(clusterName), nil return clusterRegistry.KeyStore(clusterName), nil
} }

View File

@ -4,10 +4,13 @@ import (
"fmt" "fmt"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"io/ioutil"
"k8s.io/kops/upup/pkg/api" "k8s.io/kops/upup/pkg/api"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup" "k8s.io/kops/upup/pkg/fi/cloudup"
"k8s.io/kops/upup/pkg/fi/utils" "k8s.io/kops/upup/pkg/fi/utils"
"k8s.io/kops/upup/pkg/kutil" "k8s.io/kops/upup/pkg/kutil"
"os"
"strings" "strings"
) )
@ -39,7 +42,7 @@ func init() {
cmd.Flags().BoolVar(&updateCluster.Yes, "yes", false, "Actually create cloud resources") cmd.Flags().BoolVar(&updateCluster.Yes, "yes", false, "Actually create cloud resources")
cmd.Flags().StringVar(&updateCluster.Target, "target", "direct", "Target - direct, terraform") cmd.Flags().StringVar(&updateCluster.Target, "target", "direct", "Target - direct, terraform")
cmd.Flags().StringVar(&updateCluster.Models, "model", "config,proto,cloudup", "Models to apply (separate multiple models with commas)") cmd.Flags().StringVar(&updateCluster.Models, "model", "config,proto,cloudup", "Models to apply (separate multiple models with commas)")
cmd.Flags().StringVar(&updateCluster.SSHPublicKey, "ssh-public-key", "~/.ssh/id_rsa.pub", "SSH public key to use") cmd.Flags().StringVar(&updateCluster.SSHPublicKey, "ssh-public-key", "", "SSH public key to use (deprecated: use kops create secret instead)")
cmd.Flags().StringVar(&updateCluster.OutDir, "out", "", "Path to write any local output") cmd.Flags().StringVar(&updateCluster.OutDir, "out", "", "Path to write any local output")
} }
@ -87,7 +90,21 @@ func (c *UpdateClusterCmd) Run(args []string) error {
} }
if c.SSHPublicKey != "" { if c.SSHPublicKey != "" {
fmt.Fprintf(os.Stderr, "--ssh-public-key on update is deprecated - please use `kops create secret --name %s sshpublickey admin -i ~/.ssh/id_rsa.pub` instead\n", cluster.Name)
c.SSHPublicKey = utils.ExpandPath(c.SSHPublicKey) c.SSHPublicKey = utils.ExpandPath(c.SSHPublicKey)
authorized, err := ioutil.ReadFile(c.SSHPublicKey)
if err != nil {
return fmt.Errorf("error reading SSH key file %q: %v", c.SSHPublicKey, err)
}
keyStore, err := rootCommand.KeyStore()
if err != nil {
return err
}
err = keyStore.AddSSHPublicKey(fi.SecretNameSSHPrimary, authorized)
if err != nil {
return fmt.Errorf("error addding SSH public key: %v", err)
}
} }
strict := false strict := false
@ -102,7 +119,6 @@ func (c *UpdateClusterCmd) Run(args []string) error {
Models: strings.Split(c.Models, ","), Models: strings.Split(c.Models, ","),
ClusterRegistry: clusterRegistry, ClusterRegistry: clusterRegistry,
Target: c.Target, Target: c.Target,
SSHPublicKey: c.SSHPublicKey,
OutDir: c.OutDir, OutDir: c.OutDir,
DryRun: isDryrun, DryRun: isDryrun,
} }

View File

@ -73,6 +73,14 @@ kops get clusters
You can also list the instance groups: `kops get ig --name ${NEW_NAME}` You can also list the instance groups: `kops get ig --name ${NEW_NAME}`
## Import the SSH public key
The SSH public key is not easily retrieved from the old cluster, so you must add it:
```
kops create secret --name ${NEW_NAME} sshpublickey admin -i ~/.ssh/id_rsa.pub
```
## Bring up the new cluster ## Bring up the new cluster
Use the update command to bring up the new cluster: Use the update command to bring up the new cluster:

View File

@ -2,7 +2,7 @@
# ASG for master # ASG for master
launchConfiguration/{{ $m.Name }}.masters.{{ ClusterName }}: launchConfiguration/{{ $m.Name }}.masters.{{ ClusterName }}:
sshKey: sshKey/{{ ClusterName }} sshKey: sshKey/{{ SSHKeyName }}
securityGroups: securityGroups:
- securityGroup/masters.{{ ClusterName }} - securityGroup/masters.{{ ClusterName }}
iamInstanceProfile: iamInstanceProfile/masters.{{ ClusterName }} iamInstanceProfile: iamInstanceProfile/masters.{{ ClusterName }}

View File

@ -28,7 +28,7 @@ instance/master.{{ ClusterName }}:
{{ if not (HasTag "_master_lb") }} {{ if not (HasTag "_master_lb") }}
k8s.io/dns/public: "api.{{ ClusterName }}" k8s.io/dns/public: "api.{{ ClusterName }}"
{{ end }} {{ end }}
sshKey: sshKey/{{ ClusterName }} sshKey: sshKey/{{ SSHKeyName }}
securityGroups: securityGroups:
- securityGroup/master.{{ ClusterName }} - securityGroup/master.{{ ClusterName }}
iamInstanceProfile: iamInstanceProfile/master.{{ ClusterName }} iamInstanceProfile: iamInstanceProfile/master.{{ ClusterName }}

View File

@ -46,7 +46,7 @@ securityGroupRule/all-node-to-master:
# LaunchConfiguration & ASG for nodes # LaunchConfiguration & ASG for nodes
launchConfiguration/{{ $nodeset.Name }}.{{ ClusterName }}: launchConfiguration/{{ $nodeset.Name }}.{{ ClusterName }}:
sshKey: sshKey/{{ ClusterName }} sshKey: sshKey/{{ SSHKeyName }}
securityGroups: securityGroups:
- securityGroup/nodes.{{ ClusterName }} - securityGroup/nodes.{{ ClusterName }}
iamInstanceProfile: iamInstanceProfile/nodes.{{ ClusterName }} iamInstanceProfile: iamInstanceProfile/nodes.{{ ClusterName }}

View File

@ -1,3 +1,2 @@
sshKey/{{ ClusterName }}: sshKey/{{ SSHKeyName}}:
name: kubernetes.{{ClusterName}}
publicKey: resources/ssh-public-key publicKey: resources/ssh-public-key

View File

@ -32,6 +32,9 @@ const (
SecretTypeSSHPublicKey = "SSHPublicKey" SecretTypeSSHPublicKey = "SSHPublicKey"
SecretTypeKeypair = "Keypair" SecretTypeKeypair = "Keypair"
SecretTypeSecret = "Secret" SecretTypeSecret = "Secret"
// Name for the primary SSH key
SecretNameSSHPrimary = "admin"
) )
type KeystoreItem struct { type KeystoreItem struct {

View File

@ -3,7 +3,6 @@ package cloudup
import ( import (
"fmt" "fmt"
"github.com/golang/glog" "github.com/golang/glog"
"io/ioutil"
"k8s.io/kops/upup/pkg/api" "k8s.io/kops/upup/pkg/api"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks" "k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
@ -35,10 +34,7 @@ type ApplyClusterCmd struct {
// Target specifies how we are operating e.g. direct to GCE, or AWS, or dry-run, or terraform // Target specifies how we are operating e.g. direct to GCE, or AWS, or dry-run, or terraform
Target string Target string
//// The node model to use
//NodeModel string
// The SSH public key (file) to use
SSHPublicKey string
// OutDir is a local directory in which we place output, can cache files etc // OutDir is a local directory in which we place output, can cache files etc
OutDir string OutDir string
@ -150,6 +146,18 @@ func (c *ApplyClusterCmd) Run() error {
region := "" region := ""
project := "" project := ""
var sshPublicKeys [][]byte
{
keys, err := keyStore.FindSSHPublicKeys(fi.SecretNameSSHPrimary)
if err != nil {
return fmt.Errorf("error retrieving SSH public key %q: %v", fi.SecretNameSSHPrimary, err)
}
for _, k := range keys {
sshPublicKeys = append(sshPublicKeys, k.Data)
}
}
switch cluster.Spec.CloudProvider { switch cluster.Spec.CloudProvider {
case "gce": case "gce":
{ {
@ -216,8 +224,25 @@ func (c *ApplyClusterCmd) Run() error {
"dnsZone": &awstasks.DNSZone{}, "dnsZone": &awstasks.DNSZone{},
}) })
if c.SSHPublicKey == "" { if len(sshPublicKeys) == 0 {
return fmt.Errorf("SSH public key must be specified when running with AWS") return fmt.Errorf("SSH public key must be specified when running with AWS (create with `kops create secret --name %s sshpublickey admin -i ~/.ssh/id_rsa.pub`)", cluster.Name)
}
if len(sshPublicKeys) != 1 {
return fmt.Errorf("Exactly one 'admin' SSH public key can be specified when running with AWS; please delete a key using `kops delete secret`")
} else {
l.Resources["ssh-public-key"] = fi.NewStringResource(string(sshPublicKeys[0]))
// SSHKeyName computes a unique SSH key name, combining the cluster name and the SSH public key fingerprint
l.TemplateFunctions["SSHKeyName"] = func() (string, error) {
fingerprint, err := awstasks.ComputeOpenSSHKeyFingerprint(string(sshPublicKeys[0]))
if err != nil {
return "", err
}
name := "kubernetes." + cluster.Name + "-" + fingerprint
return name, nil
}
} }
l.TemplateFunctions["MachineTypeInfo"] = awsup.GetMachineTypeInfo l.TemplateFunctions["MachineTypeInfo"] = awsup.GetMachineTypeInfo
@ -381,15 +406,6 @@ func (c *ApplyClusterCmd) Run() error {
tf.AddTo(l.TemplateFunctions) tf.AddTo(l.TemplateFunctions)
if c.SSHPublicKey != "" {
authorized, err := ioutil.ReadFile(c.SSHPublicKey)
if err != nil {
return fmt.Errorf("error reading SSH key file %q: %v", c.SSHPublicKey, err)
}
l.Resources["ssh-public-key"] = fi.NewStringResource(string(authorized))
}
taskMap, err := l.BuildTasks(modelStore, c.Models) taskMap, err := l.BuildTasks(modelStore, c.Models)
if err != nil { if err != nil {
return fmt.Errorf("error building tasks: %v", err) return fmt.Errorf("error building tasks: %v", err)

View File

@ -79,30 +79,28 @@ func (e *SSHKey) Find(c *fi.Context) (*SSHKey, error) {
return actual, nil return actual, nil
} }
// computeAWSKeyFingerprint computes the AWS-specific fingerprint of the SSH public key // parseSSHPublicKey parses the SSH public key string
func computeAWSKeyFingerprint(publicKey string) (string, error) { func parseSSHPublicKey(publicKey string) (ssh.PublicKey, error) {
tokens := strings.Fields(publicKey) tokens := strings.Fields(publicKey)
if len(tokens) < 2 { if len(tokens) < 2 {
return "", fmt.Errorf("error parsing SSH public key: %q", publicKey) return nil, fmt.Errorf("error parsing SSH public key: %q", publicKey)
} }
sshPublicKeyBytes, err := base64.StdEncoding.DecodeString(tokens[1]) sshPublicKeyBytes, err := base64.StdEncoding.DecodeString(tokens[1])
if len(tokens) < 2 { if len(tokens) < 2 {
return "", fmt.Errorf("error decoding SSH public key: %q", publicKey) return nil, fmt.Errorf("error decoding SSH public key: %q", publicKey)
} }
sshPublicKey, err := ssh.ParsePublicKey(sshPublicKeyBytes) sshPublicKey, err := ssh.ParsePublicKey(sshPublicKeyBytes)
if err != nil { if err != nil {
return "", fmt.Errorf("error parsing SSH public key: %v", err) return nil, fmt.Errorf("error parsing SSH public key: %v", err)
} }
return sshPublicKey, nil
}
der, err := toDER(sshPublicKey) // colonSeparatedHex formats the byte slice SSH-fingerprint style: hex bytes separated by colons
if err != nil { func colonSeparatedHex(data []byte) string {
return "", fmt.Errorf("error computing fingerprint for SSH public key: %v", err) sshKeyFingerprint := fmt.Sprintf("%x", data)
}
h := md5.Sum(der)
sshKeyFingerprint := fmt.Sprintf("%x", h)
var colonSeparated bytes.Buffer var colonSeparated bytes.Buffer
for i := 0; i < len(sshKeyFingerprint); i++ { for i := 0; i < len(sshKeyFingerprint); i++ {
if (i%2) == 0 && i != 0 { if (i%2) == 0 && i != 0 {
@ -111,7 +109,34 @@ func computeAWSKeyFingerprint(publicKey string) (string, error) {
colonSeparated.WriteByte(sshKeyFingerprint[i]) colonSeparated.WriteByte(sshKeyFingerprint[i])
} }
return colonSeparated.String(), nil return colonSeparated.String()
}
// computeAWSKeyFingerprint computes the AWS-specific fingerprint of the SSH public key
func computeAWSKeyFingerprint(publicKey string) (string, error) {
sshPublicKey, err := parseSSHPublicKey(publicKey)
if err != nil {
return "", err
}
der, err := toDER(sshPublicKey)
if err != nil {
return "", fmt.Errorf("error computing fingerprint for SSH public key: %v", err)
}
h := md5.Sum(der)
return colonSeparatedHex(h[:]), nil
}
// ComputeOpenSSHKeyFingerprint computes the OpenSSH fingerprint of the SSH public key
func ComputeOpenSSHKeyFingerprint(publicKey string) (string, error) {
sshPublicKey, err := parseSSHPublicKey(publicKey)
if err != nil {
return "", err
}
h := md5.Sum(sshPublicKey.Marshal())
return colonSeparatedHex(h[:]), nil
} }
// toDER gets the DER encoding of the SSH public key // toDER gets the DER encoding of the SSH public key
@ -195,6 +220,7 @@ func (_ *SSHKey) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *SSHKey) error {
e.KeyFingerprint = response.KeyFingerprint e.KeyFingerprint = response.KeyFingerprint
} }
// No tags on SSH public key
return nil //return output.AddAWSTags(cloud.Tags(), v, "vpc") return nil //return output.AddAWSTags(cloud.Tags(), v, "vpc")
} }

View File

@ -6,7 +6,7 @@ import (
"testing" "testing"
) )
func checkFingerprintEqual(t *testing.T, publicKey string, fingerprint string) { func checkAWSFingerprintEqual(t *testing.T, publicKey string, fingerprint string) {
actual, err := computeAWSKeyFingerprint(publicKey) actual, err := computeAWSKeyFingerprint(publicKey)
if err != nil { if err != nil {
t.Fatalf("Unexpected error computing AWS key fingerprint: %v", err) t.Fatalf("Unexpected error computing AWS key fingerprint: %v", err)
@ -16,7 +16,7 @@ func checkFingerprintEqual(t *testing.T, publicKey string, fingerprint string) {
} }
} }
func checkFingerprintError(t *testing.T, publicKey string, message string) { func checkAWSFingerprintError(t *testing.T, publicKey string, message string) {
_, err := computeAWSKeyFingerprint(publicKey) _, err := computeAWSKeyFingerprint(publicKey)
if err == nil { if err == nil {
t.Fatalf("Expected error %q computing AWS key fingerprint", message) t.Fatalf("Expected error %q computing AWS key fingerprint", message)
@ -27,29 +27,44 @@ func checkFingerprintError(t *testing.T, publicKey string, message string) {
} }
} }
func TestFingerprint_RsaKey1(t *testing.T) { func Test_AWSFingerprint_RsaKey1(t *testing.T) {
key := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCySdqIU+FhCWl3BNrAvPaOe5VfL2aCARUWwy91ZP+T7LBwFa9lhdttfjp/VX1D1/PVwntn2EhN079m8c2kfdmiZ/iCHqrLyIGSd+BOiCz0lT47znvANSfxYjLUuKrWWWeaXqerJkOsAD4PHchRLbZGPdbfoBKwtb/WT4GMRQmb9vmiaZYjsfdPPM9KkWI9ECoWFGjGehA8D+iYIPR711kRacb1xdYmnjHqxAZHFsb5L8wDWIeAyhy49cBD+lbzTiioq2xWLorXuFmXh6Do89PgzvHeyCLY6816f/kCX6wIFts8A2eaEHFL4rAOsuh6qHmSxGCR9peSyuRW8DxV725x justin@test" key := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCySdqIU+FhCWl3BNrAvPaOe5VfL2aCARUWwy91ZP+T7LBwFa9lhdttfjp/VX1D1/PVwntn2EhN079m8c2kfdmiZ/iCHqrLyIGSd+BOiCz0lT47znvANSfxYjLUuKrWWWeaXqerJkOsAD4PHchRLbZGPdbfoBKwtb/WT4GMRQmb9vmiaZYjsfdPPM9KkWI9ECoWFGjGehA8D+iYIPR711kRacb1xdYmnjHqxAZHFsb5L8wDWIeAyhy49cBD+lbzTiioq2xWLorXuFmXh6Do89PgzvHeyCLY6816f/kCX6wIFts8A2eaEHFL4rAOsuh6qHmSxGCR9peSyuRW8DxV725x justin@test"
checkFingerprintEqual(t, key, "85:a6:f4:64:b7:8f:4a:75:f1:ed:f9:26:1b:67:5f:f2") checkAWSFingerprintEqual(t, key, "85:a6:f4:64:b7:8f:4a:75:f1:ed:f9:26:1b:67:5f:f2")
} }
func TestFingerprint_RsaKeyEncrypted(t *testing.T) { func Test_AWSFingerprint_RsaKeyEncrypted(t *testing.T) {
// The private key is encrypted; the public key isn't // The private key is encrypted; the public key isn't
key := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCrLzpTNk5r3RWrzhRuFH8wkOQ+3mOEdaFosPzgDzQtriGU3JZ9Y3UHN4ltUOYUlapyFRaB27Pvyd48GkOSym7ZMn4/kyWn1SvXumJmW8bbX5+pTGK6p3Xu0elBPYMJHWEdZLK5gV6r15uRie9vhxknS9mOzxMcG9gdyyY3DdC3LiiRr6I8wTojP9MsWseZdPPZ5o6tMR/Zp2Q0fOb/DOhNuzunauMos+iu76YPORRFF1PaT1LoLxH7+/HwSX993JDzKytakuCoDFQ2/JvoMxkIvnVIz+MGsLKUZgmxJYQRaIL+fRR+ZBGFrOTqI72NXDmjT7aKjHHxYPfrsSggPh1J justin@machine" key := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCrLzpTNk5r3RWrzhRuFH8wkOQ+3mOEdaFosPzgDzQtriGU3JZ9Y3UHN4ltUOYUlapyFRaB27Pvyd48GkOSym7ZMn4/kyWn1SvXumJmW8bbX5+pTGK6p3Xu0elBPYMJHWEdZLK5gV6r15uRie9vhxknS9mOzxMcG9gdyyY3DdC3LiiRr6I8wTojP9MsWseZdPPZ5o6tMR/Zp2Q0fOb/DOhNuzunauMos+iu76YPORRFF1PaT1LoLxH7+/HwSX993JDzKytakuCoDFQ2/JvoMxkIvnVIz+MGsLKUZgmxJYQRaIL+fRR+ZBGFrOTqI72NXDmjT7aKjHHxYPfrsSggPh1J justin@machine"
checkFingerprintEqual(t, key, "c9:c5:05:5e:ea:54:fc:a4:7c:7c:75:5c:d2:71:5e:40") checkAWSFingerprintEqual(t, key, "c9:c5:05:5e:ea:54:fc:a4:7c:7c:75:5c:d2:71:5e:40")
} }
func TestFingerprint_TrickyWhitespace(t *testing.T) { func Test_AWSFingerprint_TrickyWhitespace(t *testing.T) {
// No name, \r instead of whitespace // No name, \r instead of whitespace
key := "ssh-rsa\rAAAAB3NzaC1yc2EAAAADAQABAAABAQCySdqIU+FhCWl3BNrAvPaOe5VfL2aCARUWwy91ZP+T7LBwFa9lhdttfjp/VX1D1/PVwntn2EhN079m8c2kfdmiZ/iCHqrLyIGSd+BOiCz0lT47znvANSfxYjLUuKrWWWeaXqerJkOsAD4PHchRLbZGPdbfoBKwtb/WT4GMRQmb9vmiaZYjsfdPPM9KkWI9ECoWFGjGehA8D+iYIPR711kRacb1xdYmnjHqxAZHFsb5L8wDWIeAyhy49cBD+lbzTiioq2xWLorXuFmXh6Do89PgzvHeyCLY6816f/kCX6wIFts8A2eaEHFL4rAOsuh6qHmSxGCR9peSyuRW8DxV725x\r" key := "ssh-rsa\rAAAAB3NzaC1yc2EAAAADAQABAAABAQCySdqIU+FhCWl3BNrAvPaOe5VfL2aCARUWwy91ZP+T7LBwFa9lhdttfjp/VX1D1/PVwntn2EhN079m8c2kfdmiZ/iCHqrLyIGSd+BOiCz0lT47znvANSfxYjLUuKrWWWeaXqerJkOsAD4PHchRLbZGPdbfoBKwtb/WT4GMRQmb9vmiaZYjsfdPPM9KkWI9ECoWFGjGehA8D+iYIPR711kRacb1xdYmnjHqxAZHFsb5L8wDWIeAyhy49cBD+lbzTiioq2xWLorXuFmXh6Do89PgzvHeyCLY6816f/kCX6wIFts8A2eaEHFL4rAOsuh6qHmSxGCR9peSyuRW8DxV725x\r"
checkFingerprintEqual(t, key, "85:a6:f4:64:b7:8f:4a:75:f1:ed:f9:26:1b:67:5f:f2") checkAWSFingerprintEqual(t, key, "85:a6:f4:64:b7:8f:4a:75:f1:ed:f9:26:1b:67:5f:f2")
} }
func TestFingerprint_DsaKey(t *testing.T) { func Test_AWSFingerprint_DsaKey(t *testing.T) {
key := "ssh-dss AAAAB3NzaC1kc3MAAACBAIcCTu3vi9rNjsnhCrHeII7jSN6/FmnIdy09pQAsMAGGvCS9HBOteCKbIyYQQ0+Gi76Oui7cJ2VQojdxOxeZPoSP+QYnA+CVYhnowVVLeRA9VBQG3ZLInoXaqe3nR4/OXhY75GmYShBBPTQ+/fWGX9ltoXfygSc4KjhBNudvj75VAAAAFQDiw8A4MhY0aHSX/mtpa7XV8+iS6wAAAIAXyQaxM/dk0o1vBV3H0V0lGhog3mF7EJPdw7jagYvXQP1tAhzNofxZVhXHr4wGfiTQv9j5plDqQzCI/15a6DRyo9zI+zdPTR41W3dGrk56O2/Qxsz3/vNip5OwpOJ88yMmBX9m36gg0WrOXcZDgErhvZWRt5cXa9QjVg/KpxYLPAAAAIB8e5M82IiRLi+k1k4LsELKArQGzVkPgynESfnEXX0TKGiR7PJvBNGaKnPJtJ0Rrc38w/hLTeklroJt9Rdey/NI9b6tc+ur2pmJdnYppnNCm03WszU4oFD/7KIqR84Hf0fMbWd1hRvznpZhngZ505KNsL+ck0+Tlq6Hdhe2baXJcA== justin@machine" key := "ssh-dss AAAAB3NzaC1kc3MAAACBAIcCTu3vi9rNjsnhCrHeII7jSN6/FmnIdy09pQAsMAGGvCS9HBOteCKbIyYQQ0+Gi76Oui7cJ2VQojdxOxeZPoSP+QYnA+CVYhnowVVLeRA9VBQG3ZLInoXaqe3nR4/OXhY75GmYShBBPTQ+/fWGX9ltoXfygSc4KjhBNudvj75VAAAAFQDiw8A4MhY0aHSX/mtpa7XV8+iS6wAAAIAXyQaxM/dk0o1vBV3H0V0lGhog3mF7EJPdw7jagYvXQP1tAhzNofxZVhXHr4wGfiTQv9j5plDqQzCI/15a6DRyo9zI+zdPTR41W3dGrk56O2/Qxsz3/vNip5OwpOJ88yMmBX9m36gg0WrOXcZDgErhvZWRt5cXa9QjVg/KpxYLPAAAAIB8e5M82IiRLi+k1k4LsELKArQGzVkPgynESfnEXX0TKGiR7PJvBNGaKnPJtJ0Rrc38w/hLTeklroJt9Rdey/NI9b6tc+ur2pmJdnYppnNCm03WszU4oFD/7KIqR84Hf0fMbWd1hRvznpZhngZ505KNsL+ck0+Tlq6Hdhe2baXJcA== justin@machine"
checkFingerprintError(t, key, "AWS can only import RSA keys") checkAWSFingerprintError(t, key, "AWS can only import RSA keys")
} }
func TestFingerprint_Ed25519Key(t *testing.T) { func Test_AWSFingerprint_Ed25519Key(t *testing.T) {
key := "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpyraYd4rUFftiEKzUO4wKFAgTkXxuJcRZwVcsuZJ8G justin@machine" key := "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpyraYd4rUFftiEKzUO4wKFAgTkXxuJcRZwVcsuZJ8G justin@machine"
checkFingerprintError(t, key, "AWS can only import RSA keys") checkAWSFingerprintError(t, key, "AWS can only import RSA keys")
}
func checkOpenSSHFingerprintEqual(t *testing.T, publicKey string, fingerprint string) {
actual, err := ComputeOpenSSHKeyFingerprint(publicKey)
if err != nil {
t.Fatalf("Unexpected error computing OpenSSH key fingerprint: %v", err)
}
if actual != fingerprint {
t.Fatalf("Expected fingerprint %q, got %q", fingerprint, actual)
}
}
func Test_OpenSSHFingerprint_RsaKey1(t *testing.T) {
key := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCySdqIU+FhCWl3BNrAvPaOe5VfL2aCARUWwy91ZP+T7LBwFa9lhdttfjp/VX1D1/PVwntn2EhN079m8c2kfdmiZ/iCHqrLyIGSd+BOiCz0lT47znvANSfxYjLUuKrWWWeaXqerJkOsAD4PHchRLbZGPdbfoBKwtb/WT4GMRQmb9vmiaZYjsfdPPM9KkWI9ECoWFGjGehA8D+iYIPR711kRacb1xdYmnjHqxAZHFsb5L8wDWIeAyhy49cBD+lbzTiioq2xWLorXuFmXh6Do89PgzvHeyCLY6816f/kCX6wIFts8A2eaEHFL4rAOsuh6qHmSxGCR9peSyuRW8DxV725x justin@test"
checkOpenSSHFingerprintEqual(t, key, "be:ba:ec:2b:9e:a0:68:b8:19:6b:9a:26:cc:b1:58:ff")
} }

View File

@ -662,7 +662,8 @@ func ListKeypairs(cloud fi.Cloud, clusterName string) ([]*ResourceTracker, error
glog.V(2).Infof("Listing EC2 Keypairs") glog.V(2).Infof("Listing EC2 Keypairs")
request := &ec2.DescribeKeyPairsInput{ request := &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{awsup.NewEC2Filter("key-name", keypairName)}, // We need to match both the name and a prefix
//Filters: []*ec2.Filter{awsup.NewEC2Filter("key-name", keypairName)},
} }
response, err := c.EC2.DescribeKeyPairs(request) response, err := c.EC2.DescribeKeyPairs(request)
if err != nil { if err != nil {
@ -673,6 +674,9 @@ func ListKeypairs(cloud fi.Cloud, clusterName string) ([]*ResourceTracker, error
for _, keypair := range response.KeyPairs { for _, keypair := range response.KeyPairs {
name := aws.StringValue(keypair.KeyName) name := aws.StringValue(keypair.KeyName)
if name != keypairName && !strings.HasPrefix(name, keypairName+"-") {
continue
}
tracker := &ResourceTracker{ tracker := &ResourceTracker{
Name: name, Name: name,
ID: name, ID: name,