base.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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 fsdriver
  15. import (
  16. "fmt"
  17. "path"
  18. "path/filepath"
  19. "strings"
  20. "syscall"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/object"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/types"
  26. deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
  27. "yunion.io/x/onecloud/pkg/httperrors"
  28. "yunion.io/x/onecloud/pkg/mcclient"
  29. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  30. "yunion.io/x/onecloud/pkg/util/procutils"
  31. )
  32. type sGuestRootFsDriver struct {
  33. object.SObject
  34. rootFs IDiskPartition
  35. }
  36. func newGuestRootFsDriver(rootFs IDiskPartition) *sGuestRootFsDriver {
  37. return &sGuestRootFsDriver{
  38. rootFs: rootFs,
  39. }
  40. }
  41. func (d *sGuestRootFsDriver) GetIRootFsDriver() IRootFsDriver {
  42. return d.GetVirtualObject().(IRootFsDriver)
  43. }
  44. func (d *sGuestRootFsDriver) DeployFiles(deploys []*deployapi.DeployContent) error {
  45. caseInsensitive := d.IsFsCaseInsensitive()
  46. for _, deploy := range deploys {
  47. var modAppend = false
  48. if deploy.Action == "append" {
  49. modAppend = true
  50. }
  51. if len(deploy.Path) == 0 {
  52. return fmt.Errorf("Deploy file missing param path")
  53. }
  54. dirname := filepath.Dir(deploy.Path)
  55. if !d.GetPartition().Exists(dirname, caseInsensitive) {
  56. modeRWXOwner := syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP | syscall.S_IROTH | syscall.S_IXOTH
  57. err := d.GetPartition().Mkdir(dirname, modeRWXOwner, caseInsensitive)
  58. if err != nil {
  59. log.Errorf("Mkdir %s fail %s", dirname, err)
  60. return errors.Wrap(err, "Mkdir")
  61. }
  62. err = d.GetPartition().Chmod(dirname, uint32(modeRWXOwner), caseInsensitive)
  63. if err != nil {
  64. log.Errorf("Chmod %s fail %s", dirname, err)
  65. return errors.Wrap(err, "Chmod")
  66. }
  67. }
  68. if len(deploy.Content) > 0 {
  69. err := d.GetPartition().FilePutContents(deploy.Path, deploy.Content, modAppend, caseInsensitive)
  70. if err != nil {
  71. log.Errorln(err)
  72. return err
  73. }
  74. }
  75. }
  76. return nil
  77. }
  78. func (d *sGuestRootFsDriver) DeployTelegraf(string) (bool, error) {
  79. return false, nil
  80. }
  81. func (d *sGuestRootFsDriver) GetPartition() IDiskPartition {
  82. return d.rootFs
  83. }
  84. func (d *sGuestRootFsDriver) RootExcludeSignatures() []string {
  85. return []string{}
  86. }
  87. func (d *sGuestRootFsDriver) IsFsCaseInsensitive() bool {
  88. return false
  89. }
  90. func (d *sGuestRootFsDriver) DeployYunionroot(rootfs IDiskPartition, pubkeys *deployapi.SSHKeys, isInit, enableCloudInit bool) error {
  91. return nil
  92. }
  93. func (d *sGuestRootFsDriver) DeployUdevSubsystemScripts(rootfs IDiskPartition) error {
  94. return nil
  95. }
  96. func (d *sGuestRootFsDriver) DeployStandbyNetworkingScripts(part IDiskPartition, nics, nicsStandby []*types.SServerNic) error {
  97. return nil
  98. }
  99. func (d *sGuestRootFsDriver) DeployFstabScripts(_ IDiskPartition, _ []*deployapi.Disk) error {
  100. return nil
  101. }
  102. func (d *sGuestRootFsDriver) EnableSerialConsole(rootfs IDiskPartition, sysInfo *jsonutils.JSONDict) error {
  103. return nil
  104. }
  105. func (d *sGuestRootFsDriver) DisableSerialConsole(rootfs IDiskPartition) error {
  106. return nil
  107. }
  108. func (d *sGuestRootFsDriver) CommitChanges(rootfs IDiskPartition) error {
  109. return nil
  110. }
  111. func (d *sGuestRootFsDriver) DetectIsUEFISupport(IDiskPartition) bool {
  112. return false
  113. }
  114. func (l *sGuestRootFsDriver) IsCloudinitInstall() bool {
  115. return false
  116. }
  117. func (l *sGuestRootFsDriver) IsResizeFsPartitionSupport() bool {
  118. return true
  119. }
  120. func (r *sGuestRootFsDriver) CleanNetworkScripts(rootFs IDiskPartition) error {
  121. return nil
  122. }
  123. func (r *sGuestRootFsDriver) AllowAdminLogin() bool {
  124. return true
  125. }
  126. func (m *sGuestRootFsDriver) DeployQgaBlackList(part IDiskPartition) error {
  127. return nil
  128. }
  129. func (r *sGuestRootFsDriver) DeployQgaService(part IDiskPartition) error {
  130. return nil
  131. }
  132. const (
  133. modeAuthorizedKeysRWX = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IXUSR
  134. modeAuthorizedKeysRW = syscall.S_IRUSR | syscall.S_IWUSR
  135. )
  136. func deployAuthorizedKeys(rootFs IDiskPartition, authFile string, uid, gid int, pubkeys *deployapi.SSHKeys, replace bool, admin bool) error {
  137. var oldKeys = ""
  138. if !replace {
  139. bOldKeys, _ := rootFs.FileGetContents(authFile, false)
  140. oldKeys = string(bOldKeys)
  141. }
  142. newKeys := MergeAuthorizedKeys(oldKeys, pubkeys, admin)
  143. if err := rootFs.FilePutContents(authFile, newKeys, false, false); err != nil {
  144. return fmt.Errorf("Put keys to %s: %v", authFile, err)
  145. }
  146. if err := rootFs.Chown(authFile, uid, gid, false); err != nil {
  147. return fmt.Errorf("Chown %s to uid: %d, gid: %d: %v", authFile, uid, gid, err)
  148. }
  149. if err := rootFs.Chmod(authFile, uint32(modeAuthorizedKeysRW), false); err != nil {
  150. return fmt.Errorf("Chmod %s to %d error: %v", authFile, uint32(modeAuthorizedKeysRW), err)
  151. }
  152. return nil
  153. }
  154. func DeployAuthorizedKeys(rootFs IDiskPartition, usrDir string, pubkeys *deployapi.SSHKeys, replace bool, admin bool) error {
  155. usrStat := rootFs.Stat(usrDir, false)
  156. if usrStat != nil {
  157. sshDir := path.Join(usrDir, ".ssh")
  158. authFile := path.Join(sshDir, "authorized_keys")
  159. fStat, _ := usrStat.Sys().(*syscall.Stat_t)
  160. uid := int(fStat.Uid)
  161. gid := int(fStat.Gid)
  162. if !rootFs.Exists(sshDir, false) {
  163. err := rootFs.Mkdir(sshDir, modeAuthorizedKeysRWX, false)
  164. if err != nil {
  165. log.Errorln(err)
  166. return err
  167. }
  168. err = rootFs.Chown(sshDir, uid, gid, false)
  169. if err != nil {
  170. log.Errorln(err)
  171. return err
  172. }
  173. }
  174. return deployAuthorizedKeys(rootFs, authFile, uid, gid, pubkeys, replace, admin)
  175. }
  176. return nil
  177. }
  178. const sshKeySignature = "@yunioncloudpods"
  179. func MergeAuthorizedKeys(oldKeys string, pubkeys *deployapi.SSHKeys, isAdmin bool) string {
  180. var allkeys = make(map[string]string)
  181. if len(oldKeys) > 0 {
  182. for _, line := range strings.Split(oldKeys, "\n") {
  183. line = strings.TrimSpace(line)
  184. dat := strings.Split(line, " ")
  185. if len(dat) > 1 {
  186. if len(dat) > 2 && dat[2] == sshKeySignature {
  187. // skip ssh keys with signature
  188. continue
  189. }
  190. if _, ok := allkeys[dat[1]]; !ok {
  191. allkeys[dat[1]] = line
  192. }
  193. }
  194. }
  195. }
  196. if len(pubkeys.DeletePublicKey) > 0 {
  197. dat := strings.Split(pubkeys.DeletePublicKey, " ")
  198. if len(dat) > 1 {
  199. if _, ok := allkeys[dat[1]]; ok {
  200. delete(allkeys, dat[1])
  201. }
  202. }
  203. }
  204. var candiateKeys []string
  205. if isAdmin {
  206. candiateKeys = []string{pubkeys.AdminPublicKey, pubkeys.ProjectPublicKey}
  207. } else {
  208. candiateKeys = []string{pubkeys.PublicKey}
  209. }
  210. for _, k := range candiateKeys {
  211. if len(k) > 0 {
  212. k = strings.TrimSpace(k)
  213. dat := strings.Split(k, " ")
  214. if len(dat) > 1 {
  215. if _, ok := allkeys[dat[1]]; !ok {
  216. allkeys[dat[1]] = strings.Join([]string{dat[0], dat[1], sshKeySignature}, " ")
  217. }
  218. }
  219. }
  220. }
  221. var keys = make([]string, 0)
  222. for _, val := range allkeys {
  223. keys = append(keys, val)
  224. }
  225. return strings.Join(keys, "\n") + "\n"
  226. }
  227. func DeployAdminAuthorizedKeys(s *mcclient.ClientSession) error {
  228. sshDir := path.Join("/root", ".ssh")
  229. output, err := procutils.NewRemoteCommandAsFarAsPossible("mkdir", "-p", sshDir).Output()
  230. if err != nil {
  231. return errors.Wrapf(err, "mkdir .ssh %s", output)
  232. }
  233. query := jsonutils.NewDict()
  234. query.Set("admin", jsonutils.JSONTrue)
  235. ret, err := modules.Sshkeypairs.List(s, query)
  236. if err != nil {
  237. return errors.Wrap(err, "modules.Sshkeypairs.List")
  238. }
  239. if len(ret.Data) == 0 {
  240. return errors.Wrap(httperrors.ErrNotFound, "Not found admin sshkey")
  241. }
  242. keys := ret.Data[0]
  243. adminPublicKey, _ := keys.GetString("public_key")
  244. pubKeys := &deployapi.SSHKeys{AdminPublicKey: adminPublicKey}
  245. var oldKeys string
  246. authFile := path.Join(sshDir, "authorized_keys")
  247. if procutils.NewRemoteCommandAsFarAsPossible("test", "-f", authFile).Run() == nil {
  248. output, err := procutils.NewRemoteCommandAsFarAsPossible("cat", authFile).Output()
  249. if err != nil {
  250. return errors.Wrapf(err, "cat: %s", output)
  251. }
  252. oldKeys = string(output)
  253. }
  254. newKeys := MergeAuthorizedKeys(oldKeys, pubKeys, true)
  255. if output, err := procutils.NewRemoteCommandAsFarAsPossible(
  256. "sh", "-c", fmt.Sprintf("echo '%s' > %s", newKeys, authFile)).Output(); err != nil {
  257. return errors.Wrapf(err, "write public keys: %s", output)
  258. }
  259. if output, err := procutils.NewRemoteCommandAsFarAsPossible(
  260. "chmod", "0644", authFile).Output(); err != nil {
  261. return errors.Wrapf(err, "chmod failed %s", output)
  262. }
  263. return nil
  264. }