console_init.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. package control
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "os/exec"
  8. "path"
  9. "regexp"
  10. "strings"
  11. "syscall"
  12. "github.com/codegangsta/cli"
  13. "github.com/rancher/os/cmd/cloudinitexecute"
  14. "github.com/rancher/os/config"
  15. "github.com/rancher/os/config/cmdline"
  16. "github.com/rancher/os/log"
  17. "github.com/rancher/os/util"
  18. )
  19. const (
  20. consoleDone = "/run/console-done"
  21. dockerHome = "/home/docker"
  22. gettyCmd = "/sbin/agetty"
  23. rancherHome = "/home/rancher"
  24. startScript = "/opt/rancher/bin/start.sh"
  25. )
  26. type symlink struct {
  27. oldname, newname string
  28. }
  29. func ConsoleInitMain() {
  30. if err := consoleInitFunc(); err != nil {
  31. log.Fatal(err)
  32. }
  33. }
  34. func consoleInitAction(c *cli.Context) error {
  35. return consoleInitFunc()
  36. }
  37. func createHomeDir(homedir string, uid, gid int) {
  38. if _, err := os.Stat(homedir); os.IsNotExist(err) {
  39. if err := os.MkdirAll(homedir, 0755); err != nil {
  40. log.Error(err)
  41. }
  42. if err := os.Chown(homedir, uid, gid); err != nil {
  43. log.Error(err)
  44. }
  45. }
  46. }
  47. func consoleInitFunc() error {
  48. cfg := config.LoadConfig()
  49. // Now that we're booted, stop writing debug messages to the console
  50. cmd := exec.Command("sudo", "dmesg", "--console-off")
  51. if err := cmd.Run(); err != nil {
  52. log.Error(err)
  53. }
  54. createHomeDir(rancherHome, 1100, 1100)
  55. createHomeDir(dockerHome, 1101, 1101)
  56. password := cmdline.GetCmdline("rancher.password")
  57. if password != "" {
  58. cmd := exec.Command("chpasswd")
  59. cmd.Stdin = strings.NewReader(fmt.Sprint("rancher:", password))
  60. if err := cmd.Run(); err != nil {
  61. log.Error(err)
  62. }
  63. cmd = exec.Command("bash", "-c", `sed -E -i 's/(rancher:.*:).*(:.*:.*:.*:.*:.*:.*)$/\1\2/' /etc/shadow`)
  64. if err := cmd.Run(); err != nil {
  65. log.Error(err)
  66. }
  67. }
  68. if err := setupSSH(cfg); err != nil {
  69. log.Error(err)
  70. }
  71. if err := writeRespawn("rancher", cfg.Rancher.SSH.Daemon, false); err != nil {
  72. log.Error(err)
  73. }
  74. if err := modifySshdConfig(); err != nil {
  75. log.Error(err)
  76. }
  77. for _, link := range []symlink{
  78. {"/var/lib/rancher/engine/docker", "/usr/bin/docker"},
  79. {"/var/lib/rancher/engine/docker-init", "/usr/bin/docker-init"},
  80. {"/var/lib/rancher/engine/docker-containerd", "/usr/bin/docker-containerd"},
  81. {"/var/lib/rancher/engine/docker-containerd-ctr", "/usr/bin/docker-containerd-ctr"},
  82. {"/var/lib/rancher/engine/docker-containerd-shim", "/usr/bin/docker-containerd-shim"},
  83. {"/var/lib/rancher/engine/dockerd", "/usr/bin/dockerd"},
  84. {"/var/lib/rancher/engine/docker-proxy", "/usr/bin/docker-proxy"},
  85. {"/var/lib/rancher/engine/docker-runc", "/usr/bin/docker-runc"},
  86. {"/usr/share/ros/os-release", "/usr/lib/os-release"},
  87. {"/usr/share/ros/os-release", "/etc/os-release"},
  88. } {
  89. syscall.Unlink(link.newname)
  90. if err := os.Symlink(link.oldname, link.newname); err != nil {
  91. log.Error(err)
  92. }
  93. }
  94. // font backslashes need to be escaped for when issue is output! (but not the others..)
  95. if err := ioutil.WriteFile("/etc/issue", []byte(config.Banner), 0644); err != nil {
  96. log.Error(err)
  97. }
  98. // write out a profile.d file for the proxy settings.
  99. // maybe write these on the host and bindmount into everywhere?
  100. proxyLines := []string{}
  101. for _, k := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
  102. if v, ok := cfg.Rancher.Environment[k]; ok {
  103. proxyLines = append(proxyLines, fmt.Sprintf("export %s=%s", k, v))
  104. }
  105. }
  106. if len(proxyLines) > 0 {
  107. proxyString := strings.Join(proxyLines, "\n")
  108. proxyString = fmt.Sprintf("#!/bin/sh\n%s\n", proxyString)
  109. if err := ioutil.WriteFile("/etc/profile.d/proxy.sh", []byte(proxyString), 0755); err != nil {
  110. log.Error(err)
  111. }
  112. }
  113. cmd = exec.Command("bash", "-c", `echo $(/sbin/ifconfig | grep -B1 "inet addr" |awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' |awk -F: '{ print $1 ": " $3}') >> /etc/issue`)
  114. if err := cmd.Run(); err != nil {
  115. log.Error(err)
  116. }
  117. cloudinitexecute.ApplyConsole(cfg)
  118. if err := util.RunScript(config.CloudConfigScriptFile); err != nil {
  119. log.Error(err)
  120. }
  121. if err := util.RunScript(startScript); err != nil {
  122. log.Error(err)
  123. }
  124. if err := ioutil.WriteFile(consoleDone, []byte(CurrentConsole()), 0644); err != nil {
  125. log.Error(err)
  126. }
  127. if err := util.RunScript("/etc/rc.local"); err != nil {
  128. log.Error(err)
  129. }
  130. os.Setenv("TERM", "linux")
  131. respawnBinPath, err := exec.LookPath("respawn")
  132. if err != nil {
  133. return err
  134. }
  135. return syscall.Exec(respawnBinPath, []string{"respawn", "-f", "/etc/respawn.conf"}, os.Environ())
  136. }
  137. func generateRespawnConf(cmdline, user string, sshd, recovery bool) string {
  138. var respawnConf bytes.Buffer
  139. autologinBin := "/usr/bin/autologin"
  140. if recovery {
  141. autologinBin = "/usr/bin/recovery"
  142. }
  143. for i := 1; i < 7; i++ {
  144. tty := fmt.Sprintf("tty%d", i)
  145. respawnConf.WriteString(gettyCmd)
  146. if strings.Contains(cmdline, fmt.Sprintf("rancher.autologin=%s", tty)) {
  147. respawnConf.WriteString(fmt.Sprintf(" -n -l %s -o %s:tty%d", autologinBin, user, i))
  148. }
  149. respawnConf.WriteString(fmt.Sprintf(" --noclear %s linux\n", tty))
  150. }
  151. for _, tty := range []string{"ttyS0", "ttyS1", "ttyS2", "ttyS3", "ttyAMA0"} {
  152. if !strings.Contains(cmdline, fmt.Sprintf("console=%s", tty)) {
  153. continue
  154. }
  155. respawnConf.WriteString(gettyCmd)
  156. if strings.Contains(cmdline, fmt.Sprintf("rancher.autologin=%s", tty)) {
  157. respawnConf.WriteString(fmt.Sprintf(" -n -l %s -o %s:%s", autologinBin, user, tty))
  158. }
  159. respawnConf.WriteString(fmt.Sprintf(" %s\n", tty))
  160. }
  161. if sshd {
  162. respawnConf.WriteString("/usr/sbin/sshd -D")
  163. }
  164. return respawnConf.String()
  165. }
  166. func writeRespawn(user string, sshd, recovery bool) error {
  167. cmdline, err := ioutil.ReadFile("/proc/cmdline")
  168. if err != nil {
  169. return err
  170. }
  171. respawn := generateRespawnConf(string(cmdline), user, sshd, recovery)
  172. files, err := ioutil.ReadDir("/etc/respawn.conf.d")
  173. if err == nil {
  174. for _, f := range files {
  175. p := path.Join("/etc/respawn.conf.d", f.Name())
  176. content, err := ioutil.ReadFile(p)
  177. if err != nil {
  178. log.Errorf("Failed to read %s: %v", p, err)
  179. continue
  180. }
  181. respawn += fmt.Sprintf("\n%s", string(content))
  182. }
  183. } else if !os.IsNotExist(err) {
  184. log.Error(err)
  185. }
  186. return ioutil.WriteFile("/etc/respawn.conf", []byte(respawn), 0644)
  187. }
  188. func modifySshdConfig() error {
  189. sshdConfig, err := ioutil.ReadFile("/etc/ssh/sshd_config")
  190. if err != nil {
  191. return err
  192. }
  193. sshdConfigString := string(sshdConfig)
  194. for _, item := range []string{
  195. "UseDNS no",
  196. "PermitRootLogin no",
  197. "ServerKeyBits 2048",
  198. "AllowGroups docker",
  199. } {
  200. match, err := regexp.Match("^"+item, sshdConfig)
  201. if err != nil {
  202. return err
  203. }
  204. if !match {
  205. sshdConfigString += fmt.Sprintf("%s\n", item)
  206. }
  207. }
  208. return ioutil.WriteFile("/etc/ssh/sshd_config", []byte(sshdConfigString), 0644)
  209. }
  210. func setupSSH(cfg *config.CloudConfig) error {
  211. for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
  212. outputFile := fmt.Sprintf("/etc/ssh/ssh_host_%s_key", keyType)
  213. outputFilePub := fmt.Sprintf("/etc/ssh/ssh_host_%s_key.pub", keyType)
  214. if _, err := os.Stat(outputFile); err == nil {
  215. continue
  216. }
  217. saved, savedExists := cfg.Rancher.SSH.Keys[keyType]
  218. pub, pubExists := cfg.Rancher.SSH.Keys[keyType+"-pub"]
  219. if savedExists && pubExists {
  220. // TODO check permissions
  221. if err := util.WriteFileAtomic(outputFile, []byte(saved), 0600); err != nil {
  222. return err
  223. }
  224. if err := util.WriteFileAtomic(outputFilePub, []byte(pub), 0600); err != nil {
  225. return err
  226. }
  227. continue
  228. }
  229. cmd := exec.Command("bash", "-c", fmt.Sprintf("ssh-keygen -f %s -N '' -t %s", outputFile, keyType))
  230. if err := cmd.Run(); err != nil {
  231. return err
  232. }
  233. savedBytes, err := ioutil.ReadFile(outputFile)
  234. if err != nil {
  235. return err
  236. }
  237. pubBytes, err := ioutil.ReadFile(outputFilePub)
  238. if err != nil {
  239. return err
  240. }
  241. config.Set(fmt.Sprintf("rancher.ssh.keys.%s", keyType), string(savedBytes))
  242. config.Set(fmt.Sprintf("rancher.ssh.keys.%s-pub", keyType), string(pubBytes))
  243. }
  244. return os.MkdirAll("/var/run/sshd", 0644)
  245. }