sshkeypairs.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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 compute
  15. import (
  16. "fmt"
  17. "os"
  18. "path"
  19. "strings"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/onecloud/pkg/mcclient"
  23. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  24. "yunion.io/x/onecloud/pkg/util/procutils"
  25. )
  26. func init() {
  27. type SshkeypairQueryOptions struct {
  28. Project string `help:"get keypair for specific project"`
  29. Admin bool `help:"get admin keypair, sysadmin ONLY option"`
  30. }
  31. getSshKeypair := func(s *mcclient.ClientSession, args *SshkeypairQueryOptions) (string, string, error) {
  32. query := jsonutils.NewDict()
  33. if args.Admin {
  34. query.Add(jsonutils.JSONTrue, "admin")
  35. }
  36. var keys jsonutils.JSONObject
  37. if len(args.Project) == 0 {
  38. listResult, err := modules.Sshkeypairs.List(s, query)
  39. if err != nil {
  40. return "", "", err
  41. }
  42. keys = listResult.Data[0]
  43. } else {
  44. result, err := modules.Sshkeypairs.GetById(s, args.Project, query)
  45. if err != nil {
  46. return "", "", err
  47. }
  48. keys = result
  49. }
  50. privKey, _ := keys.GetString("private_key")
  51. pubKey, _ := keys.GetString("public_key")
  52. return privKey, pubKey, nil
  53. }
  54. R(&SshkeypairQueryOptions{}, "sshkeypair-show", "Get ssh keypairs", func(s *mcclient.ClientSession, args *SshkeypairQueryOptions) error {
  55. privKey, pubKey, err := getSshKeypair(s, args)
  56. if err != nil {
  57. return err
  58. }
  59. fmt.Print(privKey)
  60. fmt.Print(pubKey)
  61. return nil
  62. })
  63. type SshkeypairInjectOptions struct {
  64. SshkeypairQueryOptions
  65. TargetDir string `help:"Target directory to save cloud ssh keypair"`
  66. }
  67. R(&SshkeypairInjectOptions{}, "sshkeypair-inject", "Inject ssh keypairs to local path", func(s *mcclient.ClientSession, args *SshkeypairInjectOptions) error {
  68. _, pubKey, err := getSshKeypair(s, &args.SshkeypairQueryOptions)
  69. if err != nil {
  70. return err
  71. }
  72. targetDir := args.TargetDir
  73. if targetDir == "" {
  74. homeDir, err := os.UserHomeDir()
  75. if err != nil {
  76. return errors.Wrap(err, "get current user's home dir")
  77. }
  78. targetDir = homeDir
  79. }
  80. sshDir := path.Join(targetDir, ".ssh")
  81. // MkdirAll anyways
  82. os.MkdirAll(sshDir, 0700)
  83. authFile := path.Join(sshDir, "authorized_keys")
  84. var oldKeys string
  85. if procutils.NewCommand("test", "-f", authFile).Run() == nil {
  86. output, err := procutils.NewCommand("cat", authFile).Output()
  87. if err != nil {
  88. return errors.Wrapf(err, "cat: %s", output)
  89. }
  90. oldKeys = string(output)
  91. }
  92. var MergeAuthorizedKeys = func(oldKeys string, pubKey string) string {
  93. const sshKeySignature = "@yunioncloudpods"
  94. var allkeys = make(map[string]string)
  95. if len(oldKeys) > 0 {
  96. for _, line := range strings.Split(oldKeys, "\n") {
  97. line = strings.TrimSpace(line)
  98. dat := strings.Split(line, " ")
  99. if len(dat) > 1 {
  100. if len(dat) > 2 && dat[2] == sshKeySignature {
  101. // skip ssh keys with signature
  102. continue
  103. }
  104. if _, ok := allkeys[dat[1]]; !ok {
  105. allkeys[dat[1]] = line
  106. }
  107. }
  108. }
  109. }
  110. candiateKeys := []string{pubKey}
  111. for _, k := range candiateKeys {
  112. if len(k) > 0 {
  113. k = strings.TrimSpace(k)
  114. dat := strings.Split(k, " ")
  115. if len(dat) > 1 {
  116. if _, ok := allkeys[dat[1]]; !ok {
  117. allkeys[dat[1]] = strings.Join([]string{dat[0], dat[1], sshKeySignature}, " ")
  118. }
  119. }
  120. }
  121. }
  122. var keys = make([]string, 0)
  123. for _, val := range allkeys {
  124. keys = append(keys, val)
  125. }
  126. return strings.Join(keys, "\n") + "\n"
  127. }
  128. newKeys := MergeAuthorizedKeys(oldKeys, pubKey)
  129. if output, err := procutils.NewCommand(
  130. "sh", "-c", fmt.Sprintf("echo '%s' > %s", newKeys, authFile)).Output(); err != nil {
  131. return errors.Wrapf(err, "write public keys: %s", output)
  132. }
  133. if output, err := procutils.NewCommand(
  134. "chmod", "0644", authFile).Output(); err != nil {
  135. return errors.Wrapf(err, "chmod failed %s", output)
  136. }
  137. return nil
  138. })
  139. }