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 <justin.cormack@docker.com>
This commit is contained in:
Justin Cormack 2017-07-14 16:45:44 +01:00
parent a824287800
commit cc33e67492
4 changed files with 61 additions and 33 deletions

View File

@ -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:

View File

@ -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

View File

@ -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",

View File

@ -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"},