scratch.go 15 KB

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