mirror of
				https://github.com/opencontainers/runc.git
				synced 2025-10-31 19:13:12 +08:00 
			
		
		
		
	Rename Fs fields to fs
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
		| @@ -8,7 +8,7 @@ In the design and development of libcontainer we try to follow these principles: | |||||||
| * Less code is better. | * Less code is better. | ||||||
| * Fewer components are better. Do you really need to add one more class? | * Fewer components are better. Do you really need to add one more class? | ||||||
| * 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand. | * 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand. | ||||||
| * Don't do later what you can do now. "//FIXME: refactor" is not acceptable in new code. | * Don't do later what you can do now. "//TODO: refactor" is not acceptable in new code. | ||||||
| * When hesitating between two options, choose the one that is easier to reverse. | * When hesitating between two options, choose the one that is easier to reverse. | ||||||
| * "No" is temporary; "Yes" is forever. If you're not sure about a new feature, say no. You can change your mind later. | * "No" is temporary; "Yes" is forever. If you're not sure about a new feature, say no. You can change your mind later. | ||||||
| * Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable. | * Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable. | ||||||
|   | |||||||
| @@ -83,7 +83,7 @@ func (m *Manager) Apply(pid int) error { | |||||||
| 		if err := sys.Set(d); err != nil { | 		if err := sys.Set(d); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		// FIXME: Apply should, ideally, be reentrant or be broken up into a separate | 		// TODO: Apply should, ideally, be reentrant or be broken up into a separate | ||||||
| 		// create and join phase so that the cgroup hierarchy for a container can be | 		// create and join phase so that the cgroup hierarchy for a container can be | ||||||
| 		// created then join consists of writing the process pids to cgroup.procs | 		// created then join consists of writing the process pids to cgroup.procs | ||||||
| 		p, err := d.path(name) | 		p, err := d.path(name) | ||||||
|   | |||||||
| @@ -26,22 +26,22 @@ type Config struct { | |||||||
| 	// This is required when using read only root filesystems. In these cases, a read/writeable path can be (bind) mounted somewhere inside the root filesystem to act as pivot. | 	// This is required when using read only root filesystems. In these cases, a read/writeable path can be (bind) mounted somewhere inside the root filesystem to act as pivot. | ||||||
| 	PivotDir string `json:"pivot_dir,omitempty"` | 	PivotDir string `json:"pivot_dir,omitempty"` | ||||||
|  |  | ||||||
| 	// ReadonlyFs will remount the container's rootfs as readonly where only externally mounted | 	// Path to a directory containing the container's root filesystem. | ||||||
| 	// bind mounts are writtable | 	Rootfs string `json:"rootfs,omitempty"` | ||||||
| 	ReadonlyFs bool `json:"readonly_fs,omitempty"` |  | ||||||
|  | 	// Readonlyfs will remount the container's rootfs as readonly where only externally mounted | ||||||
|  | 	// bind mounts are writtable. | ||||||
|  | 	Readonlyfs bool `json:"readonlyfs,omitempty"` | ||||||
|  |  | ||||||
| 	// Mounts specify additional source and destination paths that will be mounted inside the container's | 	// Mounts specify additional source and destination paths that will be mounted inside the container's | ||||||
| 	// rootfs and mount namespace if specified | 	// rootfs and mount namespace if specified | ||||||
| 	Mounts []*Mount `json:"mounts,omitempty"` | 	Mounts []*Mount `json:"mounts,omitempty"` | ||||||
|  |  | ||||||
| 	// The device nodes that should be automatically created within the container upon container start.  Note, make sure that the node is marked as allowed in the cgroup as well! | 	// The device nodes that should be automatically created within the container upon container start.  Note, make sure that the node is marked as allowed in the cgroup as well! | ||||||
| 	DeviceNodes []*Device `json:"device_nodes,omitempty"` | 	Devices []*Device `json:"devices,omitempty"` | ||||||
|  |  | ||||||
| 	MountLabel string `json:"mount_label,omitempty"` | 	MountLabel string `json:"mount_label,omitempty"` | ||||||
|  |  | ||||||
| 	// Pathname to container's root filesystem |  | ||||||
| 	RootFs string `json:"root_fs,omitempty"` |  | ||||||
|  |  | ||||||
| 	// Hostname optionally sets the container's hostname if provided | 	// Hostname optionally sets the container's hostname if provided | ||||||
| 	Hostname string `json:"hostname,omitempty"` | 	Hostname string `json:"hostname,omitempty"` | ||||||
|  |  | ||||||
|   | |||||||
| @@ -114,7 +114,7 @@ func TestConfigJsonFormat(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, d := range DefaultSimpleDevices { | 	for _, d := range DefaultSimpleDevices { | ||||||
| 		if !containsDevice(d, container.DeviceNodes) { | 		if !containsDevice(d, container.Devices) { | ||||||
| 			t.Logf("expected device configuration for %s", d.Path) | 			t.Logf("expected device configuration for %s", d.Path) | ||||||
| 			t.Fail() | 			t.Fail() | ||||||
| 		} | 		} | ||||||
| @@ -163,3 +163,69 @@ func TestRemoveNamespace(t *testing.T) { | |||||||
| 		t.Fatalf("namespaces should have 0 items but reports %d", len(ns)) | 		t.Fatalf("namespaces should have 0 items but reports %d", len(ns)) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestHostUIDNoUSERNS(t *testing.T) { | ||||||
|  | 	config := &Config{ | ||||||
|  | 		Namespaces: Namespaces{}, | ||||||
|  | 	} | ||||||
|  | 	uid, err := config.HostUID() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if uid != 0 { | ||||||
|  | 		t.Fatal("expected uid 0 with no USERNS but received %d", uid) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestHostUIDWithUSERNS(t *testing.T) { | ||||||
|  | 	config := &Config{ | ||||||
|  | 		Namespaces: Namespaces{{Type: NEWUSER}}, | ||||||
|  | 		UidMappings: []IDMap{ | ||||||
|  | 			{ | ||||||
|  | 				ContainerID: 0, | ||||||
|  | 				HostID:      1000, | ||||||
|  | 				Size:        1, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	uid, err := config.HostUID() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if uid != 1000 { | ||||||
|  | 		t.Fatal("expected uid 1000 with no USERNS but received %d", uid) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestHostGIDNoUSERNS(t *testing.T) { | ||||||
|  | 	config := &Config{ | ||||||
|  | 		Namespaces: Namespaces{}, | ||||||
|  | 	} | ||||||
|  | 	uid, err := config.HostGID() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if uid != 0 { | ||||||
|  | 		t.Fatal("expected gid 0 with no USERNS but received %d", uid) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestHostGIDWithUSERNS(t *testing.T) { | ||||||
|  | 	config := &Config{ | ||||||
|  | 		Namespaces: Namespaces{{Type: NEWUSER}}, | ||||||
|  | 		GidMappings: []IDMap{ | ||||||
|  | 			{ | ||||||
|  | 				ContainerID: 0, | ||||||
|  | 				HostID:      1000, | ||||||
|  | 				Size:        1, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	uid, err := config.HostGID() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if uid != 1000 { | ||||||
|  | 		t.Fatal("expected gid 1000 with no USERNS but received %d", uid) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -10,18 +10,28 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| type Device struct { | type Device struct { | ||||||
|  | 	// Device type, block, char, etc. | ||||||
| 	Type rune `json:"type,omitempty"` | 	Type rune `json:"type,omitempty"` | ||||||
| 	// It is fine if this is an empty string in the case that you are using Wildcards |  | ||||||
|  | 	// Path to the device. | ||||||
| 	Path string `json:"path,omitempty"` | 	Path string `json:"path,omitempty"` | ||||||
| 	// Use the wildcard constant for wildcards. |  | ||||||
|  | 	// Major is the device's major number. | ||||||
| 	Major int64 `json:"major,omitempty"` | 	Major int64 `json:"major,omitempty"` | ||||||
| 	// Use the wildcard constant for wildcards. |  | ||||||
|  | 	// Minor is the device's minor number. | ||||||
| 	Minor int64 `json:"minor,omitempty"` | 	Minor int64 `json:"minor,omitempty"` | ||||||
| 	// Typically just "rwm" |  | ||||||
|  | 	// Cgroup permissions format, rwm. | ||||||
| 	Permissions string `json:"permissions,omitempty"` | 	Permissions string `json:"permissions,omitempty"` | ||||||
| 	// The permission bits of the file's mode |  | ||||||
|  | 	// FileMode permission bits for the device. | ||||||
| 	FileMode os.FileMode `json:"file_mode,omitempty"` | 	FileMode os.FileMode `json:"file_mode,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Uid of the device. | ||||||
| 	Uid uint32 `json:"uid,omitempty"` | 	Uid uint32 `json:"uid,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Gid of the device. | ||||||
| 	Gid uint32 `json:"gid,omitempty"` | 	Gid uint32 `json:"gid,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -52,7 +52,7 @@ func (m *Mount) bindMount(rootfs, mountLabel string) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: (crosbymichael) This does not belong here and should be done a layer above | 	// TODO: (crosbymichael) This does not belong here and should be done a layer above | ||||||
| 	dest, err = symlink.FollowSymlinkInScope(dest, rootfs) | 	dest, err = symlink.FollowSymlinkInScope(dest, rootfs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -94,7 +94,7 @@ func (m *Mount) tmpfsMount(rootfs, mountLabel string) error { | |||||||
| 		dest = filepath.Join(rootfs, m.Destination) | 		dest = filepath.Join(rootfs, m.Destination) | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	// FIXME: (crosbymichael) This does not belong here and should be done a layer above | 	// TODO: (crosbymichael) This does not belong here and should be done a layer above | ||||||
| 	if dest, err = symlink.FollowSymlinkInScope(dest, rootfs); err != nil { | 	if dest, err = symlink.FollowSymlinkInScope(dest, rootfs); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -20,12 +20,12 @@ type Network struct { | |||||||
| 	// Address contains the IPv4 and mask to set on the network interface | 	// Address contains the IPv4 and mask to set on the network interface | ||||||
| 	Address string `json:"address,omitempty"` | 	Address string `json:"address,omitempty"` | ||||||
|  |  | ||||||
| 	// IPv6Address contains the IPv6 and mask to set on the network interface |  | ||||||
| 	IPv6Address string `json:"ipv6_address,omitempty"` |  | ||||||
|  |  | ||||||
| 	// Gateway sets the gateway address that is used as the default for the interface | 	// Gateway sets the gateway address that is used as the default for the interface | ||||||
| 	Gateway string `json:"gateway,omitempty"` | 	Gateway string `json:"gateway,omitempty"` | ||||||
|  |  | ||||||
|  | 	// IPv6Address contains the IPv6 and mask to set on the network interface | ||||||
|  | 	IPv6Address string `json:"ipv6_address,omitempty"` | ||||||
|  |  | ||||||
| 	// IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface | 	// IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface | ||||||
| 	IPv6Gateway string `json:"ipv6_gateway,omitempty"` | 	IPv6Gateway string `json:"ipv6_gateway,omitempty"` | ||||||
|  |  | ||||||
|   | |||||||
| @@ -60,10 +60,10 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func HostDevices() ([]*configs.Device, error) { | func HostDevices() ([]*configs.Device, error) { | ||||||
| 	return getDeviceNodes("/dev") | 	return getDevices("/dev") | ||||||
| } | } | ||||||
|  |  | ||||||
| func getDeviceNodes(path string) ([]*configs.Device, error) { | func getDevices(path string) ([]*configs.Device, error) { | ||||||
| 	files, err := ioutilReadDir(path) | 	files, err := ioutilReadDir(path) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -76,7 +76,7 @@ func getDeviceNodes(path string) ([]*configs.Device, error) { | |||||||
| 			case "pts", "shm", "fd", "mqueue": | 			case "pts", "shm", "fd", "mqueue": | ||||||
| 				continue | 				continue | ||||||
| 			default: | 			default: | ||||||
| 				sub, err := getDeviceNodes(filepath.Join(path, f.Name())) | 				sub, err := getDevices(filepath.Join(path, f.Name())) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					return nil, err | 					return nil, err | ||||||
| 				} | 				} | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ func testExecPS(t *testing.T, userns bool) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rootfs, err := newRootFs() | 	rootfs, err := newRootfs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| @@ -65,7 +65,7 @@ func TestIPCPrivate(t *testing.T) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rootfs, err := newRootFs() | 	rootfs, err := newRootfs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| @@ -96,7 +96,7 @@ func TestIPCHost(t *testing.T) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rootfs, err := newRootFs() | 	rootfs, err := newRootfs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| @@ -128,7 +128,7 @@ func TestIPCJoinPath(t *testing.T) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rootfs, err := newRootFs() | 	rootfs, err := newRootfs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| @@ -161,7 +161,7 @@ func TestIPCBadPath(t *testing.T) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rootfs, err := newRootFs() | 	rootfs, err := newRootfs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| @@ -181,7 +181,7 @@ func TestRlimit(t *testing.T) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rootfs, err := newRootFs() | 	rootfs, err := newRootfs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| @@ -232,7 +232,7 @@ func TestEnter(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	defer os.RemoveAll(root) | 	defer os.RemoveAll(root) | ||||||
|  |  | ||||||
| 	rootfs, err := newRootFs() | 	rootfs, err := newRootfs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| @@ -335,7 +335,7 @@ func TestFreeze(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	defer os.RemoveAll(root) | 	defer os.RemoveAll(root) | ||||||
|  |  | ||||||
| 	rootfs, err := newRootFs() | 	rootfs, err := newRootfs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ import ( | |||||||
| // and the default setup for devices | // and the default setup for devices | ||||||
| func newTemplateConfig(rootfs string) *configs.Config { | func newTemplateConfig(rootfs string) *configs.Config { | ||||||
| 	return &configs.Config{ | 	return &configs.Config{ | ||||||
| 		RootFs: rootfs, | 		Rootfs: rootfs, | ||||||
| 		Capabilities: []string{ | 		Capabilities: []string{ | ||||||
| 			"CHOWN", | 			"CHOWN", | ||||||
| 			"DAC_OVERRIDE", | 			"DAC_OVERRIDE", | ||||||
| @@ -43,7 +43,7 @@ func newTemplateConfig(rootfs string) *configs.Config { | |||||||
| 			AllowedDevices:  configs.DefaultAllowedDevices, | 			AllowedDevices:  configs.DefaultAllowedDevices, | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		DeviceNodes: configs.DefaultAutoCreatedDevices, | 		Devices: configs.DefaultAutoCreatedDevices, | ||||||
| 		Hostname:    "integration", | 		Hostname:    "integration", | ||||||
| 		Env: []string{ | 		Env: []string{ | ||||||
| 			"HOME=/root", | 			"HOME=/root", | ||||||
|   | |||||||
| @@ -29,7 +29,7 @@ type stdBuffers struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| func writeConfig(config *configs.Config) error { | func writeConfig(config *configs.Config) error { | ||||||
| 	f, err := os.OpenFile(filepath.Join(config.RootFs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) | 	f, err := os.OpenFile(filepath.Join(config.Rootfs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -51,8 +51,8 @@ func loadConfig() (*configs.Config, error) { | |||||||
| 	return container, nil | 	return container, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // newRootFs creates a new tmp directory and copies the busybox root filesystem | // newRootfs creates a new tmp directory and copies the busybox root filesystem | ||||||
| func newRootFs() (string, error) { | func newRootfs() (string, error) { | ||||||
| 	dir, err := ioutil.TempDir("", "") | 	dir, err := ioutil.TempDir("", "") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
|   | |||||||
| @@ -6,17 +6,13 @@ import ( | |||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"syscall" | 	"syscall" | ||||||
|  |  | ||||||
| 	"github.com/docker/libcontainer/apparmor" |  | ||||||
| 	"github.com/docker/libcontainer/cgroups" | 	"github.com/docker/libcontainer/cgroups" | ||||||
| 	"github.com/docker/libcontainer/configs" | 	"github.com/docker/libcontainer/configs" | ||||||
| 	"github.com/docker/libcontainer/label" |  | ||||||
| 	"github.com/docker/libcontainer/mount" |  | ||||||
| 	"github.com/docker/libcontainer/network" | 	"github.com/docker/libcontainer/network" | ||||||
| 	"github.com/docker/libcontainer/system" | 	"github.com/docker/libcontainer/system" | ||||||
| 	"github.com/golang/glog" | 	"github.com/golang/glog" | ||||||
| @@ -106,7 +102,7 @@ func (c *linuxContainer) Start(process *Process) (int, error) { | |||||||
| 	cmd.Stdout = process.Stdout | 	cmd.Stdout = process.Stdout | ||||||
| 	cmd.Stderr = process.Stderr | 	cmd.Stderr = process.Stderr | ||||||
| 	cmd.Env = c.config.Env | 	cmd.Env = c.config.Env | ||||||
| 	cmd.Dir = c.config.RootFs | 	cmd.Dir = c.config.Rootfs | ||||||
| 	if cmd.SysProcAttr == nil { | 	if cmd.SysProcAttr == nil { | ||||||
| 		cmd.SysProcAttr = &syscall.SysProcAttr{} | 		cmd.SysProcAttr = &syscall.SysProcAttr{} | ||||||
| 	} | 	} | ||||||
| @@ -161,7 +157,7 @@ func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, args []string) (int, err | |||||||
| 		return -1, terr | 		return -1, terr | ||||||
| 	} | 	} | ||||||
| 	// Enter cgroups. | 	// Enter cgroups. | ||||||
| 	if err := enterCgroups(c.state, pid.Pid); err != nil { | 	if err := c.enterCgroups(pid.Pid); err != nil { | ||||||
| 		return terminate(err) | 		return terminate(err) | ||||||
| 	} | 	} | ||||||
| 	encoder := json.NewEncoder(parent) | 	encoder := json.NewEncoder(parent) | ||||||
| @@ -398,7 +394,7 @@ func executeSetupCmd(args []string, ppid int, container *configs.Config, process | |||||||
| 	} | 	} | ||||||
| 	defer parent.Close() | 	defer parent.Close() | ||||||
| 	command.ExtraFiles = []*os.File{child} | 	command.ExtraFiles = []*os.File{child} | ||||||
| 	command.Dir = container.RootFs | 	command.Dir = container.Rootfs | ||||||
| 	command.Env = append(command.Env, | 	command.Env = append(command.Env, | ||||||
| 		fmt.Sprintf("_LIBCONTAINER_INITPID=%d", ppid), | 		fmt.Sprintf("_LIBCONTAINER_INITPID=%d", ppid), | ||||||
| 		fmt.Sprintf("_LIBCONTAINER_USERNS=1")) | 		fmt.Sprintf("_LIBCONTAINER_USERNS=1")) | ||||||
| @@ -460,120 +456,6 @@ type pid struct { | |||||||
| 	Pid int `json:"Pid"` | 	Pid int `json:"Pid"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // Finalize entering into a container and execute a specified command | func (c *linuxContainer) enterCgroups(pid int) error { | ||||||
| func InitIn(pipe *os.File) (err error) { | 	return cgroups.EnterPid(c.state.CgroupPaths, pid) | ||||||
| 	defer func() { |  | ||||||
| 		// if we have an error during the initialization of the container's init then send it back to the |  | ||||||
| 		// parent process in the form of an initError. |  | ||||||
| 		if err != nil { |  | ||||||
| 			// ensure that any data sent from the parent is consumed so it doesn't |  | ||||||
| 			// receive ECONNRESET when the child writes to the pipe. |  | ||||||
| 			ioutil.ReadAll(pipe) |  | ||||||
| 			if err := json.NewEncoder(pipe).Encode(initError{ |  | ||||||
| 				Message: err.Error(), |  | ||||||
| 			}); err != nil { |  | ||||||
| 				panic(err) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		// ensure that this pipe is always closed |  | ||||||
| 		pipe.Close() |  | ||||||
| 	}() |  | ||||||
| 	decoder := json.NewDecoder(pipe) |  | ||||||
| 	var config *configs.Config |  | ||||||
| 	if err := decoder.Decode(&config); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	var process *processArgs |  | ||||||
| 	if err := decoder.Decode(&process); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if err := finalizeSetns(config); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if err := system.Execv(process.Args[0], process.Args[0:], config.Env); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	panic("unreachable") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // finalize expects that the setns calls have been setup and that is has joined an |  | ||||||
| // existing namespace |  | ||||||
| func finalizeSetns(container *configs.Config) error { |  | ||||||
| 	// clear the current processes env and replace it with the environment defined on the container |  | ||||||
| 	if err := loadContainerEnvironment(container); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := setupRlimits(container); err != nil { |  | ||||||
| 		return fmt.Errorf("setup rlimits %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := finalizeNamespace(container); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil { |  | ||||||
| 		return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if container.ProcessLabel != "" { |  | ||||||
| 		if err := label.SetProcessLabel(container.ProcessLabel); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SetupContainer is run to setup mounts and networking related operations |  | ||||||
| // for a user namespace enabled process as a user namespace root doesn't |  | ||||||
| // have permissions to perform these operations. |  | ||||||
| // The setup process joins all the namespaces of user namespace enabled init |  | ||||||
| // except the user namespace, so it run as root in the root user namespace |  | ||||||
| // to perform these operations. |  | ||||||
| func SetupContainer(process *processArgs) error { |  | ||||||
| 	container := process.Config |  | ||||||
| 	networkState := process.NetworkState |  | ||||||
|  |  | ||||||
| 	// TODO : move to validation |  | ||||||
| 	/* |  | ||||||
| 		rootfs, err := utils.ResolveRootfs(container.RootFs) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	*/ |  | ||||||
|  |  | ||||||
| 	// clear the current processes env and replace it with the environment |  | ||||||
| 	// defined on the container |  | ||||||
| 	if err := loadContainerEnvironment(container); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	cloneFlags := container.Namespaces.CloneFlags() |  | ||||||
| 	if (cloneFlags & syscall.CLONE_NEWNET) == 0 { |  | ||||||
| 		if len(container.Networks) != 0 || len(container.Routes) != 0 { |  | ||||||
| 			return fmt.Errorf("unable to apply network parameters without network namespace") |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		if err := setupNetwork(container, networkState); err != nil { |  | ||||||
| 			return fmt.Errorf("setup networking %s", err) |  | ||||||
| 		} |  | ||||||
| 		if err := setupRoute(container); err != nil { |  | ||||||
| 			return fmt.Errorf("setup route %s", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	label.Init() |  | ||||||
|  |  | ||||||
| 	// InitializeMountNamespace() can be executed only for a new mount namespace |  | ||||||
| 	if (cloneFlags & syscall.CLONE_NEWNS) != 0 { |  | ||||||
| 		if err := mount.InitializeMountNamespace(container); err != nil { |  | ||||||
| 			return fmt.Errorf("setup mount namespace %s", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func enterCgroups(state *configs.State, pid int) error { |  | ||||||
| 	return cgroups.EnterPid(state.CgroupPaths, pid) |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										119
									
								
								linux_factory.go
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								linux_factory.go
									
									
									
									
									
								
							| @@ -141,7 +141,7 @@ func (l *linuxFactory) StartInitialization(pipefd uintptr) (err error) { | |||||||
| 	setupUserns := os.Getenv("_LIBCONTAINER_USERNS") != "" | 	setupUserns := os.Getenv("_LIBCONTAINER_USERNS") != "" | ||||||
| 	pid := os.Getenv("_LIBCONTAINER_INITPID") | 	pid := os.Getenv("_LIBCONTAINER_INITPID") | ||||||
| 	if pid != "" && !setupUserns { | 	if pid != "" && !setupUserns { | ||||||
| 		return InitIn(pipe) | 		return initIn(pipe) | ||||||
| 	} | 	} | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		// if we have an error during the initialization of the container's init then send it back to the | 		// if we have an error during the initialization of the container's init then send it back to the | ||||||
| @@ -169,7 +169,7 @@ func (l *linuxFactory) StartInitialization(pipefd uintptr) (err error) { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if setupUserns { | 	if setupUserns { | ||||||
| 		err = SetupContainer(process) | 		err = setupContainer(process) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			os.Exit(0) | 			os.Exit(0) | ||||||
| 		} else { | 		} else { | ||||||
| @@ -348,7 +348,6 @@ func (l *linuxFactory) initUserNs(uncleanRootfs string, process *processArgs) (e | |||||||
| 	if config.WorkingDir == "" { | 	if config.WorkingDir == "" { | ||||||
| 		config.WorkingDir = "/" | 		config.WorkingDir = "/" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := setupRlimits(config); err != nil { | 	if err := setupRlimits(config); err != nil { | ||||||
| 		return fmt.Errorf("setup rlimits %s", err) | 		return fmt.Errorf("setup rlimits %s", err) | ||||||
| 	} | 	} | ||||||
| @@ -557,3 +556,117 @@ func joinExistingNamespaces(namespaces []configs.Namespace) error { | |||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // setupContainer is run to setup mounts and networking related operations | ||||||
|  | // for a user namespace enabled process as a user namespace root doesn't | ||||||
|  | // have permissions to perform these operations. | ||||||
|  | // The setup process joins all the namespaces of user namespace enabled init | ||||||
|  | // except the user namespace, so it run as root in the root user namespace | ||||||
|  | // to perform these operations. | ||||||
|  | func setupContainer(process *processArgs) error { | ||||||
|  | 	container := process.Config | ||||||
|  | 	networkState := process.NetworkState | ||||||
|  |  | ||||||
|  | 	// TODO : move to validation | ||||||
|  | 	/* | ||||||
|  | 		rootfs, err := utils.ResolveRootfs(container.Rootfs) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	*/ | ||||||
|  |  | ||||||
|  | 	// clear the current processes env and replace it with the environment | ||||||
|  | 	// defined on the container | ||||||
|  | 	if err := loadContainerEnvironment(container); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cloneFlags := container.Namespaces.CloneFlags() | ||||||
|  | 	if (cloneFlags & syscall.CLONE_NEWNET) == 0 { | ||||||
|  | 		if len(container.Networks) != 0 || len(container.Routes) != 0 { | ||||||
|  | 			return fmt.Errorf("unable to apply network parameters without network namespace") | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if err := setupNetwork(container, networkState); err != nil { | ||||||
|  | 			return fmt.Errorf("setup networking %s", err) | ||||||
|  | 		} | ||||||
|  | 		if err := setupRoute(container); err != nil { | ||||||
|  | 			return fmt.Errorf("setup route %s", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	label.Init() | ||||||
|  |  | ||||||
|  | 	// InitializeMountNamespace() can be executed only for a new mount namespace | ||||||
|  | 	if (cloneFlags & syscall.CLONE_NEWNS) != 0 { | ||||||
|  | 		if err := mount.InitializeMountNamespace(container); err != nil { | ||||||
|  | 			return fmt.Errorf("setup mount namespace %s", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Finalize entering into a container and execute a specified command | ||||||
|  | func initIn(pipe *os.File) (err error) { | ||||||
|  | 	defer func() { | ||||||
|  | 		// if we have an error during the initialization of the container's init then send it back to the | ||||||
|  | 		// parent process in the form of an initError. | ||||||
|  | 		if err != nil { | ||||||
|  | 			// ensure that any data sent from the parent is consumed so it doesn't | ||||||
|  | 			// receive ECONNRESET when the child writes to the pipe. | ||||||
|  | 			ioutil.ReadAll(pipe) | ||||||
|  | 			if err := json.NewEncoder(pipe).Encode(initError{ | ||||||
|  | 				Message: err.Error(), | ||||||
|  | 			}); err != nil { | ||||||
|  | 				panic(err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// ensure that this pipe is always closed | ||||||
|  | 		pipe.Close() | ||||||
|  | 	}() | ||||||
|  | 	decoder := json.NewDecoder(pipe) | ||||||
|  | 	var config *configs.Config | ||||||
|  | 	if err := decoder.Decode(&config); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	var process *processArgs | ||||||
|  | 	if err := decoder.Decode(&process); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := finalizeSetns(config); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := system.Execv(process.Args[0], process.Args[0:], config.Env); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	panic("unreachable") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // finalize expects that the setns calls have been setup and that is has joined an | ||||||
|  | // existing namespace | ||||||
|  | func finalizeSetns(container *configs.Config) error { | ||||||
|  | 	// clear the current processes env and replace it with the environment defined on the container | ||||||
|  | 	if err := loadContainerEnvironment(container); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := setupRlimits(container); err != nil { | ||||||
|  | 		return fmt.Errorf("setup rlimits %s", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := finalizeNamespace(container); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil { | ||||||
|  | 		return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if container.ProcessLabel != "" { | ||||||
|  | 		if err := label.SetProcessLabel(container.ProcessLabel); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -86,7 +86,7 @@ func TestFactoryLoadContainer(t *testing.T) { | |||||||
| 	var ( | 	var ( | ||||||
| 		id             = "1" | 		id             = "1" | ||||||
| 		expectedConfig = &configs.Config{ | 		expectedConfig = &configs.Config{ | ||||||
| 			RootFs: "/mycontainer/root", | 			Rootfs: "/mycontainer/root", | ||||||
| 		} | 		} | ||||||
| 		expectedState = &configs.State{ | 		expectedState = &configs.State{ | ||||||
| 			InitPid: 1024, | 			InitPid: 1024, | ||||||
| @@ -119,8 +119,8 @@ func TestFactoryLoadContainer(t *testing.T) { | |||||||
|  |  | ||||||
| 	config := container.Config() | 	config := container.Config() | ||||||
|  |  | ||||||
| 	if config.RootFs != expectedConfig.RootFs { | 	if config.Rootfs != expectedConfig.Rootfs { | ||||||
| 		t.Fatalf("expected rootfs %q but received %q", expectedConfig.RootFs, config.RootFs) | 		t.Fatalf("expected rootfs %q but received %q", expectedConfig.Rootfs, config.Rootfs) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	lcontainer, ok := container.(*linuxContainer) | 	lcontainer, ok := container.(*linuxContainer) | ||||||
|   | |||||||
| @@ -33,11 +33,11 @@ func InitializeMountNamespace(config *configs.Config) (err error) { | |||||||
| 	} | 	} | ||||||
| 	// apply any user specified mounts within the new mount namespace | 	// apply any user specified mounts within the new mount namespace | ||||||
| 	for _, m := range config.Mounts { | 	for _, m := range config.Mounts { | ||||||
| 		if err := m.Mount(config.RootFs, config.MountLabel); err != nil { | 		if err := m.Mount(config.Rootfs, config.MountLabel); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if err := createDeviceNodes(config); err != nil { | 	if err := createDevices(config); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if err := setupPtmx(config); err != nil { | 	if err := setupPtmx(config); err != nil { | ||||||
| @@ -51,21 +51,21 @@ func InitializeMountNamespace(config *configs.Config) (err error) { | |||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if err := setupDevSymlinks(config.RootFs); err != nil { | 	if err := setupDevSymlinks(config.Rootfs); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if err := syscall.Chdir(config.RootFs); err != nil { | 	if err := syscall.Chdir(config.Rootfs); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if config.NoPivotRoot { | 	if config.NoPivotRoot { | ||||||
| 		err = msMoveRoot(config.RootFs) | 		err = msMoveRoot(config.Rootfs) | ||||||
| 	} else { | 	} else { | ||||||
| 		err = pivotRoot(config.RootFs, config.PivotDir) | 		err = pivotRoot(config.Rootfs, config.PivotDir) | ||||||
| 	} | 	} | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if config.ReadonlyFs { | 	if config.Readonlyfs { | ||||||
| 		if err := setReadonly(); err != nil { | 		if err := setReadonly(); err != nil { | ||||||
| 			return fmt.Errorf("set readonly %s", err) | 			return fmt.Errorf("set readonly %s", err) | ||||||
| 		} | 		} | ||||||
| @@ -77,7 +77,7 @@ func InitializeMountNamespace(config *configs.Config) (err error) { | |||||||
| // mountSystem sets up linux specific system mounts like mqueue, sys, proc, shm, and devpts | // mountSystem sets up linux specific system mounts like mqueue, sys, proc, shm, and devpts | ||||||
| // inside the mount namespace | // inside the mount namespace | ||||||
| func mountSystem(config *configs.Config) error { | func mountSystem(config *configs.Config) error { | ||||||
| 	for _, m := range newSystemMounts(config.RootFs, config.MountLabel, config.RestrictSys) { | 	for _, m := range newSystemMounts(config.Rootfs, config.MountLabel, config.RestrictSys) { | ||||||
| 		if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) { | 		if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) { | ||||||
| 			return fmt.Errorf("mkdirall %s %s", m.path, err) | 			return fmt.Errorf("mkdirall %s %s", m.path, err) | ||||||
| 		} | 		} | ||||||
| @@ -164,10 +164,10 @@ func reOpenDevNull(rootfs string) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Create the device nodes in the container. | // Create the device nodes in the container. | ||||||
| func createDeviceNodes(config *configs.Config) error { | func createDevices(config *configs.Config) error { | ||||||
| 	oldMask := syscall.Umask(0000) | 	oldMask := syscall.Umask(0000) | ||||||
| 	for _, node := range config.DeviceNodes { | 	for _, node := range config.Devices { | ||||||
| 		if err := createDeviceNode(config.RootFs, node); err != nil { | 		if err := createDeviceNode(config.Rootfs, node); err != nil { | ||||||
| 			syscall.Umask(oldMask) | 			syscall.Umask(oldMask) | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -211,5 +211,5 @@ func prepareRoot(config *configs.Config) error { | |||||||
| 	if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { | 	if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return syscall.Mount(config.RootFs, config.RootFs, "bind", syscall.MS_BIND|syscall.MS_REC, "") | 	return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "") | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func setupPtmx(config *configs.Config) error { | func setupPtmx(config *configs.Config) error { | ||||||
| 	ptmx := filepath.Join(config.RootFs, "dev/ptmx") | 	ptmx := filepath.Join(config.Rootfs, "dev/ptmx") | ||||||
| 	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { | 	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -28,7 +28,7 @@ func setupPtmx(config *configs.Config) error { | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		return console.Setup(config.RootFs, config.Console, config.MountLabel, uid, gid) | 		return console.Setup(config.Rootfs, config.Console, config.MountLabel, uid, gid) | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								process.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								process.go
									
									
									
									
									
								
							| @@ -2,19 +2,18 @@ package libcontainer | |||||||
|  |  | ||||||
| import "io" | import "io" | ||||||
|  |  | ||||||
| // Configuration for a process to be run inside a container. | // Process specifies the configuration and IO for a process inside | ||||||
|  | // a container. | ||||||
| type Process struct { | type Process struct { | ||||||
| 	// The command to be run followed by any arguments. | 	// The command to be run followed by any arguments. | ||||||
| 	Args []string | 	Args []string | ||||||
|  |  | ||||||
| 	// Stdin is a pointer to a reader which provides the standard input stream. | 	// Stdin is a pointer to a reader which provides the standard input stream. | ||||||
| 	// Stdout is a pointer to a writer which receives the standard output stream. |  | ||||||
| 	// Stderr is a pointer to a writer which receives the standard error stream. |  | ||||||
| 	// |  | ||||||
| 	// If a reader or writer is nil, the input stream is assumed to be empty and the output is |  | ||||||
| 	// discarded. |  | ||||||
| 	// |  | ||||||
| 	// Stdout and Stderr may refer to the same writer in which case the output is interspersed. |  | ||||||
| 	Stdin io.Reader | 	Stdin io.Reader | ||||||
|  |  | ||||||
|  | 	// Stdout is a pointer to a writer which receives the standard output stream. | ||||||
| 	Stdout io.Writer | 	Stdout io.Writer | ||||||
|  |  | ||||||
|  | 	// Stderr is a pointer to a writer which receives the standard error stream. | ||||||
| 	Stderr io.Writer | 	Stderr io.Writer | ||||||
| } | } | ||||||
|   | |||||||
| @@ -118,7 +118,7 @@ | |||||||
|     }, |     }, | ||||||
|     "restrict_sys": true, |     "restrict_sys": true, | ||||||
|     "apparmor_profile": "docker-default", |     "apparmor_profile": "docker-default", | ||||||
|         "device_nodes": [ |         "devices": [ | ||||||
|             { |             { | ||||||
|                 "permissions": "rwm", |                 "permissions": "rwm", | ||||||
|                 "file_mode": 438, |                 "file_mode": 438, | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ | |||||||
|         "parent": "docker" |         "parent": "docker" | ||||||
|     }, |     }, | ||||||
|     "restrict_sys": true, |     "restrict_sys": true, | ||||||
|     "device_nodes": [ |     "devices": [ | ||||||
|         { |         { | ||||||
|             "permissions": "rwm", |             "permissions": "rwm", | ||||||
|             "file_mode": 438, |             "file_mode": 438, | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ | |||||||
|         "parent": "docker" |         "parent": "docker" | ||||||
|     }, |     }, | ||||||
|     "restrict_sys": true, |     "restrict_sys": true, | ||||||
|         "device_nodes": [ |         "devices": [ | ||||||
|             { |             { | ||||||
|                 "permissions": "rwm", |                 "permissions": "rwm", | ||||||
|                 "file_mode": 438, |                 "file_mode": 438, | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ | |||||||
|         "parent": "docker" |         "parent": "docker" | ||||||
|     }, |     }, | ||||||
|     "restrict_sys": true, |     "restrict_sys": true, | ||||||
|         "device_nodes": [ |         "devices": [ | ||||||
|             { |             { | ||||||
|                 "permissions": "rwm", |                 "permissions": "rwm", | ||||||
|                 "file_mode": 438, |                 "file_mode": 438, | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ | |||||||
|         "parent": "docker" |         "parent": "docker" | ||||||
|     }, |     }, | ||||||
|     "restrict_sys": true, |     "restrict_sys": true, | ||||||
|         "device_nodes": [ |         "devices": [ | ||||||
|             { |             { | ||||||
|                 "permissions": "rwm", |                 "permissions": "rwm", | ||||||
|                 "file_mode": 438, |                 "file_mode": 438, | ||||||
|   | |||||||
| @@ -119,7 +119,7 @@ | |||||||
|     "restrict_sys": true, |     "restrict_sys": true, | ||||||
|     "process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", |     "process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", | ||||||
|         "mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", |         "mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", | ||||||
|         "device_nodes": [ |         "devices": [ | ||||||
|             { |             { | ||||||
|                 "permissions": "rwm", |                 "permissions": "rwm", | ||||||
|                 "file_mode": 438, |                 "file_mode": 438, | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ | |||||||
|         "parent": "docker" |         "parent": "docker" | ||||||
|     }, |     }, | ||||||
|     "restrict_sys": true, |     "restrict_sys": true, | ||||||
|         "device_nodes": [ |         "devices": [ | ||||||
|             { |             { | ||||||
|                 "permissions": "rwm", |                 "permissions": "rwm", | ||||||
|                 "file_mode": 438, |                 "file_mode": 438, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Michael Crosby
					Michael Crosby