func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error { userNS := false // user if c.HostConfig.UsernsMode.IsPrivate() { uidMap, gidMap := daemon.GetUIDGIDMaps() if uidMap != nil { userNS = true ns := specs.Namespace{Type: "user"} setNamespace(s, ns) s.Linux.UIDMappings = specMapping(uidMap) s.Linux.GIDMappings = specMapping(gidMap) } } // network if !c.Config.NetworkDisabled { ns := specs.Namespace{Type: "network"} parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2) if parts[0] == "container" { nc, err := daemon.getNetworkedContainer(c.ID, c.HostConfig.NetworkMode.ConnectedContainer()) if err != nil { return err } ns.Path = fmt.Sprintf("/proc/%d/ns/net", nc.State.GetPID()) if userNS { // to share a net namespace, they must also share a user namespace nsUser := specs.Namespace{Type: "user"} nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", nc.State.GetPID()) setNamespace(s, nsUser) } } else if c.HostConfig.NetworkMode.IsHost() { ns.Path = c.NetworkSettings.SandboxKey } setNamespace(s, ns) } // ipc if c.HostConfig.IpcMode.IsContainer() { ns := specs.Namespace{Type: "ipc"} ic, err := daemon.getIpcContainer(c) if err != nil { return err } ns.Path = fmt.Sprintf("/proc/%d/ns/ipc", ic.State.GetPID()) setNamespace(s, ns) if userNS { // to share an IPC namespace, they must also share a user namespace nsUser := specs.Namespace{Type: "user"} nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", ic.State.GetPID()) setNamespace(s, nsUser) } } else if c.HostConfig.IpcMode.IsHost() { oci.RemoveNamespace(s, specs.NamespaceType("ipc")) } else { ns := specs.Namespace{Type: "ipc"} setNamespace(s, ns) } // pid if c.HostConfig.PidMode.IsContainer() { ns := specs.Namespace{Type: "pid"} pc, err := daemon.getPidContainer(c) if err != nil { return err } ns.Path = fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID()) setNamespace(s, ns) if userNS { // to share a PID namespace, they must also share a user namespace nsUser := specs.Namespace{Type: "user"} nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", pc.State.GetPID()) setNamespace(s, nsUser) } } else if c.HostConfig.PidMode.IsHost() { oci.RemoveNamespace(s, specs.NamespaceType("pid")) } else { ns := specs.Namespace{Type: "pid"} setNamespace(s, ns) } // uts if c.HostConfig.UTSMode.IsHost() { oci.RemoveNamespace(s, specs.NamespaceType("uts")) s.Hostname = "" } return nil }
// InitSpec creates an OCI spec from the plugin's config. func (p *Plugin) InitSpec(s specs.Spec) (*specs.Spec, error) { s.Root = specs.Root{ Path: p.Rootfs, Readonly: false, // TODO: all plugins should be readonly? settable in config? } userMounts := make(map[string]struct{}, len(p.PluginObj.Settings.Mounts)) for _, m := range p.PluginObj.Settings.Mounts { userMounts[m.Destination] = struct{}{} } if err := os.MkdirAll(p.runtimeSourcePath, 0755); err != nil { return nil, err } mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{ Source: &p.runtimeSourcePath, Destination: defaultPluginRuntimeDestination, Type: "bind", Options: []string{"rbind", "rshared"}, }) if p.PluginObj.Config.Network.Type != "" { // TODO: if net == bridge, use libnetwork controller to create a new plugin-specific bridge, bind mount /etc/hosts and /etc/resolv.conf look at the docker code (allocateNetwork, initialize) if p.PluginObj.Config.Network.Type == "host" { oci.RemoveNamespace(&s, specs.NamespaceType("network")) } etcHosts := "/etc/hosts" resolvConf := "/etc/resolv.conf" mounts = append(mounts, types.PluginMount{ Source: &etcHosts, Destination: etcHosts, Type: "bind", Options: []string{"rbind", "ro"}, }, types.PluginMount{ Source: &resolvConf, Destination: resolvConf, Type: "bind", Options: []string{"rbind", "ro"}, }) } for _, mnt := range mounts { m := specs.Mount{ Destination: mnt.Destination, Type: mnt.Type, Options: mnt.Options, } if mnt.Source == nil { return nil, errors.New("mount source is not specified") } m.Source = *mnt.Source s.Mounts = append(s.Mounts, m) } for i, m := range s.Mounts { if strings.HasPrefix(m.Destination, "/dev/") { if _, ok := userMounts[m.Destination]; ok { s.Mounts = append(s.Mounts[:i], s.Mounts[i+1:]...) } } } if p.PluginObj.Config.PropagatedMount != "" { p.PropagatedMount = filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount) s.Linux.RootfsPropagation = "rshared" } if p.PluginObj.Config.Linux.DeviceCreation { rwm := "rwm" s.Linux.Resources.Devices = []specs.DeviceCgroup{{Allow: true, Access: &rwm}} } for _, dev := range p.PluginObj.Settings.Devices { path := *dev.Path d, dPermissions, err := oci.DevicesFromPath(path, path, "rwm") if err != nil { return nil, err } s.Linux.Devices = append(s.Linux.Devices, d...) s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, dPermissions...) } envs := make([]string, 1, len(p.PluginObj.Settings.Env)+1) envs[0] = "PATH=" + system.DefaultPathEnv envs = append(envs, p.PluginObj.Settings.Env...) args := append(p.PluginObj.Config.Entrypoint, p.PluginObj.Settings.Args...) cwd := p.PluginObj.Config.Workdir if len(cwd) == 0 { cwd = "/" } s.Process.Terminal = false s.Process.Args = args s.Process.Cwd = cwd s.Process.Env = envs s.Process.Capabilities = append(s.Process.Capabilities, p.PluginObj.Config.Linux.Capabilities...) return &s, nil }