mirror of https://github.com/kubernetes/kops.git
commit
7699dc8fd2
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 }}
|
||||||
|
|
|
@ -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 }}
|
||||||
|
|
|
@ -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 }}
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
sshKey/{{ ClusterName }}:
|
sshKey/{{ SSHKeyName}}:
|
||||||
name: kubernetes.{{ClusterName}}
|
|
||||||
publicKey: resources/ssh-public-key
|
publicKey: resources/ssh-public-key
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue