Browse code

Create a -G option that specifies the group which unix sockets belong to.

Docker-DCO-1.1-Signed-off-by: Evan Krall <krall@yelp.com> (github: EvanKrall)

Evan Krall authored on 2014/01/25 09:18:02
Showing 4 changed files
... ...
@@ -1104,10 +1104,34 @@ func ServeFd(addr string, handle http.Handler) error {
1104 1104
 	return nil
1105 1105
 }
1106 1106
 
1107
+func lookupGidByName(nameOrGid string) (int, error) {
1108
+	groups, err := user.ParseGroupFilter(func(g *user.Group) bool {
1109
+		return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
1110
+	})
1111
+	if err != nil {
1112
+		return -1, err
1113
+	}
1114
+	if groups != nil && len(groups) > 0 {
1115
+		return groups[0].Gid, nil
1116
+	}
1117
+	return -1, fmt.Errorf("Group %s not found", nameOrGid)
1118
+}
1119
+
1120
+func changeGroup(addr string, nameOrGid string) error {
1121
+	gid, err := lookupGidByName(nameOrGid)
1122
+	if err != nil {
1123
+		return err
1124
+	}
1125
+
1126
+	utils.Debugf("%s group found. gid: %d", nameOrGid, gid)
1127
+	return os.Chown(addr, 0, gid)
1128
+}
1129
+
1107 1130
 // ListenAndServe sets up the required http.Server and gets it listening for
1108 1131
 // each addr passed in and does protocol specific checking.
1109
-func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion string) error {
1132
+func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion string, socketGroup string) error {
1110 1133
 	r, err := createRouter(eng, logging, enableCors, dockerVersion)
1134
+
1111 1135
 	if err != nil {
1112 1136
 		return err
1113 1137
 	}
... ...
@@ -1138,16 +1162,14 @@ func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors
1138 1138
 			return err
1139 1139
 		}
1140 1140
 
1141
-		groups, err := user.ParseGroupFilter(func(g *user.Group) bool {
1142
-			return g.Name == "docker"
1143
-		})
1144
-		if err != nil {
1145
-			return err
1146
-		}
1147
-		if len(groups) > 0 {
1148
-			utils.Debugf("docker group found. gid: %d", groups[0].Gid)
1149
-			if err := os.Chown(addr, 0, groups[0].Gid); err != nil {
1150
-				return err
1141
+		if socketGroup != "" {
1142
+			if err := changeGroup(addr, socketGroup); err != nil {
1143
+				if socketGroup == "docker" {
1144
+					// if the user hasn't explicitly specified the group ownership, don't fail on errors.
1145
+					utils.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error())
1146
+				} else {
1147
+					return err
1148
+				}
1151 1149
 			}
1152 1150
 		}
1153 1151
 	default:
... ...
@@ -1175,7 +1197,7 @@ func ServeApi(job *engine.Job) engine.Status {
1175 1175
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
1176 1176
 		go func() {
1177 1177
 			log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
1178
-			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
1178
+			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"), job.Getenv("SocketGroup"))
1179 1179
 		}()
1180 1180
 	}
1181 1181
 
... ...
@@ -32,6 +32,7 @@ func main() {
32 32
 		bridgeIp             = flag.String([]string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
33 33
 		pidfile              = flag.String([]string{"p", "-pidfile"}, "/var/run/docker.pid", "Path to use for daemon PID file")
34 34
 		flRoot               = flag.String([]string{"g", "-graph"}, "/var/lib/docker", "Path to use as the root of the docker runtime")
35
+		flSocketGroup        = flag.String([]string{"G", "-group"}, "docker", "Group to assign the unix socket specified by -H when running in daemon mode; use '' (the empty string) to disable setting of a group")
35 36
 		flEnableCors         = flag.Bool([]string{"#api-enable-cors", "-api-enable-cors"}, false, "Enable CORS headers in the remote API")
36 37
 		flDns                = opts.NewListOpts(opts.ValidateIp4Address)
37 38
 		flEnableIptables     = flag.Bool([]string{"#iptables", "-iptables"}, true, "Disable docker's addition of iptables rules")
... ...
@@ -138,6 +139,7 @@ func main() {
138 138
 		job.SetenvBool("Logging", true)
139 139
 		job.SetenvBool("EnableCors", *flEnableCors)
140 140
 		job.Setenv("Version", dockerversion.VERSION)
141
+		job.Setenv("SocketGroup", *flSocketGroup)
141 142
 		if err := job.Run(); err != nil {
142 143
 			log.Fatal(err)
143 144
 		}
... ...
@@ -182,9 +182,12 @@ daemon will make the ownership of the Unix socket read/writable by the
182 182
 *docker* group when the daemon starts. The ``docker`` daemon must
183 183
 always run as the root user, but if you run the ``docker`` client as a user in
184 184
 the *docker* group then you don't need to add ``sudo`` to all the
185
-client commands.  
185
+client commands. As of 0.9.0, you can specify that a group other than ``docker``
186
+should own the Unix socket with the ``-G`` option.
187
+
188
+.. warning:: The *docker* group (or the group specified with ``-G``) is
189
+   root-equivalent.
186 190
 
187
-.. warning:: The *docker* group is root-equivalent.
188 191
 
189 192
 **Example:**
190 193
 
... ...
@@ -71,6 +71,7 @@ Commands
71 71
     Usage of docker:
72 72
       -D, --debug=false: Enable debug mode
73 73
       -H, --host=[]: Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise. systemd socket activation can be used with fd://[socketfd].
74
+      -G, --group="docker": Group to assign the unix socket specified by -H when running in daemon mode; use '' (the empty string) to disable setting of a group
74 75
       --api-enable-cors=false: Enable CORS headers in the remote API
75 76
       -b, --bridge="": Attach containers to a pre-existing network bridge; use 'none' to disable container networking
76 77
       --bip="": Use this CIDR notation address for the network bridge's IP, not compatible with -b