Signed-off-by: David Sheets <dsheets@docker.com>
| ... | ... |
@@ -15,7 +15,7 @@ source "$SCRIPTDIR/make/.go-autogen" |
| 15 | 15 |
|
| 16 | 16 |
integration_api_dirs=${TEST_INTEGRATION_DIR:-"$(
|
| 17 | 17 |
find ./integration -type d | |
| 18 |
- grep -vE '^(./integration$|./integration/util)')"} |
|
| 18 |
+ grep -vE '^(./integration($|/util|/internal|/testdata|/plugin$))')"} |
|
| 19 | 19 |
|
| 20 | 20 |
run_test_integration() {
|
| 21 | 21 |
[[ "$TESTFLAGS" != *-check.f* ]] && run_test_integration_suites |
| 22 | 22 |
deleted file mode 100644 |
| ... | ... |
@@ -1,162 +0,0 @@ |
| 1 |
-// +build !windows |
|
| 2 |
- |
|
| 3 |
-package main |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "fmt" |
|
| 7 |
- |
|
| 8 |
- "github.com/docker/docker/integration-cli/checker" |
|
| 9 |
- "github.com/docker/docker/integration-cli/daemon" |
|
| 10 |
- "github.com/go-check/check" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-var ( |
|
| 14 |
- authzPluginName = "riyaz/authz-no-volume-plugin" |
|
| 15 |
- authzPluginTag = "latest" |
|
| 16 |
- authzPluginNameWithTag = authzPluginName + ":" + authzPluginTag |
|
| 17 |
- authzPluginBadManifestName = "riyaz/authz-plugin-bad-manifest" |
|
| 18 |
- nonexistentAuthzPluginName = "riyaz/nonexistent-authz-plugin" |
|
| 19 |
-) |
|
| 20 |
- |
|
| 21 |
-func init() {
|
|
| 22 |
- check.Suite(&DockerAuthzV2Suite{
|
|
| 23 |
- ds: &DockerSuite{},
|
|
| 24 |
- }) |
|
| 25 |
-} |
|
| 26 |
- |
|
| 27 |
-type DockerAuthzV2Suite struct {
|
|
| 28 |
- ds *DockerSuite |
|
| 29 |
- d *daemon.Daemon |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func (s *DockerAuthzV2Suite) SetUpTest(c *check.C) {
|
|
| 33 |
- testRequires(c, DaemonIsLinux, Network, SameHostDaemon) |
|
| 34 |
- s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
|
| 35 |
- Experimental: testEnv.ExperimentalDaemon(), |
|
| 36 |
- }) |
|
| 37 |
- s.d.Start(c) |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (s *DockerAuthzV2Suite) TearDownTest(c *check.C) {
|
|
| 41 |
- if s.d != nil {
|
|
| 42 |
- s.d.Stop(c) |
|
| 43 |
- s.ds.TearDownTest(c) |
|
| 44 |
- } |
|
| 45 |
-} |
|
| 46 |
- |
|
| 47 |
-func (s *DockerAuthzV2Suite) TestAuthZPluginAllowNonVolumeRequest(c *check.C) {
|
|
| 48 |
- testRequires(c, DaemonIsLinux, IsAmd64, Network) |
|
| 49 |
- |
|
| 50 |
- // Install authz plugin |
|
| 51 |
- _, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginNameWithTag)
|
|
| 52 |
- c.Assert(err, checker.IsNil) |
|
| 53 |
- // start the daemon with the plugin and load busybox, --net=none build fails otherwise |
|
| 54 |
- // because it needs to pull busybox |
|
| 55 |
- s.d.Restart(c, "--authorization-plugin="+authzPluginNameWithTag) |
|
| 56 |
- s.d.LoadBusybox(c) |
|
| 57 |
- |
|
| 58 |
- // defer disabling the plugin |
|
| 59 |
- defer func() {
|
|
| 60 |
- s.d.Restart(c) |
|
| 61 |
- _, err = s.d.Cmd("plugin", "disable", authzPluginNameWithTag)
|
|
| 62 |
- c.Assert(err, checker.IsNil) |
|
| 63 |
- _, err = s.d.Cmd("plugin", "rm", authzPluginNameWithTag)
|
|
| 64 |
- c.Assert(err, checker.IsNil) |
|
| 65 |
- }() |
|
| 66 |
- |
|
| 67 |
- // Ensure docker run command and accompanying docker ps are successful |
|
| 68 |
- _, err = s.d.Cmd("run", "-d", "busybox", "top")
|
|
| 69 |
- c.Assert(err, check.IsNil) |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-func (s *DockerAuthzV2Suite) TestAuthZPluginDisable(c *check.C) {
|
|
| 73 |
- testRequires(c, DaemonIsLinux, IsAmd64, Network) |
|
| 74 |
- // Install authz plugin |
|
| 75 |
- _, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginNameWithTag)
|
|
| 76 |
- c.Assert(err, checker.IsNil) |
|
| 77 |
- // start the daemon with the plugin and load busybox, --net=none build fails otherwise |
|
| 78 |
- // because it needs to pull busybox |
|
| 79 |
- s.d.Restart(c, "--authorization-plugin="+authzPluginNameWithTag) |
|
| 80 |
- s.d.LoadBusybox(c) |
|
| 81 |
- |
|
| 82 |
- // defer removing the plugin |
|
| 83 |
- defer func() {
|
|
| 84 |
- s.d.Restart(c) |
|
| 85 |
- _, err = s.d.Cmd("plugin", "rm", "-f", authzPluginNameWithTag)
|
|
| 86 |
- c.Assert(err, checker.IsNil) |
|
| 87 |
- }() |
|
| 88 |
- |
|
| 89 |
- out, err := s.d.Cmd("volume", "create")
|
|
| 90 |
- c.Assert(err, check.NotNil) |
|
| 91 |
- c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
|
| 92 |
- |
|
| 93 |
- // disable the plugin |
|
| 94 |
- _, err = s.d.Cmd("plugin", "disable", authzPluginNameWithTag)
|
|
| 95 |
- c.Assert(err, checker.IsNil) |
|
| 96 |
- |
|
| 97 |
- // now test to see if the docker api works. |
|
| 98 |
- _, err = s.d.Cmd("volume", "create")
|
|
| 99 |
- c.Assert(err, checker.IsNil) |
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-func (s *DockerAuthzV2Suite) TestAuthZPluginRejectVolumeRequests(c *check.C) {
|
|
| 103 |
- testRequires(c, DaemonIsLinux, IsAmd64, Network) |
|
| 104 |
- // Install authz plugin |
|
| 105 |
- _, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginNameWithTag)
|
|
| 106 |
- c.Assert(err, checker.IsNil) |
|
| 107 |
- |
|
| 108 |
- // restart the daemon with the plugin |
|
| 109 |
- s.d.Restart(c, "--authorization-plugin="+authzPluginNameWithTag) |
|
| 110 |
- |
|
| 111 |
- // defer disabling the plugin |
|
| 112 |
- defer func() {
|
|
| 113 |
- s.d.Restart(c) |
|
| 114 |
- _, err = s.d.Cmd("plugin", "disable", authzPluginNameWithTag)
|
|
| 115 |
- c.Assert(err, checker.IsNil) |
|
| 116 |
- _, err = s.d.Cmd("plugin", "rm", authzPluginNameWithTag)
|
|
| 117 |
- c.Assert(err, checker.IsNil) |
|
| 118 |
- }() |
|
| 119 |
- |
|
| 120 |
- out, err := s.d.Cmd("volume", "create")
|
|
| 121 |
- c.Assert(err, check.NotNil) |
|
| 122 |
- c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
|
| 123 |
- |
|
| 124 |
- out, err = s.d.Cmd("volume", "ls")
|
|
| 125 |
- c.Assert(err, check.NotNil) |
|
| 126 |
- c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
|
| 127 |
- |
|
| 128 |
- // The plugin will block the command before it can determine the volume does not exist |
|
| 129 |
- out, err = s.d.Cmd("volume", "rm", "test")
|
|
| 130 |
- c.Assert(err, check.NotNil) |
|
| 131 |
- c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
|
| 132 |
- |
|
| 133 |
- out, err = s.d.Cmd("volume", "inspect", "test")
|
|
| 134 |
- c.Assert(err, check.NotNil) |
|
| 135 |
- c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
|
| 136 |
- |
|
| 137 |
- out, err = s.d.Cmd("volume", "prune", "-f")
|
|
| 138 |
- c.Assert(err, check.NotNil) |
|
| 139 |
- c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
|
| 140 |
-} |
|
| 141 |
- |
|
| 142 |
-func (s *DockerAuthzV2Suite) TestAuthZPluginBadManifestFailsDaemonStart(c *check.C) {
|
|
| 143 |
- testRequires(c, DaemonIsLinux, IsAmd64, Network) |
|
| 144 |
- // Install authz plugin with bad manifest |
|
| 145 |
- _, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginBadManifestName)
|
|
| 146 |
- c.Assert(err, checker.IsNil) |
|
| 147 |
- |
|
| 148 |
- // start the daemon with the plugin, it will error |
|
| 149 |
- c.Assert(s.d.RestartWithError("--authorization-plugin="+authzPluginBadManifestName), check.NotNil)
|
|
| 150 |
- |
|
| 151 |
- // restarting the daemon without requiring the plugin will succeed |
|
| 152 |
- s.d.Restart(c) |
|
| 153 |
-} |
|
| 154 |
- |
|
| 155 |
-func (s *DockerAuthzV2Suite) TestNonexistentAuthZPluginFailsDaemonStart(c *check.C) {
|
|
| 156 |
- testRequires(c, DaemonIsLinux, Network) |
|
| 157 |
- // start the daemon with a non-existent authz plugin, it will error |
|
| 158 |
- c.Assert(s.d.RestartWithError("--authorization-plugin="+nonexistentAuthzPluginName), check.NotNil)
|
|
| 159 |
- |
|
| 160 |
- // restarting the daemon without requiring the plugin will succeed |
|
| 161 |
- s.d.Start(c) |
|
| 162 |
-} |
| 163 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,470 +0,0 @@ |
| 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 |
- "path/filepath" |
|
| 13 |
- "strings" |
|
| 14 |
- |
|
| 15 |
- "bufio" |
|
| 16 |
- "bytes" |
|
| 17 |
- "os/exec" |
|
| 18 |
- "strconv" |
|
| 19 |
- "time" |
|
| 20 |
- |
|
| 21 |
- "net" |
|
| 22 |
- "net/http/httputil" |
|
| 23 |
- "net/url" |
|
| 24 |
- |
|
| 25 |
- "github.com/docker/docker/integration-cli/checker" |
|
| 26 |
- "github.com/docker/docker/integration-cli/daemon" |
|
| 27 |
- "github.com/docker/docker/pkg/authorization" |
|
| 28 |
- "github.com/docker/docker/pkg/plugins" |
|
| 29 |
- "github.com/go-check/check" |
|
| 30 |
-) |
|
| 31 |
- |
|
| 32 |
-const ( |
|
| 33 |
- testAuthZPlugin = "authzplugin" |
|
| 34 |
- unauthorizedMessage = "User unauthorized authz plugin" |
|
| 35 |
- errorMessage = "something went wrong..." |
|
| 36 |
- containerListAPI = "/containers/json" |
|
| 37 |
-) |
|
| 38 |
- |
|
| 39 |
-var ( |
|
| 40 |
- alwaysAllowed = []string{"/_ping", "/info"}
|
|
| 41 |
-) |
|
| 42 |
- |
|
| 43 |
-func init() {
|
|
| 44 |
- check.Suite(&DockerAuthzSuite{
|
|
| 45 |
- ds: &DockerSuite{},
|
|
| 46 |
- }) |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-type DockerAuthzSuite struct {
|
|
| 50 |
- server *httptest.Server |
|
| 51 |
- ds *DockerSuite |
|
| 52 |
- d *daemon.Daemon |
|
| 53 |
- ctrl *authorizationController |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-type authorizationController struct {
|
|
| 57 |
- reqRes authorization.Response // reqRes holds the plugin response to the initial client request |
|
| 58 |
- resRes authorization.Response // resRes holds the plugin response to the daemon response |
|
| 59 |
- psRequestCnt int // psRequestCnt counts the number of calls to list container request api |
|
| 60 |
- psResponseCnt int // psResponseCnt counts the number of calls to list containers response API |
|
| 61 |
- requestsURIs []string // requestsURIs stores all request URIs that are sent to the authorization controller |
|
| 62 |
- reqUser string |
|
| 63 |
- resUser string |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-func (s *DockerAuthzSuite) SetUpTest(c *check.C) {
|
|
| 67 |
- testRequires(c, SameHostDaemon) |
|
| 68 |
- s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
|
| 69 |
- Experimental: testEnv.ExperimentalDaemon(), |
|
| 70 |
- }) |
|
| 71 |
- s.ctrl = &authorizationController{}
|
|
| 72 |
-} |
|
| 73 |
- |
|
| 74 |
-func (s *DockerAuthzSuite) TearDownTest(c *check.C) {
|
|
| 75 |
- if s.d != nil {
|
|
| 76 |
- s.d.Stop(c) |
|
| 77 |
- s.ds.TearDownTest(c) |
|
| 78 |
- s.ctrl = nil |
|
| 79 |
- } |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func (s *DockerAuthzSuite) SetUpSuite(c *check.C) {
|
|
| 83 |
- mux := http.NewServeMux() |
|
| 84 |
- s.server = httptest.NewServer(mux) |
|
| 85 |
- |
|
| 86 |
- mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
|
|
| 87 |
- b, err := json.Marshal(plugins.Manifest{Implements: []string{authorization.AuthZApiImplements}})
|
|
| 88 |
- c.Assert(err, check.IsNil) |
|
| 89 |
- w.Write(b) |
|
| 90 |
- }) |
|
| 91 |
- |
|
| 92 |
- mux.HandleFunc("/AuthZPlugin.AuthZReq", func(w http.ResponseWriter, r *http.Request) {
|
|
| 93 |
- defer r.Body.Close() |
|
| 94 |
- body, err := ioutil.ReadAll(r.Body) |
|
| 95 |
- c.Assert(err, check.IsNil) |
|
| 96 |
- authReq := authorization.Request{}
|
|
| 97 |
- err = json.Unmarshal(body, &authReq) |
|
| 98 |
- c.Assert(err, check.IsNil) |
|
| 99 |
- |
|
| 100 |
- assertBody(c, authReq.RequestURI, authReq.RequestHeaders, authReq.RequestBody) |
|
| 101 |
- assertAuthHeaders(c, authReq.RequestHeaders) |
|
| 102 |
- |
|
| 103 |
- // Count only container list api |
|
| 104 |
- if strings.HasSuffix(authReq.RequestURI, containerListAPI) {
|
|
| 105 |
- s.ctrl.psRequestCnt++ |
|
| 106 |
- } |
|
| 107 |
- |
|
| 108 |
- s.ctrl.requestsURIs = append(s.ctrl.requestsURIs, authReq.RequestURI) |
|
| 109 |
- |
|
| 110 |
- reqRes := s.ctrl.reqRes |
|
| 111 |
- if isAllowed(authReq.RequestURI) {
|
|
| 112 |
- reqRes = authorization.Response{Allow: true}
|
|
| 113 |
- } |
|
| 114 |
- if reqRes.Err != "" {
|
|
| 115 |
- w.WriteHeader(http.StatusInternalServerError) |
|
| 116 |
- } |
|
| 117 |
- b, err := json.Marshal(reqRes) |
|
| 118 |
- c.Assert(err, check.IsNil) |
|
| 119 |
- s.ctrl.reqUser = authReq.User |
|
| 120 |
- w.Write(b) |
|
| 121 |
- }) |
|
| 122 |
- |
|
| 123 |
- mux.HandleFunc("/AuthZPlugin.AuthZRes", func(w http.ResponseWriter, r *http.Request) {
|
|
| 124 |
- defer r.Body.Close() |
|
| 125 |
- body, err := ioutil.ReadAll(r.Body) |
|
| 126 |
- c.Assert(err, check.IsNil) |
|
| 127 |
- authReq := authorization.Request{}
|
|
| 128 |
- err = json.Unmarshal(body, &authReq) |
|
| 129 |
- c.Assert(err, check.IsNil) |
|
| 130 |
- |
|
| 131 |
- assertBody(c, authReq.RequestURI, authReq.ResponseHeaders, authReq.ResponseBody) |
|
| 132 |
- assertAuthHeaders(c, authReq.ResponseHeaders) |
|
| 133 |
- |
|
| 134 |
- // Count only container list api |
|
| 135 |
- if strings.HasSuffix(authReq.RequestURI, containerListAPI) {
|
|
| 136 |
- s.ctrl.psResponseCnt++ |
|
| 137 |
- } |
|
| 138 |
- resRes := s.ctrl.resRes |
|
| 139 |
- if isAllowed(authReq.RequestURI) {
|
|
| 140 |
- resRes = authorization.Response{Allow: true}
|
|
| 141 |
- } |
|
| 142 |
- if resRes.Err != "" {
|
|
| 143 |
- w.WriteHeader(http.StatusInternalServerError) |
|
| 144 |
- } |
|
| 145 |
- b, err := json.Marshal(resRes) |
|
| 146 |
- c.Assert(err, check.IsNil) |
|
| 147 |
- s.ctrl.resUser = authReq.User |
|
| 148 |
- w.Write(b) |
|
| 149 |
- }) |
|
| 150 |
- |
|
| 151 |
- err := os.MkdirAll("/etc/docker/plugins", 0755)
|
|
| 152 |
- c.Assert(err, checker.IsNil) |
|
| 153 |
- |
|
| 154 |
- fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", testAuthZPlugin)
|
|
| 155 |
- err = ioutil.WriteFile(fileName, []byte(s.server.URL), 0644) |
|
| 156 |
- c.Assert(err, checker.IsNil) |
|
| 157 |
-} |
|
| 158 |
- |
|
| 159 |
-// check for always allowed endpoints to not inhibit test framework functions |
|
| 160 |
-func isAllowed(reqURI string) bool {
|
|
| 161 |
- for _, endpoint := range alwaysAllowed {
|
|
| 162 |
- if strings.HasSuffix(reqURI, endpoint) {
|
|
| 163 |
- return true |
|
| 164 |
- } |
|
| 165 |
- } |
|
| 166 |
- return false |
|
| 167 |
-} |
|
| 168 |
- |
|
| 169 |
-// assertAuthHeaders validates authentication headers are removed |
|
| 170 |
-func assertAuthHeaders(c *check.C, headers map[string]string) error {
|
|
| 171 |
- for k := range headers {
|
|
| 172 |
- if strings.Contains(strings.ToLower(k), "auth") || strings.Contains(strings.ToLower(k), "x-registry") {
|
|
| 173 |
- c.Errorf("Found authentication headers in request '%v'", headers)
|
|
| 174 |
- } |
|
| 175 |
- } |
|
| 176 |
- return nil |
|
| 177 |
-} |
|
| 178 |
- |
|
| 179 |
-// assertBody asserts that body is removed for non text/json requests |
|
| 180 |
-func assertBody(c *check.C, requestURI string, headers map[string]string, body []byte) {
|
|
| 181 |
- if strings.Contains(strings.ToLower(requestURI), "auth") && len(body) > 0 {
|
|
| 182 |
- //return fmt.Errorf("Body included for authentication endpoint %s", string(body))
|
|
| 183 |
- c.Errorf("Body included for authentication endpoint %s", string(body))
|
|
| 184 |
- } |
|
| 185 |
- |
|
| 186 |
- for k, v := range headers {
|
|
| 187 |
- if strings.EqualFold(k, "Content-Type") && strings.HasPrefix(v, "text/") || v == "application/json" {
|
|
| 188 |
- return |
|
| 189 |
- } |
|
| 190 |
- } |
|
| 191 |
- if len(body) > 0 {
|
|
| 192 |
- c.Errorf("Body included while it should not (Headers: '%v')", headers)
|
|
| 193 |
- } |
|
| 194 |
-} |
|
| 195 |
- |
|
| 196 |
-func (s *DockerAuthzSuite) TearDownSuite(c *check.C) {
|
|
| 197 |
- if s.server == nil {
|
|
| 198 |
- return |
|
| 199 |
- } |
|
| 200 |
- |
|
| 201 |
- s.server.Close() |
|
| 202 |
- |
|
| 203 |
- err := os.RemoveAll("/etc/docker/plugins")
|
|
| 204 |
- c.Assert(err, checker.IsNil) |
|
| 205 |
-} |
|
| 206 |
- |
|
| 207 |
-func (s *DockerAuthzSuite) TestAuthZPluginAllowRequest(c *check.C) {
|
|
| 208 |
- // start the daemon and load busybox, --net=none build fails otherwise |
|
| 209 |
- // cause it needs to pull busybox |
|
| 210 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin) |
|
| 211 |
- s.ctrl.reqRes.Allow = true |
|
| 212 |
- s.ctrl.resRes.Allow = true |
|
| 213 |
- s.d.LoadBusybox(c) |
|
| 214 |
- |
|
| 215 |
- // Ensure command successful |
|
| 216 |
- out, err := s.d.Cmd("run", "-d", "busybox", "top")
|
|
| 217 |
- c.Assert(err, check.IsNil) |
|
| 218 |
- |
|
| 219 |
- id := strings.TrimSpace(out) |
|
| 220 |
- assertURIRecorded(c, s.ctrl.requestsURIs, "/containers/create") |
|
| 221 |
- assertURIRecorded(c, s.ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", id))
|
|
| 222 |
-} |
|
| 223 |
- |
|
| 224 |
-func (s *DockerAuthzSuite) TestAuthZPluginTls(c *check.C) {
|
|
| 225 |
- |
|
| 226 |
- const testDaemonHTTPSAddr = "tcp://localhost:4271" |
|
| 227 |
- // start the daemon and load busybox, --net=none build fails otherwise |
|
| 228 |
- // cause it needs to pull busybox |
|
| 229 |
- s.d.Start(c, |
|
| 230 |
- "--authorization-plugin="+testAuthZPlugin, |
|
| 231 |
- "--tlsverify", |
|
| 232 |
- "--tlscacert", |
|
| 233 |
- "fixtures/https/ca.pem", |
|
| 234 |
- "--tlscert", |
|
| 235 |
- "fixtures/https/server-cert.pem", |
|
| 236 |
- "--tlskey", |
|
| 237 |
- "fixtures/https/server-key.pem", |
|
| 238 |
- "-H", testDaemonHTTPSAddr) |
|
| 239 |
- |
|
| 240 |
- s.ctrl.reqRes.Allow = true |
|
| 241 |
- s.ctrl.resRes.Allow = true |
|
| 242 |
- |
|
| 243 |
- out, _ := dockerCmd( |
|
| 244 |
- c, |
|
| 245 |
- "--tlsverify", |
|
| 246 |
- "--tlscacert", "fixtures/https/ca.pem", |
|
| 247 |
- "--tlscert", "fixtures/https/client-cert.pem", |
|
| 248 |
- "--tlskey", "fixtures/https/client-key.pem", |
|
| 249 |
- "-H", |
|
| 250 |
- testDaemonHTTPSAddr, |
|
| 251 |
- "version", |
|
| 252 |
- ) |
|
| 253 |
- if !strings.Contains(out, "Server") {
|
|
| 254 |
- c.Fatalf("docker version should return information of server side")
|
|
| 255 |
- } |
|
| 256 |
- |
|
| 257 |
- c.Assert(s.ctrl.reqUser, check.Equals, "client") |
|
| 258 |
- c.Assert(s.ctrl.resUser, check.Equals, "client") |
|
| 259 |
-} |
|
| 260 |
- |
|
| 261 |
-func (s *DockerAuthzSuite) TestAuthZPluginDenyRequest(c *check.C) {
|
|
| 262 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin) |
|
| 263 |
- s.ctrl.reqRes.Allow = false |
|
| 264 |
- s.ctrl.reqRes.Msg = unauthorizedMessage |
|
| 265 |
- |
|
| 266 |
- // Ensure command is blocked |
|
| 267 |
- res, err := s.d.Cmd("ps")
|
|
| 268 |
- c.Assert(err, check.NotNil) |
|
| 269 |
- c.Assert(s.ctrl.psRequestCnt, check.Equals, 1) |
|
| 270 |
- c.Assert(s.ctrl.psResponseCnt, check.Equals, 0) |
|
| 271 |
- |
|
| 272 |
- // Ensure unauthorized message appears in response |
|
| 273 |
- c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s\n", testAuthZPlugin, unauthorizedMessage))
|
|
| 274 |
-} |
|
| 275 |
- |
|
| 276 |
-// TestAuthZPluginAPIDenyResponse validates that when authorization plugin deny the request, the status code is forbidden |
|
| 277 |
-func (s *DockerAuthzSuite) TestAuthZPluginAPIDenyResponse(c *check.C) {
|
|
| 278 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin) |
|
| 279 |
- s.ctrl.reqRes.Allow = false |
|
| 280 |
- s.ctrl.resRes.Msg = unauthorizedMessage |
|
| 281 |
- |
|
| 282 |
- daemonURL, err := url.Parse(s.d.Sock()) |
|
| 283 |
- |
|
| 284 |
- conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10) |
|
| 285 |
- c.Assert(err, check.IsNil) |
|
| 286 |
- client := httputil.NewClientConn(conn, nil) |
|
| 287 |
- req, err := http.NewRequest("GET", "/version", nil)
|
|
| 288 |
- c.Assert(err, check.IsNil) |
|
| 289 |
- resp, err := client.Do(req) |
|
| 290 |
- |
|
| 291 |
- c.Assert(err, check.IsNil) |
|
| 292 |
- c.Assert(resp.StatusCode, checker.Equals, http.StatusForbidden) |
|
| 293 |
- c.Assert(err, checker.IsNil) |
|
| 294 |
-} |
|
| 295 |
- |
|
| 296 |
-func (s *DockerAuthzSuite) TestAuthZPluginDenyResponse(c *check.C) {
|
|
| 297 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin) |
|
| 298 |
- s.ctrl.reqRes.Allow = true |
|
| 299 |
- s.ctrl.resRes.Allow = false |
|
| 300 |
- s.ctrl.resRes.Msg = unauthorizedMessage |
|
| 301 |
- |
|
| 302 |
- // Ensure command is blocked |
|
| 303 |
- res, err := s.d.Cmd("ps")
|
|
| 304 |
- c.Assert(err, check.NotNil) |
|
| 305 |
- c.Assert(s.ctrl.psRequestCnt, check.Equals, 1) |
|
| 306 |
- c.Assert(s.ctrl.psResponseCnt, check.Equals, 1) |
|
| 307 |
- |
|
| 308 |
- // Ensure unauthorized message appears in response |
|
| 309 |
- c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s\n", testAuthZPlugin, unauthorizedMessage))
|
|
| 310 |
-} |
|
| 311 |
- |
|
| 312 |
-// TestAuthZPluginAllowEventStream verifies event stream propagates correctly after request pass through by the authorization plugin |
|
| 313 |
-func (s *DockerAuthzSuite) TestAuthZPluginAllowEventStream(c *check.C) {
|
|
| 314 |
- testRequires(c, DaemonIsLinux) |
|
| 315 |
- |
|
| 316 |
- // start the daemon and load busybox to avoid pulling busybox from Docker Hub |
|
| 317 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin) |
|
| 318 |
- s.ctrl.reqRes.Allow = true |
|
| 319 |
- s.ctrl.resRes.Allow = true |
|
| 320 |
- s.d.LoadBusybox(c) |
|
| 321 |
- |
|
| 322 |
- startTime := strconv.FormatInt(daemonTime(c).Unix(), 10) |
|
| 323 |
- // Add another command to to enable event pipelining |
|
| 324 |
- eventsCmd := exec.Command(dockerBinary, "--host", s.d.Sock(), "events", "--since", startTime) |
|
| 325 |
- stdout, err := eventsCmd.StdoutPipe() |
|
| 326 |
- if err != nil {
|
|
| 327 |
- c.Assert(err, check.IsNil) |
|
| 328 |
- } |
|
| 329 |
- |
|
| 330 |
- observer := eventObserver{
|
|
| 331 |
- buffer: new(bytes.Buffer), |
|
| 332 |
- command: eventsCmd, |
|
| 333 |
- scanner: bufio.NewScanner(stdout), |
|
| 334 |
- startTime: startTime, |
|
| 335 |
- } |
|
| 336 |
- |
|
| 337 |
- err = observer.Start() |
|
| 338 |
- c.Assert(err, checker.IsNil) |
|
| 339 |
- defer observer.Stop() |
|
| 340 |
- |
|
| 341 |
- // Create a container and wait for the creation events |
|
| 342 |
- out, err := s.d.Cmd("run", "-d", "busybox", "top")
|
|
| 343 |
- c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 344 |
- containerID := strings.TrimSpace(out) |
|
| 345 |
- c.Assert(s.d.WaitRun(containerID), checker.IsNil) |
|
| 346 |
- |
|
| 347 |
- events := map[string]chan bool{
|
|
| 348 |
- "create": make(chan bool, 1), |
|
| 349 |
- "start": make(chan bool, 1), |
|
| 350 |
- } |
|
| 351 |
- |
|
| 352 |
- matcher := matchEventLine(containerID, "container", events) |
|
| 353 |
- processor := processEventMatch(events) |
|
| 354 |
- go observer.Match(matcher, processor) |
|
| 355 |
- |
|
| 356 |
- // Ensure all events are received |
|
| 357 |
- for event, eventChannel := range events {
|
|
| 358 |
- |
|
| 359 |
- select {
|
|
| 360 |
- case <-time.After(30 * time.Second): |
|
| 361 |
- // Fail the test |
|
| 362 |
- observer.CheckEventError(c, containerID, event, matcher) |
|
| 363 |
- c.FailNow() |
|
| 364 |
- case <-eventChannel: |
|
| 365 |
- // Ignore, event received |
|
| 366 |
- } |
|
| 367 |
- } |
|
| 368 |
- |
|
| 369 |
- // Ensure both events and container endpoints are passed to the authorization plugin |
|
| 370 |
- assertURIRecorded(c, s.ctrl.requestsURIs, "/events") |
|
| 371 |
- assertURIRecorded(c, s.ctrl.requestsURIs, "/containers/create") |
|
| 372 |
- assertURIRecorded(c, s.ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", containerID))
|
|
| 373 |
-} |
|
| 374 |
- |
|
| 375 |
-func (s *DockerAuthzSuite) TestAuthZPluginErrorResponse(c *check.C) {
|
|
| 376 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin) |
|
| 377 |
- s.ctrl.reqRes.Allow = true |
|
| 378 |
- s.ctrl.resRes.Err = errorMessage |
|
| 379 |
- |
|
| 380 |
- // Ensure command is blocked |
|
| 381 |
- res, err := s.d.Cmd("ps")
|
|
| 382 |
- c.Assert(err, check.NotNil) |
|
| 383 |
- |
|
| 384 |
- c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s\n", testAuthZPlugin, authorization.AuthZApiResponse, errorMessage))
|
|
| 385 |
-} |
|
| 386 |
- |
|
| 387 |
-func (s *DockerAuthzSuite) TestAuthZPluginErrorRequest(c *check.C) {
|
|
| 388 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin) |
|
| 389 |
- s.ctrl.reqRes.Err = errorMessage |
|
| 390 |
- |
|
| 391 |
- // Ensure command is blocked |
|
| 392 |
- res, err := s.d.Cmd("ps")
|
|
| 393 |
- c.Assert(err, check.NotNil) |
|
| 394 |
- |
|
| 395 |
- c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s\n", testAuthZPlugin, authorization.AuthZApiRequest, errorMessage))
|
|
| 396 |
-} |
|
| 397 |
- |
|
| 398 |
-func (s *DockerAuthzSuite) TestAuthZPluginEnsureNoDuplicatePluginRegistration(c *check.C) {
|
|
| 399 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin) |
|
| 400 |
- |
|
| 401 |
- s.ctrl.reqRes.Allow = true |
|
| 402 |
- s.ctrl.resRes.Allow = true |
|
| 403 |
- |
|
| 404 |
- out, err := s.d.Cmd("ps")
|
|
| 405 |
- c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 406 |
- |
|
| 407 |
- // assert plugin is only called once.. |
|
| 408 |
- c.Assert(s.ctrl.psRequestCnt, check.Equals, 1) |
|
| 409 |
- c.Assert(s.ctrl.psResponseCnt, check.Equals, 1) |
|
| 410 |
-} |
|
| 411 |
- |
|
| 412 |
-func (s *DockerAuthzSuite) TestAuthZPluginEnsureLoadImportWorking(c *check.C) {
|
|
| 413 |
- s.d.Start(c, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin) |
|
| 414 |
- s.ctrl.reqRes.Allow = true |
|
| 415 |
- s.ctrl.resRes.Allow = true |
|
| 416 |
- s.d.LoadBusybox(c) |
|
| 417 |
- |
|
| 418 |
- tmp, err := ioutil.TempDir("", "test-authz-load-import")
|
|
| 419 |
- c.Assert(err, check.IsNil) |
|
| 420 |
- defer os.RemoveAll(tmp) |
|
| 421 |
- |
|
| 422 |
- savedImagePath := filepath.Join(tmp, "save.tar") |
|
| 423 |
- |
|
| 424 |
- out, err := s.d.Cmd("save", "-o", savedImagePath, "busybox")
|
|
| 425 |
- c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 426 |
- out, err = s.d.Cmd("load", "--input", savedImagePath)
|
|
| 427 |
- c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 428 |
- |
|
| 429 |
- exportedImagePath := filepath.Join(tmp, "export.tar") |
|
| 430 |
- |
|
| 431 |
- out, err = s.d.Cmd("run", "-d", "--name", "testexport", "busybox")
|
|
| 432 |
- c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 433 |
- out, err = s.d.Cmd("export", "-o", exportedImagePath, "testexport")
|
|
| 434 |
- c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 435 |
- out, err = s.d.Cmd("import", exportedImagePath)
|
|
| 436 |
- c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 437 |
-} |
|
| 438 |
- |
|
| 439 |
-func (s *DockerAuthzSuite) TestAuthZPluginHeader(c *check.C) {
|
|
| 440 |
- s.d.Start(c, "--debug", "--authorization-plugin="+testAuthZPlugin) |
|
| 441 |
- s.ctrl.reqRes.Allow = true |
|
| 442 |
- s.ctrl.resRes.Allow = true |
|
| 443 |
- s.d.LoadBusybox(c) |
|
| 444 |
- |
|
| 445 |
- daemonURL, err := url.Parse(s.d.Sock()) |
|
| 446 |
- |
|
| 447 |
- conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10) |
|
| 448 |
- c.Assert(err, check.IsNil) |
|
| 449 |
- client := httputil.NewClientConn(conn, nil) |
|
| 450 |
- req, err := http.NewRequest("GET", "/version", nil)
|
|
| 451 |
- c.Assert(err, check.IsNil) |
|
| 452 |
- resp, err := client.Do(req) |
|
| 453 |
- |
|
| 454 |
- c.Assert(err, check.IsNil) |
|
| 455 |
- c.Assert(resp.Header["Content-Type"][0], checker.Equals, "application/json") |
|
| 456 |
-} |
|
| 457 |
- |
|
| 458 |
-// assertURIRecorded verifies that the given URI was sent and recorded in the authz plugin |
|
| 459 |
-func assertURIRecorded(c *check.C, uris []string, uri string) {
|
|
| 460 |
- var found bool |
|
| 461 |
- for _, u := range uris {
|
|
| 462 |
- if strings.Contains(u, uri) {
|
|
| 463 |
- found = true |
|
| 464 |
- break |
|
| 465 |
- } |
|
| 466 |
- } |
|
| 467 |
- if !found {
|
|
| 468 |
- c.Fatalf("Expected to find URI '%s', recorded uris '%s'", uri, strings.Join(uris, ","))
|
|
| 469 |
- } |
|
| 470 |
-} |
| 471 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,23 +0,0 @@ |
| 1 |
-MIID0TCCAzqgAwIBAgIJAP2r7GqEJwSnMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD |
|
| 2 |
-VQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMG |
|
| 3 |
-A1UEChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTERMA8GA1UEAxMI |
|
| 4 |
-Y2hhbmdlbWUxETAPBgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWls |
|
| 5 |
-QGhvc3QuZG9tYWluMB4XDTEzMTIwMzE2NTYzMFoXDTIzMTIwMTE2NTYzMFowgaIx |
|
| 6 |
-CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2Nv |
|
| 7 |
-MRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYD |
|
| 8 |
-VQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEW |
|
| 9 |
-EG1haWxAaG9zdC5kb21haW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALAn |
|
| 10 |
-0xDw+5y7ZptQacq66pUhRu82JP2WU6IDgo5QUtNU6/CX5PwQATe/OnYTZQFbksxp |
|
| 11 |
-AU9boG0FCkgxfsgPYXEuZxVEGKI2fxfKHOZZI8mrkWmj6eWU/0cvCjGVc9rTITP5 |
|
| 12 |
-sNQvg+hORyVDdNp2IdsbMJayiB3AQYMFx3vSDOMTAgMBAAGjggELMIIBBzAdBgNV |
|
| 13 |
-HQ4EFgQUZu7DFz09q0QBa2+ymRm9qgK1NPswgdcGA1UdIwSBzzCBzIAUZu7DFz09 |
|
| 14 |
-q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD |
|
| 15 |
-QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x |
|
| 16 |
-ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI |
|
| 17 |
-Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq |
|
| 18 |
-hCcEpzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAF8fJKKM+/oOdnNi |
|
| 19 |
-zEd0M1+PmZOyqvjYQn/2ZR8UHH6Imgc/OPQKZXf0bVE1Txc/DaUNn9Isd1SuCuaE |
|
| 20 |
-ic3vAIYYU7PmgeNN6vwec48V96T7jr+GAi6AVMhQEc2hHCfVtx11Xx+x6aHDZzJt |
|
| 21 |
-Zxtf5lL6KSO9Y+EFwM+rju6hm5hW |
| 1 | 2 |
deleted file mode 100644 |
| ... | ... |
@@ -1,73 +0,0 @@ |
| 1 |
-Certificate: |
|
| 2 |
- Data: |
|
| 3 |
- Version: 3 (0x2) |
|
| 4 |
- Serial Number: 3 (0x3) |
|
| 5 |
- Signature Algorithm: sha1WithRSAEncryption |
|
| 6 |
- Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain |
|
| 7 |
- Validity |
|
| 8 |
- Not Before: Dec 4 14:17:54 2013 GMT |
|
| 9 |
- Not After : Dec 2 14:17:54 2023 GMT |
|
| 10 |
- Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=client/name=changeme/emailAddress=mail@host.domain |
|
| 11 |
- Subject Public Key Info: |
|
| 12 |
- Public Key Algorithm: rsaEncryption |
|
| 13 |
- Public-Key: (1024 bit) |
|
| 14 |
- Modulus: |
|
| 15 |
- 00:ca:c9:05:d0:09:4e:3e:a4:fc:d5:14:f4:a5:e8: |
|
| 16 |
- 34:d3:6b:51:e3:f3:62:ea:a1:f0:e8:ed:c4:2a:bc: |
|
| 17 |
- f0:4f:ca:07:df:e3:88:fa:f4:21:99:35:0e:3d:ea: |
|
| 18 |
- b0:86:e7:c4:d2:8a:83:2b:42:b8:ec:a3:99:62:70: |
|
| 19 |
- 81:46:cc:fc:a5:1d:d2:63:e8:eb:07:25:9a:e2:25: |
|
| 20 |
- 6d:11:56:f2:1a:51:a1:b6:3e:1c:57:32:e9:7b:2c: |
|
| 21 |
- aa:1b:cc:97:2d:89:2d:b1:c9:5e:35:28:4d:7c:fa: |
|
| 22 |
- 65:31:3e:f7:70:dd:6e:0b:3c:58:af:a8:2e:24:c0: |
|
| 23 |
- 7e:4e:78:7d:0a:9e:8f:42:43 |
|
| 24 |
- Exponent: 65537 (0x10001) |
|
| 25 |
- X509v3 extensions: |
|
| 26 |
- X509v3 Basic Constraints: |
|
| 27 |
- CA:FALSE |
|
| 28 |
- Netscape Comment: |
|
| 29 |
- Easy-RSA Generated Certificate |
|
| 30 |
- X509v3 Subject Key Identifier: |
|
| 31 |
- DE:42:EF:2D:98:A3:6C:A8:AA:E0:8C:71:2C:9D:64:23:A9:E2:7E:81 |
|
| 32 |
- X509v3 Authority Key Identifier: |
|
| 33 |
- keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB |
|
| 34 |
- DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain |
|
| 35 |
- serial:FD:AB:EC:6A:84:27:04:A7 |
|
| 36 |
- |
|
| 37 |
- X509v3 Extended Key Usage: |
|
| 38 |
- TLS Web Client Authentication |
|
| 39 |
- X509v3 Key Usage: |
|
| 40 |
- Digital Signature |
|
| 41 |
- Signature Algorithm: sha1WithRSAEncryption |
|
| 42 |
- 1c:44:26:ea:e1:66:25:cb:e4:8e:57:1c:f6:b9:17:22:62:40: |
|
| 43 |
- 12:90:8f:3b:b2:61:7a:54:94:8f:b1:20:0b:bf:a3:51:e3:fa: |
|
| 44 |
- 1c:a1:be:92:3a:d0:76:44:c0:57:83:ab:6a:e4:1a:45:49:a4: |
|
| 45 |
- af:39:0d:60:32:fc:3a:be:d7:fb:5d:99:7a:1f:87:e7:d5:ab: |
|
| 46 |
- 84:a2:5e:90:d8:bf:fa:89:6d:32:26:02:5e:31:35:68:7f:31: |
|
| 47 |
- f5:6b:51:46:bc:af:70:ed:5a:09:7d:ec:b2:48:4f:fe:c5:2f: |
|
| 48 |
- 56:04:ad:f6:c1:d2:2a:e4:6a:c4:87:fe:08:35:c5:38:cb:5e: |
|
| 49 |
- 4a:c4 |
|
| 50 |
-MIIEFTCCA36gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx |
|
| 51 |
-CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv |
|
| 52 |
-cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l |
|
| 53 |
-MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv |
|
| 54 |
-bWFpbjAeFw0xMzEyMDQxNDE3NTRaFw0yMzEyMDIxNDE3NTRaMIGgMQswCQYDVQQG |
|
| 55 |
-EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE |
|
| 56 |
-ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEPMA0GA1UEAxMGY2xp |
|
| 57 |
-ZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0 |
|
| 58 |
-LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyskF0AlOPqT81RT0 |
|
| 59 |
-peg002tR4/Ni6qHw6O3EKrzwT8oH3+OI+vQhmTUOPeqwhufE0oqDK0K47KOZYnCB |
|
| 60 |
-Rsz8pR3SY+jrByWa4iVtEVbyGlGhtj4cVzLpeyyqG8yXLYktscleNShNfPplMT73 |
|
| 61 |
-cN1uCzxYr6guJMB+Tnh9Cp6PQkMCAwEAAaOCAVkwggFVMAkGA1UdEwQCMAAwLQYJ |
|
| 62 |
-YIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV |
|
| 63 |
-HQ4EFgQU3kLvLZijbKiq4IxxLJ1kI6nifoEwgdcGA1UdIwSBzzCBzIAUZu7DFz09 |
|
| 64 |
-q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD |
|
| 65 |
-QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x |
|
| 66 |
-ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI |
|
| 67 |
-Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq |
|
| 68 |
-hCcEpzATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcN |
|
| 69 |
-AQEFBQADgYEAHEQm6uFmJcvkjlcc9rkXImJAEpCPO7JhelSUj7EgC7+jUeP6HKG+ |
|
| 70 |
-kjrQdkTAV4OrauQaRUmkrzkNYDL8Or7X+12Zeh+H59WrhKJekNi/+oltMiYCXjE1 |
|
| 71 |
-aH8x9WtRRryvcO1aCX3sskhP/sUvVgSt9sHSKuRqxIf+CDXFOMteSsQ= |
| 1 | 2 |
deleted file mode 100644 |
| ... | ... |
@@ -1,16 +0,0 @@ |
| 1 |
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMrJBdAJTj6k/NUU |
|
| 2 |
-9KXoNNNrUePzYuqh8OjtxCq88E/KB9/jiPr0IZk1Dj3qsIbnxNKKgytCuOyjmWJw |
|
| 3 |
-gUbM/KUd0mPo6wclmuIlbRFW8hpRobY+HFcy6XssqhvMly2JLbHJXjUoTXz6ZTE+ |
|
| 4 |
-93Ddbgs8WK+oLiTAfk54fQqej0JDAgMBAAECgYBOFEzKp2qbMEexe9ofL2N3rDDh |
|
| 5 |
-xkrl8OijpzkLA6i78BxMFn4dsnZlWUpciMrjhsYAExkiRRSS+QMMJimAq1jzQqc3 |
|
| 6 |
-FAQV2XGYwkd0cUn7iZGvfNnEPysjsfyYQM+m+sT0ATj4BZjVShC6kkSjTdm1leLN |
|
| 7 |
-OSvcHdcu3Xxg9ufF0QJBAPYdnNt5sIndt2WECePuRVi+uF4mlxTobFY0fjn26yhC |
|
| 8 |
-4RsnhhD3Vldygo9gvnkwrAZYaALGSPBewes2InxvjA8CQQDS7erKiNXpwoqz5XiU |
|
| 9 |
-SVEsIIVTdWzBjGbIqMOu/hUwM5FK4j6JTBks0aTGMyh0YV9L1EzM0X79J29JahCe |
|
| 10 |
-iQKNAkBKNMOGqTpBV0hko1sYDk96YobUXG5RL4L6uvkUIQ7mJMQam+AgXXL7Ctuy |
|
| 11 |
-v0iu4a38e8tgisiTMP7nHHtpaXihAkAOiN54/lzfMsykANgCP9scE1GcoqbP34Dl |
|
| 12 |
-qttxH4kOPT9xzY1JoLjLYdbc4YGUI3GRpBt2sajygNkmUey7P+2xAkBBsVCZFvTw |
|
| 13 |
-qHvOpPS2kX5ml5xoc/QAHK9N7kR+X7XFYx82RTVSqJEK4lPb+aEWn+CjiIewO4Q5 |
|
| 14 |
-ksDFuNxAzbhl |
| 1 | 2 |
deleted file mode 100644 |
| ... | ... |
@@ -1,76 +0,0 @@ |
| 1 |
-Certificate: |
|
| 2 |
- Data: |
|
| 3 |
- Version: 3 (0x2) |
|
| 4 |
- Serial Number: 4 (0x4) |
|
| 5 |
- Signature Algorithm: sha1WithRSAEncryption |
|
| 6 |
- Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain |
|
| 7 |
- Validity |
|
| 8 |
- Not Before: Dec 4 15:01:20 2013 GMT |
|
| 9 |
- Not After : Dec 2 15:01:20 2023 GMT |
|
| 10 |
- Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=*/name=changeme/emailAddress=mail@host.domain |
|
| 11 |
- Subject Public Key Info: |
|
| 12 |
- Public Key Algorithm: rsaEncryption |
|
| 13 |
- Public-Key: (1024 bit) |
|
| 14 |
- Modulus: |
|
| 15 |
- 00:c1:ff:7d:30:6f:64:4a:b1:92:b1:71:d1:c1:74: |
|
| 16 |
- e2:1d:db:2d:11:24:e1:00:d4:00:ae:6f:c8:9e:ae: |
|
| 17 |
- 67:b3:4a:bd:f7:e6:9e:57:6d:19:4c:3c:23:94:2d: |
|
| 18 |
- 3d:d6:63:84:d8:fa:76:2b:38:12:c1:ed:20:9d:32: |
|
| 19 |
- e0:e8:c2:bf:9a:77:70:04:3f:7f:ca:8c:2c:82:d6: |
|
| 20 |
- 3d:25:5c:02:1a:4f:64:93:03:dd:9c:42:97:5e:09: |
|
| 21 |
- 49:af:f0:c2:e1:30:08:0e:21:46:95:d1:13:59:c0: |
|
| 22 |
- c8:76:be:94:0d:8b:43:67:21:33:b2:08:60:9d:76: |
|
| 23 |
- a8:05:32:1e:f9:95:09:14:75 |
|
| 24 |
- Exponent: 65537 (0x10001) |
|
| 25 |
- X509v3 extensions: |
|
| 26 |
- X509v3 Basic Constraints: |
|
| 27 |
- CA:FALSE |
|
| 28 |
- Netscape Cert Type: |
|
| 29 |
- SSL Server |
|
| 30 |
- Netscape Comment: |
|
| 31 |
- Easy-RSA Generated Server Certificate |
|
| 32 |
- X509v3 Subject Key Identifier: |
|
| 33 |
- 14:02:FD:FD:DD:13:38:E0:71:EA:D1:BE:C0:0E:89:1A:2D:B6:19:06 |
|
| 34 |
- X509v3 Authority Key Identifier: |
|
| 35 |
- keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB |
|
| 36 |
- DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain |
|
| 37 |
- serial:FD:AB:EC:6A:84:27:04:A7 |
|
| 38 |
- |
|
| 39 |
- X509v3 Extended Key Usage: |
|
| 40 |
- TLS Web Server Authentication |
|
| 41 |
- X509v3 Key Usage: |
|
| 42 |
- Digital Signature, Key Encipherment |
|
| 43 |
- Signature Algorithm: sha1WithRSAEncryption |
|
| 44 |
- 40:0f:10:39:c4:b7:0f:0d:2f:bf:d2:16:cc:8e:d3:9a:fb:8b: |
|
| 45 |
- ce:4b:7b:0d:48:77:ce:f1:fe:d5:8f:ea:b1:71:ed:49:1d:9f: |
|
| 46 |
- 23:3a:16:d4:70:7c:c5:29:bf:e4:90:34:d0:f0:00:24:f4:e4: |
|
| 47 |
- df:2c:c3:83:01:66:61:c9:a8:ab:29:e7:98:6d:27:89:4a:76: |
|
| 48 |
- c9:2e:19:8e:fe:6e:d5:f8:99:11:0e:97:67:4b:34:e3:1e:e3: |
|
| 49 |
- 9f:35:00:a5:32:f9:b5:2c:f2:e0:c5:2e:cc:81:bd:18:dd:5c: |
|
| 50 |
- 12:c8:6b:fa:0c:17:74:30:55:f6:6e:20:9a:6c:1e:09:b4:0c: |
|
| 51 |
- 15:42 |
|
| 52 |
-MIIEKjCCA5OgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx |
|
| 53 |
-CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv |
|
| 54 |
-cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l |
|
| 55 |
-MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv |
|
| 56 |
-bWFpbjAeFw0xMzEyMDQxNTAxMjBaFw0yMzEyMDIxNTAxMjBaMIGbMQswCQYDVQQG |
|
| 57 |
-EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE |
|
| 58 |
-ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEKMAgGA1UEAxQBKjER |
|
| 59 |
-MA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21h |
|
| 60 |
-aW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMH/fTBvZEqxkrFx0cF04h3b |
|
| 61 |
-LREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y4OjCv5p3 |
|
| 62 |
-cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+lA2LQ2ch |
|
| 63 |
-M7IIYJ12qAUyHvmVCRR1AgMBAAGjggFzMIIBbzAJBgNVHRMEAjAAMBEGCWCGSAGG |
|
| 64 |
-+EIBAQQEAwIGQDA0BglghkgBhvhCAQ0EJxYlRWFzeS1SU0EgR2VuZXJhdGVkIFNl |
|
| 65 |
-cnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUFAL9/d0TOOBx6tG+wA6JGi22GQYw |
|
| 66 |
-gdcGA1UdIwSBzzCBzIAUZu7DFz09q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJ |
|
| 67 |
-BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUw |
|
| 68 |
-EwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQD |
|
| 69 |
-EwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1h |
|
| 70 |
-aWxAaG9zdC5kb21haW6CCQD9q+xqhCcEpzATBgNVHSUEDDAKBggrBgEFBQcDATAL |
|
| 71 |
-BgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAQA8QOcS3Dw0vv9IWzI7TmvuL |
|
| 72 |
-zkt7DUh3zvH+1Y/qsXHtSR2fIzoW1HB8xSm/5JA00PAAJPTk3yzDgwFmYcmoqynn |
|
| 73 |
-mG0niUp2yS4Zjv5u1fiZEQ6XZ0s04x7jnzUApTL5tSzy4MUuzIG9GN1cEshr+gwX |
|
| 74 |
-dDBV9m4gmmweCbQMFUI= |
| 1 | 2 |
deleted file mode 100644 |
| ... | ... |
@@ -1,16 +0,0 @@ |
| 1 |
-MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMH/fTBvZEqxkrFx |
|
| 2 |
-0cF04h3bLREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y |
|
| 3 |
-4OjCv5p3cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+ |
|
| 4 |
-lA2LQ2chM7IIYJ12qAUyHvmVCRR1AgMBAAECgYAmwckb9RUfSwyYgLm8IYLPHiuJ |
|
| 5 |
-wkllZfVg5Bo7gXJcQnFjZmJ56uTj8xvUjZlODIHM63TSO5ibv6kFXtXKCqZGd2M+ |
|
| 6 |
-wGbhZ0f+2GvKcwMmJERnIQjuoNaYSQLT0tM0VB9Iz0rJlZC+tzPZ+5pPqEumRdsS |
|
| 7 |
-IzWNXfF42AhcbwAQYQJBAPVXtMYIJc9EZsz86ZcQiMPWUpCX5vnRmtwL8kKyR8D5 |
|
| 8 |
-4KfYeiowyFffSRMMcclwNHq7TgSXN+nIXM9WyzyzwikCQQDKbNA28AgZp9aT54HP |
|
| 9 |
-WnbeE2pmt+uk/zl/BtxJSoK6H+69Jec+lf7EgL7HgOWYRSNot4uQWu8IhsHLTiUq |
|
| 10 |
-+0FtAkEAqwlRxRy4/x24bP+D+QRV0/D97j93joFJbE4Hved7jlSlAV4xDGilwlyv |
|
| 11 |
-HNB4Iu5OJ6Gcaibhm+FKkmD3noHSwQJBAIpu3fokLzX0bS+bDFBU6qO3HXX/47xj |
|
| 12 |
-+tsfQvkwZrSI8AkU6c8IX0HdVhsz0FBRQAT2ORDQz1XCarfxykNZrwUCQQCGCBIc |
|
| 13 |
-BBCWzhHlswlGidWJg3HqqO6hPPClEr3B5G87oCsdeYwiO23XT6rUnoJXfJHp6oCW |
|
| 14 |
-5nCwDu5ZTP+khltg |
| 1 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,128 @@ |
| 0 |
+package container |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "io" |
|
| 5 |
+ "os" |
|
| 6 |
+ "time" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/docker/docker/api/types" |
|
| 9 |
+ "github.com/docker/docker/api/types/container" |
|
| 10 |
+ networktypes "github.com/docker/docker/api/types/network" |
|
| 11 |
+ "github.com/docker/docker/client" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// CreateVMemLabel creates a container with the supplied parameters |
|
| 15 |
+func CreateVMemLabel(client client.APIClient, v []string, memoryMB int, labels map[string]string, img string, cmd []string) (string, error) {
|
|
| 16 |
+ ctx := context.Background() |
|
| 17 |
+ config := container.Config{
|
|
| 18 |
+ Cmd: cmd, |
|
| 19 |
+ Image: img, |
|
| 20 |
+ Labels: labels, |
|
| 21 |
+ } |
|
| 22 |
+ hostConfig := container.HostConfig{
|
|
| 23 |
+ Binds: v, |
|
| 24 |
+ Resources: container.Resources{
|
|
| 25 |
+ Memory: int64(memoryMB * 1024 * 1024), |
|
| 26 |
+ }, |
|
| 27 |
+ } |
|
| 28 |
+ networkingConfig := networktypes.NetworkingConfig{}
|
|
| 29 |
+ name := "" |
|
| 30 |
+ response, err := client.ContainerCreate(ctx, &config, &hostConfig, &networkingConfig, name) |
|
| 31 |
+ |
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ return "", err |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ return response.ID, nil |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// Run runs the command provided in the container image named |
|
| 40 |
+func Run(client client.APIClient, img string, cmd []string) (string, error) {
|
|
| 41 |
+ return RunV(client, []string{}, img, cmd)
|
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// RunV runs the command provided in the container image named with |
|
| 45 |
+// the equivalent of -v bind mounts |
|
| 46 |
+func RunV(client client.APIClient, v []string, img string, cmd []string) (string, error) {
|
|
| 47 |
+ return RunVMem(client, v, 0, img, cmd) |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// RunVMem runs the command provided in the container image named with |
|
| 51 |
+// the equivalent of -v bind mounts and a specified memory limit |
|
| 52 |
+func RunVMem(client client.APIClient, v []string, memoryMB int, img string, cmd []string) (string, error) {
|
|
| 53 |
+ return RunVMemLabel(client, v, memoryMB, map[string]string{}, img, cmd)
|
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// RunVLabel runs the command provided in the container image named |
|
| 57 |
+// with the equivalent of -v bind mounts and the specified labels |
|
| 58 |
+func RunVLabel(client client.APIClient, v []string, labels map[string]string, img string, cmd []string) (string, error) {
|
|
| 59 |
+ return RunVMemLabel(client, v, 0, labels, img, cmd) |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+// RunVMemLabel runs the command provided in the container image named |
|
| 63 |
+// with the equivalent of -v bind mounts, a specified memory limit, |
|
| 64 |
+// and the specified labels |
|
| 65 |
+func RunVMemLabel(client client.APIClient, v []string, memoryMB int, labels map[string]string, img string, cmd []string) (string, error) {
|
|
| 66 |
+ containerID, err := CreateVMemLabel(client, v, memoryMB, labels, img, cmd) |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return "", err |
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ ctx := context.Background() |
|
| 72 |
+ if err := client.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil {
|
|
| 73 |
+ return "", err |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ return containerID, nil |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Wait waits until the named container has exited |
|
| 80 |
+func Wait(client client.APIClient, container string) (int64, error) {
|
|
| 81 |
+ resultC, errC := client.ContainerWait(context.Background(), container, "") |
|
| 82 |
+ |
|
| 83 |
+ select {
|
|
| 84 |
+ case result := <-resultC: |
|
| 85 |
+ return result.StatusCode, nil |
|
| 86 |
+ case err := <-errC: |
|
| 87 |
+ return -1, err |
|
| 88 |
+ } |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+// Stop stops the named container |
|
| 92 |
+func Stop(client client.APIClient, container string) error {
|
|
| 93 |
+ timeout := time.Duration(10) * time.Second |
|
| 94 |
+ ctx := context.Background() |
|
| 95 |
+ return client.ContainerStop(ctx, container, &timeout) |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// Start starts the named container |
|
| 99 |
+func Start(client client.APIClient, container string) error {
|
|
| 100 |
+ ctx := context.Background() |
|
| 101 |
+ return client.ContainerStart(ctx, container, types.ContainerStartOptions{})
|
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+// Kill kills the named container with SIGKILL |
|
| 105 |
+func Kill(client client.APIClient, container string) error {
|
|
| 106 |
+ ctx := context.Background() |
|
| 107 |
+ return client.ContainerKill(ctx, container, "KILL") |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+// Export exports a container's file system as a tarball |
|
| 111 |
+func Export(client client.APIClient, path, name string) error {
|
|
| 112 |
+ ctx := context.Background() |
|
| 113 |
+ responseReader, err := client.ContainerExport(ctx, name) |
|
| 114 |
+ if err != nil {
|
|
| 115 |
+ return err |
|
| 116 |
+ } |
|
| 117 |
+ defer responseReader.Close() |
|
| 118 |
+ |
|
| 119 |
+ file, err := os.Create(path) |
|
| 120 |
+ if err != nil {
|
|
| 121 |
+ return err |
|
| 122 |
+ } |
|
| 123 |
+ defer file.Close() |
|
| 124 |
+ |
|
| 125 |
+ _, err = io.Copy(file, responseReader) |
|
| 126 |
+ return err |
|
| 127 |
+} |
| 0 | 128 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,66 @@ |
| 0 |
+package image |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "io" |
|
| 5 |
+ "os" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/docker/docker/api/types" |
|
| 8 |
+ "github.com/docker/docker/client" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+// Save saves an image to a tarball names by path |
|
| 12 |
+func Save(client client.APIClient, path, image string) error {
|
|
| 13 |
+ ctx := context.Background() |
|
| 14 |
+ responseReader, err := client.ImageSave(ctx, []string{image})
|
|
| 15 |
+ if err != nil {
|
|
| 16 |
+ return err |
|
| 17 |
+ } |
|
| 18 |
+ defer responseReader.Close() |
|
| 19 |
+ file, err := os.Create(path) |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ return err |
|
| 22 |
+ } |
|
| 23 |
+ defer file.Close() |
|
| 24 |
+ _, err = io.Copy(file, responseReader) |
|
| 25 |
+ return err |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+// Load loads an image from a tarball named by path |
|
| 29 |
+func Load(client client.APIClient, path string) error {
|
|
| 30 |
+ file, err := os.Open(path) |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ return err |
|
| 33 |
+ } |
|
| 34 |
+ defer file.Close() |
|
| 35 |
+ quiet := true |
|
| 36 |
+ ctx := context.Background() |
|
| 37 |
+ response, err := client.ImageLoad(ctx, file, quiet) |
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ return err |
|
| 40 |
+ } |
|
| 41 |
+ defer response.Body.Close() |
|
| 42 |
+ return nil |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// Import imports the contents of a tarball named by path |
|
| 46 |
+func Import(client client.APIClient, path string) error {
|
|
| 47 |
+ file, err := os.Open(path) |
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ return err |
|
| 50 |
+ } |
|
| 51 |
+ defer file.Close() |
|
| 52 |
+ options := types.ImageImportOptions{}
|
|
| 53 |
+ ref := "" |
|
| 54 |
+ source := types.ImageImportSource{
|
|
| 55 |
+ Source: file, |
|
| 56 |
+ SourceName: "-", |
|
| 57 |
+ } |
|
| 58 |
+ ctx := context.Background() |
|
| 59 |
+ responseReader, err := client.ImageImport(ctx, source, ref, options) |
|
| 60 |
+ if err != nil {
|
|
| 61 |
+ return err |
|
| 62 |
+ } |
|
| 63 |
+ defer responseReader.Close() |
|
| 64 |
+ return nil |
|
| 65 |
+} |
| 0 | 66 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,59 @@ |
| 0 |
+package plugin |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "io/ioutil" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/api/types" |
|
| 7 |
+ "github.com/docker/docker/client" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// InstallGrantAllPermissions installs the plugin named and grants it |
|
| 11 |
+// all permissions it may require |
|
| 12 |
+func InstallGrantAllPermissions(client client.APIClient, name string) error {
|
|
| 13 |
+ ctx := context.Background() |
|
| 14 |
+ options := types.PluginInstallOptions{
|
|
| 15 |
+ RemoteRef: name, |
|
| 16 |
+ AcceptAllPermissions: true, |
|
| 17 |
+ } |
|
| 18 |
+ responseReader, err := client.PluginInstall(ctx, "", options) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ return err |
|
| 21 |
+ } |
|
| 22 |
+ defer responseReader.Close() |
|
| 23 |
+ // we have to read the response out here because the client API |
|
| 24 |
+ // actually starts a goroutine which we can only be sure has |
|
| 25 |
+ // completed when we get EOF from reading responseBody |
|
| 26 |
+ _, err = ioutil.ReadAll(responseReader) |
|
| 27 |
+ return err |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// Enable enables the named plugin |
|
| 31 |
+func Enable(client client.APIClient, name string) error {
|
|
| 32 |
+ ctx := context.Background() |
|
| 33 |
+ options := types.PluginEnableOptions{}
|
|
| 34 |
+ return client.PluginEnable(ctx, name, options) |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// Disable disables the named plugin |
|
| 38 |
+func Disable(client client.APIClient, name string) error {
|
|
| 39 |
+ ctx := context.Background() |
|
| 40 |
+ options := types.PluginDisableOptions{}
|
|
| 41 |
+ return client.PluginDisable(ctx, name, options) |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// Rm removes the named plugin |
|
| 45 |
+func Rm(client client.APIClient, name string) error {
|
|
| 46 |
+ return remove(client, name, false) |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+// RmF forces the removal of the named plugin |
|
| 50 |
+func RmF(client client.APIClient, name string) error {
|
|
| 51 |
+ return remove(client, name, true) |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func remove(client client.APIClient, name string, force bool) error {
|
|
| 55 |
+ ctx := context.Background() |
|
| 56 |
+ options := types.PluginRemoveOptions{Force: force}
|
|
| 57 |
+ return client.PluginRemove(ctx, name, options) |
|
| 58 |
+} |
| 0 | 59 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,45 @@ |
| 0 |
+package system |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "testing" |
|
| 5 |
+ "time" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/docker/docker/api/types" |
|
| 8 |
+ "github.com/docker/docker/api/types/events" |
|
| 9 |
+ "github.com/docker/docker/client" |
|
| 10 |
+ "github.com/docker/docker/internal/test/environment" |
|
| 11 |
+ "github.com/stretchr/testify/require" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// Time provides the current time on the daemon host |
|
| 15 |
+func Time(t *testing.T, client client.APIClient, testEnv *environment.Execution) time.Time {
|
|
| 16 |
+ if testEnv.IsLocalDaemon() {
|
|
| 17 |
+ return time.Now() |
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ ctx := context.Background() |
|
| 21 |
+ info, err := client.Info(ctx) |
|
| 22 |
+ require.Nil(t, err) |
|
| 23 |
+ |
|
| 24 |
+ dt, err := time.Parse(time.RFC3339Nano, info.SystemTime) |
|
| 25 |
+ require.Nil(t, err, "invalid time format in GET /info response") |
|
| 26 |
+ return dt |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// Version provides the version of the daemon |
|
| 30 |
+func Version(client client.APIClient) (types.Version, error) {
|
|
| 31 |
+ ctx := context.Background() |
|
| 32 |
+ return client.ServerVersion(ctx) |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// EventsSince returns event and error streams since a provided time |
|
| 36 |
+func EventsSince(client client.APIClient, since string) (<-chan events.Message, <-chan error, func()) {
|
|
| 37 |
+ eventOptions := types.EventsOptions{
|
|
| 38 |
+ Since: since, |
|
| 39 |
+ } |
|
| 40 |
+ ctx, cancel := context.WithCancel(context.Background()) |
|
| 41 |
+ events, errs := client.Events(ctx, eventOptions) |
|
| 42 |
+ |
|
| 43 |
+ return events, errs, cancel |
|
| 44 |
+} |
| 0 | 45 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,61 @@ |
| 0 |
+package volume |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/api/types" |
|
| 6 |
+ "github.com/docker/docker/api/types/filters" |
|
| 7 |
+ volumetypes "github.com/docker/docker/api/types/volume" |
|
| 8 |
+ "github.com/docker/docker/client" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+// Create creates a volume using the named driver with the specified options |
|
| 12 |
+func Create(client client.APIClient, driver string, opts map[string]string) (string, error) {
|
|
| 13 |
+ volReq := volumetypes.VolumesCreateBody{
|
|
| 14 |
+ Driver: driver, |
|
| 15 |
+ DriverOpts: opts, |
|
| 16 |
+ Name: "", |
|
| 17 |
+ } |
|
| 18 |
+ |
|
| 19 |
+ ctx := context.Background() |
|
| 20 |
+ vol, err := client.VolumeCreate(ctx, volReq) |
|
| 21 |
+ if err != nil {
|
|
| 22 |
+ return "", err |
|
| 23 |
+ } |
|
| 24 |
+ return vol.Name, nil |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+// Rm removes the volume named |
|
| 28 |
+func Rm(client client.APIClient, name string) error {
|
|
| 29 |
+ ctx := context.Background() |
|
| 30 |
+ return client.VolumeRemove(ctx, name, false) |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+// Ls lists the volumes available |
|
| 34 |
+func Ls(client client.APIClient) ([]string, error) {
|
|
| 35 |
+ ctx := context.Background() |
|
| 36 |
+ volumes, err := client.VolumeList(ctx, filters.Args{})
|
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ return []string{}, err
|
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ names := []string{}
|
|
| 42 |
+ for _, volume := range volumes.Volumes {
|
|
| 43 |
+ names = append(names, volume.Name) |
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ return names, nil |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+// Prune removes all volumes not used by at least one container |
|
| 50 |
+func Prune(client client.APIClient) (types.VolumesPruneReport, error) {
|
|
| 51 |
+ ctx := context.Background() |
|
| 52 |
+ |
|
| 53 |
+ return client.VolumesPrune(ctx, filters.Args{})
|
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// Inspect retrieves detailed information about the named volume |
|
| 57 |
+func Inspect(client client.APIClient, name string) (types.Volume, error) {
|
|
| 58 |
+ ctx := context.Background() |
|
| 59 |
+ return client.VolumeInspect(ctx, name) |
|
| 60 |
+} |
| 0 | 61 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,370 @@ |
| 0 |
+// +build !windows |
|
| 1 |
+ |
|
| 2 |
+package authz |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "context" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "io" |
|
| 8 |
+ "io/ioutil" |
|
| 9 |
+ "net" |
|
| 10 |
+ "net/http" |
|
| 11 |
+ "net/http/httputil" |
|
| 12 |
+ "net/url" |
|
| 13 |
+ "os" |
|
| 14 |
+ "path/filepath" |
|
| 15 |
+ "strconv" |
|
| 16 |
+ "strings" |
|
| 17 |
+ "testing" |
|
| 18 |
+ "time" |
|
| 19 |
+ |
|
| 20 |
+ eventtypes "github.com/docker/docker/api/types/events" |
|
| 21 |
+ "github.com/docker/docker/integration/internal/api/container" |
|
| 22 |
+ "github.com/docker/docker/integration/internal/api/image" |
|
| 23 |
+ "github.com/docker/docker/integration/internal/api/system" |
|
| 24 |
+ "github.com/docker/docker/integration/util/request" |
|
| 25 |
+ "github.com/docker/docker/pkg/authorization" |
|
| 26 |
+ "github.com/gotestyourself/gotestyourself/skip" |
|
| 27 |
+ "github.com/stretchr/testify/require" |
|
| 28 |
+) |
|
| 29 |
+ |
|
| 30 |
+const ( |
|
| 31 |
+ testAuthZPlugin = "authzplugin" |
|
| 32 |
+ unauthorizedMessage = "User unauthorized authz plugin" |
|
| 33 |
+ errorMessage = "something went wrong..." |
|
| 34 |
+ serverVersionAPI = "/version" |
|
| 35 |
+) |
|
| 36 |
+ |
|
| 37 |
+var ( |
|
| 38 |
+ alwaysAllowed = []string{"/_ping", "/info"}
|
|
| 39 |
+ ctrl *authorizationController |
|
| 40 |
+) |
|
| 41 |
+ |
|
| 42 |
+type authorizationController struct {
|
|
| 43 |
+ reqRes authorization.Response // reqRes holds the plugin response to the initial client request |
|
| 44 |
+ resRes authorization.Response // resRes holds the plugin response to the daemon response |
|
| 45 |
+ versionReqCount int // versionReqCount counts the number of requests to the server version API endpoint |
|
| 46 |
+ versionResCount int // versionResCount counts the number of responses from the server version API endpoint |
|
| 47 |
+ requestsURIs []string // requestsURIs stores all request URIs that are sent to the authorization controller |
|
| 48 |
+ reqUser string |
|
| 49 |
+ resUser string |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func setupTestV1(t *testing.T) func() {
|
|
| 53 |
+ ctrl = &authorizationController{}
|
|
| 54 |
+ teardown := setupTest(t) |
|
| 55 |
+ |
|
| 56 |
+ err := os.MkdirAll("/etc/docker/plugins", 0755)
|
|
| 57 |
+ require.Nil(t, err) |
|
| 58 |
+ |
|
| 59 |
+ fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", testAuthZPlugin)
|
|
| 60 |
+ err = ioutil.WriteFile(fileName, []byte(server.URL), 0644) |
|
| 61 |
+ require.Nil(t, err) |
|
| 62 |
+ |
|
| 63 |
+ return func() {
|
|
| 64 |
+ err := os.RemoveAll("/etc/docker/plugins")
|
|
| 65 |
+ require.Nil(t, err) |
|
| 66 |
+ |
|
| 67 |
+ teardown() |
|
| 68 |
+ ctrl = nil |
|
| 69 |
+ } |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+// check for always allowed endpoints to not inhibit test framework functions |
|
| 73 |
+func isAllowed(reqURI string) bool {
|
|
| 74 |
+ for _, endpoint := range alwaysAllowed {
|
|
| 75 |
+ if strings.HasSuffix(reqURI, endpoint) {
|
|
| 76 |
+ return true |
|
| 77 |
+ } |
|
| 78 |
+ } |
|
| 79 |
+ return false |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func TestAuthZPluginAllowRequest(t *testing.T) {
|
|
| 83 |
+ defer setupTestV1(t)() |
|
| 84 |
+ ctrl.reqRes.Allow = true |
|
| 85 |
+ ctrl.resRes.Allow = true |
|
| 86 |
+ d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin) |
|
| 87 |
+ |
|
| 88 |
+ client, err := d.NewClient() |
|
| 89 |
+ require.Nil(t, err) |
|
| 90 |
+ |
|
| 91 |
+ // Ensure command successful |
|
| 92 |
+ id, err := container.Run(client, "busybox", []string{"top"})
|
|
| 93 |
+ require.Nil(t, err) |
|
| 94 |
+ |
|
| 95 |
+ assertURIRecorded(t, ctrl.requestsURIs, "/containers/create") |
|
| 96 |
+ assertURIRecorded(t, ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", id))
|
|
| 97 |
+ |
|
| 98 |
+ _, err = system.Version(client) |
|
| 99 |
+ require.Nil(t, err) |
|
| 100 |
+ require.Equal(t, 1, ctrl.versionReqCount) |
|
| 101 |
+ require.Equal(t, 1, ctrl.versionResCount) |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+func TestAuthZPluginTLS(t *testing.T) {
|
|
| 105 |
+ defer setupTestV1(t)() |
|
| 106 |
+ const ( |
|
| 107 |
+ testDaemonHTTPSAddr = "tcp://localhost:4271" |
|
| 108 |
+ cacertPath = "../../testdata/https/ca.pem" |
|
| 109 |
+ serverCertPath = "../../testdata/https/server-cert.pem" |
|
| 110 |
+ serverKeyPath = "../../testdata/https/server-key.pem" |
|
| 111 |
+ clientCertPath = "../../testdata/https/client-cert.pem" |
|
| 112 |
+ clientKeyPath = "../../testdata/https/client-key.pem" |
|
| 113 |
+ ) |
|
| 114 |
+ |
|
| 115 |
+ d.Start(t, |
|
| 116 |
+ "--authorization-plugin="+testAuthZPlugin, |
|
| 117 |
+ "--tlsverify", |
|
| 118 |
+ "--tlscacert", cacertPath, |
|
| 119 |
+ "--tlscert", serverCertPath, |
|
| 120 |
+ "--tlskey", serverKeyPath, |
|
| 121 |
+ "-H", testDaemonHTTPSAddr) |
|
| 122 |
+ |
|
| 123 |
+ ctrl.reqRes.Allow = true |
|
| 124 |
+ ctrl.resRes.Allow = true |
|
| 125 |
+ |
|
| 126 |
+ client, err := request.NewTLSAPIClient(t, testDaemonHTTPSAddr, cacertPath, clientCertPath, clientKeyPath) |
|
| 127 |
+ require.Nil(t, err) |
|
| 128 |
+ |
|
| 129 |
+ _, err = system.Version(client) |
|
| 130 |
+ require.Nil(t, err) |
|
| 131 |
+ |
|
| 132 |
+ require.Equal(t, "client", ctrl.reqUser) |
|
| 133 |
+ require.Equal(t, "client", ctrl.resUser) |
|
| 134 |
+} |
|
| 135 |
+ |
|
| 136 |
+func TestAuthZPluginDenyRequest(t *testing.T) {
|
|
| 137 |
+ defer setupTestV1(t)() |
|
| 138 |
+ d.Start(t, "--authorization-plugin="+testAuthZPlugin) |
|
| 139 |
+ ctrl.reqRes.Allow = false |
|
| 140 |
+ ctrl.reqRes.Msg = unauthorizedMessage |
|
| 141 |
+ |
|
| 142 |
+ client, err := d.NewClient() |
|
| 143 |
+ require.Nil(t, err) |
|
| 144 |
+ |
|
| 145 |
+ // Ensure command is blocked |
|
| 146 |
+ _, err = system.Version(client) |
|
| 147 |
+ require.NotNil(t, err) |
|
| 148 |
+ require.Equal(t, 1, ctrl.versionReqCount) |
|
| 149 |
+ require.Equal(t, 0, ctrl.versionResCount) |
|
| 150 |
+ |
|
| 151 |
+ // Ensure unauthorized message appears in response |
|
| 152 |
+ require.Equal(t, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s", testAuthZPlugin, unauthorizedMessage), err.Error())
|
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+// TestAuthZPluginAPIDenyResponse validates that when authorization |
|
| 156 |
+// plugin deny the request, the status code is forbidden |
|
| 157 |
+func TestAuthZPluginAPIDenyResponse(t *testing.T) {
|
|
| 158 |
+ defer setupTestV1(t)() |
|
| 159 |
+ d.Start(t, "--authorization-plugin="+testAuthZPlugin) |
|
| 160 |
+ ctrl.reqRes.Allow = false |
|
| 161 |
+ ctrl.resRes.Msg = unauthorizedMessage |
|
| 162 |
+ |
|
| 163 |
+ daemonURL, err := url.Parse(d.Sock()) |
|
| 164 |
+ require.Nil(t, err) |
|
| 165 |
+ |
|
| 166 |
+ conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10) |
|
| 167 |
+ require.Nil(t, err) |
|
| 168 |
+ client := httputil.NewClientConn(conn, nil) |
|
| 169 |
+ req, err := http.NewRequest("GET", "/version", nil)
|
|
| 170 |
+ require.Nil(t, err) |
|
| 171 |
+ resp, err := client.Do(req) |
|
| 172 |
+ |
|
| 173 |
+ require.Nil(t, err) |
|
| 174 |
+ require.Equal(t, http.StatusForbidden, resp.StatusCode) |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+func TestAuthZPluginDenyResponse(t *testing.T) {
|
|
| 178 |
+ defer setupTestV1(t)() |
|
| 179 |
+ d.Start(t, "--authorization-plugin="+testAuthZPlugin) |
|
| 180 |
+ ctrl.reqRes.Allow = true |
|
| 181 |
+ ctrl.resRes.Allow = false |
|
| 182 |
+ ctrl.resRes.Msg = unauthorizedMessage |
|
| 183 |
+ |
|
| 184 |
+ client, err := d.NewClient() |
|
| 185 |
+ require.Nil(t, err) |
|
| 186 |
+ |
|
| 187 |
+ // Ensure command is blocked |
|
| 188 |
+ _, err = system.Version(client) |
|
| 189 |
+ require.NotNil(t, err) |
|
| 190 |
+ require.Equal(t, 1, ctrl.versionReqCount) |
|
| 191 |
+ require.Equal(t, 1, ctrl.versionResCount) |
|
| 192 |
+ |
|
| 193 |
+ // Ensure unauthorized message appears in response |
|
| 194 |
+ require.Equal(t, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s", testAuthZPlugin, unauthorizedMessage), err.Error())
|
|
| 195 |
+} |
|
| 196 |
+ |
|
| 197 |
+// TestAuthZPluginAllowEventStream verifies event stream propagates |
|
| 198 |
+// correctly after request pass through by the authorization plugin |
|
| 199 |
+func TestAuthZPluginAllowEventStream(t *testing.T) {
|
|
| 200 |
+ skip.IfCondition(t, testEnv.DaemonInfo.OSType != "linux") |
|
| 201 |
+ |
|
| 202 |
+ defer setupTestV1(t)() |
|
| 203 |
+ ctrl.reqRes.Allow = true |
|
| 204 |
+ ctrl.resRes.Allow = true |
|
| 205 |
+ d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin) |
|
| 206 |
+ |
|
| 207 |
+ client, err := d.NewClient() |
|
| 208 |
+ require.Nil(t, err) |
|
| 209 |
+ |
|
| 210 |
+ startTime := strconv.FormatInt(system.Time(t, client, testEnv).Unix(), 10) |
|
| 211 |
+ events, errs, cancel := system.EventsSince(client, startTime) |
|
| 212 |
+ defer cancel() |
|
| 213 |
+ |
|
| 214 |
+ // Create a container and wait for the creation events |
|
| 215 |
+ id, err := container.Run(client, "busybox", []string{"top"})
|
|
| 216 |
+ require.Nil(t, err) |
|
| 217 |
+ for i := 0; i < 100; i++ {
|
|
| 218 |
+ c, err := client.ContainerInspect(context.Background(), id) |
|
| 219 |
+ require.Nil(t, err) |
|
| 220 |
+ if c.State.Running {
|
|
| 221 |
+ break |
|
| 222 |
+ } |
|
| 223 |
+ if i == 99 {
|
|
| 224 |
+ t.Fatal("Container didn't run within 10s")
|
|
| 225 |
+ } |
|
| 226 |
+ time.Sleep(100 * time.Millisecond) |
|
| 227 |
+ } |
|
| 228 |
+ |
|
| 229 |
+ created := false |
|
| 230 |
+ started := false |
|
| 231 |
+ for !created && !started {
|
|
| 232 |
+ select {
|
|
| 233 |
+ case event := <-events: |
|
| 234 |
+ if event.Type == eventtypes.ContainerEventType && event.Actor.ID == id {
|
|
| 235 |
+ if event.Action == "create" {
|
|
| 236 |
+ created = true |
|
| 237 |
+ } |
|
| 238 |
+ if event.Action == "start" {
|
|
| 239 |
+ started = true |
|
| 240 |
+ } |
|
| 241 |
+ } |
|
| 242 |
+ case err := <-errs: |
|
| 243 |
+ if err == io.EOF {
|
|
| 244 |
+ t.Fatal("premature end of event stream")
|
|
| 245 |
+ } |
|
| 246 |
+ require.Nil(t, err) |
|
| 247 |
+ case <-time.After(30 * time.Second): |
|
| 248 |
+ // Fail the test |
|
| 249 |
+ t.Fatal("event stream timeout")
|
|
| 250 |
+ } |
|
| 251 |
+ } |
|
| 252 |
+ |
|
| 253 |
+ // Ensure both events and container endpoints are passed to the |
|
| 254 |
+ // authorization plugin |
|
| 255 |
+ assertURIRecorded(t, ctrl.requestsURIs, "/events") |
|
| 256 |
+ assertURIRecorded(t, ctrl.requestsURIs, "/containers/create") |
|
| 257 |
+ assertURIRecorded(t, ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", id))
|
|
| 258 |
+} |
|
| 259 |
+ |
|
| 260 |
+func TestAuthZPluginErrorResponse(t *testing.T) {
|
|
| 261 |
+ defer setupTestV1(t)() |
|
| 262 |
+ d.Start(t, "--authorization-plugin="+testAuthZPlugin) |
|
| 263 |
+ ctrl.reqRes.Allow = true |
|
| 264 |
+ ctrl.resRes.Err = errorMessage |
|
| 265 |
+ |
|
| 266 |
+ client, err := d.NewClient() |
|
| 267 |
+ require.Nil(t, err) |
|
| 268 |
+ |
|
| 269 |
+ // Ensure command is blocked |
|
| 270 |
+ _, err = system.Version(client) |
|
| 271 |
+ require.NotNil(t, err) |
|
| 272 |
+ require.Equal(t, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s", testAuthZPlugin, authorization.AuthZApiResponse, errorMessage), err.Error())
|
|
| 273 |
+} |
|
| 274 |
+ |
|
| 275 |
+func TestAuthZPluginErrorRequest(t *testing.T) {
|
|
| 276 |
+ defer setupTestV1(t)() |
|
| 277 |
+ d.Start(t, "--authorization-plugin="+testAuthZPlugin) |
|
| 278 |
+ ctrl.reqRes.Err = errorMessage |
|
| 279 |
+ |
|
| 280 |
+ client, err := d.NewClient() |
|
| 281 |
+ require.Nil(t, err) |
|
| 282 |
+ |
|
| 283 |
+ // Ensure command is blocked |
|
| 284 |
+ _, err = system.Version(client) |
|
| 285 |
+ require.NotNil(t, err) |
|
| 286 |
+ require.Equal(t, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s", testAuthZPlugin, authorization.AuthZApiRequest, errorMessage), err.Error())
|
|
| 287 |
+} |
|
| 288 |
+ |
|
| 289 |
+func TestAuthZPluginEnsureNoDuplicatePluginRegistration(t *testing.T) {
|
|
| 290 |
+ defer setupTestV1(t)() |
|
| 291 |
+ d.Start(t, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin) |
|
| 292 |
+ |
|
| 293 |
+ ctrl.reqRes.Allow = true |
|
| 294 |
+ ctrl.resRes.Allow = true |
|
| 295 |
+ |
|
| 296 |
+ client, err := d.NewClient() |
|
| 297 |
+ require.Nil(t, err) |
|
| 298 |
+ |
|
| 299 |
+ _, err = system.Version(client) |
|
| 300 |
+ require.Nil(t, err) |
|
| 301 |
+ |
|
| 302 |
+ // assert plugin is only called once.. |
|
| 303 |
+ require.Equal(t, 1, ctrl.versionReqCount) |
|
| 304 |
+ require.Equal(t, 1, ctrl.versionResCount) |
|
| 305 |
+} |
|
| 306 |
+ |
|
| 307 |
+func TestAuthZPluginEnsureLoadImportWorking(t *testing.T) {
|
|
| 308 |
+ defer setupTestV1(t)() |
|
| 309 |
+ ctrl.reqRes.Allow = true |
|
| 310 |
+ ctrl.resRes.Allow = true |
|
| 311 |
+ d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin) |
|
| 312 |
+ |
|
| 313 |
+ client, err := d.NewClient() |
|
| 314 |
+ require.Nil(t, err) |
|
| 315 |
+ |
|
| 316 |
+ tmp, err := ioutil.TempDir("", "test-authz-load-import")
|
|
| 317 |
+ require.Nil(t, err) |
|
| 318 |
+ defer os.RemoveAll(tmp) |
|
| 319 |
+ |
|
| 320 |
+ savedImagePath := filepath.Join(tmp, "save.tar") |
|
| 321 |
+ |
|
| 322 |
+ err = image.Save(client, savedImagePath, "busybox") |
|
| 323 |
+ require.Nil(t, err) |
|
| 324 |
+ err = image.Load(client, savedImagePath) |
|
| 325 |
+ require.Nil(t, err) |
|
| 326 |
+ |
|
| 327 |
+ exportedImagePath := filepath.Join(tmp, "export.tar") |
|
| 328 |
+ |
|
| 329 |
+ id, err := container.Run(client, "busybox", []string{})
|
|
| 330 |
+ require.Nil(t, err) |
|
| 331 |
+ err = container.Export(client, exportedImagePath, id) |
|
| 332 |
+ require.Nil(t, err) |
|
| 333 |
+ err = image.Import(client, exportedImagePath) |
|
| 334 |
+ require.Nil(t, err) |
|
| 335 |
+} |
|
| 336 |
+ |
|
| 337 |
+func TestAuthZPluginHeader(t *testing.T) {
|
|
| 338 |
+ defer setupTestV1(t)() |
|
| 339 |
+ ctrl.reqRes.Allow = true |
|
| 340 |
+ ctrl.resRes.Allow = true |
|
| 341 |
+ d.StartWithBusybox(t, "--debug", "--authorization-plugin="+testAuthZPlugin) |
|
| 342 |
+ |
|
| 343 |
+ daemonURL, err := url.Parse(d.Sock()) |
|
| 344 |
+ require.Nil(t, err) |
|
| 345 |
+ |
|
| 346 |
+ conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10) |
|
| 347 |
+ require.Nil(t, err) |
|
| 348 |
+ client := httputil.NewClientConn(conn, nil) |
|
| 349 |
+ req, err := http.NewRequest("GET", "/version", nil)
|
|
| 350 |
+ require.Nil(t, err) |
|
| 351 |
+ resp, err := client.Do(req) |
|
| 352 |
+ require.Nil(t, err) |
|
| 353 |
+ require.Equal(t, "application/json", resp.Header["Content-Type"][0]) |
|
| 354 |
+} |
|
| 355 |
+ |
|
| 356 |
+// assertURIRecorded verifies that the given URI was sent and recorded |
|
| 357 |
+// in the authz plugin |
|
| 358 |
+func assertURIRecorded(t *testing.T, uris []string, uri string) {
|
|
| 359 |
+ var found bool |
|
| 360 |
+ for _, u := range uris {
|
|
| 361 |
+ if strings.Contains(u, uri) {
|
|
| 362 |
+ found = true |
|
| 363 |
+ break |
|
| 364 |
+ } |
|
| 365 |
+ } |
|
| 366 |
+ if !found {
|
|
| 367 |
+ t.Fatalf("Expected to find URI '%s', recorded uris '%s'", uri, strings.Join(uris, ","))
|
|
| 368 |
+ } |
|
| 369 |
+} |
| 0 | 370 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,153 @@ |
| 0 |
+// +build !windows |
|
| 1 |
+ |
|
| 2 |
+package authz |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "context" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "os" |
|
| 8 |
+ "strings" |
|
| 9 |
+ "testing" |
|
| 10 |
+ |
|
| 11 |
+ "github.com/docker/docker/integration/internal/api/container" |
|
| 12 |
+ "github.com/docker/docker/integration/internal/api/plugin" |
|
| 13 |
+ "github.com/docker/docker/integration/internal/api/volume" |
|
| 14 |
+ "github.com/docker/docker/integration/util/requirement" |
|
| 15 |
+ "github.com/gotestyourself/gotestyourself/skip" |
|
| 16 |
+ "github.com/stretchr/testify/require" |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+var ( |
|
| 20 |
+ authzPluginName = "riyaz/authz-no-volume-plugin" |
|
| 21 |
+ authzPluginTag = "latest" |
|
| 22 |
+ authzPluginNameWithTag = authzPluginName + ":" + authzPluginTag |
|
| 23 |
+ authzPluginBadManifestName = "riyaz/authz-plugin-bad-manifest" |
|
| 24 |
+ nonexistentAuthzPluginName = "riyaz/nonexistent-authz-plugin" |
|
| 25 |
+) |
|
| 26 |
+ |
|
| 27 |
+func setupTestV2(t *testing.T) func() {
|
|
| 28 |
+ skip.IfCondition(t, testEnv.DaemonInfo.OSType != "linux") |
|
| 29 |
+ requirement.HasHubConnectivity(t) |
|
| 30 |
+ |
|
| 31 |
+ teardown := setupTest(t) |
|
| 32 |
+ |
|
| 33 |
+ d.Start(t) |
|
| 34 |
+ |
|
| 35 |
+ return teardown |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func TestAuthZPluginV2AllowNonVolumeRequest(t *testing.T) {
|
|
| 39 |
+ skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
|
|
| 40 |
+ defer setupTestV2(t)() |
|
| 41 |
+ |
|
| 42 |
+ client, err := d.NewClient() |
|
| 43 |
+ require.Nil(t, err) |
|
| 44 |
+ |
|
| 45 |
+ // Install authz plugin |
|
| 46 |
+ err = plugin.InstallGrantAllPermissions(client, authzPluginNameWithTag) |
|
| 47 |
+ require.Nil(t, err) |
|
| 48 |
+ // start the daemon with the plugin and load busybox, --net=none build fails otherwise |
|
| 49 |
+ // because it needs to pull busybox |
|
| 50 |
+ d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag) |
|
| 51 |
+ d.LoadBusybox(t) |
|
| 52 |
+ |
|
| 53 |
+ // Ensure docker run command and accompanying docker ps are successful |
|
| 54 |
+ id, err := container.Run(client, "busybox", []string{"top"})
|
|
| 55 |
+ require.Nil(t, err) |
|
| 56 |
+ |
|
| 57 |
+ _, err = client.ContainerInspect(context.Background(), id) |
|
| 58 |
+ require.Nil(t, err) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func TestAuthZPluginV2Disable(t *testing.T) {
|
|
| 62 |
+ skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
|
|
| 63 |
+ defer setupTestV2(t)() |
|
| 64 |
+ |
|
| 65 |
+ client, err := d.NewClient() |
|
| 66 |
+ require.Nil(t, err) |
|
| 67 |
+ |
|
| 68 |
+ // Install authz plugin |
|
| 69 |
+ err = plugin.InstallGrantAllPermissions(client, authzPluginNameWithTag) |
|
| 70 |
+ require.Nil(t, err) |
|
| 71 |
+ |
|
| 72 |
+ d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag) |
|
| 73 |
+ d.LoadBusybox(t) |
|
| 74 |
+ |
|
| 75 |
+ _, err = volume.Create(client, "local", map[string]string{})
|
|
| 76 |
+ require.NotNil(t, err) |
|
| 77 |
+ require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
|
| 78 |
+ |
|
| 79 |
+ // disable the plugin |
|
| 80 |
+ err = plugin.Disable(client, authzPluginNameWithTag) |
|
| 81 |
+ require.Nil(t, err) |
|
| 82 |
+ |
|
| 83 |
+ // now test to see if the docker api works. |
|
| 84 |
+ _, err = volume.Create(client, "local", map[string]string{})
|
|
| 85 |
+ require.Nil(t, err) |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+func TestAuthZPluginV2RejectVolumeRequests(t *testing.T) {
|
|
| 89 |
+ skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
|
|
| 90 |
+ defer setupTestV2(t)() |
|
| 91 |
+ |
|
| 92 |
+ client, err := d.NewClient() |
|
| 93 |
+ require.Nil(t, err) |
|
| 94 |
+ |
|
| 95 |
+ // Install authz plugin |
|
| 96 |
+ err = plugin.InstallGrantAllPermissions(client, authzPluginNameWithTag) |
|
| 97 |
+ require.Nil(t, err) |
|
| 98 |
+ |
|
| 99 |
+ // restart the daemon with the plugin |
|
| 100 |
+ d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag) |
|
| 101 |
+ |
|
| 102 |
+ _, err = volume.Create(client, "local", map[string]string{})
|
|
| 103 |
+ require.NotNil(t, err) |
|
| 104 |
+ require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
|
| 105 |
+ |
|
| 106 |
+ _, err = volume.Ls(client) |
|
| 107 |
+ require.NotNil(t, err) |
|
| 108 |
+ require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
|
| 109 |
+ |
|
| 110 |
+ // The plugin will block the command before it can determine the volume does not exist |
|
| 111 |
+ err = volume.Rm(client, "test") |
|
| 112 |
+ require.NotNil(t, err) |
|
| 113 |
+ require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
|
| 114 |
+ |
|
| 115 |
+ _, err = volume.Inspect(client, "test") |
|
| 116 |
+ require.NotNil(t, err) |
|
| 117 |
+ require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
|
| 118 |
+ |
|
| 119 |
+ _, err = volume.Prune(client) |
|
| 120 |
+ require.NotNil(t, err) |
|
| 121 |
+ require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+func TestAuthZPluginV2BadManifestFailsDaemonStart(t *testing.T) {
|
|
| 125 |
+ skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
|
|
| 126 |
+ defer setupTestV2(t)() |
|
| 127 |
+ |
|
| 128 |
+ client, err := d.NewClient() |
|
| 129 |
+ require.Nil(t, err) |
|
| 130 |
+ |
|
| 131 |
+ // Install authz plugin with bad manifest |
|
| 132 |
+ err = plugin.InstallGrantAllPermissions(client, authzPluginBadManifestName) |
|
| 133 |
+ require.Nil(t, err) |
|
| 134 |
+ |
|
| 135 |
+ // start the daemon with the plugin, it will error |
|
| 136 |
+ err = d.RestartWithError("--authorization-plugin=" + authzPluginBadManifestName)
|
|
| 137 |
+ require.NotNil(t, err) |
|
| 138 |
+ |
|
| 139 |
+ // restarting the daemon without requiring the plugin will succeed |
|
| 140 |
+ d.Start(t) |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+func TestAuthZPluginV2NonexistentFailsDaemonStart(t *testing.T) {
|
|
| 144 |
+ defer setupTestV2(t)() |
|
| 145 |
+ |
|
| 146 |
+ // start the daemon with a non-existent authz plugin, it will error |
|
| 147 |
+ err := d.RestartWithError("--authorization-plugin=" + nonexistentAuthzPluginName)
|
|
| 148 |
+ require.NotNil(t, err) |
|
| 149 |
+ |
|
| 150 |
+ // restarting the daemon without requiring the plugin will succeed |
|
| 151 |
+ d.Start(t) |
|
| 152 |
+} |
| 0 | 153 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,177 @@ |
| 0 |
+// +build !windows |
|
| 1 |
+ |
|
| 2 |
+package authz |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "encoding/json" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "io/ioutil" |
|
| 8 |
+ "net/http" |
|
| 9 |
+ "net/http/httptest" |
|
| 10 |
+ "os" |
|
| 11 |
+ "strings" |
|
| 12 |
+ "testing" |
|
| 13 |
+ |
|
| 14 |
+ "github.com/docker/docker/integration-cli/daemon" |
|
| 15 |
+ "github.com/docker/docker/internal/test/environment" |
|
| 16 |
+ "github.com/docker/docker/pkg/authorization" |
|
| 17 |
+ "github.com/docker/docker/pkg/plugins" |
|
| 18 |
+) |
|
| 19 |
+ |
|
| 20 |
+var ( |
|
| 21 |
+ testEnv *environment.Execution |
|
| 22 |
+ d *daemon.Daemon |
|
| 23 |
+ server *httptest.Server |
|
| 24 |
+) |
|
| 25 |
+ |
|
| 26 |
+const dockerdBinary = "dockerd" |
|
| 27 |
+ |
|
| 28 |
+func TestMain(m *testing.M) {
|
|
| 29 |
+ var err error |
|
| 30 |
+ testEnv, err = environment.New() |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ fmt.Println(err) |
|
| 33 |
+ os.Exit(1) |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ testEnv.Print() |
|
| 37 |
+ setupSuite() |
|
| 38 |
+ exitCode := m.Run() |
|
| 39 |
+ teardownSuite() |
|
| 40 |
+ |
|
| 41 |
+ os.Exit(exitCode) |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+func setupTest(t *testing.T) func() {
|
|
| 45 |
+ environment.ProtectAll(t, testEnv) |
|
| 46 |
+ |
|
| 47 |
+ d = daemon.New(t, "", dockerdBinary, daemon.Config{
|
|
| 48 |
+ Experimental: testEnv.DaemonInfo.ExperimentalBuild, |
|
| 49 |
+ }) |
|
| 50 |
+ |
|
| 51 |
+ return func() {
|
|
| 52 |
+ if d != nil {
|
|
| 53 |
+ d.Stop(t) |
|
| 54 |
+ } |
|
| 55 |
+ testEnv.Clean(t) |
|
| 56 |
+ } |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func setupSuite() {
|
|
| 60 |
+ mux := http.NewServeMux() |
|
| 61 |
+ server = httptest.NewServer(mux) |
|
| 62 |
+ |
|
| 63 |
+ mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
|
|
| 64 |
+ b, err := json.Marshal(plugins.Manifest{Implements: []string{authorization.AuthZApiImplements}})
|
|
| 65 |
+ if err != nil {
|
|
| 66 |
+ panic("could not marshal json for /Plugin.Activate: " + err.Error())
|
|
| 67 |
+ } |
|
| 68 |
+ w.Write(b) |
|
| 69 |
+ }) |
|
| 70 |
+ |
|
| 71 |
+ mux.HandleFunc("/AuthZPlugin.AuthZReq", func(w http.ResponseWriter, r *http.Request) {
|
|
| 72 |
+ defer r.Body.Close() |
|
| 73 |
+ body, err := ioutil.ReadAll(r.Body) |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ panic("could not read body for /AuthZPlugin.AuthZReq: " + err.Error())
|
|
| 76 |
+ } |
|
| 77 |
+ authReq := authorization.Request{}
|
|
| 78 |
+ err = json.Unmarshal(body, &authReq) |
|
| 79 |
+ if err != nil {
|
|
| 80 |
+ panic("could not unmarshal json for /AuthZPlugin.AuthZReq: " + err.Error())
|
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ assertBody(authReq.RequestURI, authReq.RequestHeaders, authReq.RequestBody) |
|
| 84 |
+ assertAuthHeaders(authReq.RequestHeaders) |
|
| 85 |
+ |
|
| 86 |
+ // Count only server version api |
|
| 87 |
+ if strings.HasSuffix(authReq.RequestURI, serverVersionAPI) {
|
|
| 88 |
+ ctrl.versionReqCount++ |
|
| 89 |
+ } |
|
| 90 |
+ |
|
| 91 |
+ ctrl.requestsURIs = append(ctrl.requestsURIs, authReq.RequestURI) |
|
| 92 |
+ |
|
| 93 |
+ reqRes := ctrl.reqRes |
|
| 94 |
+ if isAllowed(authReq.RequestURI) {
|
|
| 95 |
+ reqRes = authorization.Response{Allow: true}
|
|
| 96 |
+ } |
|
| 97 |
+ if reqRes.Err != "" {
|
|
| 98 |
+ w.WriteHeader(http.StatusInternalServerError) |
|
| 99 |
+ } |
|
| 100 |
+ b, err := json.Marshal(reqRes) |
|
| 101 |
+ if err != nil {
|
|
| 102 |
+ panic("could not marshal json for /AuthZPlugin.AuthZReq: " + err.Error())
|
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ ctrl.reqUser = authReq.User |
|
| 106 |
+ w.Write(b) |
|
| 107 |
+ }) |
|
| 108 |
+ |
|
| 109 |
+ mux.HandleFunc("/AuthZPlugin.AuthZRes", func(w http.ResponseWriter, r *http.Request) {
|
|
| 110 |
+ defer r.Body.Close() |
|
| 111 |
+ body, err := ioutil.ReadAll(r.Body) |
|
| 112 |
+ if err != nil {
|
|
| 113 |
+ panic("could not read body for /AuthZPlugin.AuthZRes: " + err.Error())
|
|
| 114 |
+ } |
|
| 115 |
+ authReq := authorization.Request{}
|
|
| 116 |
+ err = json.Unmarshal(body, &authReq) |
|
| 117 |
+ if err != nil {
|
|
| 118 |
+ panic("could not unmarshal json for /AuthZPlugin.AuthZRes: " + err.Error())
|
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ assertBody(authReq.RequestURI, authReq.ResponseHeaders, authReq.ResponseBody) |
|
| 122 |
+ assertAuthHeaders(authReq.ResponseHeaders) |
|
| 123 |
+ |
|
| 124 |
+ // Count only server version api |
|
| 125 |
+ if strings.HasSuffix(authReq.RequestURI, serverVersionAPI) {
|
|
| 126 |
+ ctrl.versionResCount++ |
|
| 127 |
+ } |
|
| 128 |
+ resRes := ctrl.resRes |
|
| 129 |
+ if isAllowed(authReq.RequestURI) {
|
|
| 130 |
+ resRes = authorization.Response{Allow: true}
|
|
| 131 |
+ } |
|
| 132 |
+ if resRes.Err != "" {
|
|
| 133 |
+ w.WriteHeader(http.StatusInternalServerError) |
|
| 134 |
+ } |
|
| 135 |
+ b, err := json.Marshal(resRes) |
|
| 136 |
+ if err != nil {
|
|
| 137 |
+ panic("could not marshal json for /AuthZPlugin.AuthZRes: " + err.Error())
|
|
| 138 |
+ } |
|
| 139 |
+ ctrl.resUser = authReq.User |
|
| 140 |
+ w.Write(b) |
|
| 141 |
+ }) |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+func teardownSuite() {
|
|
| 145 |
+ if server == nil {
|
|
| 146 |
+ return |
|
| 147 |
+ } |
|
| 148 |
+ |
|
| 149 |
+ server.Close() |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+// assertAuthHeaders validates authentication headers are removed |
|
| 153 |
+func assertAuthHeaders(headers map[string]string) error {
|
|
| 154 |
+ for k := range headers {
|
|
| 155 |
+ if strings.Contains(strings.ToLower(k), "auth") || strings.Contains(strings.ToLower(k), "x-registry") {
|
|
| 156 |
+ panic(fmt.Sprintf("Found authentication headers in request '%v'", headers))
|
|
| 157 |
+ } |
|
| 158 |
+ } |
|
| 159 |
+ return nil |
|
| 160 |
+} |
|
| 161 |
+ |
|
| 162 |
+// assertBody asserts that body is removed for non text/json requests |
|
| 163 |
+func assertBody(requestURI string, headers map[string]string, body []byte) {
|
|
| 164 |
+ if strings.Contains(strings.ToLower(requestURI), "auth") && len(body) > 0 {
|
|
| 165 |
+ panic("Body included for authentication endpoint " + string(body))
|
|
| 166 |
+ } |
|
| 167 |
+ |
|
| 168 |
+ for k, v := range headers {
|
|
| 169 |
+ if strings.EqualFold(k, "Content-Type") && strings.HasPrefix(v, "text/") || v == "application/json" {
|
|
| 170 |
+ return |
|
| 171 |
+ } |
|
| 172 |
+ } |
|
| 173 |
+ if len(body) > 0 {
|
|
| 174 |
+ panic(fmt.Sprintf("Body included while it should not (Headers: '%v')", headers))
|
|
| 175 |
+ } |
|
| 176 |
+} |
| 0 | 177 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,23 @@ |
| 0 |
+-----BEGIN CERTIFICATE----- |
|
| 1 |
+MIID0TCCAzqgAwIBAgIJAP2r7GqEJwSnMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD |
|
| 2 |
+VQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMG |
|
| 3 |
+A1UEChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTERMA8GA1UEAxMI |
|
| 4 |
+Y2hhbmdlbWUxETAPBgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWls |
|
| 5 |
+QGhvc3QuZG9tYWluMB4XDTEzMTIwMzE2NTYzMFoXDTIzMTIwMTE2NTYzMFowgaIx |
|
| 6 |
+CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2Nv |
|
| 7 |
+MRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYD |
|
| 8 |
+VQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEW |
|
| 9 |
+EG1haWxAaG9zdC5kb21haW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALAn |
|
| 10 |
+0xDw+5y7ZptQacq66pUhRu82JP2WU6IDgo5QUtNU6/CX5PwQATe/OnYTZQFbksxp |
|
| 11 |
+AU9boG0FCkgxfsgPYXEuZxVEGKI2fxfKHOZZI8mrkWmj6eWU/0cvCjGVc9rTITP5 |
|
| 12 |
+sNQvg+hORyVDdNp2IdsbMJayiB3AQYMFx3vSDOMTAgMBAAGjggELMIIBBzAdBgNV |
|
| 13 |
+HQ4EFgQUZu7DFz09q0QBa2+ymRm9qgK1NPswgdcGA1UdIwSBzzCBzIAUZu7DFz09 |
|
| 14 |
+q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD |
|
| 15 |
+QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x |
|
| 16 |
+ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI |
|
| 17 |
+Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq |
|
| 18 |
+hCcEpzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAF8fJKKM+/oOdnNi |
|
| 19 |
+zEd0M1+PmZOyqvjYQn/2ZR8UHH6Imgc/OPQKZXf0bVE1Txc/DaUNn9Isd1SuCuaE |
|
| 20 |
+ic3vAIYYU7PmgeNN6vwec48V96T7jr+GAi6AVMhQEc2hHCfVtx11Xx+x6aHDZzJt |
|
| 21 |
+Zxtf5lL6KSO9Y+EFwM+rju6hm5hW |
|
| 22 |
+-----END CERTIFICATE----- |
| 0 | 23 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,73 @@ |
| 0 |
+Certificate: |
|
| 1 |
+ Data: |
|
| 2 |
+ Version: 3 (0x2) |
|
| 3 |
+ Serial Number: 3 (0x3) |
|
| 4 |
+ Signature Algorithm: sha1WithRSAEncryption |
|
| 5 |
+ Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain |
|
| 6 |
+ Validity |
|
| 7 |
+ Not Before: Dec 4 14:17:54 2013 GMT |
|
| 8 |
+ Not After : Dec 2 14:17:54 2023 GMT |
|
| 9 |
+ Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=client/name=changeme/emailAddress=mail@host.domain |
|
| 10 |
+ Subject Public Key Info: |
|
| 11 |
+ Public Key Algorithm: rsaEncryption |
|
| 12 |
+ Public-Key: (1024 bit) |
|
| 13 |
+ Modulus: |
|
| 14 |
+ 00:ca:c9:05:d0:09:4e:3e:a4:fc:d5:14:f4:a5:e8: |
|
| 15 |
+ 34:d3:6b:51:e3:f3:62:ea:a1:f0:e8:ed:c4:2a:bc: |
|
| 16 |
+ f0:4f:ca:07:df:e3:88:fa:f4:21:99:35:0e:3d:ea: |
|
| 17 |
+ b0:86:e7:c4:d2:8a:83:2b:42:b8:ec:a3:99:62:70: |
|
| 18 |
+ 81:46:cc:fc:a5:1d:d2:63:e8:eb:07:25:9a:e2:25: |
|
| 19 |
+ 6d:11:56:f2:1a:51:a1:b6:3e:1c:57:32:e9:7b:2c: |
|
| 20 |
+ aa:1b:cc:97:2d:89:2d:b1:c9:5e:35:28:4d:7c:fa: |
|
| 21 |
+ 65:31:3e:f7:70:dd:6e:0b:3c:58:af:a8:2e:24:c0: |
|
| 22 |
+ 7e:4e:78:7d:0a:9e:8f:42:43 |
|
| 23 |
+ Exponent: 65537 (0x10001) |
|
| 24 |
+ X509v3 extensions: |
|
| 25 |
+ X509v3 Basic Constraints: |
|
| 26 |
+ CA:FALSE |
|
| 27 |
+ Netscape Comment: |
|
| 28 |
+ Easy-RSA Generated Certificate |
|
| 29 |
+ X509v3 Subject Key Identifier: |
|
| 30 |
+ DE:42:EF:2D:98:A3:6C:A8:AA:E0:8C:71:2C:9D:64:23:A9:E2:7E:81 |
|
| 31 |
+ X509v3 Authority Key Identifier: |
|
| 32 |
+ keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB |
|
| 33 |
+ DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain |
|
| 34 |
+ serial:FD:AB:EC:6A:84:27:04:A7 |
|
| 35 |
+ |
|
| 36 |
+ X509v3 Extended Key Usage: |
|
| 37 |
+ TLS Web Client Authentication |
|
| 38 |
+ X509v3 Key Usage: |
|
| 39 |
+ Digital Signature |
|
| 40 |
+ Signature Algorithm: sha1WithRSAEncryption |
|
| 41 |
+ 1c:44:26:ea:e1:66:25:cb:e4:8e:57:1c:f6:b9:17:22:62:40: |
|
| 42 |
+ 12:90:8f:3b:b2:61:7a:54:94:8f:b1:20:0b:bf:a3:51:e3:fa: |
|
| 43 |
+ 1c:a1:be:92:3a:d0:76:44:c0:57:83:ab:6a:e4:1a:45:49:a4: |
|
| 44 |
+ af:39:0d:60:32:fc:3a:be:d7:fb:5d:99:7a:1f:87:e7:d5:ab: |
|
| 45 |
+ 84:a2:5e:90:d8:bf:fa:89:6d:32:26:02:5e:31:35:68:7f:31: |
|
| 46 |
+ f5:6b:51:46:bc:af:70:ed:5a:09:7d:ec:b2:48:4f:fe:c5:2f: |
|
| 47 |
+ 56:04:ad:f6:c1:d2:2a:e4:6a:c4:87:fe:08:35:c5:38:cb:5e: |
|
| 48 |
+ 4a:c4 |
|
| 49 |
+-----BEGIN CERTIFICATE----- |
|
| 50 |
+MIIEFTCCA36gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx |
|
| 51 |
+CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv |
|
| 52 |
+cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l |
|
| 53 |
+MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv |
|
| 54 |
+bWFpbjAeFw0xMzEyMDQxNDE3NTRaFw0yMzEyMDIxNDE3NTRaMIGgMQswCQYDVQQG |
|
| 55 |
+EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE |
|
| 56 |
+ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEPMA0GA1UEAxMGY2xp |
|
| 57 |
+ZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0 |
|
| 58 |
+LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyskF0AlOPqT81RT0 |
|
| 59 |
+peg002tR4/Ni6qHw6O3EKrzwT8oH3+OI+vQhmTUOPeqwhufE0oqDK0K47KOZYnCB |
|
| 60 |
+Rsz8pR3SY+jrByWa4iVtEVbyGlGhtj4cVzLpeyyqG8yXLYktscleNShNfPplMT73 |
|
| 61 |
+cN1uCzxYr6guJMB+Tnh9Cp6PQkMCAwEAAaOCAVkwggFVMAkGA1UdEwQCMAAwLQYJ |
|
| 62 |
+YIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV |
|
| 63 |
+HQ4EFgQU3kLvLZijbKiq4IxxLJ1kI6nifoEwgdcGA1UdIwSBzzCBzIAUZu7DFz09 |
|
| 64 |
+q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD |
|
| 65 |
+QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x |
|
| 66 |
+ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI |
|
| 67 |
+Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq |
|
| 68 |
+hCcEpzATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcN |
|
| 69 |
+AQEFBQADgYEAHEQm6uFmJcvkjlcc9rkXImJAEpCPO7JhelSUj7EgC7+jUeP6HKG+ |
|
| 70 |
+kjrQdkTAV4OrauQaRUmkrzkNYDL8Or7X+12Zeh+H59WrhKJekNi/+oltMiYCXjE1 |
|
| 71 |
+aH8x9WtRRryvcO1aCX3sskhP/sUvVgSt9sHSKuRqxIf+CDXFOMteSsQ= |
|
| 72 |
+-----END CERTIFICATE----- |
| 0 | 73 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,16 @@ |
| 0 |
+-----BEGIN PRIVATE KEY----- |
|
| 1 |
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMrJBdAJTj6k/NUU |
|
| 2 |
+9KXoNNNrUePzYuqh8OjtxCq88E/KB9/jiPr0IZk1Dj3qsIbnxNKKgytCuOyjmWJw |
|
| 3 |
+gUbM/KUd0mPo6wclmuIlbRFW8hpRobY+HFcy6XssqhvMly2JLbHJXjUoTXz6ZTE+ |
|
| 4 |
+93Ddbgs8WK+oLiTAfk54fQqej0JDAgMBAAECgYBOFEzKp2qbMEexe9ofL2N3rDDh |
|
| 5 |
+xkrl8OijpzkLA6i78BxMFn4dsnZlWUpciMrjhsYAExkiRRSS+QMMJimAq1jzQqc3 |
|
| 6 |
+FAQV2XGYwkd0cUn7iZGvfNnEPysjsfyYQM+m+sT0ATj4BZjVShC6kkSjTdm1leLN |
|
| 7 |
+OSvcHdcu3Xxg9ufF0QJBAPYdnNt5sIndt2WECePuRVi+uF4mlxTobFY0fjn26yhC |
|
| 8 |
+4RsnhhD3Vldygo9gvnkwrAZYaALGSPBewes2InxvjA8CQQDS7erKiNXpwoqz5XiU |
|
| 9 |
+SVEsIIVTdWzBjGbIqMOu/hUwM5FK4j6JTBks0aTGMyh0YV9L1EzM0X79J29JahCe |
|
| 10 |
+iQKNAkBKNMOGqTpBV0hko1sYDk96YobUXG5RL4L6uvkUIQ7mJMQam+AgXXL7Ctuy |
|
| 11 |
+v0iu4a38e8tgisiTMP7nHHtpaXihAkAOiN54/lzfMsykANgCP9scE1GcoqbP34Dl |
|
| 12 |
+qttxH4kOPT9xzY1JoLjLYdbc4YGUI3GRpBt2sajygNkmUey7P+2xAkBBsVCZFvTw |
|
| 13 |
+qHvOpPS2kX5ml5xoc/QAHK9N7kR+X7XFYx82RTVSqJEK4lPb+aEWn+CjiIewO4Q5 |
|
| 14 |
+ksDFuNxAzbhl |
|
| 15 |
+-----END PRIVATE KEY----- |
| 0 | 16 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,76 @@ |
| 0 |
+Certificate: |
|
| 1 |
+ Data: |
|
| 2 |
+ Version: 3 (0x2) |
|
| 3 |
+ Serial Number: 4 (0x4) |
|
| 4 |
+ Signature Algorithm: sha1WithRSAEncryption |
|
| 5 |
+ Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain |
|
| 6 |
+ Validity |
|
| 7 |
+ Not Before: Dec 4 15:01:20 2013 GMT |
|
| 8 |
+ Not After : Dec 2 15:01:20 2023 GMT |
|
| 9 |
+ Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=*/name=changeme/emailAddress=mail@host.domain |
|
| 10 |
+ Subject Public Key Info: |
|
| 11 |
+ Public Key Algorithm: rsaEncryption |
|
| 12 |
+ Public-Key: (1024 bit) |
|
| 13 |
+ Modulus: |
|
| 14 |
+ 00:c1:ff:7d:30:6f:64:4a:b1:92:b1:71:d1:c1:74: |
|
| 15 |
+ e2:1d:db:2d:11:24:e1:00:d4:00:ae:6f:c8:9e:ae: |
|
| 16 |
+ 67:b3:4a:bd:f7:e6:9e:57:6d:19:4c:3c:23:94:2d: |
|
| 17 |
+ 3d:d6:63:84:d8:fa:76:2b:38:12:c1:ed:20:9d:32: |
|
| 18 |
+ e0:e8:c2:bf:9a:77:70:04:3f:7f:ca:8c:2c:82:d6: |
|
| 19 |
+ 3d:25:5c:02:1a:4f:64:93:03:dd:9c:42:97:5e:09: |
|
| 20 |
+ 49:af:f0:c2:e1:30:08:0e:21:46:95:d1:13:59:c0: |
|
| 21 |
+ c8:76:be:94:0d:8b:43:67:21:33:b2:08:60:9d:76: |
|
| 22 |
+ a8:05:32:1e:f9:95:09:14:75 |
|
| 23 |
+ Exponent: 65537 (0x10001) |
|
| 24 |
+ X509v3 extensions: |
|
| 25 |
+ X509v3 Basic Constraints: |
|
| 26 |
+ CA:FALSE |
|
| 27 |
+ Netscape Cert Type: |
|
| 28 |
+ SSL Server |
|
| 29 |
+ Netscape Comment: |
|
| 30 |
+ Easy-RSA Generated Server Certificate |
|
| 31 |
+ X509v3 Subject Key Identifier: |
|
| 32 |
+ 14:02:FD:FD:DD:13:38:E0:71:EA:D1:BE:C0:0E:89:1A:2D:B6:19:06 |
|
| 33 |
+ X509v3 Authority Key Identifier: |
|
| 34 |
+ keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB |
|
| 35 |
+ DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain |
|
| 36 |
+ serial:FD:AB:EC:6A:84:27:04:A7 |
|
| 37 |
+ |
|
| 38 |
+ X509v3 Extended Key Usage: |
|
| 39 |
+ TLS Web Server Authentication |
|
| 40 |
+ X509v3 Key Usage: |
|
| 41 |
+ Digital Signature, Key Encipherment |
|
| 42 |
+ Signature Algorithm: sha1WithRSAEncryption |
|
| 43 |
+ 40:0f:10:39:c4:b7:0f:0d:2f:bf:d2:16:cc:8e:d3:9a:fb:8b: |
|
| 44 |
+ ce:4b:7b:0d:48:77:ce:f1:fe:d5:8f:ea:b1:71:ed:49:1d:9f: |
|
| 45 |
+ 23:3a:16:d4:70:7c:c5:29:bf:e4:90:34:d0:f0:00:24:f4:e4: |
|
| 46 |
+ df:2c:c3:83:01:66:61:c9:a8:ab:29:e7:98:6d:27:89:4a:76: |
|
| 47 |
+ c9:2e:19:8e:fe:6e:d5:f8:99:11:0e:97:67:4b:34:e3:1e:e3: |
|
| 48 |
+ 9f:35:00:a5:32:f9:b5:2c:f2:e0:c5:2e:cc:81:bd:18:dd:5c: |
|
| 49 |
+ 12:c8:6b:fa:0c:17:74:30:55:f6:6e:20:9a:6c:1e:09:b4:0c: |
|
| 50 |
+ 15:42 |
|
| 51 |
+-----BEGIN CERTIFICATE----- |
|
| 52 |
+MIIEKjCCA5OgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx |
|
| 53 |
+CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv |
|
| 54 |
+cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l |
|
| 55 |
+MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv |
|
| 56 |
+bWFpbjAeFw0xMzEyMDQxNTAxMjBaFw0yMzEyMDIxNTAxMjBaMIGbMQswCQYDVQQG |
|
| 57 |
+EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE |
|
| 58 |
+ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEKMAgGA1UEAxQBKjER |
|
| 59 |
+MA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21h |
|
| 60 |
+aW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMH/fTBvZEqxkrFx0cF04h3b |
|
| 61 |
+LREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y4OjCv5p3 |
|
| 62 |
+cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+lA2LQ2ch |
|
| 63 |
+M7IIYJ12qAUyHvmVCRR1AgMBAAGjggFzMIIBbzAJBgNVHRMEAjAAMBEGCWCGSAGG |
|
| 64 |
++EIBAQQEAwIGQDA0BglghkgBhvhCAQ0EJxYlRWFzeS1SU0EgR2VuZXJhdGVkIFNl |
|
| 65 |
+cnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUFAL9/d0TOOBx6tG+wA6JGi22GQYw |
|
| 66 |
+gdcGA1UdIwSBzzCBzIAUZu7DFz09q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJ |
|
| 67 |
+BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUw |
|
| 68 |
+EwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQD |
|
| 69 |
+EwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1h |
|
| 70 |
+aWxAaG9zdC5kb21haW6CCQD9q+xqhCcEpzATBgNVHSUEDDAKBggrBgEFBQcDATAL |
|
| 71 |
+BgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAQA8QOcS3Dw0vv9IWzI7TmvuL |
|
| 72 |
+zkt7DUh3zvH+1Y/qsXHtSR2fIzoW1HB8xSm/5JA00PAAJPTk3yzDgwFmYcmoqynn |
|
| 73 |
+mG0niUp2yS4Zjv5u1fiZEQ6XZ0s04x7jnzUApTL5tSzy4MUuzIG9GN1cEshr+gwX |
|
| 74 |
+dDBV9m4gmmweCbQMFUI= |
|
| 75 |
+-----END CERTIFICATE----- |
| 0 | 76 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,16 @@ |
| 0 |
+-----BEGIN PRIVATE KEY----- |
|
| 1 |
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMH/fTBvZEqxkrFx |
|
| 2 |
+0cF04h3bLREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y |
|
| 3 |
+4OjCv5p3cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+ |
|
| 4 |
+lA2LQ2chM7IIYJ12qAUyHvmVCRR1AgMBAAECgYAmwckb9RUfSwyYgLm8IYLPHiuJ |
|
| 5 |
+wkllZfVg5Bo7gXJcQnFjZmJ56uTj8xvUjZlODIHM63TSO5ibv6kFXtXKCqZGd2M+ |
|
| 6 |
+wGbhZ0f+2GvKcwMmJERnIQjuoNaYSQLT0tM0VB9Iz0rJlZC+tzPZ+5pPqEumRdsS |
|
| 7 |
+IzWNXfF42AhcbwAQYQJBAPVXtMYIJc9EZsz86ZcQiMPWUpCX5vnRmtwL8kKyR8D5 |
|
| 8 |
+4KfYeiowyFffSRMMcclwNHq7TgSXN+nIXM9WyzyzwikCQQDKbNA28AgZp9aT54HP |
|
| 9 |
+WnbeE2pmt+uk/zl/BtxJSoK6H+69Jec+lf7EgL7HgOWYRSNot4uQWu8IhsHLTiUq |
|
| 10 |
++0FtAkEAqwlRxRy4/x24bP+D+QRV0/D97j93joFJbE4Hved7jlSlAV4xDGilwlyv |
|
| 11 |
+HNB4Iu5OJ6Gcaibhm+FKkmD3noHSwQJBAIpu3fokLzX0bS+bDFBU6qO3HXX/47xj |
|
| 12 |
++tsfQvkwZrSI8AkU6c8IX0HdVhsz0FBRQAT2ORDQz1XCarfxykNZrwUCQQCGCBIc |
|
| 13 |
+BBCWzhHlswlGidWJg3HqqO6hPPClEr3B5G87oCsdeYwiO23XT6rUnoJXfJHp6oCW |
|
| 14 |
+5nCwDu5ZTP+khltg |
|
| 15 |
+-----END PRIVATE KEY----- |
| ... | ... |
@@ -1,9 +1,15 @@ |
| 1 | 1 |
package request |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "net" |
|
| 5 |
+ "net/http" |
|
| 4 | 6 |
"testing" |
| 7 |
+ "time" |
|
| 5 | 8 |
|
| 9 |
+ "github.com/docker/docker/api" |
|
| 6 | 10 |
"github.com/docker/docker/client" |
| 11 |
+ "github.com/docker/go-connections/sockets" |
|
| 12 |
+ "github.com/docker/go-connections/tlsconfig" |
|
| 7 | 13 |
"github.com/stretchr/testify/require" |
| 8 | 14 |
) |
| 9 | 15 |
|
| ... | ... |
@@ -13,3 +19,35 @@ func NewAPIClient(t *testing.T) client.APIClient {
|
| 13 | 13 |
require.NoError(t, err) |
| 14 | 14 |
return clt |
| 15 | 15 |
} |
| 16 |
+ |
|
| 17 |
+// NewTLSAPIClient returns a docker API client configured with the |
|
| 18 |
+// provided TLS settings |
|
| 19 |
+func NewTLSAPIClient(t *testing.T, host, cacertPath, certPath, keyPath string) (client.APIClient, error) {
|
|
| 20 |
+ opts := tlsconfig.Options{
|
|
| 21 |
+ CAFile: cacertPath, |
|
| 22 |
+ CertFile: certPath, |
|
| 23 |
+ KeyFile: keyPath, |
|
| 24 |
+ ExclusiveRootPools: true, |
|
| 25 |
+ } |
|
| 26 |
+ config, err := tlsconfig.Client(opts) |
|
| 27 |
+ require.Nil(t, err) |
|
| 28 |
+ tr := &http.Transport{
|
|
| 29 |
+ TLSClientConfig: config, |
|
| 30 |
+ DialContext: (&net.Dialer{
|
|
| 31 |
+ KeepAlive: 30 * time.Second, |
|
| 32 |
+ Timeout: 30 * time.Second, |
|
| 33 |
+ }).DialContext, |
|
| 34 |
+ } |
|
| 35 |
+ proto, addr, _, err := client.ParseHost(host) |
|
| 36 |
+ require.Nil(t, err) |
|
| 37 |
+ |
|
| 38 |
+ sockets.ConfigureTransport(tr, proto, addr) |
|
| 39 |
+ |
|
| 40 |
+ httpClient := &http.Client{
|
|
| 41 |
+ Transport: tr, |
|
| 42 |
+ CheckRedirect: client.CheckRedirect, |
|
| 43 |
+ } |
|
| 44 |
+ verStr := api.DefaultVersion |
|
| 45 |
+ customHeaders := map[string]string{}
|
|
| 46 |
+ return client.NewClient(host, verStr, httpClient, customHeaders) |
|
| 47 |
+} |
| 16 | 48 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,31 @@ |
| 0 |
+package requirement |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/http" |
|
| 4 |
+ "strings" |
|
| 5 |
+ "testing" |
|
| 6 |
+ "time" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/gotestyourself/gotestyourself/skip" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+// HasHubConnectivity checks to see if https://hub.docker.com is |
|
| 12 |
+// accessible from the present environment |
|
| 13 |
+func HasHubConnectivity(t *testing.T) {
|
|
| 14 |
+ // Set a timeout on the GET at 15s |
|
| 15 |
+ var timeout = 15 * time.Second |
|
| 16 |
+ var url = "https://hub.docker.com" |
|
| 17 |
+ |
|
| 18 |
+ client := http.Client{
|
|
| 19 |
+ Timeout: timeout, |
|
| 20 |
+ } |
|
| 21 |
+ |
|
| 22 |
+ resp, err := client.Get(url) |
|
| 23 |
+ if err != nil && strings.Contains(err.Error(), "use of closed network connection") {
|
|
| 24 |
+ t.Fatalf("Timeout for GET request on %s", url)
|
|
| 25 |
+ } |
|
| 26 |
+ if resp != nil {
|
|
| 27 |
+ resp.Body.Close() |
|
| 28 |
+ } |
|
| 29 |
+ skip.IfCondition(t, err != nil) |
|
| 30 |
+} |