mirror of https://github.com/kubernetes/kops.git
Move ssh key functions to pkg/pki
Preparation for reuse by the roundtrip mock tests.
This commit is contained in:
parent
b6eededf7c
commit
f8c57c2f57
|
|
@ -30,6 +30,7 @@ go_library(
|
||||||
"//pkg/model/components:go_default_library",
|
"//pkg/model/components:go_default_library",
|
||||||
"//pkg/model/iam:go_default_library",
|
"//pkg/model/iam:go_default_library",
|
||||||
"//pkg/model/resources:go_default_library",
|
"//pkg/model/resources:go_default_library",
|
||||||
|
"//pkg/pki:go_default_library",
|
||||||
"//pkg/tokens:go_default_library",
|
"//pkg/tokens:go_default_library",
|
||||||
"//upup/pkg/fi:go_default_library",
|
"//upup/pkg/fi:go_default_library",
|
||||||
"//upup/pkg/fi/cloudup/awstasks:go_default_library",
|
"//upup/pkg/fi/cloudup/awstasks:go_default_library",
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"k8s.io/kops/pkg/apis/kops"
|
"k8s.io/kops/pkg/apis/kops"
|
||||||
|
"k8s.io/kops/pkg/pki"
|
||||||
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
|
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -126,7 +128,7 @@ func (c *KopsModelContext) SSHKeyName() (string, error) {
|
||||||
return name, nil
|
return name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fingerprint, err := awstasks.ComputeOpenSSHKeyFingerprint(string(c.SSHPublicKeys[0]))
|
fingerprint, err := pki.ComputeOpenSSHKeyFingerprint(string(c.SSHPublicKeys[0]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,15 @@ go_library(
|
||||||
"certificate.go",
|
"certificate.go",
|
||||||
"csr.go",
|
"csr.go",
|
||||||
"privatekey.go",
|
"privatekey.go",
|
||||||
|
"sshkey.go",
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kops/pkg/pki",
|
importpath = "k8s.io/kops/pkg/pki",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = ["//vendor/github.com/golang/glog:go_default_library"],
|
deps = [
|
||||||
|
"//upup/pkg/fi/utils:go_default_library",
|
||||||
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
|
"//vendor/golang.org/x/crypto/ssh:go_default_library",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
go_test(
|
go_test(
|
||||||
|
|
@ -17,6 +22,7 @@ go_test(
|
||||||
srcs = [
|
srcs = [
|
||||||
"certificate_test.go",
|
"certificate_test.go",
|
||||||
"privatekey_test.go",
|
"privatekey_test.go",
|
||||||
|
"sshkey_test.go",
|
||||||
],
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
|
"k8s.io/kops/upup/pkg/fi/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseSSHPublicKey parses the SSH public key string
|
||||||
|
func parseSSHPublicKey(publicKey string) (ssh.PublicKey, error) {
|
||||||
|
tokens := strings.Fields(publicKey)
|
||||||
|
if len(tokens) < 2 {
|
||||||
|
return nil, fmt.Errorf("error parsing SSH public key: %q", publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
sshPublicKeyBytes, err := base64.StdEncoding.DecodeString(tokens[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error decoding SSH public key: %q err: %s", publicKey, err)
|
||||||
|
}
|
||||||
|
if len(tokens) < 2 {
|
||||||
|
return nil, fmt.Errorf("error decoding SSH public key: %q", publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
sshPublicKey, err := ssh.ParsePublicKey(sshPublicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing SSH public key: %v", err)
|
||||||
|
}
|
||||||
|
return sshPublicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// colonSeparatedHex formats the byte slice SSH-fingerprint style: hex bytes separated by colons
|
||||||
|
func colonSeparatedHex(data []byte) string {
|
||||||
|
sshKeyFingerprint := fmt.Sprintf("%x", data)
|
||||||
|
var colonSeparated bytes.Buffer
|
||||||
|
for i := 0; i < len(sshKeyFingerprint); i++ {
|
||||||
|
if (i%2) == 0 && i != 0 {
|
||||||
|
colonSeparated.WriteByte(':')
|
||||||
|
}
|
||||||
|
colonSeparated.WriteByte(sshKeyFingerprint[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// 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("Unexpected type of SSH key (%q); AWS can only import RSA keys", typeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
der, err := x509.MarshalPKIXPublicKey(cryptoKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error marshalling SSH public key: %v", err)
|
||||||
|
}
|
||||||
|
return der, nil
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package awstasks
|
package pki
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkAWSFingerprintEqual(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)
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ func checkAWSFingerprintEqual(t *testing.T, publicKey string, fingerprint string
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAWSFingerprintError(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)
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +66,7 @@ go_library(
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/diff:go_default_library",
|
"//pkg/diff:go_default_library",
|
||||||
"//pkg/featureflag:go_default_library",
|
"//pkg/featureflag:go_default_library",
|
||||||
|
"//pkg/pki:go_default_library",
|
||||||
"//upup/pkg/fi:go_default_library",
|
"//upup/pkg/fi:go_default_library",
|
||||||
"//upup/pkg/fi/cloudup/awsup:go_default_library",
|
"//upup/pkg/fi/cloudup/awsup:go_default_library",
|
||||||
"//upup/pkg/fi/cloudup/cloudformation:go_default_library",
|
"//upup/pkg/fi/cloudup/cloudformation:go_default_library",
|
||||||
|
|
@ -80,7 +81,6 @@ go_library(
|
||||||
"//vendor/github.com/aws/aws-sdk-go/service/iam:go_default_library",
|
"//vendor/github.com/aws/aws-sdk-go/service/iam:go_default_library",
|
||||||
"//vendor/github.com/aws/aws-sdk-go/service/route53:go_default_library",
|
"//vendor/github.com/aws/aws-sdk-go/service/route53:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
"//vendor/golang.org/x/crypto/ssh:go_default_library",
|
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||||
],
|
],
|
||||||
|
|
@ -94,7 +94,6 @@ go_test(
|
||||||
"elastic_ip_test.go",
|
"elastic_ip_test.go",
|
||||||
"internetgateway_test.go",
|
"internetgateway_test.go",
|
||||||
"securitygroup_test.go",
|
"securitygroup_test.go",
|
||||||
"sshkey_test.go",
|
|
||||||
"subnet_test.go",
|
"subnet_test.go",
|
||||||
"vpc_test.go",
|
"vpc_test.go",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,25 +17,18 @@ limitations under the License.
|
||||||
package awstasks
|
package awstasks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto"
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"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"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
|
"k8s.io/kops/pkg/pki"
|
||||||
"k8s.io/kops/upup/pkg/fi"
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||||
"k8s.io/kops/upup/pkg/fi/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate fitask -type=SSHKey
|
//go:generate fitask -type=SSHKey
|
||||||
|
|
@ -102,100 +95,6 @@ func (e *SSHKey) find(cloud awsup.AWSCloud) (*SSHKey, error) {
|
||||||
return actual, nil
|
return actual, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseSSHPublicKey parses the SSH public key string
|
|
||||||
func parseSSHPublicKey(publicKey string) (ssh.PublicKey, error) {
|
|
||||||
tokens := strings.Fields(publicKey)
|
|
||||||
if len(tokens) < 2 {
|
|
||||||
return nil, fmt.Errorf("error parsing SSH public key: %q", publicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
sshPublicKeyBytes, err := base64.StdEncoding.DecodeString(tokens[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error decoding SSH public key: %q err: %s", publicKey, err)
|
|
||||||
}
|
|
||||||
if len(tokens) < 2 {
|
|
||||||
return nil, fmt.Errorf("error decoding SSH public key: %q", publicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
sshPublicKey, err := ssh.ParsePublicKey(sshPublicKeyBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error parsing SSH public key: %v", err)
|
|
||||||
}
|
|
||||||
return sshPublicKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// colonSeparatedHex formats the byte slice SSH-fingerprint style: hex bytes separated by colons
|
|
||||||
func colonSeparatedHex(data []byte) string {
|
|
||||||
sshKeyFingerprint := fmt.Sprintf("%x", data)
|
|
||||||
var colonSeparated bytes.Buffer
|
|
||||||
for i := 0; i < len(sshKeyFingerprint); i++ {
|
|
||||||
if (i%2) == 0 && i != 0 {
|
|
||||||
colonSeparated.WriteByte(':')
|
|
||||||
}
|
|
||||||
colonSeparated.WriteByte(sshKeyFingerprint[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
// 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("Unexpected type of SSH key (%q); AWS can only import RSA keys", 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 {
|
||||||
publicKey, err := e.PublicKey.AsString()
|
publicKey, err := e.PublicKey.AsString()
|
||||||
|
|
@ -203,7 +102,7 @@ func (e *SSHKey) Run(c *fi.Context) error {
|
||||||
return fmt.Errorf("error reading SSH public key: %v", err)
|
return fmt.Errorf("error reading SSH public key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
keyFingerprint, err := computeAWSKeyFingerprint(publicKey)
|
keyFingerprint, err := pki.ComputeAWSKeyFingerprint(publicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error computing key fingerprint for SSH key: %v", err)
|
return fmt.Errorf("error computing key fingerprint for SSH key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue