keypair.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. package aws
  15. import (
  16. "bytes"
  17. "crypto/md5"
  18. "crypto/rsa"
  19. "crypto/x509"
  20. "encoding/base64"
  21. "fmt"
  22. "strconv"
  23. "time"
  24. "github.com/aokoli/goutils"
  25. "golang.org/x/crypto/ssh"
  26. "yunion.io/x/cloudmux/pkg/cloudprovider"
  27. "yunion.io/x/pkg/errors"
  28. )
  29. type SKeypair struct {
  30. KeyPairFingerPrint string `xml:"keyFingerprint"`
  31. KeyName string `xml:"keyName"`
  32. KeyPairId string `xml:"keyPairId"`
  33. KeyType string `xml:"keyType"`
  34. CreateTime time.Time `xml:"createTime"`
  35. PublicKey string `xml:"publicKey"`
  36. }
  37. // 只支持计算Openssh ras 格式公钥转换成DER格式后的MD5。
  38. func md5Fingerprint(publickey string) (string, error) {
  39. pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publickey))
  40. if err != nil {
  41. return "", fmt.Errorf("publicKey error %s", err)
  42. }
  43. der := []byte{}
  44. cryptoPub, ok := pk.(ssh.CryptoPublicKey)
  45. if !ok {
  46. return "", fmt.Errorf("public key trans to crypto public key failed")
  47. }
  48. switch pk.Type() {
  49. case ssh.KeyAlgoRSA:
  50. pubKey := cryptoPub.CryptoPublicKey()
  51. rsaPK, ok := pubKey.(*rsa.PublicKey)
  52. if !ok {
  53. return "", fmt.Errorf("crypto public key trans to ras publickey failed")
  54. }
  55. der, err = x509.MarshalPKIXPublicKey(rsaPK)
  56. if err != nil {
  57. return "", fmt.Errorf("MarshalPKIXPublicKey ras publickey failed")
  58. }
  59. default:
  60. return "", fmt.Errorf("unsupport public key format.Only ssh-rsa supported")
  61. }
  62. var ret bytes.Buffer
  63. fp := md5.Sum(der)
  64. for i, b := range fp {
  65. ret.WriteString(fmt.Sprintf("%02x", b))
  66. if i < len(fp)-1 {
  67. ret.WriteString(":")
  68. }
  69. }
  70. return ret.String(), nil
  71. }
  72. func (self *SRegion) GetKeypairs(finger string, name string) ([]SKeypair, error) {
  73. params := map[string]string{
  74. "IncludePublicKey": "true",
  75. }
  76. if len(finger) > 0 {
  77. params["Filter.1.Name"] = "fingerprint"
  78. params["Filter.1.Value.1"] = finger
  79. }
  80. if len(name) > 0 {
  81. params["KeyName"] = name
  82. }
  83. ret := struct {
  84. KeySet []SKeypair `xml:"keySet>item"`
  85. }{}
  86. err := self.ec2Request("DescribeKeyPairs", params, &ret)
  87. return ret.KeySet, err
  88. }
  89. // Aws貌似不支持ssh-dss格式密钥
  90. func (self *SRegion) ImportKeypair(name string, pubKey string) (*SKeypair, error) {
  91. params := map[string]string{
  92. "KeyName": name,
  93. "PublicKeyMaterial": base64.StdEncoding.EncodeToString([]byte(pubKey)),
  94. }
  95. ret := &SKeypair{}
  96. return ret, self.ec2Request("ImportKeyPair", params, ret)
  97. }
  98. func (self *SRegion) AttachKeypair(instanceId string, keypairName string) error {
  99. return cloudprovider.ErrNotSupported
  100. }
  101. func (self *SRegion) DetachKeyPair(instanceId string, keypairName string) error {
  102. return cloudprovider.ErrNotSupported
  103. }
  104. func (self *SRegion) lookUpAwsKeypair(publicKey string) (string, error) {
  105. // https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/ec2-key-pairs.html
  106. fingerprint, err := md5Fingerprint(publicKey)
  107. if err != nil {
  108. return "", err
  109. }
  110. keyparis, err := self.GetKeypairs(fingerprint, "")
  111. if err != nil {
  112. return "", errors.Wrapf(err, "GetKeypairs")
  113. }
  114. if len(keyparis) > 0 {
  115. return keyparis[0].KeyName, nil
  116. }
  117. return "", errors.Wrapf(cloudprovider.ErrNotFound, "%s", publicKey)
  118. }
  119. func (self *SRegion) importAwsKeypair(publicKey string) (string, error) {
  120. prefix, e := goutils.RandomAlphabetic(6)
  121. if e != nil {
  122. return "", fmt.Errorf("publicKey error %s", e)
  123. }
  124. name := prefix + strconv.FormatInt(time.Now().Unix(), 10)
  125. if k, e := self.ImportKeypair(name, publicKey); e != nil {
  126. return "", fmt.Errorf("keypair import error %s", e)
  127. } else {
  128. return k.KeyName, nil
  129. }
  130. }
  131. func (self *SRegion) SyncKeypair(publicKey string) (string, error) {
  132. name, e := self.lookUpAwsKeypair(publicKey)
  133. if e == nil {
  134. return name, nil
  135. }
  136. return self.importAwsKeypair(publicKey)
  137. }