Browse code

Extend Docker authorization with TLS user information

Currently Docker authorization framework does not use any user
information, which already available in the Docker context for TLS
connection.
The purpose of this CR is to complete the existing authz work by adding
the basic client certificate details (SUBJECT_NAME) and authentication
method (TLS) to the authz request.

We think this should be the default behavior when no extended
authorization module is specified (currently WIP under #20883).

Signed-off-by: Liron Levin <liron@twistlock.com>

Liron Levin authored on 2016/03/27 21:53:37
Showing 2 changed files
... ...
@@ -13,11 +13,19 @@ import (
13 13
 func NewAuthorizationMiddleware(plugins []authorization.Plugin) Middleware {
14 14
 	return func(handler httputils.APIFunc) httputils.APIFunc {
15 15
 		return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
16
-			// FIXME: fill when authN gets in
17
-			// User and UserAuthNMethod are taken from AuthN plugins
18
-			// Currently tracked in https://github.com/docker/docker/pull/13994
16
+
19 17
 			user := ""
20 18
 			userAuthNMethod := ""
19
+
20
+			// Default authorization using existing TLS connection credentials
21
+			// FIXME: Non trivial authorization mechanisms (such as advanced certificate validations, kerberos support
22
+			// and ldap) will be extracted using AuthN feature, which is tracked under:
23
+			// https://github.com/docker/docker/pull/20883
24
+			if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
25
+				user = r.TLS.PeerCertificates[0].Subject.CommonName
26
+				userAuthNMethod = "TLS"
27
+			}
28
+
21 29
 			authCtx := authorization.NewCtx(plugins, user, userAuthNMethod, r.Method, r.RequestURI)
22 30
 
23 31
 			if err := authCtx.AuthZRequest(w, r); err != nil {
... ...
@@ -53,6 +53,8 @@ type authorizationController struct {
53 53
 	psRequestCnt  int                    // psRequestCnt counts the number of calls to list container request api
54 54
 	psResponseCnt int                    // psResponseCnt counts the number of calls to list containers response API
55 55
 	requestsURIs  []string               // requestsURIs stores all request URIs that are sent to the authorization controller
56
+	reqUser       string
57
+	resUser       string
56 58
 }
57 59
 
58 60
 func (s *DockerAuthzSuite) SetUpTest(c *check.C) {
... ...
@@ -104,6 +106,7 @@ func (s *DockerAuthzSuite) SetUpSuite(c *check.C) {
104 104
 		}
105 105
 		b, err := json.Marshal(reqRes)
106 106
 		c.Assert(err, check.IsNil)
107
+		s.ctrl.reqUser = authReq.User
107 108
 		w.Write(b)
108 109
 	})
109 110
 
... ...
@@ -131,6 +134,7 @@ func (s *DockerAuthzSuite) SetUpSuite(c *check.C) {
131 131
 		}
132 132
 		b, err := json.Marshal(resRes)
133 133
 		c.Assert(err, check.IsNil)
134
+		s.ctrl.resUser = authReq.User
134 135
 		w.Write(b)
135 136
 	})
136 137
 
... ...
@@ -213,6 +217,45 @@ func (s *DockerAuthzSuite) TestAuthZPluginAllowRequest(c *check.C) {
213 213
 	c.Assert(s.ctrl.psResponseCnt, check.Equals, 1)
214 214
 }
215 215
 
216
+func (s *DockerAuthzSuite) TestAuthZPluginTls(c *check.C) {
217
+
218
+	const testDaemonHTTPSAddr = "tcp://localhost:4271"
219
+	// start the daemon and load busybox, --net=none build fails otherwise
220
+	// cause it needs to pull busybox
221
+	if err := s.d.Start(
222
+		"--authorization-plugin="+testAuthZPlugin,
223
+		"--tlsverify",
224
+		"--tlscacert",
225
+		"fixtures/https/ca.pem",
226
+		"--tlscert",
227
+		"fixtures/https/server-cert.pem",
228
+		"--tlskey",
229
+		"fixtures/https/server-key.pem",
230
+		"-H", testDaemonHTTPSAddr); err != nil {
231
+		c.Fatalf("Could not start daemon with busybox: %v", err)
232
+	}
233
+
234
+	s.ctrl.reqRes.Allow = true
235
+	s.ctrl.resRes.Allow = true
236
+
237
+	out, _ := dockerCmd(
238
+		c,
239
+		"--tlsverify",
240
+		"--tlscacert", "fixtures/https/ca.pem",
241
+		"--tlscert", "fixtures/https/client-cert.pem",
242
+		"--tlskey", "fixtures/https/client-key.pem",
243
+		"-H",
244
+		testDaemonHTTPSAddr,
245
+		"version",
246
+	)
247
+	if !strings.Contains(out, "Server") {
248
+		c.Fatalf("docker version should return information of server side")
249
+	}
250
+
251
+	c.Assert(s.ctrl.reqUser, check.Equals, "client")
252
+	c.Assert(s.ctrl.resUser, check.Equals, "client")
253
+}
254
+
216 255
 func (s *DockerAuthzSuite) TestAuthZPluginDenyRequest(c *check.C) {
217 256
 	err := s.d.Start("--authorization-plugin=" + testAuthZPlugin)
218 257
 	c.Assert(err, check.IsNil)