console_init.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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(cfg); 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. // write out a profile.d file for the PATH settings.
  114. pathLines := []string{}
  115. for _, k := range []string{"PATH", "path"} {
  116. if v, ok := cfg.Rancher.Environment[k]; ok {
  117. for _, p := range strings.Split(v, ",") {
  118. pathLines = append(pathLines, fmt.Sprintf("export PATH=$PATH:%s", strings.TrimSpace(p)))
  119. }
  120. }
  121. }
  122. if len(pathLines) > 0 {
  123. pathString := strings.Join(pathLines, "\n")
  124. pathString = fmt.Sprintf("#!/bin/sh\n%s\n", pathString)
  125. if err := ioutil.WriteFile("/etc/profile.d/path.sh", []byte(pathString), 0755); err != nil {
  126. log.Error(err)
  127. }
  128. }
  129. 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`)
  130. if err := cmd.Run(); err != nil {
  131. log.Error(err)
  132. }
  133. cloudinitexecute.ApplyConsole(cfg)
  134. if err := util.RunScript(config.CloudConfigScriptFile); err != nil {
  135. log.Error(err)
  136. }
  137. if err := util.RunScript(startScript); err != nil {
  138. log.Error(err)
  139. }
  140. if err := ioutil.WriteFile(consoleDone, []byte(CurrentConsole()), 0644); err != nil {
  141. log.Error(err)
  142. }
  143. if err := util.RunScript("/etc/rc.local"); err != nil {
  144. log.Error(err)
  145. }
  146. os.Setenv("TERM", "linux")
  147. respawnBinPath, err := exec.LookPath("respawn")
  148. if err != nil {
  149. return err
  150. }
  151. return syscall.Exec(respawnBinPath, []string{"respawn", "-f", "/etc/respawn.conf"}, os.Environ())
  152. }
  153. func generateRespawnConf(cmdline, user string, sshd, recovery bool) string {
  154. var respawnConf bytes.Buffer
  155. autologinBin := "/usr/bin/autologin"
  156. if recovery {
  157. autologinBin = "/usr/bin/recovery"
  158. }
  159. for i := 1; i < 7; i++ {
  160. tty := fmt.Sprintf("tty%d", i)
  161. respawnConf.WriteString(gettyCmd)
  162. if strings.Contains(cmdline, fmt.Sprintf("rancher.autologin=%s", tty)) {
  163. respawnConf.WriteString(fmt.Sprintf(" -n -l %s -o %s:tty%d", autologinBin, user, i))
  164. }
  165. respawnConf.WriteString(fmt.Sprintf(" --noclear %s linux\n", tty))
  166. }
  167. for _, tty := range []string{"ttyS0", "ttyS1", "ttyS2", "ttyS3", "ttyAMA0"} {
  168. if !strings.Contains(cmdline, fmt.Sprintf("console=%s", tty)) {
  169. continue
  170. }
  171. respawnConf.WriteString(gettyCmd)
  172. if strings.Contains(cmdline, fmt.Sprintf("rancher.autologin=%s", tty)) {
  173. respawnConf.WriteString(fmt.Sprintf(" -n -l %s -o %s:%s", autologinBin, user, tty))
  174. }
  175. respawnConf.WriteString(fmt.Sprintf(" %s\n", tty))
  176. }
  177. if sshd {
  178. respawnConf.WriteString("/usr/sbin/sshd -D")
  179. }
  180. return respawnConf.String()
  181. }
  182. func writeRespawn(user string, sshd, recovery bool) error {
  183. cmdline, err := ioutil.ReadFile("/proc/cmdline")
  184. if err != nil {
  185. return err
  186. }
  187. respawn := generateRespawnConf(string(cmdline), user, sshd, recovery)
  188. files, err := ioutil.ReadDir("/etc/respawn.conf.d")
  189. if err == nil {
  190. for _, f := range files {
  191. p := path.Join("/etc/respawn.conf.d", f.Name())
  192. content, err := ioutil.ReadFile(p)
  193. if err != nil {
  194. log.Errorf("Failed to read %s: %v", p, err)
  195. continue
  196. }
  197. respawn += fmt.Sprintf("\n%s", string(content))
  198. }
  199. } else if !os.IsNotExist(err) {
  200. log.Error(err)
  201. }
  202. return ioutil.WriteFile("/etc/respawn.conf", []byte(respawn), 0644)
  203. }
  204. func modifySshdConfig(cfg *config.CloudConfig) error {
  205. sshdConfig, err := ioutil.ReadFile("/etc/ssh/sshd_config")
  206. if err != nil {
  207. return err
  208. }
  209. sshdConfigString := string(sshdConfig)
  210. modifiedLines := []string{
  211. "UseDNS no",
  212. "PermitRootLogin no",
  213. "ServerKeyBits 2048",
  214. "AllowGroups docker",
  215. }
  216. if cfg.Rancher.SSH.Port > 0 && cfg.Rancher.SSH.Port < 65355 {
  217. modifiedLines = append(modifiedLines, fmt.Sprintf("Port %d", cfg.Rancher.SSH.Port))
  218. }
  219. if cfg.Rancher.SSH.ListenAddress != "" {
  220. modifiedLines = append(modifiedLines, fmt.Sprintf("ListenAddress %s", cfg.Rancher.SSH.ListenAddress))
  221. }
  222. for _, item := range modifiedLines {
  223. match, err := regexp.Match("^"+item, sshdConfig)
  224. if err != nil {
  225. return err
  226. }
  227. if !match {
  228. sshdConfigString += fmt.Sprintf("%s\n", item)
  229. }
  230. }
  231. return ioutil.WriteFile("/etc/ssh/sshd_config", []byte(sshdConfigString), 0644)
  232. }
  233. func setupSSH(cfg *config.CloudConfig) error {
  234. for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
  235. outputFile := fmt.Sprintf("/etc/ssh/ssh_host_%s_key", keyType)
  236. outputFilePub := fmt.Sprintf("/etc/ssh/ssh_host_%s_key.pub", keyType)
  237. if _, err := os.Stat(outputFile); err == nil {
  238. continue
  239. }
  240. saved, savedExists := cfg.Rancher.SSH.Keys[keyType]
  241. pub, pubExists := cfg.Rancher.SSH.Keys[keyType+"-pub"]
  242. if savedExists && pubExists {
  243. // TODO check permissions
  244. if err := util.WriteFileAtomic(outputFile, []byte(saved), 0600); err != nil {
  245. return err
  246. }
  247. if err := util.WriteFileAtomic(outputFilePub, []byte(pub), 0600); err != nil {
  248. return err
  249. }
  250. continue
  251. }
  252. cmd := exec.Command("bash", "-c", fmt.Sprintf("ssh-keygen -f %s -N '' -t %s", outputFile, keyType))
  253. if err := cmd.Run(); err != nil {
  254. return err
  255. }
  256. savedBytes, err := ioutil.ReadFile(outputFile)
  257. if err != nil {
  258. return err
  259. }
  260. pubBytes, err := ioutil.ReadFile(outputFilePub)
  261. if err != nil {
  262. return err
  263. }
  264. config.Set(fmt.Sprintf("rancher.ssh.keys.%s", keyType), string(savedBytes))
  265. config.Set(fmt.Sprintf("rancher.ssh.keys.%s-pub", keyType), string(pubBytes))
  266. }
  267. return os.MkdirAll("/var/run/sshd", 0644)
  268. }