upup: Fix fingerprint calculation in AWS keypair

Both fix the calculation itself to match AWS's weird fingerprint
algorithm, and also fix the comparison logic by which we infer that if
the fingerprint matches, that the public key matches also.
This commit is contained in:
Justin Santa Barbara 2016-05-15 23:27:04 -04:00
parent b083e4f6bf
commit 8752db39aa
1 changed files with 50 additions and 11 deletions

View File

@ -4,7 +4,11 @@ import (
"fmt" "fmt"
"bytes" "bytes"
"crypto"
"crypto/dsa"
"crypto/md5" "crypto/md5"
"crypto/rsa"
"crypto/x509"
"encoding/base64" "encoding/base64"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
@ -12,6 +16,8 @@ import (
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"k8s.io/kube-deploy/upup/pkg/fi" "k8s.io/kube-deploy/upup/pkg/fi"
"k8s.io/kube-deploy/upup/pkg/fi/cloudup/awsup" "k8s.io/kube-deploy/upup/pkg/fi/cloudup/awsup"
"k8s.io/kube-deploy/upup/pkg/fi/utils"
"reflect"
"strings" "strings"
) )
@ -63,6 +69,14 @@ func (e *SSHKey) Find(c *fi.Context) (*SSHKey, error) {
KeyFingerprint: k.KeyFingerprint, KeyFingerprint: k.KeyFingerprint,
} }
// Avoid spurious changes
if fi.StringValue(actual.KeyFingerprint) == fi.StringValue(e.KeyFingerprint) {
glog.V(2).Infof("SSH key fingerprints match; assuming public keys match")
actual.PublicKey = e.PublicKey
} else {
glog.V(2).Infof("Computed SSH key fingerprint mismatch: %q %q", fi.StringValue(e.KeyFingerprint), fi.StringValue(actual.KeyFingerprint))
}
return actual, nil return actual, nil
} }
@ -82,13 +96,16 @@ func computeAwsKeyFingerprint(publicKey fi.Resource) (string, error) {
return "", fmt.Errorf("error decoding SSH public key: %s", publicKeyString) return "", fmt.Errorf("error decoding SSH public key: %s", publicKeyString)
} }
// We don't technically need to parse and remarshal it, but it ensures the key is valid
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 "", fmt.Errorf("error parsing SSH public key: %v", err)
} }
h := md5.Sum(sshPublicKey.Marshal()) der, err := toDER(sshPublicKey)
if err != nil {
return "", fmt.Errorf("error computing fingerprint for SSH public key: %v", err)
}
h := md5.Sum(der)
sshKeyFingerprint := fmt.Sprintf("%x", h) sshKeyFingerprint := fmt.Sprintf("%x", h)
var colonSeparated bytes.Buffer var colonSeparated bytes.Buffer
@ -102,6 +119,37 @@ func computeAwsKeyFingerprint(publicKey fi.Resource) (string, error) {
return colonSeparated.String(), nil return colonSeparated.String(), nil
} }
// toDER gets the DER encoding of the SSH public key
// Annoyingly, the ssh code wraps the actual crypto keys, so we have to use reflection tricks
func toDER(pubkey ssh.PublicKey) ([]byte, error) {
pubkeyValue := reflect.ValueOf(pubkey)
typeName := utils.BuildTypeName(pubkeyValue.Type())
var cryptoKey crypto.PublicKey
switch typeName {
case "*rsaPublicKey":
var rsaPublicKey *rsa.PublicKey
targetType := reflect.ValueOf(rsaPublicKey).Type()
rsaPublicKey = pubkeyValue.Convert(targetType).Interface().(*rsa.PublicKey)
cryptoKey = rsaPublicKey
case "*dsaPublicKey":
var dsaPublicKey *dsa.PublicKey
targetType := reflect.ValueOf(dsaPublicKey).Type()
dsaPublicKey = pubkeyValue.Convert(targetType).Interface().(*dsa.PublicKey)
cryptoKey = dsaPublicKey
default:
return nil, fmt.Errorf("Unknown type for SSH PublicKey; cannot compute fingerprint: %q", typeName)
}
der, err := x509.MarshalPKIXPublicKey(cryptoKey)
if err != nil {
return nil, fmt.Errorf("error marshalling SSH public key: %v", err)
}
return der, nil
}
func (e *SSHKey) Run(c *fi.Context) error { func (e *SSHKey) Run(c *fi.Context) error {
if e.KeyFingerprint == nil && e.PublicKey != nil { if e.KeyFingerprint == nil && e.PublicKey != nil {
keyFingerprint, err := computeAwsKeyFingerprint(e.PublicKey) keyFingerprint, err := computeAwsKeyFingerprint(e.PublicKey)
@ -119,15 +167,6 @@ func (s *SSHKey) CheckChanges(a, e, changes *SSHKey) error {
if changes.Name != nil { if changes.Name != nil {
return fi.CannotChangeField("Name") return fi.CannotChangeField("Name")
} }
if changes.KeyFingerprint == nil {
if e.PublicKey != nil && a.PublicKey == nil {
glog.V(2).Infof("SSH key fingerprints match; assuming public keys match")
changes.PublicKey = nil
}
} else {
glog.V(2).Infof("Computed SSH key fingerprint mismatch: %q %q", fi.StringValue(e.KeyFingerprint), fi.StringValue(a.KeyFingerprint))
}
} }
return nil return nil
} }