| 0 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,63 @@ |
| 0 |
+## libcontainer - reference implementation for containers |
|
| 1 |
+ |
|
| 2 |
+#### playground |
|
| 3 |
+ |
|
| 4 |
+ |
|
| 5 |
+Use the cli package to test out functionality |
|
| 6 |
+ |
|
| 7 |
+First setup a container configuration. You will need a root fs, better go the path to a |
|
| 8 |
+stopped docker container and use that. |
|
| 9 |
+ |
|
| 10 |
+ |
|
| 11 |
+```json |
|
| 12 |
+{
|
|
| 13 |
+ "id": "koye", |
|
| 14 |
+ "namespace_pid": 12265, |
|
| 15 |
+ "command": {
|
|
| 16 |
+ "args": [ |
|
| 17 |
+ "/bin/bash" |
|
| 18 |
+ ], |
|
| 19 |
+ "environment": [ |
|
| 20 |
+ "HOME=/", |
|
| 21 |
+ "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin", |
|
| 22 |
+ "container=docker", |
|
| 23 |
+ "TERM=xterm" |
|
| 24 |
+ ] |
|
| 25 |
+ }, |
|
| 26 |
+ "rootfs": "/root/development/gocode/src/github.com/docker/libcontainer/namespaces/ubuntu", |
|
| 27 |
+ "network": null, |
|
| 28 |
+ "user": "", |
|
| 29 |
+ "working_dir": "", |
|
| 30 |
+ "namespaces": [ |
|
| 31 |
+ "NEWNET", |
|
| 32 |
+ "NEWIPC", |
|
| 33 |
+ "NEWNS", |
|
| 34 |
+ "NEWPID", |
|
| 35 |
+ "NEWUTS" |
|
| 36 |
+ ], |
|
| 37 |
+ "capabilities": [ |
|
| 38 |
+ "SETPCAP", |
|
| 39 |
+ "SYS_MODULE", |
|
| 40 |
+ "SYS_RAWIO", |
|
| 41 |
+ "SYS_PACCT", |
|
| 42 |
+ "SYS_ADMIN", |
|
| 43 |
+ "SYS_NICE", |
|
| 44 |
+ "SYS_RESOURCE", |
|
| 45 |
+ "SYS_TIME", |
|
| 46 |
+ "SYS_TTY_CONFIG", |
|
| 47 |
+ "MKNOD", |
|
| 48 |
+ "AUDIT_WRITE", |
|
| 49 |
+ "AUDIT_CONTROL", |
|
| 50 |
+ "MAC_OVERRIDE", |
|
| 51 |
+ "MAC_ADMIN" |
|
| 52 |
+ ] |
|
| 53 |
+} |
|
| 54 |
+``` |
|
| 55 |
+ |
|
| 56 |
+After you have a json file and a rootfs path to use just run: |
|
| 57 |
+`./cli exec container.json` |
|
| 58 |
+ |
|
| 59 |
+ |
|
| 60 |
+If you want to attach to an existing namespace just use the same json |
|
| 61 |
+file with the container still running and do: |
|
| 62 |
+`./cli execin container.json` |
| 0 | 63 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,49 @@ |
| 0 |
+package capabilities |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 4 |
+ "github.com/syndtr/gocapability/capability" |
|
| 5 |
+ "os" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+var capMap = map[libcontainer.Capability]capability.Cap{
|
|
| 9 |
+ libcontainer.CAP_SETPCAP: capability.CAP_SETPCAP, |
|
| 10 |
+ libcontainer.CAP_SYS_MODULE: capability.CAP_SYS_MODULE, |
|
| 11 |
+ libcontainer.CAP_SYS_RAWIO: capability.CAP_SYS_RAWIO, |
|
| 12 |
+ libcontainer.CAP_SYS_PACCT: capability.CAP_SYS_PACCT, |
|
| 13 |
+ libcontainer.CAP_SYS_ADMIN: capability.CAP_SYS_ADMIN, |
|
| 14 |
+ libcontainer.CAP_SYS_NICE: capability.CAP_SYS_NICE, |
|
| 15 |
+ libcontainer.CAP_SYS_RESOURCE: capability.CAP_SYS_RESOURCE, |
|
| 16 |
+ libcontainer.CAP_SYS_TIME: capability.CAP_SYS_TIME, |
|
| 17 |
+ libcontainer.CAP_SYS_TTY_CONFIG: capability.CAP_SYS_TTY_CONFIG, |
|
| 18 |
+ libcontainer.CAP_MKNOD: capability.CAP_MKNOD, |
|
| 19 |
+ libcontainer.CAP_AUDIT_WRITE: capability.CAP_AUDIT_WRITE, |
|
| 20 |
+ libcontainer.CAP_AUDIT_CONTROL: capability.CAP_AUDIT_CONTROL, |
|
| 21 |
+ libcontainer.CAP_MAC_OVERRIDE: capability.CAP_MAC_OVERRIDE, |
|
| 22 |
+ libcontainer.CAP_MAC_ADMIN: capability.CAP_MAC_ADMIN, |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// DropCapabilities drops capabilities for the current process based |
|
| 26 |
+// on the container's configuration. |
|
| 27 |
+func DropCapabilities(container *libcontainer.Container) error {
|
|
| 28 |
+ if drop := getCapabilities(container); len(drop) > 0 {
|
|
| 29 |
+ c, err := capability.NewPid(os.Getpid()) |
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ return err |
|
| 32 |
+ } |
|
| 33 |
+ c.Unset(capability.CAPS|capability.BOUNDS, drop...) |
|
| 34 |
+ |
|
| 35 |
+ if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
|
|
| 36 |
+ return err |
|
| 37 |
+ } |
|
| 38 |
+ } |
|
| 39 |
+ return nil |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func getCapabilities(container *libcontainer.Container) []capability.Cap {
|
|
| 43 |
+ drop := []capability.Cap{}
|
|
| 44 |
+ for _, c := range container.Capabilities {
|
|
| 45 |
+ drop = append(drop, capMap[c]) |
|
| 46 |
+ } |
|
| 47 |
+ return drop |
|
| 48 |
+} |
| 0 | 49 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,171 @@ |
| 0 |
+package main |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "flag" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 7 |
+ "github.com/dotcloud/docker/pkg/libcontainer/namespaces" |
|
| 8 |
+ "github.com/dotcloud/docker/pkg/libcontainer/network" |
|
| 9 |
+ "github.com/dotcloud/docker/pkg/libcontainer/utils" |
|
| 10 |
+ "os" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+var ( |
|
| 14 |
+ displayPid bool |
|
| 15 |
+ newCommand string |
|
| 16 |
+ usrNet bool |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+func init() {
|
|
| 20 |
+ flag.BoolVar(&displayPid, "pid", false, "display the pid before waiting") |
|
| 21 |
+ flag.StringVar(&newCommand, "cmd", "/bin/bash", "command to run in the existing namespace") |
|
| 22 |
+ flag.BoolVar(&usrNet, "net", false, "user a net namespace") |
|
| 23 |
+ flag.Parse() |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func exec(container *libcontainer.Container) error {
|
|
| 27 |
+ var ( |
|
| 28 |
+ netFile *os.File |
|
| 29 |
+ err error |
|
| 30 |
+ ) |
|
| 31 |
+ container.NetNsFd = 0 |
|
| 32 |
+ |
|
| 33 |
+ if usrNet {
|
|
| 34 |
+ netFile, err = os.Open("/root/nsroot/test")
|
|
| 35 |
+ if err != nil {
|
|
| 36 |
+ return err |
|
| 37 |
+ } |
|
| 38 |
+ container.NetNsFd = netFile.Fd() |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ pid, err := namespaces.Exec(container) |
|
| 42 |
+ if err != nil {
|
|
| 43 |
+ return fmt.Errorf("error exec container %s", err)
|
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ if displayPid {
|
|
| 47 |
+ fmt.Println(pid) |
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ exitcode, err := utils.WaitOnPid(pid) |
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ return fmt.Errorf("error waiting on child %s", err)
|
|
| 53 |
+ } |
|
| 54 |
+ fmt.Println(exitcode) |
|
| 55 |
+ if usrNet {
|
|
| 56 |
+ netFile.Close() |
|
| 57 |
+ if err := network.DeleteNetworkNamespace("/root/nsroot/test"); err != nil {
|
|
| 58 |
+ return err |
|
| 59 |
+ } |
|
| 60 |
+ } |
|
| 61 |
+ os.Exit(exitcode) |
|
| 62 |
+ return nil |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+func execIn(container *libcontainer.Container) error {
|
|
| 66 |
+ f, err := os.Open("/root/nsroot/test")
|
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return err |
|
| 69 |
+ } |
|
| 70 |
+ container.NetNsFd = f.Fd() |
|
| 71 |
+ pid, err := namespaces.ExecIn(container, &libcontainer.Command{
|
|
| 72 |
+ Env: container.Command.Env, |
|
| 73 |
+ Args: []string{
|
|
| 74 |
+ newCommand, |
|
| 75 |
+ }, |
|
| 76 |
+ }) |
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ return fmt.Errorf("error exexin container %s", err)
|
|
| 79 |
+ } |
|
| 80 |
+ exitcode, err := utils.WaitOnPid(pid) |
|
| 81 |
+ if err != nil {
|
|
| 82 |
+ return fmt.Errorf("error waiting on child %s", err)
|
|
| 83 |
+ } |
|
| 84 |
+ os.Exit(exitcode) |
|
| 85 |
+ return nil |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+func createNet(config *libcontainer.Network) error {
|
|
| 89 |
+ root := "/root/nsroot" |
|
| 90 |
+ if err := network.SetupNamespaceMountDir(root); err != nil {
|
|
| 91 |
+ return err |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ nspath := root + "/test" |
|
| 95 |
+ if err := network.CreateNetworkNamespace(nspath); err != nil {
|
|
| 96 |
+ return nil |
|
| 97 |
+ } |
|
| 98 |
+ if err := network.CreateVethPair("veth0", config.TempVethName); err != nil {
|
|
| 99 |
+ return err |
|
| 100 |
+ } |
|
| 101 |
+ if err := network.SetInterfaceMaster("veth0", config.Bridge); err != nil {
|
|
| 102 |
+ return err |
|
| 103 |
+ } |
|
| 104 |
+ if err := network.InterfaceUp("veth0"); err != nil {
|
|
| 105 |
+ return err |
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ f, err := os.Open(nspath) |
|
| 109 |
+ if err != nil {
|
|
| 110 |
+ return err |
|
| 111 |
+ } |
|
| 112 |
+ defer f.Close() |
|
| 113 |
+ |
|
| 114 |
+ if err := network.SetInterfaceInNamespaceFd("veth1", int(f.Fd())); err != nil {
|
|
| 115 |
+ return err |
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ /* |
|
| 119 |
+ if err := network.SetupVethInsideNamespace(f.Fd(), config); err != nil {
|
|
| 120 |
+ return err |
|
| 121 |
+ } |
|
| 122 |
+ */ |
|
| 123 |
+ return nil |
|
| 124 |
+} |
|
| 125 |
+ |
|
| 126 |
+func printErr(err error) {
|
|
| 127 |
+ fmt.Fprintln(os.Stderr, err) |
|
| 128 |
+ os.Exit(1) |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func main() {
|
|
| 132 |
+ var ( |
|
| 133 |
+ err error |
|
| 134 |
+ cliCmd = flag.Arg(0) |
|
| 135 |
+ config = flag.Arg(1) |
|
| 136 |
+ ) |
|
| 137 |
+ f, err := os.Open(config) |
|
| 138 |
+ if err != nil {
|
|
| 139 |
+ printErr(err) |
|
| 140 |
+ } |
|
| 141 |
+ |
|
| 142 |
+ dec := json.NewDecoder(f) |
|
| 143 |
+ var container *libcontainer.Container |
|
| 144 |
+ |
|
| 145 |
+ if err := dec.Decode(&container); err != nil {
|
|
| 146 |
+ printErr(err) |
|
| 147 |
+ } |
|
| 148 |
+ f.Close() |
|
| 149 |
+ |
|
| 150 |
+ switch cliCmd {
|
|
| 151 |
+ case "exec": |
|
| 152 |
+ err = exec(container) |
|
| 153 |
+ case "execin": |
|
| 154 |
+ err = execIn(container) |
|
| 155 |
+ case "net": |
|
| 156 |
+ err = createNet(&libcontainer.Network{
|
|
| 157 |
+ TempVethName: "veth1", |
|
| 158 |
+ IP: "172.17.0.100/16", |
|
| 159 |
+ Gateway: "172.17.42.1", |
|
| 160 |
+ Mtu: 1500, |
|
| 161 |
+ Bridge: "docker0", |
|
| 162 |
+ }) |
|
| 163 |
+ default: |
|
| 164 |
+ err = fmt.Errorf("command not supported: %s", cliCmd)
|
|
| 165 |
+ } |
|
| 166 |
+ |
|
| 167 |
+ if err != nil {
|
|
| 168 |
+ printErr(err) |
|
| 169 |
+ } |
|
| 170 |
+} |
| 0 | 171 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+package libcontainer |
|
| 1 |
+ |
|
| 2 |
+type Container struct {
|
|
| 3 |
+ ID string `json:"id,omitempty"` |
|
| 4 |
+ NsPid int `json:"namespace_pid,omitempty"` |
|
| 5 |
+ Command *Command `json:"command,omitempty"` |
|
| 6 |
+ RootFs string `json:"rootfs,omitempty"` |
|
| 7 |
+ ReadonlyFs bool `json:"readonly_fs,omitempty"` |
|
| 8 |
+ NetNsFd uintptr `json:"network_namespace_fd,omitempty"` |
|
| 9 |
+ User string `json:"user,omitempty"` |
|
| 10 |
+ WorkingDir string `json:"working_dir,omitempty"` |
|
| 11 |
+ Namespaces Namespaces `json:"namespaces,omitempty"` |
|
| 12 |
+ Capabilities Capabilities `json:"capabilities,omitempty"` |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+type Command struct {
|
|
| 16 |
+ Args []string `json:"args,omitempty"` |
|
| 17 |
+ Env []string `json:"environment,omitempty"` |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+type Network struct {
|
|
| 21 |
+ TempVethName string `json:"temp_veth,omitempty"` |
|
| 22 |
+ IP string `json:"ip,omitempty"` |
|
| 23 |
+ Gateway string `json:"gateway,omitempty"` |
|
| 24 |
+ Bridge string `json:"bridge,omitempty"` |
|
| 25 |
+ Mtu int `json:"mtu,omitempty"` |
|
| 26 |
+} |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,38 @@ |
| 0 |
+{
|
|
| 1 |
+ "id": "koye", |
|
| 2 |
+ "namespace_pid": 3117, |
|
| 3 |
+ "command": {
|
|
| 4 |
+ "args": [ |
|
| 5 |
+ "/bin/bash" |
|
| 6 |
+ ], |
|
| 7 |
+ "environment": [ |
|
| 8 |
+ "HOME=/", |
|
| 9 |
+ "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin", |
|
| 10 |
+ "container=docker", |
|
| 11 |
+ "TERM=xterm" |
|
| 12 |
+ ] |
|
| 13 |
+ }, |
|
| 14 |
+ "rootfs": "/root/main/mycontainer", |
|
| 15 |
+ "namespaces": [ |
|
| 16 |
+ "NEWIPC", |
|
| 17 |
+ "NEWNS", |
|
| 18 |
+ "NEWPID", |
|
| 19 |
+ "NEWUTS" |
|
| 20 |
+ ], |
|
| 21 |
+ "capabilities": [ |
|
| 22 |
+ "SETPCAP", |
|
| 23 |
+ "SYS_MODULE", |
|
| 24 |
+ "SYS_RAWIO", |
|
| 25 |
+ "SYS_PACCT", |
|
| 26 |
+ "SYS_ADMIN", |
|
| 27 |
+ "SYS_NICE", |
|
| 28 |
+ "SYS_RESOURCE", |
|
| 29 |
+ "SYS_TIME", |
|
| 30 |
+ "SYS_TTY_CONFIG", |
|
| 31 |
+ "MKNOD", |
|
| 32 |
+ "AUDIT_WRITE", |
|
| 33 |
+ "AUDIT_CONTROL", |
|
| 34 |
+ "MAC_OVERRIDE", |
|
| 35 |
+ "MAC_ADMIN" |
|
| 36 |
+ ] |
|
| 37 |
+} |
| 0 | 9 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,164 @@ |
| 0 |
+package namespaces |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "os" |
|
| 5 |
+ "syscall" |
|
| 6 |
+ "unsafe" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+const ( |
|
| 10 |
+ TIOCGPTN = 0x80045430 |
|
| 11 |
+ TIOCSPTLCK = 0x40045431 |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+func chroot(dir string) error {
|
|
| 15 |
+ return syscall.Chroot(dir) |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func chdir(dir string) error {
|
|
| 19 |
+ return syscall.Chdir(dir) |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+func exec(cmd string, args []string, env []string) error {
|
|
| 23 |
+ return syscall.Exec(cmd, args, env) |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func fork() (int, error) {
|
|
| 27 |
+ syscall.ForkLock.Lock() |
|
| 28 |
+ pid, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) |
|
| 29 |
+ syscall.ForkLock.Unlock() |
|
| 30 |
+ if err != 0 {
|
|
| 31 |
+ return -1, err |
|
| 32 |
+ } |
|
| 33 |
+ return int(pid), nil |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func vfork() (int, error) {
|
|
| 37 |
+ syscall.ForkLock.Lock() |
|
| 38 |
+ pid, _, err := syscall.Syscall(syscall.SYS_VFORK, 0, 0, 0) |
|
| 39 |
+ syscall.ForkLock.Unlock() |
|
| 40 |
+ if err != 0 {
|
|
| 41 |
+ return -1, err |
|
| 42 |
+ } |
|
| 43 |
+ return int(pid), nil |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func mount(source, target, fstype string, flags uintptr, data string) error {
|
|
| 47 |
+ return syscall.Mount(source, target, fstype, flags, data) |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func unmount(target string, flags int) error {
|
|
| 51 |
+ return syscall.Unmount(target, flags) |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func pivotroot(newroot, putold string) error {
|
|
| 55 |
+ return syscall.PivotRoot(newroot, putold) |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func unshare(flags int) error {
|
|
| 59 |
+ return syscall.Unshare(flags) |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+func clone(flags uintptr) (int, error) {
|
|
| 63 |
+ syscall.ForkLock.Lock() |
|
| 64 |
+ pid, _, err := syscall.RawSyscall(syscall.SYS_CLONE, flags, 0, 0) |
|
| 65 |
+ syscall.ForkLock.Unlock() |
|
| 66 |
+ if err != 0 {
|
|
| 67 |
+ return -1, err |
|
| 68 |
+ } |
|
| 69 |
+ return int(pid), nil |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func setns(fd uintptr, flags uintptr) error {
|
|
| 73 |
+ _, _, err := syscall.RawSyscall(SYS_SETNS, fd, flags, 0) |
|
| 74 |
+ if err != 0 {
|
|
| 75 |
+ return err |
|
| 76 |
+ } |
|
| 77 |
+ return nil |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+func usetCloseOnExec(fd uintptr) error {
|
|
| 81 |
+ if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0); err != 0 {
|
|
| 82 |
+ return err |
|
| 83 |
+ } |
|
| 84 |
+ return nil |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+func setgroups(gids []int) error {
|
|
| 88 |
+ return syscall.Setgroups(gids) |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+func setresgid(rgid, egid, sgid int) error {
|
|
| 92 |
+ return syscall.Setresgid(rgid, egid, sgid) |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func setresuid(ruid, euid, suid int) error {
|
|
| 96 |
+ return syscall.Setresuid(ruid, euid, suid) |
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+func sethostname(name string) error {
|
|
| 100 |
+ return syscall.Sethostname([]byte(name)) |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func setsid() (int, error) {
|
|
| 104 |
+ return syscall.Setsid() |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+func ioctl(fd uintptr, flag, data uintptr) error {
|
|
| 108 |
+ if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
|
|
| 109 |
+ return err |
|
| 110 |
+ } |
|
| 111 |
+ return nil |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+func openpmtx() (*os.File, error) {
|
|
| 115 |
+ return os.OpenFile("/dev/ptmx", syscall.O_RDONLY|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+func unlockpt(f *os.File) error {
|
|
| 119 |
+ var u int |
|
| 120 |
+ return ioctl(f.Fd(), TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) |
|
| 121 |
+} |
|
| 122 |
+ |
|
| 123 |
+func ptsname(f *os.File) (string, error) {
|
|
| 124 |
+ var n int |
|
| 125 |
+ if err := ioctl(f.Fd(), TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
|
|
| 126 |
+ return "", err |
|
| 127 |
+ } |
|
| 128 |
+ return fmt.Sprintf("/dev/pts/%d", n), nil
|
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func closefd(fd uintptr) error {
|
|
| 132 |
+ return syscall.Close(int(fd)) |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+func dup2(fd1, fd2 uintptr) error {
|
|
| 136 |
+ return syscall.Dup2(int(fd1), int(fd2)) |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+func mknod(path string, mode uint32, dev int) error {
|
|
| 140 |
+ return syscall.Mknod(path, mode, dev) |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+func parentDeathSignal() error {
|
|
| 144 |
+ if _, _, err := syscall.RawSyscall6(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGKILL), 0, 0, 0, 0); err != 0 {
|
|
| 145 |
+ return err |
|
| 146 |
+ } |
|
| 147 |
+ return nil |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+func setctty() error {
|
|
| 151 |
+ if _, _, err := syscall.RawSyscall(syscall.SYS_IOCTL, 0, uintptr(syscall.TIOCSCTTY), 0); err != 0 {
|
|
| 152 |
+ return err |
|
| 153 |
+ } |
|
| 154 |
+ return nil |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+func mkfifo(name string, mode uint32) error {
|
|
| 158 |
+ return syscall.Mkfifo(name, mode) |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+func umask(mask int) int {
|
|
| 162 |
+ return syscall.Umask(mask) |
|
| 163 |
+} |
| 0 | 164 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,266 @@ |
| 0 |
+/* |
|
| 1 |
+ Higher level convience functions for setting up a container |
|
| 2 |
+*/ |
|
| 3 |
+ |
|
| 4 |
+package namespaces |
|
| 5 |
+ |
|
| 6 |
+import ( |
|
| 7 |
+ "errors" |
|
| 8 |
+ "fmt" |
|
| 9 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 10 |
+ "github.com/dotcloud/docker/pkg/libcontainer/capabilities" |
|
| 11 |
+ "github.com/dotcloud/docker/pkg/libcontainer/utils" |
|
| 12 |
+ "io" |
|
| 13 |
+ "log" |
|
| 14 |
+ "os" |
|
| 15 |
+ "path/filepath" |
|
| 16 |
+ "syscall" |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+var ( |
|
| 20 |
+ ErrExistingNetworkNamespace = errors.New("specified both CLONE_NEWNET and an existing network namespace")
|
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+// Exec will spawn new namespaces with the specified Container configuration |
|
| 24 |
+// in the RootFs path and return the pid of the new containerized process. |
|
| 25 |
+// |
|
| 26 |
+// If an existing network namespace is specified the container |
|
| 27 |
+// will join that namespace. If an existing network namespace is not specified but CLONE_NEWNET is, |
|
| 28 |
+// the container will be spawned with a new network namespace with no configuration. Omiting an |
|
| 29 |
+// existing network namespace and the CLONE_NEWNET option in the container configuration will allow |
|
| 30 |
+// the container to the the host's networking options and configuration. |
|
| 31 |
+func Exec(container *libcontainer.Container) (pid int, err error) {
|
|
| 32 |
+ // a user cannot pass CLONE_NEWNET and an existing net namespace fd to join |
|
| 33 |
+ if container.NetNsFd > 0 && container.Namespaces.Contains(libcontainer.CLONE_NEWNET) {
|
|
| 34 |
+ return -1, ErrExistingNetworkNamespace |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ rootfs, err := resolveRootfs(container) |
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ return -1, err |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ master, console, err := createMasterAndConsole() |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ return -1, err |
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ logger, err := os.OpenFile("/root/logs", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
|
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ return -1, err |
|
| 50 |
+ } |
|
| 51 |
+ log.SetOutput(logger) |
|
| 52 |
+ |
|
| 53 |
+ // we need CLONE_VFORK so we can wait on the child |
|
| 54 |
+ flag := getNamespaceFlags(container.Namespaces) | CLONE_VFORK |
|
| 55 |
+ |
|
| 56 |
+ if pid, err = clone(uintptr(flag | SIGCHLD)); err != nil {
|
|
| 57 |
+ return -1, fmt.Errorf("error cloning process: %s", err)
|
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ if pid == 0 {
|
|
| 61 |
+ // welcome to your new namespace ;) |
|
| 62 |
+ // |
|
| 63 |
+ // any errors encoutered inside the namespace we should write |
|
| 64 |
+ // out to a log or a pipe to our parent and exit(1) |
|
| 65 |
+ // because writing to stderr will not work after we close |
|
| 66 |
+ if err := closeMasterAndStd(master); err != nil {
|
|
| 67 |
+ writeError("close master and std %s", err)
|
|
| 68 |
+ } |
|
| 69 |
+ slave, err := openTerminal(console, syscall.O_RDWR) |
|
| 70 |
+ if err != nil {
|
|
| 71 |
+ writeError("open terminal %s", err)
|
|
| 72 |
+ } |
|
| 73 |
+ if err := dupSlave(slave); err != nil {
|
|
| 74 |
+ writeError("dup2 slave %s", err)
|
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ if container.NetNsFd > 0 {
|
|
| 78 |
+ if err := JoinExistingNamespace(container.NetNsFd, libcontainer.CLONE_NEWNET); err != nil {
|
|
| 79 |
+ writeError("join existing net namespace %s", err)
|
|
| 80 |
+ } |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ if _, err := setsid(); err != nil {
|
|
| 84 |
+ writeError("setsid %s", err)
|
|
| 85 |
+ } |
|
| 86 |
+ if err := setctty(); err != nil {
|
|
| 87 |
+ writeError("setctty %s", err)
|
|
| 88 |
+ } |
|
| 89 |
+ if err := parentDeathSignal(); err != nil {
|
|
| 90 |
+ writeError("parent deth signal %s", err)
|
|
| 91 |
+ } |
|
| 92 |
+ if err := SetupNewMountNamespace(rootfs, console, container.ReadonlyFs); err != nil {
|
|
| 93 |
+ writeError("setup mount namespace %s", err)
|
|
| 94 |
+ } |
|
| 95 |
+ if err := sethostname(container.ID); err != nil {
|
|
| 96 |
+ writeError("sethostname %s", err)
|
|
| 97 |
+ } |
|
| 98 |
+ if err := capabilities.DropCapabilities(container); err != nil {
|
|
| 99 |
+ writeError("drop capabilities %s", err)
|
|
| 100 |
+ } |
|
| 101 |
+ if err := setupUser(container); err != nil {
|
|
| 102 |
+ writeError("setup user %s", err)
|
|
| 103 |
+ } |
|
| 104 |
+ if container.WorkingDir != "" {
|
|
| 105 |
+ if err := chdir(container.WorkingDir); err != nil {
|
|
| 106 |
+ writeError("chdir to %s %s", container.WorkingDir, err)
|
|
| 107 |
+ } |
|
| 108 |
+ } |
|
| 109 |
+ if err := exec(container.Command.Args[0], container.Command.Args[0:], container.Command.Env); err != nil {
|
|
| 110 |
+ writeError("exec %s", err)
|
|
| 111 |
+ } |
|
| 112 |
+ panic("unreachable")
|
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ go func() {
|
|
| 116 |
+ if _, err := io.Copy(os.Stdout, master); err != nil {
|
|
| 117 |
+ log.Println(err) |
|
| 118 |
+ } |
|
| 119 |
+ }() |
|
| 120 |
+ go func() {
|
|
| 121 |
+ if _, err := io.Copy(master, os.Stdin); err != nil {
|
|
| 122 |
+ log.Println(err) |
|
| 123 |
+ } |
|
| 124 |
+ }() |
|
| 125 |
+ return pid, nil |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+// ExecIn will spawn a new command inside an existing container's namespaces. The existing container's |
|
| 129 |
+// pid and namespace configuration is needed along with the specific capabilities that should |
|
| 130 |
+// be dropped once inside the namespace. |
|
| 131 |
+func ExecIn(container *libcontainer.Container, cmd *libcontainer.Command) (int, error) {
|
|
| 132 |
+ if container.NsPid <= 0 {
|
|
| 133 |
+ return -1, libcontainer.ErrInvalidPid |
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ fds, err := getNsFds(container) |
|
| 137 |
+ if err != nil {
|
|
| 138 |
+ return -1, err |
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ if container.NetNsFd > 0 {
|
|
| 142 |
+ fds = append(fds, container.NetNsFd) |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ pid, err := fork() |
|
| 146 |
+ if err != nil {
|
|
| 147 |
+ for _, fd := range fds {
|
|
| 148 |
+ syscall.Close(int(fd)) |
|
| 149 |
+ } |
|
| 150 |
+ return -1, err |
|
| 151 |
+ } |
|
| 152 |
+ |
|
| 153 |
+ if pid == 0 {
|
|
| 154 |
+ for _, fd := range fds {
|
|
| 155 |
+ if fd > 0 {
|
|
| 156 |
+ if err := JoinExistingNamespace(fd, ""); err != nil {
|
|
| 157 |
+ for _, fd := range fds {
|
|
| 158 |
+ syscall.Close(int(fd)) |
|
| 159 |
+ } |
|
| 160 |
+ writeError("join existing namespace for %d %s", fd, err)
|
|
| 161 |
+ } |
|
| 162 |
+ } |
|
| 163 |
+ syscall.Close(int(fd)) |
|
| 164 |
+ } |
|
| 165 |
+ |
|
| 166 |
+ if container.Namespaces.Contains(libcontainer.CLONE_NEWNS) && |
|
| 167 |
+ container.Namespaces.Contains(libcontainer.CLONE_NEWPID) {
|
|
| 168 |
+ // important: |
|
| 169 |
+ // |
|
| 170 |
+ // we need to fork and unshare so that re can remount proc and sys within |
|
| 171 |
+ // the namespace so the CLONE_NEWPID namespace will take effect |
|
| 172 |
+ // if we don't fork we would end up unmounting proc and sys for the entire |
|
| 173 |
+ // namespace |
|
| 174 |
+ child, err := fork() |
|
| 175 |
+ if err != nil {
|
|
| 176 |
+ writeError("fork child %s", err)
|
|
| 177 |
+ } |
|
| 178 |
+ |
|
| 179 |
+ if child == 0 {
|
|
| 180 |
+ if err := unshare(CLONE_NEWNS); err != nil {
|
|
| 181 |
+ writeError("unshare newns %s", err)
|
|
| 182 |
+ } |
|
| 183 |
+ if err := remountProc(); err != nil {
|
|
| 184 |
+ writeError("remount proc %s", err)
|
|
| 185 |
+ } |
|
| 186 |
+ if err := remountSys(); err != nil {
|
|
| 187 |
+ writeError("remount sys %s", err)
|
|
| 188 |
+ } |
|
| 189 |
+ if err := capabilities.DropCapabilities(container); err != nil {
|
|
| 190 |
+ writeError("drop caps %s", err)
|
|
| 191 |
+ } |
|
| 192 |
+ if err := exec(cmd.Args[0], cmd.Args[0:], cmd.Env); err != nil {
|
|
| 193 |
+ writeError("exec %s", err)
|
|
| 194 |
+ } |
|
| 195 |
+ panic("unreachable")
|
|
| 196 |
+ } |
|
| 197 |
+ exit, err := utils.WaitOnPid(child) |
|
| 198 |
+ if err != nil {
|
|
| 199 |
+ writeError("wait on child %s", err)
|
|
| 200 |
+ } |
|
| 201 |
+ os.Exit(exit) |
|
| 202 |
+ } |
|
| 203 |
+ if err := exec(cmd.Args[0], cmd.Args[0:], cmd.Env); err != nil {
|
|
| 204 |
+ writeError("exec %s", err)
|
|
| 205 |
+ } |
|
| 206 |
+ panic("unreachable")
|
|
| 207 |
+ } |
|
| 208 |
+ return pid, err |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+func resolveRootfs(container *libcontainer.Container) (string, error) {
|
|
| 212 |
+ rootfs, err := filepath.Abs(container.RootFs) |
|
| 213 |
+ if err != nil {
|
|
| 214 |
+ return "", err |
|
| 215 |
+ } |
|
| 216 |
+ return filepath.EvalSymlinks(rootfs) |
|
| 217 |
+} |
|
| 218 |
+ |
|
| 219 |
+func createMasterAndConsole() (*os.File, string, error) {
|
|
| 220 |
+ master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
|
| 221 |
+ if err != nil {
|
|
| 222 |
+ return nil, "", err |
|
| 223 |
+ } |
|
| 224 |
+ |
|
| 225 |
+ console, err := ptsname(master) |
|
| 226 |
+ if err != nil {
|
|
| 227 |
+ return nil, "", err |
|
| 228 |
+ } |
|
| 229 |
+ |
|
| 230 |
+ if err := unlockpt(master); err != nil {
|
|
| 231 |
+ return nil, "", err |
|
| 232 |
+ } |
|
| 233 |
+ return master, console, nil |
|
| 234 |
+} |
|
| 235 |
+ |
|
| 236 |
+func closeMasterAndStd(master *os.File) error {
|
|
| 237 |
+ closefd(master.Fd()) |
|
| 238 |
+ closefd(0) |
|
| 239 |
+ closefd(1) |
|
| 240 |
+ closefd(2) |
|
| 241 |
+ |
|
| 242 |
+ return nil |
|
| 243 |
+} |
|
| 244 |
+ |
|
| 245 |
+func dupSlave(slave *os.File) error {
|
|
| 246 |
+ // we close Stdin,etc so our pty slave should have fd 0 |
|
| 247 |
+ if slave.Fd() != 0 {
|
|
| 248 |
+ return fmt.Errorf("slave fd not 0 %d", slave.Fd())
|
|
| 249 |
+ } |
|
| 250 |
+ if err := dup2(slave.Fd(), 1); err != nil {
|
|
| 251 |
+ return err |
|
| 252 |
+ } |
|
| 253 |
+ if err := dup2(slave.Fd(), 2); err != nil {
|
|
| 254 |
+ return err |
|
| 255 |
+ } |
|
| 256 |
+ return nil |
|
| 257 |
+} |
|
| 258 |
+ |
|
| 259 |
+func openTerminal(name string, flag int) (*os.File, error) {
|
|
| 260 |
+ r, e := syscall.Open(name, flag, 0) |
|
| 261 |
+ if e != nil {
|
|
| 262 |
+ return nil, &os.PathError{"open", name, e}
|
|
| 263 |
+ } |
|
| 264 |
+ return os.NewFile(uintptr(r), name), nil |
|
| 265 |
+} |
| 0 | 7 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,207 @@ |
| 0 |
+package namespaces |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "log" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path/filepath" |
|
| 7 |
+ "syscall" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+var ( |
|
| 11 |
+ // default mount point options |
|
| 12 |
+ defaults = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+func SetupNewMountNamespace(rootfs, console string, readonly bool) error {
|
|
| 16 |
+ if err := mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil {
|
|
| 17 |
+ return fmt.Errorf("mounting / as slave %s", err)
|
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ if err := mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
|
|
| 21 |
+ return fmt.Errorf("mouting %s as bind %s", rootfs, err)
|
|
| 22 |
+ } |
|
| 23 |
+ |
|
| 24 |
+ if readonly {
|
|
| 25 |
+ if err := mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil {
|
|
| 26 |
+ return fmt.Errorf("mounting %s as readonly %s", rootfs, err)
|
|
| 27 |
+ } |
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ if err := mountSystem(rootfs); err != nil {
|
|
| 31 |
+ return fmt.Errorf("mount system %s", err)
|
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ if err := copyDevNodes(rootfs); err != nil {
|
|
| 35 |
+ return fmt.Errorf("copy dev nodes %s", err)
|
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ ptmx := filepath.Join(rootfs, "dev/ptmx") |
|
| 39 |
+ if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
|
|
| 40 |
+ return err |
|
| 41 |
+ } |
|
| 42 |
+ if err := os.Symlink(filepath.Join(rootfs, "pts/ptmx"), ptmx); err != nil {
|
|
| 43 |
+ return fmt.Errorf("symlink dev ptmx %s", err)
|
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ if err := setupDev(rootfs); err != nil {
|
|
| 47 |
+ return err |
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ if err := setupConsole(rootfs, console); err != nil {
|
|
| 51 |
+ return err |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ if err := chdir(rootfs); err != nil {
|
|
| 55 |
+ return fmt.Errorf("chdir into %s %s", rootfs, err)
|
|
| 56 |
+ } |
|
| 57 |
+ |
|
| 58 |
+ if err := mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil {
|
|
| 59 |
+ return fmt.Errorf("mount move %s into / %s", rootfs, err)
|
|
| 60 |
+ } |
|
| 61 |
+ |
|
| 62 |
+ if err := chroot("."); err != nil {
|
|
| 63 |
+ return fmt.Errorf("chroot . %s", err)
|
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ if err := chdir("/"); err != nil {
|
|
| 67 |
+ return fmt.Errorf("chdir / %s", err)
|
|
| 68 |
+ } |
|
| 69 |
+ |
|
| 70 |
+ umask(0022) |
|
| 71 |
+ |
|
| 72 |
+ return nil |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+func copyDevNodes(rootfs string) error {
|
|
| 76 |
+ umask(0000) |
|
| 77 |
+ |
|
| 78 |
+ for _, node := range []string{
|
|
| 79 |
+ "null", |
|
| 80 |
+ "zero", |
|
| 81 |
+ "full", |
|
| 82 |
+ "random", |
|
| 83 |
+ "urandom", |
|
| 84 |
+ "tty", |
|
| 85 |
+ } {
|
|
| 86 |
+ stat, err := os.Stat(filepath.Join("/dev", node))
|
|
| 87 |
+ if err != nil {
|
|
| 88 |
+ return err |
|
| 89 |
+ } |
|
| 90 |
+ |
|
| 91 |
+ var ( |
|
| 92 |
+ dest = filepath.Join(rootfs, "dev", node) |
|
| 93 |
+ st = stat.Sys().(*syscall.Stat_t) |
|
| 94 |
+ ) |
|
| 95 |
+ |
|
| 96 |
+ log.Printf("copy %s to %s %d\n", node, dest, st.Rdev)
|
|
| 97 |
+ if err := mknod(dest, st.Mode, int(st.Rdev)); err != nil && !os.IsExist(err) {
|
|
| 98 |
+ return fmt.Errorf("copy %s %s", node, err)
|
|
| 99 |
+ } |
|
| 100 |
+ } |
|
| 101 |
+ return nil |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+func setupDev(rootfs string) error {
|
|
| 105 |
+ for _, link := range []struct {
|
|
| 106 |
+ from string |
|
| 107 |
+ to string |
|
| 108 |
+ }{
|
|
| 109 |
+ {"/proc/kcore", "/dev/core"},
|
|
| 110 |
+ {"/proc/self/fd", "/dev/fd"},
|
|
| 111 |
+ {"/proc/self/fd/0", "/dev/stdin"},
|
|
| 112 |
+ {"/proc/self/fd/1", "/dev/stdout"},
|
|
| 113 |
+ {"/proc/self/fd/2", "/dev/stderr"},
|
|
| 114 |
+ } {
|
|
| 115 |
+ dest := filepath.Join(rootfs, link.to) |
|
| 116 |
+ if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
|
|
| 117 |
+ return fmt.Errorf("remove %s %s", dest, err)
|
|
| 118 |
+ } |
|
| 119 |
+ if err := os.Symlink(link.from, dest); err != nil {
|
|
| 120 |
+ return fmt.Errorf("symlink %s %s", dest, err)
|
|
| 121 |
+ } |
|
| 122 |
+ } |
|
| 123 |
+ return nil |
|
| 124 |
+} |
|
| 125 |
+ |
|
| 126 |
+func setupConsole(rootfs, console string) error {
|
|
| 127 |
+ umask(0000) |
|
| 128 |
+ |
|
| 129 |
+ stat, err := os.Stat(console) |
|
| 130 |
+ if err != nil {
|
|
| 131 |
+ return fmt.Errorf("stat console %s %s", console, err)
|
|
| 132 |
+ } |
|
| 133 |
+ st := stat.Sys().(*syscall.Stat_t) |
|
| 134 |
+ |
|
| 135 |
+ dest := filepath.Join(rootfs, "dev/console") |
|
| 136 |
+ if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
|
|
| 137 |
+ return fmt.Errorf("remove %s %s", dest, err)
|
|
| 138 |
+ } |
|
| 139 |
+ |
|
| 140 |
+ if err := os.Chmod(console, 0600); err != nil {
|
|
| 141 |
+ return err |
|
| 142 |
+ } |
|
| 143 |
+ if err := os.Chown(console, 0, 0); err != nil {
|
|
| 144 |
+ return err |
|
| 145 |
+ } |
|
| 146 |
+ |
|
| 147 |
+ if err := mknod(dest, (st.Mode&^07777)|0600, int(st.Rdev)); err != nil {
|
|
| 148 |
+ return fmt.Errorf("mknod %s %s", dest, err)
|
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ if err := mount(console, dest, "bind", syscall.MS_BIND, ""); err != nil {
|
|
| 152 |
+ return fmt.Errorf("bind %s to %s %s", console, dest, err)
|
|
| 153 |
+ } |
|
| 154 |
+ return nil |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+// mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts |
|
| 158 |
+// inside the mount namespace |
|
| 159 |
+func mountSystem(rootfs string) error {
|
|
| 160 |
+ mounts := []struct {
|
|
| 161 |
+ source string |
|
| 162 |
+ path string |
|
| 163 |
+ device string |
|
| 164 |
+ flags int |
|
| 165 |
+ data string |
|
| 166 |
+ }{
|
|
| 167 |
+ {source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaults},
|
|
| 168 |
+ {source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaults},
|
|
| 169 |
+ {source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: "mode=755"},
|
|
| 170 |
+ {source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaults, data: "mode=1777"},
|
|
| 171 |
+ {source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: "newinstance,ptmxmode=0666,mode=620,gid=5"},
|
|
| 172 |
+ {source: "tmpfs", path: filepath.Join(rootfs, "run"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_STRICTATIME, data: "mode=755"},
|
|
| 173 |
+ } |
|
| 174 |
+ for _, m := range mounts {
|
|
| 175 |
+ if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
|
|
| 176 |
+ return fmt.Errorf("mkdirall %s %s", m.path, err)
|
|
| 177 |
+ } |
|
| 178 |
+ if err := mount(m.source, m.path, m.device, uintptr(m.flags), m.data); err != nil {
|
|
| 179 |
+ return fmt.Errorf("mounting %s into %s %s", m.source, m.path, err)
|
|
| 180 |
+ } |
|
| 181 |
+ } |
|
| 182 |
+ return nil |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+func remountProc() error {
|
|
| 186 |
+ if err := unmount("/proc", syscall.MNT_DETACH); err != nil {
|
|
| 187 |
+ return err |
|
| 188 |
+ } |
|
| 189 |
+ if err := mount("proc", "/proc", "proc", uintptr(defaults), ""); err != nil {
|
|
| 190 |
+ return err |
|
| 191 |
+ } |
|
| 192 |
+ return nil |
|
| 193 |
+} |
|
| 194 |
+ |
|
| 195 |
+func remountSys() error {
|
|
| 196 |
+ if err := unmount("/sys", syscall.MNT_DETACH); err != nil {
|
|
| 197 |
+ if err != syscall.EINVAL {
|
|
| 198 |
+ return err |
|
| 199 |
+ } |
|
| 200 |
+ } else {
|
|
| 201 |
+ if err := mount("sysfs", "/sys", "sysfs", uintptr(defaults), ""); err != nil {
|
|
| 202 |
+ return err |
|
| 203 |
+ } |
|
| 204 |
+ } |
|
| 205 |
+ return nil |
|
| 206 |
+} |
| 0 | 207 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,70 @@ |
| 0 |
+/* |
|
| 1 |
+ TODO |
|
| 2 |
+ pivot root |
|
| 3 |
+ cgroups |
|
| 4 |
+ more mount stuff that I probably am forgetting |
|
| 5 |
+ apparmor |
|
| 6 |
+*/ |
|
| 7 |
+ |
|
| 8 |
+package namespaces |
|
| 9 |
+ |
|
| 10 |
+import ( |
|
| 11 |
+ "fmt" |
|
| 12 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 13 |
+ "github.com/dotcloud/docker/pkg/libcontainer/utils" |
|
| 14 |
+ "os" |
|
| 15 |
+ "path/filepath" |
|
| 16 |
+ "syscall" |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+// CreateNewNamespace creates a new namespace and binds it's fd to the specified path |
|
| 20 |
+func CreateNewNamespace(namespace libcontainer.Namespace, bindTo string) error {
|
|
| 21 |
+ var ( |
|
| 22 |
+ flag = namespaceMap[namespace] |
|
| 23 |
+ name = namespaceFileMap[namespace] |
|
| 24 |
+ nspath = filepath.Join("/proc/self/ns", name)
|
|
| 25 |
+ ) |
|
| 26 |
+ // TODO: perform validation on name and flag |
|
| 27 |
+ |
|
| 28 |
+ pid, err := fork() |
|
| 29 |
+ if err != nil {
|
|
| 30 |
+ return err |
|
| 31 |
+ } |
|
| 32 |
+ |
|
| 33 |
+ if pid == 0 {
|
|
| 34 |
+ if err := unshare(flag); err != nil {
|
|
| 35 |
+ writeError("unshare %s", err)
|
|
| 36 |
+ } |
|
| 37 |
+ if err := mount(nspath, bindTo, "none", syscall.MS_BIND, ""); err != nil {
|
|
| 38 |
+ writeError("bind mount %s", err)
|
|
| 39 |
+ } |
|
| 40 |
+ os.Exit(0) |
|
| 41 |
+ } |
|
| 42 |
+ exit, err := utils.WaitOnPid(pid) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ return err |
|
| 45 |
+ } |
|
| 46 |
+ if exit != 0 {
|
|
| 47 |
+ return fmt.Errorf("exit status %d", exit)
|
|
| 48 |
+ } |
|
| 49 |
+ return err |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// JoinExistingNamespace uses the fd of an existing linux namespace and |
|
| 53 |
+// has the current process join that namespace or the spacespace specified by ns |
|
| 54 |
+func JoinExistingNamespace(fd uintptr, ns libcontainer.Namespace) error {
|
|
| 55 |
+ flag := namespaceMap[ns] |
|
| 56 |
+ if err := setns(fd, uintptr(flag)); err != nil {
|
|
| 57 |
+ return err |
|
| 58 |
+ } |
|
| 59 |
+ return nil |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+// getNamespaceFlags parses the container's Namespaces options to set the correct |
|
| 63 |
+// flags on clone, unshare, and setns |
|
| 64 |
+func getNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
|
|
| 65 |
+ for _, ns := range namespaces {
|
|
| 66 |
+ flag |= namespaceMap[ns] |
|
| 67 |
+ } |
|
| 68 |
+ return |
|
| 69 |
+} |
| 0 | 70 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,35 @@ |
| 0 |
+package namespaces |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+const ( |
|
| 7 |
+ SIGCHLD = 0x14 |
|
| 8 |
+ CLONE_VFORK = 0x00004000 |
|
| 9 |
+ CLONE_NEWNS = 0x00020000 |
|
| 10 |
+ CLONE_NEWUTS = 0x04000000 |
|
| 11 |
+ CLONE_NEWIPC = 0x08000000 |
|
| 12 |
+ CLONE_NEWUSER = 0x10000000 |
|
| 13 |
+ CLONE_NEWPID = 0x20000000 |
|
| 14 |
+ CLONE_NEWNET = 0x40000000 |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+var namespaceMap = map[libcontainer.Namespace]int{
|
|
| 18 |
+ "": 0, |
|
| 19 |
+ libcontainer.CLONE_NEWNS: CLONE_NEWNS, |
|
| 20 |
+ libcontainer.CLONE_NEWUTS: CLONE_NEWUTS, |
|
| 21 |
+ libcontainer.CLONE_NEWIPC: CLONE_NEWIPC, |
|
| 22 |
+ libcontainer.CLONE_NEWUSER: CLONE_NEWUSER, |
|
| 23 |
+ libcontainer.CLONE_NEWPID: CLONE_NEWPID, |
|
| 24 |
+ libcontainer.CLONE_NEWNET: CLONE_NEWNET, |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+var namespaceFileMap = map[libcontainer.Namespace]string{
|
|
| 28 |
+ libcontainer.CLONE_NEWNS: "mnt", |
|
| 29 |
+ libcontainer.CLONE_NEWUTS: "uts", |
|
| 30 |
+ libcontainer.CLONE_NEWIPC: "ipc", |
|
| 31 |
+ libcontainer.CLONE_NEWUSER: "user", |
|
| 32 |
+ libcontainer.CLONE_NEWPID: "pid", |
|
| 33 |
+ libcontainer.CLONE_NEWNET: "net", |
|
| 34 |
+} |
| 0 | 35 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,108 @@ |
| 0 |
+package namespaces |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path/filepath" |
|
| 7 |
+ "strconv" |
|
| 8 |
+ "strings" |
|
| 9 |
+ "syscall" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+func addEnvIfNotSet(container *libcontainer.Container, key, value string) {
|
|
| 13 |
+ jv := fmt.Sprintf("%s=%s", key, value)
|
|
| 14 |
+ if len(container.Command.Env) == 0 {
|
|
| 15 |
+ container.Command.Env = []string{jv}
|
|
| 16 |
+ return |
|
| 17 |
+ } |
|
| 18 |
+ |
|
| 19 |
+ for _, v := range container.Command.Env {
|
|
| 20 |
+ parts := strings.Split(v, "=") |
|
| 21 |
+ if parts[0] == key {
|
|
| 22 |
+ return |
|
| 23 |
+ } |
|
| 24 |
+ } |
|
| 25 |
+ container.Command.Env = append(container.Command.Env, jv) |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+// print and error to stderr and exit(1) |
|
| 29 |
+func writeError(format string, v ...interface{}) {
|
|
| 30 |
+ fmt.Fprintf(os.Stderr, format, v...) |
|
| 31 |
+ os.Exit(1) |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+// getNsFds inspects the container's namespace configuration and opens the fds to |
|
| 35 |
+// each of the namespaces. |
|
| 36 |
+func getNsFds(container *libcontainer.Container) ([]uintptr, error) {
|
|
| 37 |
+ var ( |
|
| 38 |
+ namespaces = []string{}
|
|
| 39 |
+ fds = []uintptr{}
|
|
| 40 |
+ ) |
|
| 41 |
+ |
|
| 42 |
+ for _, ns := range container.Namespaces {
|
|
| 43 |
+ namespaces = append(namespaces, namespaceFileMap[ns]) |
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ for _, ns := range namespaces {
|
|
| 47 |
+ fd, err := getNsFd(container.NsPid, ns) |
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ for _, fd = range fds {
|
|
| 50 |
+ syscall.Close(int(fd)) |
|
| 51 |
+ } |
|
| 52 |
+ return nil, err |
|
| 53 |
+ } |
|
| 54 |
+ fds = append(fds, fd) |
|
| 55 |
+ } |
|
| 56 |
+ return fds, nil |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+// getNsFd returns the fd for a specific pid and namespace option |
|
| 60 |
+func getNsFd(pid int, ns string) (uintptr, error) {
|
|
| 61 |
+ nspath := filepath.Join("/proc", strconv.Itoa(pid), "ns", ns)
|
|
| 62 |
+ // OpenFile adds closOnExec |
|
| 63 |
+ f, err := os.OpenFile(nspath, os.O_RDONLY, 0666) |
|
| 64 |
+ if err != nil {
|
|
| 65 |
+ return 0, err |
|
| 66 |
+ } |
|
| 67 |
+ return f.Fd(), nil |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+// setupEnvironment adds additional environment variables to the container's |
|
| 71 |
+// Command such as USER, LOGNAME, container, and TERM |
|
| 72 |
+func setupEnvironment(container *libcontainer.Container) {
|
|
| 73 |
+ addEnvIfNotSet(container, "container", "docker") |
|
| 74 |
+ // TODO: check if pty |
|
| 75 |
+ addEnvIfNotSet(container, "TERM", "xterm") |
|
| 76 |
+ // TODO: get username from container |
|
| 77 |
+ addEnvIfNotSet(container, "USER", "root") |
|
| 78 |
+ addEnvIfNotSet(container, "LOGNAME", "root") |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func setupUser(container *libcontainer.Container) error {
|
|
| 82 |
+ // TODO: honor user passed on container |
|
| 83 |
+ if err := setgroups(nil); err != nil {
|
|
| 84 |
+ return err |
|
| 85 |
+ } |
|
| 86 |
+ if err := setresgid(0, 0, 0); err != nil {
|
|
| 87 |
+ return err |
|
| 88 |
+ } |
|
| 89 |
+ if err := setresuid(0, 0, 0); err != nil {
|
|
| 90 |
+ return err |
|
| 91 |
+ } |
|
| 92 |
+ return nil |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func getMasterAndConsole(container *libcontainer.Container) (string, *os.File, error) {
|
|
| 96 |
+ master, err := openpmtx() |
|
| 97 |
+ if err != nil {
|
|
| 98 |
+ return "", nil, err |
|
| 99 |
+ } |
|
| 100 |
+ |
|
| 101 |
+ console, err := ptsname(master) |
|
| 102 |
+ if err != nil {
|
|
| 103 |
+ master.Close() |
|
| 104 |
+ return "", nil, err |
|
| 105 |
+ } |
|
| 106 |
+ return console, master, nil |
|
| 107 |
+} |
| 0 | 108 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,104 @@ |
| 0 |
+package network |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "github.com/dotcloud/docker/pkg/netlink" |
|
| 5 |
+ "net" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+var ( |
|
| 9 |
+ ErrNoDefaultRoute = errors.New("no default network route found")
|
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+func InterfaceUp(name string) error {
|
|
| 13 |
+ iface, err := net.InterfaceByName(name) |
|
| 14 |
+ if err != nil {
|
|
| 15 |
+ return err |
|
| 16 |
+ } |
|
| 17 |
+ return netlink.NetworkLinkUp(iface) |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func InterfaceDown(name string) error {
|
|
| 21 |
+ iface, err := net.InterfaceByName(name) |
|
| 22 |
+ if err != nil {
|
|
| 23 |
+ return err |
|
| 24 |
+ } |
|
| 25 |
+ return netlink.NetworkLinkDown(iface) |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func ChangeInterfaceName(old, newName string) error {
|
|
| 29 |
+ iface, err := net.InterfaceByName(old) |
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ return err |
|
| 32 |
+ } |
|
| 33 |
+ return netlink.NetworkChangeName(iface, newName) |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func CreateVethPair(name1, name2 string) error {
|
|
| 37 |
+ return netlink.NetworkCreateVethPair(name1, name2) |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+func SetInterfaceInNamespacePid(name string, nsPid int) error {
|
|
| 41 |
+ iface, err := net.InterfaceByName(name) |
|
| 42 |
+ if err != nil {
|
|
| 43 |
+ return err |
|
| 44 |
+ } |
|
| 45 |
+ return netlink.NetworkSetNsPid(iface, nsPid) |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+func SetInterfaceInNamespaceFd(name string, fd int) error {
|
|
| 49 |
+ iface, err := net.InterfaceByName(name) |
|
| 50 |
+ if err != nil {
|
|
| 51 |
+ return err |
|
| 52 |
+ } |
|
| 53 |
+ return netlink.NetworkSetNsFd(iface, fd) |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+func SetInterfaceMaster(name, master string) error {
|
|
| 57 |
+ iface, err := net.InterfaceByName(name) |
|
| 58 |
+ if err != nil {
|
|
| 59 |
+ return err |
|
| 60 |
+ } |
|
| 61 |
+ masterIface, err := net.InterfaceByName(master) |
|
| 62 |
+ if err != nil {
|
|
| 63 |
+ return err |
|
| 64 |
+ } |
|
| 65 |
+ return netlink.NetworkSetMaster(iface, masterIface) |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+func SetDefaultGateway(ip string) error {
|
|
| 69 |
+ return netlink.AddDefaultGw(net.ParseIP(ip)) |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func SetInterfaceIp(name string, rawIp string) error {
|
|
| 73 |
+ iface, err := net.InterfaceByName(name) |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ return err |
|
| 76 |
+ } |
|
| 77 |
+ ip, ipNet, err := net.ParseCIDR(rawIp) |
|
| 78 |
+ if err != nil {
|
|
| 79 |
+ return err |
|
| 80 |
+ } |
|
| 81 |
+ return netlink.NetworkLinkAddIp(iface, ip, ipNet) |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+func SetMtu(name string, mtu int) error {
|
|
| 85 |
+ iface, err := net.InterfaceByName(name) |
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return err |
|
| 88 |
+ } |
|
| 89 |
+ return netlink.NetworkSetMTU(iface, mtu) |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func GetDefaultMtu() (int, error) {
|
|
| 93 |
+ routes, err := netlink.NetworkGetRoutes() |
|
| 94 |
+ if err != nil {
|
|
| 95 |
+ return -1, err |
|
| 96 |
+ } |
|
| 97 |
+ for _, r := range routes {
|
|
| 98 |
+ if r.Default {
|
|
| 99 |
+ return r.Iface.MTU, nil |
|
| 100 |
+ } |
|
| 101 |
+ } |
|
| 102 |
+ return -1, ErrNoDefaultRoute |
|
| 103 |
+} |
| 0 | 104 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,85 @@ |
| 0 |
+package network |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 5 |
+ "github.com/dotcloud/docker/pkg/libcontainer/namespaces" |
|
| 6 |
+ "os" |
|
| 7 |
+ "syscall" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// SetupVeth sets up an existing network namespace with the specified |
|
| 11 |
+// network configuration. |
|
| 12 |
+func SetupVeth(config *libcontainer.Network) error {
|
|
| 13 |
+ if err := InterfaceDown(config.TempVethName); err != nil {
|
|
| 14 |
+ return fmt.Errorf("interface down %s %s", config.TempVethName, err)
|
|
| 15 |
+ } |
|
| 16 |
+ if err := ChangeInterfaceName(config.TempVethName, "eth0"); err != nil {
|
|
| 17 |
+ return fmt.Errorf("change %s to eth0 %s", config.TempVethName, err)
|
|
| 18 |
+ } |
|
| 19 |
+ if err := SetInterfaceIp("eth0", config.IP); err != nil {
|
|
| 20 |
+ return fmt.Errorf("set eth0 ip %s", err)
|
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ if err := SetMtu("eth0", config.Mtu); err != nil {
|
|
| 24 |
+ return fmt.Errorf("set eth0 mtu to %d %s", config.Mtu, err)
|
|
| 25 |
+ } |
|
| 26 |
+ if err := InterfaceUp("eth0"); err != nil {
|
|
| 27 |
+ return fmt.Errorf("eth0 up %s", err)
|
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ if err := SetMtu("lo", config.Mtu); err != nil {
|
|
| 31 |
+ return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
|
|
| 32 |
+ } |
|
| 33 |
+ if err := InterfaceUp("lo"); err != nil {
|
|
| 34 |
+ return fmt.Errorf("lo up %s", err)
|
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ if config.Gateway != "" {
|
|
| 38 |
+ if err := SetDefaultGateway(config.Gateway); err != nil {
|
|
| 39 |
+ return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
|
|
| 40 |
+ } |
|
| 41 |
+ } |
|
| 42 |
+ return nil |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// SetupNamespaceMountDir prepares a new root for use as a mount |
|
| 46 |
+// source for bind mounting namespace fd to an outside path |
|
| 47 |
+func SetupNamespaceMountDir(root string) error {
|
|
| 48 |
+ if err := os.MkdirAll(root, 0666); err != nil {
|
|
| 49 |
+ return err |
|
| 50 |
+ } |
|
| 51 |
+ // make sure mounts are not unmounted by other mnt namespaces |
|
| 52 |
+ if err := syscall.Mount("", root, "none", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil && err != syscall.EINVAL {
|
|
| 53 |
+ return err |
|
| 54 |
+ } |
|
| 55 |
+ if err := syscall.Mount(root, root, "none", syscall.MS_BIND, ""); err != nil {
|
|
| 56 |
+ return err |
|
| 57 |
+ } |
|
| 58 |
+ return nil |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// CreateNetworkNamespace creates a new network namespace and binds it's fd |
|
| 62 |
+// at the binding path |
|
| 63 |
+func CreateNetworkNamespace(bindingPath string) error {
|
|
| 64 |
+ f, err := os.OpenFile(bindingPath, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0) |
|
| 65 |
+ if err != nil {
|
|
| 66 |
+ return err |
|
| 67 |
+ } |
|
| 68 |
+ f.Close() |
|
| 69 |
+ |
|
| 70 |
+ if err := namespaces.CreateNewNamespace(libcontainer.CLONE_NEWNET, bindingPath); err != nil {
|
|
| 71 |
+ return err |
|
| 72 |
+ } |
|
| 73 |
+ return nil |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+// DeleteNetworkNamespace unmounts the binding path and removes the |
|
| 77 |
+// file so that no references to the fd are present and the network |
|
| 78 |
+// namespace is automatically cleaned up |
|
| 79 |
+func DeleteNetworkNamespace(bindingPath string) error {
|
|
| 80 |
+ if err := syscall.Unmount(bindingPath, 0); err != nil {
|
|
| 81 |
+ return err |
|
| 82 |
+ } |
|
| 83 |
+ return os.Remove(bindingPath) |
|
| 84 |
+} |
| 0 | 85 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,22 @@ |
| 0 |
+{
|
|
| 1 |
+ "id": "koye", |
|
| 2 |
+ "namespace_pid": 3745, |
|
| 3 |
+ "command": {
|
|
| 4 |
+ "args": [ |
|
| 5 |
+ "/usr/lib/systemd/systemd" |
|
| 6 |
+ ], |
|
| 7 |
+ "environment": [ |
|
| 8 |
+ "HOME=/", |
|
| 9 |
+ "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin", |
|
| 10 |
+ "container=docker", |
|
| 11 |
+ "TERM=" |
|
| 12 |
+ ] |
|
| 13 |
+ }, |
|
| 14 |
+ "rootfs": "/root/main/mycontainer", |
|
| 15 |
+ "namespaces": [ |
|
| 16 |
+ "NEWIPC", |
|
| 17 |
+ "NEWNS", |
|
| 18 |
+ "NEWPID", |
|
| 19 |
+ "NEWUTS" |
|
| 20 |
+ ] |
|
| 21 |
+} |
| 0 | 22 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,49 @@ |
| 0 |
+package libcontainer |
|
| 1 |
+ |
|
| 2 |
+type Namespace string |
|
| 3 |
+type Namespaces []Namespace |
|
| 4 |
+ |
|
| 5 |
+func (n Namespaces) Contains(ns Namespace) bool {
|
|
| 6 |
+ for _, nns := range n {
|
|
| 7 |
+ if nns == ns {
|
|
| 8 |
+ return true |
|
| 9 |
+ } |
|
| 10 |
+ } |
|
| 11 |
+ return false |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+type Capability string |
|
| 15 |
+type Capabilities []Capability |
|
| 16 |
+ |
|
| 17 |
+func (c Capabilities) Contains(capp Capability) bool {
|
|
| 18 |
+ for _, cc := range c {
|
|
| 19 |
+ if cc == capp {
|
|
| 20 |
+ return true |
|
| 21 |
+ } |
|
| 22 |
+ } |
|
| 23 |
+ return false |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+const ( |
|
| 27 |
+ CAP_SETPCAP Capability = "SETPCAP" |
|
| 28 |
+ CAP_SYS_MODULE Capability = "SYS_MODULE" |
|
| 29 |
+ CAP_SYS_RAWIO Capability = "SYS_RAWIO" |
|
| 30 |
+ CAP_SYS_PACCT Capability = "SYS_PACCT" |
|
| 31 |
+ CAP_SYS_ADMIN Capability = "SYS_ADMIN" |
|
| 32 |
+ CAP_SYS_NICE Capability = "SYS_NICE" |
|
| 33 |
+ CAP_SYS_RESOURCE Capability = "SYS_RESOURCE" |
|
| 34 |
+ CAP_SYS_TIME Capability = "SYS_TIME" |
|
| 35 |
+ CAP_SYS_TTY_CONFIG Capability = "SYS_TTY_CONFIG" |
|
| 36 |
+ CAP_MKNOD Capability = "MKNOD" |
|
| 37 |
+ CAP_AUDIT_WRITE Capability = "AUDIT_WRITE" |
|
| 38 |
+ CAP_AUDIT_CONTROL Capability = "AUDIT_CONTROL" |
|
| 39 |
+ CAP_MAC_OVERRIDE Capability = "MAC_OVERRIDE" |
|
| 40 |
+ CAP_MAC_ADMIN Capability = "MAC_ADMIN" |
|
| 41 |
+ |
|
| 42 |
+ CLONE_NEWNS Namespace = "NEWNS" // mount |
|
| 43 |
+ CLONE_NEWUTS Namespace = "NEWUTS" // utsname |
|
| 44 |
+ CLONE_NEWIPC Namespace = "NEWIPC" // ipc |
|
| 45 |
+ CLONE_NEWUSER Namespace = "NEWUSER" // user |
|
| 46 |
+ CLONE_NEWPID Namespace = "NEWPID" // pid |
|
| 47 |
+ CLONE_NEWNET Namespace = "NEWNET" // network |
|
| 48 |
+) |
| 0 | 49 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,22 @@ |
| 0 |
+{
|
|
| 1 |
+ "id": "koye", |
|
| 2 |
+ "namespace_pid": 3745, |
|
| 3 |
+ "command": {
|
|
| 4 |
+ "args": [ |
|
| 5 |
+ "/sbin/init" |
|
| 6 |
+ ], |
|
| 7 |
+ "environment": [ |
|
| 8 |
+ "HOME=/", |
|
| 9 |
+ "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin", |
|
| 10 |
+ "container=docker", |
|
| 11 |
+ "TERM=xterm" |
|
| 12 |
+ ] |
|
| 13 |
+ }, |
|
| 14 |
+ "rootfs": "/var/lib/docker/btrfs/subvolumes/7c0f15df1ad2e2fe04d7a6e079aec17406e9465a6a37dd16cb0dd754fc0167b3", |
|
| 15 |
+ "namespaces": [ |
|
| 16 |
+ "NEWIPC", |
|
| 17 |
+ "NEWNS", |
|
| 18 |
+ "NEWPID", |
|
| 19 |
+ "NEWUTS" |
|
| 20 |
+ ] |
|
| 21 |
+} |
| 0 | 22 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,33 @@ |
| 0 |
+package utils |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "crypto/rand" |
|
| 4 |
+ "encoding/hex" |
|
| 5 |
+ "io" |
|
| 6 |
+ "os" |
|
| 7 |
+ "syscall" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func WaitOnPid(pid int) (exitcode int, err error) {
|
|
| 11 |
+ child, err := os.FindProcess(pid) |
|
| 12 |
+ if err != nil {
|
|
| 13 |
+ return -1, err |
|
| 14 |
+ } |
|
| 15 |
+ state, err := child.Wait() |
|
| 16 |
+ if err != nil {
|
|
| 17 |
+ return -1, err |
|
| 18 |
+ } |
|
| 19 |
+ return getExitCode(state), nil |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+func getExitCode(state *os.ProcessState) int {
|
|
| 23 |
+ return state.Sys().(syscall.WaitStatus).ExitStatus() |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func GenerateRandomName(size int) (string, error) {
|
|
| 27 |
+ id := make([]byte, size) |
|
| 28 |
+ if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
|
| 29 |
+ return "", err |
|
| 30 |
+ } |
|
| 31 |
+ return hex.EncodeToString(id), nil |
|
| 32 |
+} |