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.
- `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
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

@ -51,34 +51,37 @@ type File struct {
// Image is the type of an image config
type Image struct {
Name string `yaml:"name" json:"name"`
Image string `yaml:"image" json:"image"`
Capabilities *[]string `yaml:"capabilities" json:"capabilities,omitempty"`
Ambient *[]string `yaml:"ambient" json:"ambient,omitempty"`
Mounts *[]specs.Mount `yaml:"mounts" json:"mounts,omitempty"`
Binds *[]string `yaml:"binds" json:"binds,omitempty"`
Tmpfs *[]string `yaml:"tmpfs" json:"tmpfs,omitempty"`
Command *[]string `yaml:"command" json:"command,omitempty"`
Env *[]string `yaml:"env" json:"env,omitempty"`
Cwd string `yaml:"cwd" json:"cwd"`
Net string `yaml:"net" json:"net"`
Pid string `yaml:"pid" json:"pid"`
Ipc string `yaml:"ipc" json:"ipc"`
Uts string `yaml:"uts" json:"uts"`
Hostname string `yaml:"hostname" json:"hostname"`
Readonly *bool `yaml:"readonly" json:"readonly,omitempty"`
MaskedPaths *[]string `yaml:"maskedPaths" json:"maskedPaths,omitempty"`
ReadonlyPaths *[]string `yaml:"readonlyPaths" json:"readonlyPaths,omitempty"`
UID *string `yaml:"uid" json:"uid,omitempty"`
GID *string `yaml:"gid" json:"gid,omitempty"`
AdditionalGids *[]string `yaml:"additionalGids" json:"additionalGids,omitempty"`
NoNewPrivileges *bool `yaml:"noNewPrivileges" json:"noNewPrivileges,omitempty"`
OOMScoreAdj *int `yaml:"oomScoreAdj" json:"oomScoreAdj,omitempty"`
DisableOOMKiller *bool `yaml:"disableOOMKiller" json:"disableOOMKiller,omitempty"`
RootfsPropagation *string `yaml:"rootfsPropagation" json:"rootfsPropagation,omitempty"`
CgroupsPath *string `yaml:"cgroupsPath" json:"cgroupsPath,omitempty"`
Sysctl *map[string]string `yaml:"sysctl" json:"sysctl,omitempty"`
Rlimits *[]string `yaml:"rlimits" json:"rlimits,omitempty"`
Name string `yaml:"name" json:"name"`
Image string `yaml:"image" json:"image"`
Capabilities *[]string `yaml:"capabilities" json:"capabilities,omitempty"`
Ambient *[]string `yaml:"ambient" json:"ambient,omitempty"`
Mounts *[]specs.Mount `yaml:"mounts" json:"mounts,omitempty"`
Binds *[]string `yaml:"binds" json:"binds,omitempty"`
Tmpfs *[]string `yaml:"tmpfs" json:"tmpfs,omitempty"`
Command *[]string `yaml:"command" json:"command,omitempty"`
Env *[]string `yaml:"env" json:"env,omitempty"`
Cwd string `yaml:"cwd" json:"cwd"`
Net string `yaml:"net" json:"net"`
Pid string `yaml:"pid" json:"pid"`
Ipc string `yaml:"ipc" json:"ipc"`
Uts string `yaml:"uts" json:"uts"`
Userns string `yaml:"userns" json:"userns"`
Hostname string `yaml:"hostname" json:"hostname"`
Readonly *bool `yaml:"readonly" json:"readonly,omitempty"`
MaskedPaths *[]string `yaml:"maskedPaths" json:"maskedPaths,omitempty"`
ReadonlyPaths *[]string `yaml:"readonlyPaths" json:"readonlyPaths,omitempty"`
UID *string `yaml:"uid" json:"uid,omitempty"`
GID *string `yaml:"gid" json:"gid,omitempty"`
AdditionalGids *[]string `yaml:"additionalGids" json:"additionalGids,omitempty"`
NoNewPrivileges *bool `yaml:"noNewPrivileges" json:"noNewPrivileges,omitempty"`
OOMScoreAdj *int `yaml:"oomScoreAdj" json:"oomScoreAdj,omitempty"`
DisableOOMKiller *bool `yaml:"disableOOMKiller" json:"disableOOMKiller,omitempty"`
RootfsPropagation *string `yaml:"rootfsPropagation" json:"rootfsPropagation,omitempty"`
CgroupsPath *string `yaml:"cgroupsPath" json:"cgroupsPath,omitempty"`
Sysctl *map[string]string `yaml:"sysctl" json:"sysctl,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
@ -390,6 +393,17 @@ func assignString(v1, v2 *string) string {
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
// values where there is always an explicit override eg "none"
func assignStringEmpty(v1, v2 string) string {
@ -604,7 +618,7 @@ func ConfigInspectToOCI(yaml Image, inspect types.ImageInspect, idMap map[string
namespaces := []specs.LinuxNamespace{}
// 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)
if netNS != "host" && netNS != "root" {
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})
}
// 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
namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.MountNamespace})
// TODO user, cgroup namespaces
// TODO cgroup namespaces
// Capabilities
capCheck := map[string]bool{}
@ -806,9 +829,9 @@ func ConfigInspectToOCI(yaml Image, inspect types.ImageInspect, idMap map[string
oci.Mounts = mountList
oci.Linux = &specs.Linux{
// UIDMappings
// GIDMappings
Sysctl: assignMaps(label.Sysctl, yaml.Sysctl),
UIDMappings: assignMappings(label.UIDMappings, yaml.UIDMappings),
GIDMappings: assignMappings(label.GIDMappings, yaml.GIDMappings),
Sysctl: assignMaps(label.Sysctl, yaml.Sysctl),
Resources: &specs.LinuxResources{
// Devices
DisableOOMKiller: assignBoolPtr(label.DisableOOMKiller, yaml.DisableOOMKiller),

View File

@ -59,6 +59,19 @@ var schema = string(`
"type": "array",
"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": {
"type": "object",
"additionalProperties": false,
@ -78,6 +91,7 @@ var schema = string(`
"pid": { "type": "string"},
"ipc": { "type": "string"},
"uts": { "type": "string"},
"userns": { "type": "string"},
"readonly": { "type": "boolean"},
"maskedPaths": { "$ref": "#/definitions/strings" },
"readonlyPaths": { "$ref": "#/definitions/strings" },
@ -97,7 +111,9 @@ var schema = string(`
"type": "array",
"items": { "$ref": "#/definitions/strings" }
},
"rlimits": { "$ref": "#/definitions/strings" }
"rlimits": { "$ref": "#/definitions/strings" },
"uidMappings": { "$ref": "#/definitions/idmappings" },
"gidMappings": { "$ref": "#/definitions/idmappings" }
}
},
"images": {