Add partial user namespace support

This adds the OCI parts needed into the yaml, but there are still
permissions issues in practise so marked as experimental.

It may just need further documentation to resolve the issues.

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
This commit is contained in:
Justin Cormack 2017-07-03 14:45:58 +01:00
parent c7c4c9ef2a
commit a73c3d3667
3 changed files with 76 additions and 34 deletions

View File

@ -158,6 +158,9 @@ bind mounted into a container.
- `sysctl` sets a list of `sysctl` key value pairs that are set inside the container namespace. - `sysctl` sets a list of `sysctl` key value pairs that are set inside the container namespace.
- `rmlimits` sets a list of `rlimit` values in the form `name,soft,hard`, eg `nofile,100,200`. You can use `unlimited` as a value too. - `rmlimits` sets a list of `rlimit` values in the form `name,soft,hard`, eg `nofile,100,200`. You can use `unlimited` as a value too.
There are experimental `userns`, `uidMappings` and `gidMappings` options for user namespaces but these are not yet supported, and may have
permissions issues in use.
### Mount Options ### Mount Options
When mounting filesystem paths into a container - whether as part of `onboot` or `services` - there are several options of which you need to be aware. Using them properly is necessary for your containers to function properly. When mounting filesystem paths into a container - whether as part of `onboot` or `services` - there are several options of which you need to be aware. Using them properly is necessary for your containers to function properly.

View File

@ -65,6 +65,7 @@ type Image struct {
Pid string `yaml:"pid" json:"pid"` Pid string `yaml:"pid" json:"pid"`
Ipc string `yaml:"ipc" json:"ipc"` Ipc string `yaml:"ipc" json:"ipc"`
Uts string `yaml:"uts" json:"uts"` Uts string `yaml:"uts" json:"uts"`
Userns string `yaml:"userns" json:"userns"`
Hostname string `yaml:"hostname" json:"hostname"` Hostname string `yaml:"hostname" json:"hostname"`
Readonly *bool `yaml:"readonly" json:"readonly,omitempty"` Readonly *bool `yaml:"readonly" json:"readonly,omitempty"`
MaskedPaths *[]string `yaml:"maskedPaths" json:"maskedPaths,omitempty"` MaskedPaths *[]string `yaml:"maskedPaths" json:"maskedPaths,omitempty"`
@ -79,6 +80,8 @@ type Image struct {
CgroupsPath *string `yaml:"cgroupsPath" json:"cgroupsPath,omitempty"` CgroupsPath *string `yaml:"cgroupsPath" json:"cgroupsPath,omitempty"`
Sysctl *map[string]string `yaml:"sysctl" json:"sysctl,omitempty"` Sysctl *map[string]string `yaml:"sysctl" json:"sysctl,omitempty"`
Rlimits *[]string `yaml:"rlimits" json:"rlimits,omitempty"` Rlimits *[]string `yaml:"rlimits" json:"rlimits,omitempty"`
UIDMappings *[]specs.LinuxIDMapping `yaml:"uidMappings" json:"uidMappings,omitempty"`
GIDMappings *[]specs.LinuxIDMapping `yaml:"gidMappings" json:"gidMappings,omitempty"`
} }
// github.com/go-yaml/yaml treats map keys as interface{} while encoding/json // github.com/go-yaml/yaml treats map keys as interface{} while encoding/json
@ -390,6 +393,17 @@ func assignString(v1, v2 *string) string {
return "" return ""
} }
// assignMappings does prdered overrides from UID, GID maps
func assignMappings(v1, v2 *[]specs.LinuxIDMapping) []specs.LinuxIDMapping {
if v2 != nil {
return *v2
}
if v1 != nil {
return *v1
}
return []specs.LinuxIDMapping{}
}
// assignStringEmpty does ordered overrides if strings are empty, for // assignStringEmpty does ordered overrides if strings are empty, for
// values where there is always an explicit override eg "none" // values where there is always an explicit override eg "none"
func assignStringEmpty(v1, v2 string) string { func assignStringEmpty(v1, v2 string) string {
@ -604,7 +618,7 @@ func ConfigInspectToOCI(yaml Image, inspect types.ImageInspect, idMap map[string
namespaces := []specs.LinuxNamespace{} namespaces := []specs.LinuxNamespace{}
// to attach to an existing namespace, easiest to bind mount with nsfs in a system container // to attach to an existing namespace, easiest to bind mount with nsfs in a system container
// net, ipc and uts namespaces: default to not creating a new namespace (usually host namespace) // net, ipc, and uts namespaces: default to not creating a new namespace (usually host namespace)
netNS := assignStringEmpty3("root", label.Net, yaml.Net) netNS := assignStringEmpty3("root", label.Net, yaml.Net)
if netNS != "host" && netNS != "root" { if netNS != "host" && netNS != "root" {
if netNS == "none" || netNS == "new" { if netNS == "none" || netNS == "new" {
@ -636,10 +650,19 @@ func ConfigInspectToOCI(yaml Image, inspect types.ImageInspect, idMap map[string
namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.PIDNamespace, Path: pidNS}) namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.PIDNamespace, Path: pidNS})
} }
// do not create a user namespace unless asked, needs additional configuration
userNS := assignStringEmpty3("root", label.Userns, yaml.Userns)
if userNS != "host" && userNS != "root" {
if userNS == "new" {
userNS = ""
}
namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.UserNamespace, Path: userNS})
}
// Always create a new mount namespace // Always create a new mount namespace
namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.MountNamespace}) namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.MountNamespace})
// TODO user, cgroup namespaces // TODO cgroup namespaces
// Capabilities // Capabilities
capCheck := map[string]bool{} capCheck := map[string]bool{}
@ -806,8 +829,8 @@ func ConfigInspectToOCI(yaml Image, inspect types.ImageInspect, idMap map[string
oci.Mounts = mountList oci.Mounts = mountList
oci.Linux = &specs.Linux{ oci.Linux = &specs.Linux{
// UIDMappings UIDMappings: assignMappings(label.UIDMappings, yaml.UIDMappings),
// GIDMappings GIDMappings: assignMappings(label.GIDMappings, yaml.GIDMappings),
Sysctl: assignMaps(label.Sysctl, yaml.Sysctl), Sysctl: assignMaps(label.Sysctl, yaml.Sysctl),
Resources: &specs.LinuxResources{ Resources: &specs.LinuxResources{
// Devices // Devices

View File

@ -59,6 +59,19 @@ var schema = string(`
"type": "array", "type": "array",
"items": { "$ref": "#/definitions/mount" } "items": { "$ref": "#/definitions/mount" }
}, },
"idmapping": {
"type": "object",
"additionalProperties": false,
"properties": {
"hostID": { "type": "integer" },
"containerID": { "type": "integer" },
"size": { "type": "integer" }
}
},
"idmappings": {
"type": "array",
"items": { "$ref": "#/definitions/idmapping" }
},
"image": { "image": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@ -78,6 +91,7 @@ var schema = string(`
"pid": { "type": "string"}, "pid": { "type": "string"},
"ipc": { "type": "string"}, "ipc": { "type": "string"},
"uts": { "type": "string"}, "uts": { "type": "string"},
"userns": { "type": "string"},
"readonly": { "type": "boolean"}, "readonly": { "type": "boolean"},
"maskedPaths": { "$ref": "#/definitions/strings" }, "maskedPaths": { "$ref": "#/definitions/strings" },
"readonlyPaths": { "$ref": "#/definitions/strings" }, "readonlyPaths": { "$ref": "#/definitions/strings" },
@ -97,7 +111,9 @@ var schema = string(`
"type": "array", "type": "array",
"items": { "$ref": "#/definitions/strings" } "items": { "$ref": "#/definitions/strings" }
}, },
"rlimits": { "$ref": "#/definitions/strings" } "rlimits": { "$ref": "#/definitions/strings" },
"uidMappings": { "$ref": "#/definitions/idmappings" },
"gidMappings": { "$ref": "#/definitions/idmappings" }
} }
}, },
"images": { "images": {