From cc33e674925a5bdc06632f9aba5c39c86d205530 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Fri, 14 Jul 2017 16:45:44 +0100 Subject: [PATCH] Allow uid, gid fields to be numeric or names Previously I was forcing them to be strings, which is horrible. Now you can either specify a numeric uid or the name of a service to use the allocated id for that service. Signed-off-by: Justin Cormack --- docs/yaml.md | 4 +-- src/moby/config.go | 76 ++++++++++++++++++++++++++++------------- src/moby/config_test.go | 4 +-- src/moby/schema.go | 10 +++--- 4 files changed, 61 insertions(+), 33 deletions(-) diff --git a/docs/yaml.md b/docs/yaml.md index b802c6f7e..52e149706 100644 --- a/docs/yaml.md +++ b/docs/yaml.md @@ -15,8 +15,8 @@ Each section adds file to the root file system. Sections may be omitted. Each container that is specified is allocated a unique `uid` and `gid` that it may use if it wishes to run as an isolated user (or user namespace). Anywhere you specify a `uid` or `gid` -field you specify a string that can either be the numeric id, or if you use a name it will -refer to the id allocated to the container with that name. +field you specify either the numeric id, or if you use a name it will refer to the id allocated +to the container with that name. ``` services: diff --git a/src/moby/config.go b/src/moby/config.go index 2f113e5df..d790d3cf9 100644 --- a/src/moby/config.go +++ b/src/moby/config.go @@ -50,8 +50,8 @@ type File struct { Source string Optional bool Mode string - UID string `yaml:"uid" json:"uid"` - GID string `yaml:"gid" json:"gid"` + UID interface{} `yaml:"uid" json:"uid"` + GID interface{} `yaml:"gid" json:"gid"` } // Image is the type of an image config @@ -75,9 +75,9 @@ type Image struct { 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"` + UID *interface{} `yaml:"uid" json:"uid,omitempty"` + GID *interface{} `yaml:"gid" json:"gid,omitempty"` + AdditionalGids *[]interface{} `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"` @@ -369,6 +369,29 @@ func assignUint32Array(v1, v2 *[]uint32) []uint32 { return []uint32{} } +// assignInterface does ordered overrides from Go interfaces +// we return 0 as we are using this for uid and this is the default +func assignInterface(v1, v2 *interface{}) interface{} { + if v2 != nil { + return *v2 + } + if v1 != nil { + return *v1 + } + return 0 +} + +// assignInterfaceArray does ordered overrides from arrays of Go interfaces +func assignInterfaceArray(v1, v2 *[]interface{}) []interface{} { + if v2 != nil { + return *v2 + } + if v1 != nil { + return *v1 + } + return []interface{}{} +} + // assignStrings does ordered overrides from JSON string array pointers func assignStrings(v1, v2 *[]string) []string { if v2 != nil { @@ -424,7 +447,7 @@ func assignString(v1, v2 *string) string { return "" } -// assignMappings does prdered overrides from UID, GID maps +// assignMappings does ordered overrides from UID, GID maps func assignMappings(v1, v2 *[]specs.LinuxIDMapping) []specs.LinuxIDMapping { if v2 != nil { return *v2 @@ -512,20 +535,25 @@ var allCaps = []string{ "CAP_WAKE_ALARM", } -func idNumeric(id string, idMap map[string]uint32) (uint32, error) { - if id == "" || id == "root" { - return 0, nil - } - for k, v := range idMap { - if id == k { - return v, nil +func idNumeric(v interface{}, idMap map[string]uint32) (uint32, error) { + switch id := v.(type) { + case nil: + return uint32(0), nil + case int: + return uint32(id), nil + case string: + if id == "" || id == "root" { + return uint32(0), nil } + for k, v := range idMap { + if id == k { + return v, nil + } + } + return 0, fmt.Errorf("Cannot find id: %s", id) + default: + return 0, fmt.Errorf("Bad type for uid or gid") } - v, err := strconv.ParseUint(id, 10, 32) - if err != nil { - return 0, fmt.Errorf("Cannot find or parse id (%s): %v", id, err) - } - return uint32(v), nil } // ConfigInspectToOCI converts a config and the output of image inspect to an OCI config @@ -797,19 +825,19 @@ func ConfigInspectToOCI(yaml Image, inspect types.ImageInspect, idMap map[string } // handle mapping of named uid, gid to numbers - uidString := assignString(label.UID, yaml.UID) - gidString := assignString(label.GID, yaml.GID) - agStrings := assignStrings(label.AdditionalGids, yaml.AdditionalGids) - uid, err := idNumeric(uidString, idMap) + uidIf := assignInterface(label.UID, yaml.UID) + gidIf := assignInterface(label.GID, yaml.GID) + agIf := assignInterfaceArray(label.AdditionalGids, yaml.AdditionalGids) + uid, err := idNumeric(uidIf, idMap) if err != nil { return oci, err } - gid, err := idNumeric(gidString, idMap) + gid, err := idNumeric(gidIf, idMap) if err != nil { return oci, err } additionalGroups := []uint32{} - for _, id := range agStrings { + for _, id := range agIf { ag, err := idNumeric(id, idMap) if err != nil { return oci, err diff --git a/src/moby/config_test.go b/src/moby/config_test.go index b42d00b24..7e99aae92 100644 --- a/src/moby/config_test.go +++ b/src/moby/config_test.go @@ -81,8 +81,8 @@ func TestInvalidCap(t *testing.T) { func TestIdMap(t *testing.T) { idMap := map[string]uint32{"test": 199} - uid := "test" - gid := "76" + var uid interface{} = "test" + var gid interface{} = 76 yaml := Image{ Name: "test", diff --git a/src/moby/schema.go b/src/moby/schema.go index effe4836d..e5ca5b785 100644 --- a/src/moby/schema.go +++ b/src/moby/schema.go @@ -27,8 +27,8 @@ var schema = string(` "source": {"type": "string"}, "optional": {"type": "boolean"}, "mode": {"type": "string"}, - "uid": {"type": "string"}, - "gid": {"type": "string"} + "uid": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, + "gid": {"anyOf": [{"type": "string"}, {"type": "integer"}]} } }, "files": { @@ -97,11 +97,11 @@ var schema = string(` "readonly": { "type": "boolean"}, "maskedPaths": { "$ref": "#/definitions/strings" }, "readonlyPaths": { "$ref": "#/definitions/strings" }, - "uid": {"type": "string"}, - "gid": {"type": "string"}, + "uid": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, + "gid": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "additionalGids": { "type": "array", - "items": { "type": "string" } + "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]} }, "noNewPrivileges": {"type": "boolean"}, "hostname": {"type": "string"},