console_init.go 9.0 KB

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