package sysinit import ( "encoding/json" "flag" "fmt" "github.com/dotcloud/docker/pkg/netlink" "github.com/dotcloud/docker/utils" "github.com/syndtr/gocapability/capability" "io/ioutil" "log" "net" "os" "os/exec" "strconv" "strings" "syscall" ) type DockerInitArgs struct { user string gateway string ip string workDir string privileged bool env []string args []string mtu int } func setupHostname(args *DockerInitArgs) error { hostname := getEnv(args, "HOSTNAME") if hostname == "" { return nil } return setHostname(hostname) } // Setup networking func setupNetworking(args *DockerInitArgs) error { if args.ip != "" { // eth0 iface, err := net.InterfaceByName("eth0") if err != nil { return fmt.Errorf("Unable to set up networking: %v", err) } ip, ipNet, err := net.ParseCIDR(args.ip) if err != nil { return fmt.Errorf("Unable to set up networking: %v", err) } if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil { return fmt.Errorf("Unable to set up networking: %v", err) } if err := netlink.NetworkSetMTU(iface, args.mtu); err != nil { return fmt.Errorf("Unable to set MTU: %v", err) } if err := netlink.NetworkLinkUp(iface); err != nil { return fmt.Errorf("Unable to set up networking: %v", err) } // loopback iface, err = net.InterfaceByName("lo") if err != nil { return fmt.Errorf("Unable to set up networking: %v", err) } if err := netlink.NetworkLinkUp(iface); err != nil { return fmt.Errorf("Unable to set up networking: %v", err) } } if args.gateway != "" { gw := net.ParseIP(args.gateway) if gw == nil { return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.gateway) } if err := netlink.AddDefaultGw(gw); err != nil { return fmt.Errorf("Unable to set up networking: %v", err) } } return nil } // Setup working directory func setupWorkingDirectory(args *DockerInitArgs) error { if args.workDir == "" { return nil } if err := syscall.Chdir(args.workDir); err != nil { return fmt.Errorf("Unable to change dir to %v: %v", args.workDir, err) } return nil } // Takes care of dropping privileges to the desired user func changeUser(args *DockerInitArgs) error { if args.user == "" { return nil } userent, err := utils.UserLookup(args.user) if err != nil { return fmt.Errorf("Unable to find user %v: %v", args.user, err) } uid, err := strconv.Atoi(userent.Uid) if err != nil { return fmt.Errorf("Invalid uid: %v", userent.Uid) } gid, err := strconv.Atoi(userent.Gid) if err != nil { return fmt.Errorf("Invalid gid: %v", userent.Gid) } if err := syscall.Setgid(gid); err != nil { return fmt.Errorf("setgid failed: %v", err) } if err := syscall.Setuid(uid); err != nil { return fmt.Errorf("setuid failed: %v", err) } return nil } func setupCapabilities(args *DockerInitArgs) error { if args.privileged { return nil } drop := []capability.Cap{ capability.CAP_SETPCAP, capability.CAP_SYS_MODULE, capability.CAP_SYS_RAWIO, capability.CAP_SYS_PACCT, capability.CAP_SYS_ADMIN, capability.CAP_SYS_NICE, capability.CAP_SYS_RESOURCE, capability.CAP_SYS_TIME, capability.CAP_SYS_TTY_CONFIG, capability.CAP_MKNOD, capability.CAP_AUDIT_WRITE, capability.CAP_AUDIT_CONTROL, capability.CAP_MAC_OVERRIDE, capability.CAP_MAC_ADMIN, } c, err := capability.NewPid(os.Getpid()) if err != nil { return err } c.Unset(capability.CAPS|capability.BOUNDS, drop...) if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil { return err } return nil } // Clear environment pollution introduced by lxc-start func setupEnv(args *DockerInitArgs) { os.Clearenv() for _, kv := range args.env { parts := strings.SplitN(kv, "=", 2) if len(parts) == 1 { parts = append(parts, "") } os.Setenv(parts[0], parts[1]) } } func getEnv(args *DockerInitArgs, key string) string { for _, kv := range args.env { parts := strings.SplitN(kv, "=", 2) if parts[0] == key && len(parts) == 2 { return parts[1] } } return "" } func executeProgram(args *DockerInitArgs) error { setupEnv(args) if err := setupHostname(args); err != nil { return err } if err := setupNetworking(args); err != nil { return err } if err := setupCapabilities(args); err != nil { return err } if err := setupWorkingDirectory(args); err != nil { return err } if err := changeUser(args); err != nil { return err } path, err := exec.LookPath(args.args[0]) if err != nil { log.Printf("Unable to locate %v", args.args[0]) os.Exit(127) } if err := syscall.Exec(path, args.args, os.Environ()); err != nil { panic(err) } // Will never reach here return nil } // Sys Init code // This code is run INSIDE the container and is responsible for setting // up the environment before running the actual process func SysInit() { if len(os.Args) <= 1 { fmt.Println("You should not invoke dockerinit manually") os.Exit(1) } // Get cmdline arguments user := flag.String("u", "", "username or uid") gateway := flag.String("g", "", "gateway address") ip := flag.String("i", "", "ip address") workDir := flag.String("w", "", "workdir") privileged := flag.Bool("privileged", false, "privileged mode") mtu := flag.Int("mtu", 1500, "interface mtu") flag.Parse() // Get env var env []string content, err := ioutil.ReadFile("/.dockerenv") if err != nil { log.Fatalf("Unable to load environment variables: %v", err) } if err := json.Unmarshal(content, &env); err != nil { log.Fatalf("Unable to unmarshal environment variables: %v", err) } // Propagate the plugin-specific container env variable env = append(env, "container="+os.Getenv("container")) args := &DockerInitArgs{ user: *user, gateway: *gateway, ip: *ip, workDir: *workDir, privileged: *privileged, env: env, args: flag.Args(), mtu: *mtu, } if err := executeProgram(args); err != nil { log.Fatal(err) } }