scratch.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. package dockerlaunch
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "os/exec"
  9. "path"
  10. "strconv"
  11. "strings"
  12. "syscall"
  13. log "github.com/Sirupsen/logrus"
  14. "github.com/docker/libnetwork/resolvconf"
  15. "github.com/rancher/docker-from-scratch/selinux"
  16. "github.com/rancher/docker-from-scratch/util"
  17. "github.com/rancher/netconf"
  18. )
  19. const (
  20. defaultPrefix = "/usr"
  21. iptables = "/sbin/iptables"
  22. modprobe = "/sbin/modprobe"
  23. systemdRoot = "/sys/fs/cgroup/systemd/user.slice"
  24. distSuffix = ".dist"
  25. )
  26. var (
  27. mounts = [][]string{
  28. {"devtmpfs", "/dev", "devtmpfs", ""},
  29. {"none", "/dev/pts", "devpts", ""},
  30. {"shm", "/dev/shm", "tmpfs", "rw,nosuid,nodev,noexec,relatime,size=65536k"},
  31. {"mqueue", "/dev/mqueue", "mqueue", "rw,nosuid,nodev,noexec,relatime"},
  32. {"none", "/proc", "proc", ""},
  33. {"none", "/run", "tmpfs", ""},
  34. {"none", "/sys", "sysfs", ""},
  35. {"none", "/sys/fs/cgroup", "tmpfs", ""},
  36. }
  37. optionalMounts = [][]string{
  38. {"none", "/sys/fs/selinux", "selinuxfs", ""},
  39. }
  40. systemdMounts = [][]string{
  41. {"systemd", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd"},
  42. }
  43. )
  44. type Config struct {
  45. Fork bool
  46. PidOne bool
  47. CommandName string
  48. DnsConfig netconf.DnsConfig
  49. BridgeName string
  50. BridgeAddress string
  51. BridgeMtu int
  52. CgroupHierarchy map[string]string
  53. LogFile string
  54. NoLog bool
  55. EmulateSystemd bool
  56. NoFiles uint64
  57. Environment []string
  58. GraphDirectory string
  59. }
  60. func createMounts(mounts ...[]string) error {
  61. for _, mount := range mounts {
  62. log.Debugf("Mounting %s %s %s %s", mount[0], mount[1], mount[2], mount[3])
  63. err := util.Mount(mount[0], mount[1], mount[2], mount[3])
  64. if err != nil {
  65. return err
  66. }
  67. }
  68. return nil
  69. }
  70. func createOptionalMounts(mounts ...[]string) {
  71. for _, mount := range mounts {
  72. log.Debugf("Mounting %s %s %s %s", mount[0], mount[1], mount[2], mount[3])
  73. err := util.Mount(mount[0], mount[1], mount[2], mount[3])
  74. if err != nil {
  75. log.Debugf("Unable to mount %s %s %s %s: %s", mount[0], mount[1], mount[2], mount[3], err)
  76. }
  77. }
  78. }
  79. func createDirs(dirs ...string) error {
  80. for _, dir := range dirs {
  81. if _, err := os.Stat(dir); os.IsNotExist(err) {
  82. log.Debugf("Creating %s", dir)
  83. err = os.MkdirAll(dir, 0755)
  84. if err != nil {
  85. return err
  86. }
  87. }
  88. }
  89. return nil
  90. }
  91. func mountCgroups(hierarchyConfig map[string]string) error {
  92. f, err := os.Open("/proc/cgroups")
  93. if err != nil {
  94. return err
  95. }
  96. defer f.Close()
  97. scanner := bufio.NewScanner(f)
  98. hierarchies := make(map[string][]string)
  99. for scanner.Scan() {
  100. text := scanner.Text()
  101. log.Debugf("/proc/cgroups: %s", text)
  102. fields := strings.SplitN(text, "\t", 3)
  103. cgroup := fields[0]
  104. if cgroup == "" || cgroup[0] == '#' || len(fields) < 3 {
  105. continue
  106. }
  107. hierarchy := hierarchyConfig[cgroup]
  108. if hierarchy == "" {
  109. hierarchy = fields[1]
  110. }
  111. if hierarchy == "0" {
  112. hierarchy = cgroup
  113. }
  114. hierarchies[hierarchy] = append(hierarchies[hierarchy], cgroup)
  115. }
  116. for _, hierarchy := range hierarchies {
  117. if err := mountCgroup(strings.Join(hierarchy, ",")); err != nil {
  118. return err
  119. }
  120. }
  121. if err = scanner.Err(); err != nil {
  122. return err
  123. }
  124. log.Debug("Done mouting cgroupfs")
  125. return nil
  126. }
  127. func CreateSymlinks(pathSets [][]string) error {
  128. for _, paths := range pathSets {
  129. if err := CreateSymlink(paths[0], paths[1]); err != nil {
  130. return err
  131. }
  132. }
  133. return nil
  134. }
  135. func CreateSymlink(src, dest string) error {
  136. if _, err := os.Lstat(dest); os.IsNotExist(err) {
  137. log.Debugf("Symlinking %s => %s", dest, src)
  138. if err = os.Symlink(src, dest); err != nil {
  139. return err
  140. }
  141. }
  142. return nil
  143. }
  144. func mountCgroup(cgroup string) error {
  145. if err := createDirs("/sys/fs/cgroup/" + cgroup); err != nil {
  146. return err
  147. }
  148. if err := createMounts([][]string{{"none", "/sys/fs/cgroup/" + cgroup, "cgroup", cgroup}}...); err != nil {
  149. return err
  150. }
  151. parts := strings.Split(cgroup, ",")
  152. if len(parts) > 1 {
  153. for _, part := range parts {
  154. if err := CreateSymlink("/sys/fs/cgroup/"+cgroup, "/sys/fs/cgroup/"+part); err != nil {
  155. return err
  156. }
  157. }
  158. }
  159. return nil
  160. }
  161. func execDocker(config *Config, docker, cmd string, args []string) (*exec.Cmd, error) {
  162. if len(args) > 0 && args[0] == "docker" {
  163. args = args[1:]
  164. }
  165. log.Debugf("Launching Docker %s %s %v", docker, cmd, args)
  166. env := os.Environ()
  167. if len(config.Environment) != 0 {
  168. env = append(env, config.Environment...)
  169. }
  170. if config.Fork {
  171. cmd := exec.Command(docker, args...)
  172. if !config.NoLog {
  173. cmd.Stdout = os.Stdout
  174. cmd.Stderr = os.Stderr
  175. }
  176. cmd.Env = env
  177. err := cmd.Start()
  178. if err != nil {
  179. return cmd, err
  180. }
  181. if config.PidOne {
  182. PidOne()
  183. }
  184. return cmd, err
  185. } else {
  186. err := syscall.Exec(expand(docker), append([]string{cmd}, args...), env)
  187. return nil, err
  188. }
  189. }
  190. func copyDefault(folder, name string) error {
  191. defaultFile := path.Join(defaultPrefix, folder, name)
  192. if err := CopyFile(defaultFile, folder, name); err != nil {
  193. return err
  194. }
  195. return nil
  196. }
  197. func copyDefaultFolder(folder string) error {
  198. defaultFolder := path.Join(defaultPrefix, folder)
  199. files, _ := ioutil.ReadDir(defaultFolder)
  200. for _, file := range files {
  201. if file.IsDir() {
  202. continue
  203. }
  204. if err := copyDefault(folder, file.Name()); err != nil {
  205. return err
  206. }
  207. }
  208. return nil
  209. }
  210. func defaultFiles(files ...string) error {
  211. for _, file := range files {
  212. dir := path.Dir(file)
  213. name := path.Base(file)
  214. if err := copyDefault(dir, name); err != nil {
  215. return err
  216. }
  217. }
  218. return nil
  219. }
  220. func defaultFolders(folders ...string) error {
  221. for _, folder := range folders {
  222. copyDefaultFolder(folder)
  223. }
  224. return nil
  225. }
  226. func CopyFile(src, folder, name string) error {
  227. if _, err := os.Stat(src); os.IsNotExist(err) {
  228. return nil
  229. }
  230. dst := path.Join(folder, name)
  231. if _, err := os.Stat(dst); err == nil {
  232. return nil
  233. }
  234. if err := createDirs(folder); err != nil {
  235. return err
  236. }
  237. srcFile, err := os.Open(src)
  238. if err != nil {
  239. return err
  240. }
  241. defer srcFile.Close()
  242. dstFile, err := os.Create(dst)
  243. if err != nil {
  244. return err
  245. }
  246. defer dstFile.Close()
  247. _, err = io.Copy(dstFile, srcFile)
  248. return err
  249. }
  250. func tryCreateFile(name, content string) error {
  251. if _, err := os.Stat(name); err == nil {
  252. return nil
  253. }
  254. if err := createDirs(path.Dir(name)); err != nil {
  255. return err
  256. }
  257. return ioutil.WriteFile(name, []byte(content), 0644)
  258. }
  259. func createPasswd() error {
  260. return tryCreateFile("/etc/passwd", "root:x:0:0:root:/root:/bin/sh\n")
  261. }
  262. func createGroup() error {
  263. return tryCreateFile("/etc/group", "root:x:0:\n")
  264. }
  265. func setupNetworking(config *Config) error {
  266. if config == nil {
  267. return nil
  268. }
  269. hostname, err := os.Hostname()
  270. if err != nil {
  271. return err
  272. }
  273. tryCreateFile("/etc/hosts", `127.0.0.1 localhost
  274. ::1 localhost ip6-localhost ip6-loopback
  275. fe00::0 ip6-localnet
  276. ff00::0 ip6-mcastprefix
  277. ff02::1 ip6-allnodes
  278. ff02::2 ip6-allrouters
  279. 127.0.1.1 `+hostname)
  280. if len(config.DnsConfig.Nameservers) != 0 {
  281. if _, err := resolvconf.Build("/etc/resolv.conf", config.DnsConfig.Nameservers, config.DnsConfig.Search, nil); err != nil {
  282. return err
  283. }
  284. }
  285. if config.BridgeName != "" {
  286. log.Debugf("Creating bridge %s (%s)", config.BridgeName, config.BridgeAddress)
  287. if err := netconf.ApplyNetworkConfigs(&netconf.NetworkConfig{
  288. Interfaces: map[string]netconf.InterfaceConfig{
  289. config.BridgeName: {
  290. Address: config.BridgeAddress,
  291. MTU: config.BridgeMtu,
  292. Bridge: "true",
  293. },
  294. },
  295. }); err != nil {
  296. return err
  297. }
  298. }
  299. return nil
  300. }
  301. func ParseConfig(config *Config, args ...string) []string {
  302. for i, arg := range args {
  303. if strings.HasPrefix(arg, "--bip") {
  304. config.BridgeAddress = util.GetValue(i, args)
  305. } else if strings.HasPrefix(arg, "--fixed-cidr") {
  306. config.BridgeAddress = util.GetValue(i, args)
  307. } else if strings.HasPrefix(arg, "-b") || strings.HasPrefix(arg, "--bridge") {
  308. config.BridgeName = util.GetValue(i, args)
  309. } else if strings.HasPrefix(arg, "--mtu") {
  310. mtu, err := strconv.Atoi(util.GetValue(i, args))
  311. if err != nil {
  312. config.BridgeMtu = mtu
  313. }
  314. } else if strings.HasPrefix(arg, "-g") || strings.HasPrefix(arg, "--graph") {
  315. config.GraphDirectory = util.GetValue(i, args)
  316. }
  317. }
  318. if config.BridgeName != "" && config.BridgeAddress != "" {
  319. newArgs := []string{}
  320. skip := false
  321. for _, arg := range args {
  322. if skip {
  323. skip = false
  324. continue
  325. }
  326. if arg == "--bip" {
  327. skip = true
  328. continue
  329. } else if strings.HasPrefix(arg, "--bip=") {
  330. continue
  331. }
  332. newArgs = append(newArgs, arg)
  333. }
  334. args = newArgs
  335. }
  336. return args
  337. }
  338. func PrepareFs(config *Config) error {
  339. if err := createMounts(mounts...); err != nil {
  340. return err
  341. }
  342. createOptionalMounts(optionalMounts...)
  343. if err := mountCgroups(config.CgroupHierarchy); err != nil {
  344. return err
  345. }
  346. if err := createLayout(config); err != nil {
  347. return err
  348. }
  349. if err := firstPrepare(); err != nil {
  350. return err
  351. }
  352. return nil
  353. }
  354. func touchSocket(path string) error {
  355. if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
  356. return err
  357. }
  358. return ioutil.WriteFile(path, []byte{}, 0700)
  359. }
  360. func touchSockets(args ...string) error {
  361. touched := false
  362. for i, arg := range args {
  363. if strings.HasPrefix(arg, "-H") {
  364. val := util.GetValue(i, args)
  365. if strings.HasPrefix(val, "unix://") {
  366. val = val[len("unix://"):]
  367. log.Debugf("Creating temp file at %s", val)
  368. if err := touchSocket(val); err != nil {
  369. return err
  370. }
  371. touched = true
  372. }
  373. }
  374. }
  375. if !touched {
  376. return touchSocket("/var/run/docker.sock")
  377. }
  378. return nil
  379. }
  380. func createLayout(config *Config) error {
  381. if err := createDirs("/tmp", "/root/.ssh", "/var", "/usr/lib"); err != nil {
  382. return err
  383. }
  384. graphDirectory := config.GraphDirectory
  385. if config.GraphDirectory == "" {
  386. graphDirectory = "/var/lib/docker"
  387. }
  388. if err := createDirs(graphDirectory); err != nil {
  389. return err
  390. }
  391. selinux.SetFileContext(graphDirectory, "system_u:object_r:var_lib_t:s0")
  392. return CreateSymlinks([][]string{
  393. {"usr/lib", "/lib"},
  394. {"usr/sbin", "/sbin"},
  395. {"../run", "/var/run"},
  396. })
  397. }
  398. func firstPrepare() error {
  399. os.Setenv("PATH", "/sbin:/usr/sbin:/usr/bin")
  400. if err := defaultFiles(
  401. "/etc/ssl/certs/ca-certificates.crt",
  402. "/etc/passwd",
  403. "/etc/group",
  404. ); err != nil {
  405. return err
  406. }
  407. if err := defaultFolders(
  408. "/etc/selinux",
  409. "/etc/selinux/ros",
  410. "/etc/selinux/ros/policy",
  411. "/etc/selinux/ros/contexts",
  412. ); err != nil {
  413. return err
  414. }
  415. if err := createPasswd(); err != nil {
  416. return err
  417. }
  418. if err := createGroup(); err != nil {
  419. return err
  420. }
  421. return nil
  422. }
  423. func secondPrepare(config *Config, docker string, args ...string) error {
  424. if err := setupNetworking(config); err != nil {
  425. return err
  426. }
  427. if err := touchSockets(args...); err != nil {
  428. return err
  429. }
  430. if err := setupLogging(config); err != nil {
  431. return err
  432. }
  433. for _, i := range []string{docker, iptables, modprobe} {
  434. if err := setupBin(config, i); err != nil {
  435. return err
  436. }
  437. }
  438. if err := setUlimit(config); err != nil {
  439. return err
  440. }
  441. if err := setupSystemd(config); err != nil {
  442. return err
  443. }
  444. return nil
  445. }
  446. func setupSystemd(config *Config) error {
  447. if !config.EmulateSystemd {
  448. return nil
  449. }
  450. cgroups, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", os.Getpid()))
  451. if err != nil {
  452. return err
  453. }
  454. if strings.Contains(string(cgroups), "name=systemd") {
  455. return nil
  456. }
  457. if err := createMounts(systemdMounts...); err != nil {
  458. return err
  459. }
  460. if err := os.Mkdir(systemdRoot, 0755); err != nil {
  461. return err
  462. }
  463. return ioutil.WriteFile(path.Join(systemdRoot, "cgroup.procs"), []byte(strconv.Itoa(os.Getpid())+"\n"), 0644)
  464. }
  465. func expand(bin string) string {
  466. expanded, err := exec.LookPath(bin)
  467. if err == nil {
  468. return expanded
  469. }
  470. return bin
  471. }
  472. func setupBin(config *Config, bin string) error {
  473. expanded, err := exec.LookPath(bin)
  474. if err == nil {
  475. return nil
  476. }
  477. expanded, err = exec.LookPath(bin + distSuffix)
  478. if err != nil {
  479. // Purposely not returning error
  480. return nil
  481. }
  482. return CreateSymlink(expanded, expanded[:len(expanded)-len(distSuffix)])
  483. }
  484. func setupLogging(config *Config) error {
  485. if config.LogFile == "" {
  486. return nil
  487. }
  488. if err := createDirs(path.Dir(config.LogFile)); err != nil {
  489. return err
  490. }
  491. output, err := os.OpenFile(config.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
  492. if err != nil {
  493. return err
  494. }
  495. syscall.Dup3(int(output.Fd()), int(os.Stdout.Fd()), 0)
  496. syscall.Dup3(int(output.Fd()), int(os.Stderr.Fd()), 0)
  497. return nil
  498. }
  499. func setUlimit(cfg *Config) error {
  500. var rLimit syscall.Rlimit
  501. if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
  502. return err
  503. }
  504. if cfg.NoFiles == 0 {
  505. rLimit.Max = 1000000
  506. } else {
  507. rLimit.Max = cfg.NoFiles
  508. }
  509. rLimit.Cur = rLimit.Max
  510. return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
  511. }
  512. func runOrExec(config *Config, docker string, args ...string) (*exec.Cmd, error) {
  513. if err := secondPrepare(config, docker, args...); err != nil {
  514. return nil, err
  515. }
  516. cmd := "docker"
  517. if config != nil && config.CommandName != "" {
  518. cmd = config.CommandName
  519. }
  520. return execDocker(config, docker, cmd, args)
  521. }
  522. func LaunchDocker(config *Config, docker string, args ...string) (*exec.Cmd, error) {
  523. if err := PrepareFs(config); err != nil {
  524. return nil, err
  525. }
  526. return runOrExec(config, docker, args...)
  527. }
  528. func Main() {
  529. if os.Getenv("DOCKER_LAUNCH_DEBUG") == "true" {
  530. log.SetLevel(log.DebugLevel)
  531. }
  532. if len(os.Args) < 2 {
  533. log.Fatalf("Usage Example: %s /usr/bin/docker -d -D", os.Args[0])
  534. }
  535. args := []string{}
  536. if len(os.Args) > 1 {
  537. args = os.Args[2:]
  538. }
  539. var config Config
  540. args = ParseConfig(&config, args...)
  541. if os.Getenv("DOCKER_LAUNCH_REAP") == "true" {
  542. config.Fork = true
  543. config.PidOne = true
  544. }
  545. log.Debugf("Launch config %#v", config)
  546. _, err := LaunchDocker(&config, os.Args[1], args...)
  547. if err != nil {
  548. log.Fatal(err)
  549. }
  550. }