yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/keypair.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package aws 16 17 import ( 18 "bytes" 19 "crypto/md5" 20 "crypto/rsa" 21 "crypto/x509" 22 "fmt" 23 "strconv" 24 "time" 25 26 "github.com/aokoli/goutils" 27 "github.com/aws/aws-sdk-go/service/ec2" 28 "golang.org/x/crypto/ssh" 29 30 "yunion.io/x/pkg/errors" 31 32 "yunion.io/x/cloudmux/pkg/cloudprovider" 33 ) 34 35 type SKeypair struct { 36 KeyPairFingerPrint string 37 KeyPairName string 38 } 39 40 // 只支持计算Openssh ras 格式公钥转换成DER格式后的MD5。 41 func md5Fingerprint(publickey string) (string, error) { 42 pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publickey)) 43 if err != nil { 44 return "", fmt.Errorf("publicKey error %s", err) 45 } 46 47 der := []byte{} 48 cryptoPub, ok := pk.(ssh.CryptoPublicKey) 49 if !ok { 50 return "", fmt.Errorf("public key trans to crypto public key failed") 51 } 52 53 switch pk.Type() { 54 case ssh.KeyAlgoRSA: 55 rsaPK, ok := cryptoPub.CryptoPublicKey().(*rsa.PublicKey) 56 if !ok { 57 return "", fmt.Errorf("crypto public key trans to ras publickey failed") 58 } 59 der, err = x509.MarshalPKIXPublicKey(rsaPK) 60 if err != nil { 61 return "", fmt.Errorf("MarshalPKIXPublicKey ras publickey failed") 62 } 63 default: 64 return "", fmt.Errorf("unsupport public key format.Only ssh-rsa supported") 65 } 66 67 var ret bytes.Buffer 68 fp := md5.Sum(der) 69 for i, b := range fp { 70 ret.WriteString(fmt.Sprintf("%02x", b)) 71 if i < len(fp)-1 { 72 ret.WriteString(":") 73 } 74 } 75 76 return ret.String(), nil 77 } 78 79 func (self *SRegion) GetKeypairs(finger string, name string, offset int, limit int) ([]SKeypair, int, error) { 80 params := &ec2.DescribeKeyPairsInput{} 81 filters := []*ec2.Filter{} 82 if len(finger) > 0 { 83 filters = AppendSingleValueFilter(filters, "fingerprint", finger) 84 } 85 86 if len(name) > 0 { 87 params.SetKeyNames([]*string{&name}) 88 } 89 90 if len(filters) > 0 { 91 params.SetFilters(filters) 92 } 93 94 ec2Client, err := self.getEc2Client() 95 if err != nil { 96 return nil, 0, errors.Wrap(err, "getEc2Client") 97 } 98 ret, err := ec2Client.DescribeKeyPairs(params) 99 if err != nil { 100 return nil, 0, err 101 } 102 103 keypairs := []SKeypair{} 104 for _, item := range ret.KeyPairs { 105 if err := FillZero(item); err != nil { 106 return nil, 0, err 107 } 108 109 keypairs = append(keypairs, SKeypair{*item.KeyFingerprint, *item.KeyName}) 110 } 111 112 return keypairs, len(keypairs), nil 113 } 114 115 // Aws貌似不支持ssh-dss格式密钥 116 func (self *SRegion) ImportKeypair(name string, pubKey string) (*SKeypair, error) { 117 params := &ec2.ImportKeyPairInput{} 118 params.SetKeyName(name) 119 params.SetPublicKeyMaterial([]byte(pubKey)) 120 ec2Client, err := self.getEc2Client() 121 if err != nil { 122 return nil, errors.Wrap(err, "getEc2Client") 123 } 124 ret, err := ec2Client.ImportKeyPair(params) 125 if err != nil { 126 return nil, errors.Wrap(err, "ImportKeyPair") 127 } else { 128 return &SKeypair{StrVal(ret.KeyFingerprint), StrVal(ret.KeyName)}, nil 129 } 130 } 131 132 func (self *SRegion) AttachKeypair(instanceId string, keypairName string) error { 133 return cloudprovider.ErrNotSupported 134 } 135 136 func (self *SRegion) DetachKeyPair(instanceId string, keypairName string) error { 137 return cloudprovider.ErrNotSupported 138 } 139 140 func (self *SRegion) lookUpAwsKeypair(publicKey string) (string, error) { 141 // https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/ec2-key-pairs.html 142 fingerprint, err := md5Fingerprint(publicKey) 143 if err != nil { 144 return "", err 145 } 146 147 ks, total, err := self.GetKeypairs(fingerprint, "", 0, 1) 148 if total < 1 { 149 return "", fmt.Errorf("keypair not found %s", err) 150 } else { 151 return ks[0].KeyPairName, nil 152 } 153 } 154 155 func (self *SRegion) importAwsKeypair(publicKey string) (string, error) { 156 prefix, e := goutils.RandomAlphabetic(6) 157 if e != nil { 158 return "", fmt.Errorf("publicKey error %s", e) 159 } 160 161 name := prefix + strconv.FormatInt(time.Now().Unix(), 10) 162 if k, e := self.ImportKeypair(name, publicKey); e != nil { 163 return "", fmt.Errorf("keypair import error %s", e) 164 } else { 165 return k.KeyPairName, nil 166 } 167 } 168 169 func (self *SRegion) syncKeypair(publicKey string) (string, error) { 170 name, e := self.lookUpAwsKeypair(publicKey) 171 if e == nil { 172 return name, nil 173 } 174 return self.importAwsKeypair(publicKey) 175 }