github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/integration-cli/docker_cli_authz_unix_test.go (about) 1 // +build !windows 2 3 package main 4 5 import ( 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "net/http" 10 "net/http/httptest" 11 "os" 12 "strings" 13 14 "github.com/docker/docker/pkg/authorization" 15 "github.com/docker/docker/pkg/integration/checker" 16 "github.com/docker/docker/pkg/plugins" 17 "github.com/go-check/check" 18 ) 19 20 const ( 21 testAuthZPlugin = "authzplugin" 22 unauthorizedMessage = "User unauthorized authz plugin" 23 errorMessage = "something went wrong..." 24 containerListAPI = "/containers/json" 25 ) 26 27 func init() { 28 check.Suite(&DockerAuthzSuite{ 29 ds: &DockerSuite{}, 30 }) 31 } 32 33 type DockerAuthzSuite struct { 34 server *httptest.Server 35 ds *DockerSuite 36 d *Daemon 37 ctrl *authorizationController 38 } 39 40 type authorizationController struct { 41 reqRes authorization.Response // reqRes holds the plugin response to the initial client request 42 resRes authorization.Response // resRes holds the plugin response to the daemon response 43 psRequestCnt int // psRequestCnt counts the number of calls to list container request api 44 psResponseCnt int // psResponseCnt counts the number of calls to list containers response API 45 requestsURIs []string // requestsURIs stores all request URIs that are sent to the authorization controller 46 47 } 48 49 func (s *DockerAuthzSuite) SetUpTest(c *check.C) { 50 s.d = NewDaemon(c) 51 s.ctrl = &authorizationController{} 52 } 53 54 func (s *DockerAuthzSuite) TearDownTest(c *check.C) { 55 s.d.Stop() 56 s.ds.TearDownTest(c) 57 s.ctrl = nil 58 } 59 60 func (s *DockerAuthzSuite) SetUpSuite(c *check.C) { 61 mux := http.NewServeMux() 62 s.server = httptest.NewServer(mux) 63 c.Assert(s.server, check.NotNil, check.Commentf("Failed to start a HTTP Server")) 64 65 mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { 66 b, err := json.Marshal(plugins.Manifest{Implements: []string{authorization.AuthZApiImplements}}) 67 c.Assert(err, check.IsNil) 68 w.Write(b) 69 }) 70 71 mux.HandleFunc("/AuthZPlugin.AuthZReq", func(w http.ResponseWriter, r *http.Request) { 72 if s.ctrl.reqRes.Err != "" { 73 w.WriteHeader(http.StatusInternalServerError) 74 } 75 b, err := json.Marshal(s.ctrl.reqRes) 76 c.Assert(err, check.IsNil) 77 w.Write(b) 78 defer r.Body.Close() 79 body, err := ioutil.ReadAll(r.Body) 80 c.Assert(err, check.IsNil) 81 authReq := authorization.Request{} 82 err = json.Unmarshal(body, &authReq) 83 c.Assert(err, check.IsNil) 84 85 assertBody(c, authReq.RequestURI, authReq.RequestHeaders, authReq.RequestBody) 86 assertAuthHeaders(c, authReq.RequestHeaders) 87 88 // Count only container list api 89 if strings.HasSuffix(authReq.RequestURI, containerListAPI) { 90 s.ctrl.psRequestCnt++ 91 } 92 93 s.ctrl.requestsURIs = append(s.ctrl.requestsURIs, authReq.RequestURI) 94 }) 95 96 mux.HandleFunc("/AuthZPlugin.AuthZRes", func(w http.ResponseWriter, r *http.Request) { 97 if s.ctrl.resRes.Err != "" { 98 w.WriteHeader(http.StatusInternalServerError) 99 } 100 b, err := json.Marshal(s.ctrl.resRes) 101 c.Assert(err, check.IsNil) 102 w.Write(b) 103 104 defer r.Body.Close() 105 body, err := ioutil.ReadAll(r.Body) 106 c.Assert(err, check.IsNil) 107 authReq := authorization.Request{} 108 err = json.Unmarshal(body, &authReq) 109 c.Assert(err, check.IsNil) 110 111 assertBody(c, authReq.RequestURI, authReq.ResponseHeaders, authReq.ResponseBody) 112 assertAuthHeaders(c, authReq.ResponseHeaders) 113 114 // Count only container list api 115 if strings.HasSuffix(authReq.RequestURI, containerListAPI) { 116 s.ctrl.psResponseCnt++ 117 } 118 }) 119 120 err := os.MkdirAll("/etc/docker/plugins", 0755) 121 c.Assert(err, checker.IsNil) 122 123 fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", testAuthZPlugin) 124 err = ioutil.WriteFile(fileName, []byte(s.server.URL), 0644) 125 c.Assert(err, checker.IsNil) 126 } 127 128 // assertAuthHeaders validates authentication headers are removed 129 func assertAuthHeaders(c *check.C, headers map[string]string) error { 130 for k := range headers { 131 if strings.Contains(strings.ToLower(k), "auth") || strings.Contains(strings.ToLower(k), "x-registry") { 132 c.Errorf("Found authentication headers in request '%v'", headers) 133 } 134 } 135 return nil 136 } 137 138 // assertBody asserts that body is removed for non text/json requests 139 func assertBody(c *check.C, requestURI string, headers map[string]string, body []byte) { 140 141 if strings.Contains(strings.ToLower(requestURI), "auth") && len(body) > 0 { 142 //return fmt.Errorf("Body included for authentication endpoint %s", string(body)) 143 c.Errorf("Body included for authentication endpoint %s", string(body)) 144 } 145 146 for k, v := range headers { 147 if strings.EqualFold(k, "Content-Type") && strings.HasPrefix(v, "text/") || v == "application/json" { 148 return 149 } 150 } 151 if len(body) > 0 { 152 c.Errorf("Body included while it should not (Headers: '%v')", headers) 153 } 154 } 155 156 func (s *DockerAuthzSuite) TearDownSuite(c *check.C) { 157 if s.server == nil { 158 return 159 } 160 161 s.server.Close() 162 163 err := os.RemoveAll("/etc/docker/plugins") 164 c.Assert(err, checker.IsNil) 165 } 166 167 func (s *DockerAuthzSuite) TestAuthZPluginAllowRequest(c *check.C) { 168 169 err := s.d.Start("--authz-plugin=" + testAuthZPlugin) 170 c.Assert(err, check.IsNil) 171 s.ctrl.reqRes.Allow = true 172 s.ctrl.resRes.Allow = true 173 174 // Ensure command successful 175 out, err := s.d.Cmd("run", "-d", "--name", "container1", "busybox:latest", "top") 176 c.Assert(err, check.IsNil) 177 178 // Extract the id of the created container 179 res := strings.Split(strings.TrimSpace(out), "\n") 180 id := res[len(res)-1] 181 assertURIRecorded(c, s.ctrl.requestsURIs, "/containers/create") 182 assertURIRecorded(c, s.ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", id)) 183 184 out, err = s.d.Cmd("ps") 185 c.Assert(err, check.IsNil) 186 c.Assert(assertContainerList(out, []string{id}), check.Equals, true) 187 c.Assert(s.ctrl.psRequestCnt, check.Equals, 1) 188 c.Assert(s.ctrl.psResponseCnt, check.Equals, 1) 189 } 190 191 func (s *DockerAuthzSuite) TestAuthZPluginDenyRequest(c *check.C) { 192 193 err := s.d.Start("--authz-plugin=" + testAuthZPlugin) 194 c.Assert(err, check.IsNil) 195 s.ctrl.reqRes.Allow = false 196 s.ctrl.reqRes.Msg = unauthorizedMessage 197 198 // Ensure command is blocked 199 res, err := s.d.Cmd("ps") 200 c.Assert(err, check.NotNil) 201 c.Assert(s.ctrl.psRequestCnt, check.Equals, 1) 202 c.Assert(s.ctrl.psResponseCnt, check.Equals, 0) 203 204 // Ensure unauthorized message appears in response 205 c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: %s\n", unauthorizedMessage)) 206 } 207 208 func (s *DockerAuthzSuite) TestAuthZPluginDenyResponse(c *check.C) { 209 210 err := s.d.Start("--authz-plugin=" + testAuthZPlugin) 211 c.Assert(err, check.IsNil) 212 s.ctrl.reqRes.Allow = true 213 s.ctrl.resRes.Allow = false 214 s.ctrl.resRes.Msg = unauthorizedMessage 215 216 // Ensure command is blocked 217 res, err := s.d.Cmd("ps") 218 c.Assert(err, check.NotNil) 219 c.Assert(s.ctrl.psRequestCnt, check.Equals, 1) 220 c.Assert(s.ctrl.psResponseCnt, check.Equals, 1) 221 222 // Ensure unauthorized message appears in response 223 c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: %s\n", unauthorizedMessage)) 224 } 225 226 func (s *DockerAuthzSuite) TestAuthZPluginErrorResponse(c *check.C) { 227 err := s.d.Start("--authz-plugin=" + testAuthZPlugin) 228 c.Assert(err, check.IsNil) 229 s.ctrl.reqRes.Allow = true 230 s.ctrl.resRes.Err = errorMessage 231 232 // Ensure command is blocked 233 res, err := s.d.Cmd("ps") 234 c.Assert(err, check.NotNil) 235 236 c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: Plugin Error: %s, %s\n", errorMessage, authorization.AuthZApiResponse)) 237 } 238 239 func (s *DockerAuthzSuite) TestAuthZPluginErrorRequest(c *check.C) { 240 err := s.d.Start("--authz-plugin=" + testAuthZPlugin) 241 c.Assert(err, check.IsNil) 242 s.ctrl.reqRes.Err = errorMessage 243 244 // Ensure command is blocked 245 res, err := s.d.Cmd("ps") 246 c.Assert(err, check.NotNil) 247 248 c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: Plugin Error: %s, %s\n", errorMessage, authorization.AuthZApiRequest)) 249 } 250 251 // assertURIRecorded verifies that the given URI was sent and recorded in the authz plugin 252 func assertURIRecorded(c *check.C, uris []string, uri string) { 253 254 found := false 255 for _, u := range uris { 256 if strings.Contains(u, uri) { 257 found = true 258 } 259 } 260 if !found { 261 c.Fatalf("Expected to find URI '%s', recorded uris '%s'", uri, strings.Join(uris, ",")) 262 } 263 }