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  }