integration/plugin/authz: port tests from integration-cli
Signed-off-by: David Sheets <dsheets@docker.com>
This commit is contained in:
parent
c982ee805d
commit
928b0631c9
23 changed files with 1338 additions and 837 deletions
|
@ -15,7 +15,7 @@ source "$SCRIPTDIR/make/.go-autogen"
|
|||
|
||||
integration_api_dirs=${TEST_INTEGRATION_DIR:-"$(
|
||||
find ./integration -type d |
|
||||
grep -vE '^(./integration$|./integration/util)')"}
|
||||
grep -vE '^(./integration($|/util|/internal|/testdata|/plugin$))')"}
|
||||
|
||||
run_test_integration() {
|
||||
[[ "$TESTFLAGS" != *-check.f* ]] && run_test_integration_suites
|
||||
|
|
|
@ -1,162 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
var (
|
||||
authzPluginName = "riyaz/authz-no-volume-plugin"
|
||||
authzPluginTag = "latest"
|
||||
authzPluginNameWithTag = authzPluginName + ":" + authzPluginTag
|
||||
authzPluginBadManifestName = "riyaz/authz-plugin-bad-manifest"
|
||||
nonexistentAuthzPluginName = "riyaz/nonexistent-authz-plugin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
check.Suite(&DockerAuthzV2Suite{
|
||||
ds: &DockerSuite{},
|
||||
})
|
||||
}
|
||||
|
||||
type DockerAuthzV2Suite struct {
|
||||
ds *DockerSuite
|
||||
d *daemon.Daemon
|
||||
}
|
||||
|
||||
func (s *DockerAuthzV2Suite) SetUpTest(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, Network, SameHostDaemon)
|
||||
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.ExperimentalDaemon(),
|
||||
})
|
||||
s.d.Start(c)
|
||||
}
|
||||
|
||||
func (s *DockerAuthzV2Suite) TearDownTest(c *check.C) {
|
||||
if s.d != nil {
|
||||
s.d.Stop(c)
|
||||
s.ds.TearDownTest(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerAuthzV2Suite) TestAuthZPluginAllowNonVolumeRequest(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, IsAmd64, Network)
|
||||
|
||||
// Install authz plugin
|
||||
_, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
// start the daemon with the plugin and load busybox, --net=none build fails otherwise
|
||||
// because it needs to pull busybox
|
||||
s.d.Restart(c, "--authorization-plugin="+authzPluginNameWithTag)
|
||||
s.d.LoadBusybox(c)
|
||||
|
||||
// defer disabling the plugin
|
||||
defer func() {
|
||||
s.d.Restart(c)
|
||||
_, err = s.d.Cmd("plugin", "disable", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
_, err = s.d.Cmd("plugin", "rm", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}()
|
||||
|
||||
// Ensure docker run command and accompanying docker ps are successful
|
||||
_, err = s.d.Cmd("run", "-d", "busybox", "top")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerAuthzV2Suite) TestAuthZPluginDisable(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, IsAmd64, Network)
|
||||
// Install authz plugin
|
||||
_, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
// start the daemon with the plugin and load busybox, --net=none build fails otherwise
|
||||
// because it needs to pull busybox
|
||||
s.d.Restart(c, "--authorization-plugin="+authzPluginNameWithTag)
|
||||
s.d.LoadBusybox(c)
|
||||
|
||||
// defer removing the plugin
|
||||
defer func() {
|
||||
s.d.Restart(c)
|
||||
_, err = s.d.Cmd("plugin", "rm", "-f", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}()
|
||||
|
||||
out, err := s.d.Cmd("volume", "create")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
||||
|
||||
// disable the plugin
|
||||
_, err = s.d.Cmd("plugin", "disable", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// now test to see if the docker api works.
|
||||
_, err = s.d.Cmd("volume", "create")
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerAuthzV2Suite) TestAuthZPluginRejectVolumeRequests(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, IsAmd64, Network)
|
||||
// Install authz plugin
|
||||
_, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// restart the daemon with the plugin
|
||||
s.d.Restart(c, "--authorization-plugin="+authzPluginNameWithTag)
|
||||
|
||||
// defer disabling the plugin
|
||||
defer func() {
|
||||
s.d.Restart(c)
|
||||
_, err = s.d.Cmd("plugin", "disable", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
_, err = s.d.Cmd("plugin", "rm", authzPluginNameWithTag)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}()
|
||||
|
||||
out, err := s.d.Cmd("volume", "create")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
||||
|
||||
out, err = s.d.Cmd("volume", "ls")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
||||
|
||||
// The plugin will block the command before it can determine the volume does not exist
|
||||
out, err = s.d.Cmd("volume", "rm", "test")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
||||
|
||||
out, err = s.d.Cmd("volume", "inspect", "test")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
||||
|
||||
out, err = s.d.Cmd("volume", "prune", "-f")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
|
||||
}
|
||||
|
||||
func (s *DockerAuthzV2Suite) TestAuthZPluginBadManifestFailsDaemonStart(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, IsAmd64, Network)
|
||||
// Install authz plugin with bad manifest
|
||||
_, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginBadManifestName)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// start the daemon with the plugin, it will error
|
||||
c.Assert(s.d.RestartWithError("--authorization-plugin="+authzPluginBadManifestName), check.NotNil)
|
||||
|
||||
// restarting the daemon without requiring the plugin will succeed
|
||||
s.d.Restart(c)
|
||||
}
|
||||
|
||||
func (s *DockerAuthzV2Suite) TestNonexistentAuthZPluginFailsDaemonStart(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, Network)
|
||||
// start the daemon with a non-existent authz plugin, it will error
|
||||
c.Assert(s.d.RestartWithError("--authorization-plugin="+nonexistentAuthzPluginName), check.NotNil)
|
||||
|
||||
// restarting the daemon without requiring the plugin will succeed
|
||||
s.d.Start(c)
|
||||
}
|
|
@ -1,470 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"bufio"
|
||||
"bytes"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"net"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/pkg/authorization"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
const (
|
||||
testAuthZPlugin = "authzplugin"
|
||||
unauthorizedMessage = "User unauthorized authz plugin"
|
||||
errorMessage = "something went wrong..."
|
||||
containerListAPI = "/containers/json"
|
||||
)
|
||||
|
||||
var (
|
||||
alwaysAllowed = []string{"/_ping", "/info"}
|
||||
)
|
||||
|
||||
func init() {
|
||||
check.Suite(&DockerAuthzSuite{
|
||||
ds: &DockerSuite{},
|
||||
})
|
||||
}
|
||||
|
||||
type DockerAuthzSuite struct {
|
||||
server *httptest.Server
|
||||
ds *DockerSuite
|
||||
d *daemon.Daemon
|
||||
ctrl *authorizationController
|
||||
}
|
||||
|
||||
type authorizationController struct {
|
||||
reqRes authorization.Response // reqRes holds the plugin response to the initial client request
|
||||
resRes authorization.Response // resRes holds the plugin response to the daemon response
|
||||
psRequestCnt int // psRequestCnt counts the number of calls to list container request api
|
||||
psResponseCnt int // psResponseCnt counts the number of calls to list containers response API
|
||||
requestsURIs []string // requestsURIs stores all request URIs that are sent to the authorization controller
|
||||
reqUser string
|
||||
resUser string
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) SetUpTest(c *check.C) {
|
||||
testRequires(c, SameHostDaemon)
|
||||
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.ExperimentalDaemon(),
|
||||
})
|
||||
s.ctrl = &authorizationController{}
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TearDownTest(c *check.C) {
|
||||
if s.d != nil {
|
||||
s.d.Stop(c)
|
||||
s.ds.TearDownTest(c)
|
||||
s.ctrl = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) SetUpSuite(c *check.C) {
|
||||
mux := http.NewServeMux()
|
||||
s.server = httptest.NewServer(mux)
|
||||
|
||||
mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
|
||||
b, err := json.Marshal(plugins.Manifest{Implements: []string{authorization.AuthZApiImplements}})
|
||||
c.Assert(err, check.IsNil)
|
||||
w.Write(b)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/AuthZPlugin.AuthZReq", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
c.Assert(err, check.IsNil)
|
||||
authReq := authorization.Request{}
|
||||
err = json.Unmarshal(body, &authReq)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
assertBody(c, authReq.RequestURI, authReq.RequestHeaders, authReq.RequestBody)
|
||||
assertAuthHeaders(c, authReq.RequestHeaders)
|
||||
|
||||
// Count only container list api
|
||||
if strings.HasSuffix(authReq.RequestURI, containerListAPI) {
|
||||
s.ctrl.psRequestCnt++
|
||||
}
|
||||
|
||||
s.ctrl.requestsURIs = append(s.ctrl.requestsURIs, authReq.RequestURI)
|
||||
|
||||
reqRes := s.ctrl.reqRes
|
||||
if isAllowed(authReq.RequestURI) {
|
||||
reqRes = authorization.Response{Allow: true}
|
||||
}
|
||||
if reqRes.Err != "" {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
b, err := json.Marshal(reqRes)
|
||||
c.Assert(err, check.IsNil)
|
||||
s.ctrl.reqUser = authReq.User
|
||||
w.Write(b)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/AuthZPlugin.AuthZRes", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
c.Assert(err, check.IsNil)
|
||||
authReq := authorization.Request{}
|
||||
err = json.Unmarshal(body, &authReq)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
assertBody(c, authReq.RequestURI, authReq.ResponseHeaders, authReq.ResponseBody)
|
||||
assertAuthHeaders(c, authReq.ResponseHeaders)
|
||||
|
||||
// Count only container list api
|
||||
if strings.HasSuffix(authReq.RequestURI, containerListAPI) {
|
||||
s.ctrl.psResponseCnt++
|
||||
}
|
||||
resRes := s.ctrl.resRes
|
||||
if isAllowed(authReq.RequestURI) {
|
||||
resRes = authorization.Response{Allow: true}
|
||||
}
|
||||
if resRes.Err != "" {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
b, err := json.Marshal(resRes)
|
||||
c.Assert(err, check.IsNil)
|
||||
s.ctrl.resUser = authReq.User
|
||||
w.Write(b)
|
||||
})
|
||||
|
||||
err := os.MkdirAll("/etc/docker/plugins", 0755)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", testAuthZPlugin)
|
||||
err = ioutil.WriteFile(fileName, []byte(s.server.URL), 0644)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// check for always allowed endpoints to not inhibit test framework functions
|
||||
func isAllowed(reqURI string) bool {
|
||||
for _, endpoint := range alwaysAllowed {
|
||||
if strings.HasSuffix(reqURI, endpoint) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// assertAuthHeaders validates authentication headers are removed
|
||||
func assertAuthHeaders(c *check.C, headers map[string]string) error {
|
||||
for k := range headers {
|
||||
if strings.Contains(strings.ToLower(k), "auth") || strings.Contains(strings.ToLower(k), "x-registry") {
|
||||
c.Errorf("Found authentication headers in request '%v'", headers)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// assertBody asserts that body is removed for non text/json requests
|
||||
func assertBody(c *check.C, requestURI string, headers map[string]string, body []byte) {
|
||||
if strings.Contains(strings.ToLower(requestURI), "auth") && len(body) > 0 {
|
||||
//return fmt.Errorf("Body included for authentication endpoint %s", string(body))
|
||||
c.Errorf("Body included for authentication endpoint %s", string(body))
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
if strings.EqualFold(k, "Content-Type") && strings.HasPrefix(v, "text/") || v == "application/json" {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(body) > 0 {
|
||||
c.Errorf("Body included while it should not (Headers: '%v')", headers)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TearDownSuite(c *check.C) {
|
||||
if s.server == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.server.Close()
|
||||
|
||||
err := os.RemoveAll("/etc/docker/plugins")
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginAllowRequest(c *check.C) {
|
||||
// start the daemon and load busybox, --net=none build fails otherwise
|
||||
// cause it needs to pull busybox
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Allow = true
|
||||
s.ctrl.resRes.Allow = true
|
||||
s.d.LoadBusybox(c)
|
||||
|
||||
// Ensure command successful
|
||||
out, err := s.d.Cmd("run", "-d", "busybox", "top")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
id := strings.TrimSpace(out)
|
||||
assertURIRecorded(c, s.ctrl.requestsURIs, "/containers/create")
|
||||
assertURIRecorded(c, s.ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", id))
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginTls(c *check.C) {
|
||||
|
||||
const testDaemonHTTPSAddr = "tcp://localhost:4271"
|
||||
// start the daemon and load busybox, --net=none build fails otherwise
|
||||
// cause it needs to pull busybox
|
||||
s.d.Start(c,
|
||||
"--authorization-plugin="+testAuthZPlugin,
|
||||
"--tlsverify",
|
||||
"--tlscacert",
|
||||
"fixtures/https/ca.pem",
|
||||
"--tlscert",
|
||||
"fixtures/https/server-cert.pem",
|
||||
"--tlskey",
|
||||
"fixtures/https/server-key.pem",
|
||||
"-H", testDaemonHTTPSAddr)
|
||||
|
||||
s.ctrl.reqRes.Allow = true
|
||||
s.ctrl.resRes.Allow = true
|
||||
|
||||
out, _ := dockerCmd(
|
||||
c,
|
||||
"--tlsverify",
|
||||
"--tlscacert", "fixtures/https/ca.pem",
|
||||
"--tlscert", "fixtures/https/client-cert.pem",
|
||||
"--tlskey", "fixtures/https/client-key.pem",
|
||||
"-H",
|
||||
testDaemonHTTPSAddr,
|
||||
"version",
|
||||
)
|
||||
if !strings.Contains(out, "Server") {
|
||||
c.Fatalf("docker version should return information of server side")
|
||||
}
|
||||
|
||||
c.Assert(s.ctrl.reqUser, check.Equals, "client")
|
||||
c.Assert(s.ctrl.resUser, check.Equals, "client")
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginDenyRequest(c *check.C) {
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Allow = false
|
||||
s.ctrl.reqRes.Msg = unauthorizedMessage
|
||||
|
||||
// Ensure command is blocked
|
||||
res, err := s.d.Cmd("ps")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(s.ctrl.psRequestCnt, check.Equals, 1)
|
||||
c.Assert(s.ctrl.psResponseCnt, check.Equals, 0)
|
||||
|
||||
// Ensure unauthorized message appears in response
|
||||
c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s\n", testAuthZPlugin, unauthorizedMessage))
|
||||
}
|
||||
|
||||
// TestAuthZPluginAPIDenyResponse validates that when authorization plugin deny the request, the status code is forbidden
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginAPIDenyResponse(c *check.C) {
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Allow = false
|
||||
s.ctrl.resRes.Msg = unauthorizedMessage
|
||||
|
||||
daemonURL, err := url.Parse(s.d.Sock())
|
||||
|
||||
conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10)
|
||||
c.Assert(err, check.IsNil)
|
||||
client := httputil.NewClientConn(conn, nil)
|
||||
req, err := http.NewRequest("GET", "/version", nil)
|
||||
c.Assert(err, check.IsNil)
|
||||
resp, err := client.Do(req)
|
||||
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusForbidden)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginDenyResponse(c *check.C) {
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Allow = true
|
||||
s.ctrl.resRes.Allow = false
|
||||
s.ctrl.resRes.Msg = unauthorizedMessage
|
||||
|
||||
// Ensure command is blocked
|
||||
res, err := s.d.Cmd("ps")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(s.ctrl.psRequestCnt, check.Equals, 1)
|
||||
c.Assert(s.ctrl.psResponseCnt, check.Equals, 1)
|
||||
|
||||
// Ensure unauthorized message appears in response
|
||||
c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s\n", testAuthZPlugin, unauthorizedMessage))
|
||||
}
|
||||
|
||||
// TestAuthZPluginAllowEventStream verifies event stream propagates correctly after request pass through by the authorization plugin
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginAllowEventStream(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
// start the daemon and load busybox to avoid pulling busybox from Docker Hub
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Allow = true
|
||||
s.ctrl.resRes.Allow = true
|
||||
s.d.LoadBusybox(c)
|
||||
|
||||
startTime := strconv.FormatInt(daemonTime(c).Unix(), 10)
|
||||
// Add another command to to enable event pipelining
|
||||
eventsCmd := exec.Command(dockerBinary, "--host", s.d.Sock(), "events", "--since", startTime)
|
||||
stdout, err := eventsCmd.StdoutPipe()
|
||||
if err != nil {
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
observer := eventObserver{
|
||||
buffer: new(bytes.Buffer),
|
||||
command: eventsCmd,
|
||||
scanner: bufio.NewScanner(stdout),
|
||||
startTime: startTime,
|
||||
}
|
||||
|
||||
err = observer.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer observer.Stop()
|
||||
|
||||
// Create a container and wait for the creation events
|
||||
out, err := s.d.Cmd("run", "-d", "busybox", "top")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
containerID := strings.TrimSpace(out)
|
||||
c.Assert(s.d.WaitRun(containerID), checker.IsNil)
|
||||
|
||||
events := map[string]chan bool{
|
||||
"create": make(chan bool, 1),
|
||||
"start": make(chan bool, 1),
|
||||
}
|
||||
|
||||
matcher := matchEventLine(containerID, "container", events)
|
||||
processor := processEventMatch(events)
|
||||
go observer.Match(matcher, processor)
|
||||
|
||||
// Ensure all events are received
|
||||
for event, eventChannel := range events {
|
||||
|
||||
select {
|
||||
case <-time.After(30 * time.Second):
|
||||
// Fail the test
|
||||
observer.CheckEventError(c, containerID, event, matcher)
|
||||
c.FailNow()
|
||||
case <-eventChannel:
|
||||
// Ignore, event received
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure both events and container endpoints are passed to the authorization plugin
|
||||
assertURIRecorded(c, s.ctrl.requestsURIs, "/events")
|
||||
assertURIRecorded(c, s.ctrl.requestsURIs, "/containers/create")
|
||||
assertURIRecorded(c, s.ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", containerID))
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginErrorResponse(c *check.C) {
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Allow = true
|
||||
s.ctrl.resRes.Err = errorMessage
|
||||
|
||||
// Ensure command is blocked
|
||||
res, err := s.d.Cmd("ps")
|
||||
c.Assert(err, check.NotNil)
|
||||
|
||||
c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s\n", testAuthZPlugin, authorization.AuthZApiResponse, errorMessage))
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginErrorRequest(c *check.C) {
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Err = errorMessage
|
||||
|
||||
// Ensure command is blocked
|
||||
res, err := s.d.Cmd("ps")
|
||||
c.Assert(err, check.NotNil)
|
||||
|
||||
c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s\n", testAuthZPlugin, authorization.AuthZApiRequest, errorMessage))
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginEnsureNoDuplicatePluginRegistration(c *check.C) {
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin)
|
||||
|
||||
s.ctrl.reqRes.Allow = true
|
||||
s.ctrl.resRes.Allow = true
|
||||
|
||||
out, err := s.d.Cmd("ps")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
|
||||
// assert plugin is only called once..
|
||||
c.Assert(s.ctrl.psRequestCnt, check.Equals, 1)
|
||||
c.Assert(s.ctrl.psResponseCnt, check.Equals, 1)
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginEnsureLoadImportWorking(c *check.C) {
|
||||
s.d.Start(c, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Allow = true
|
||||
s.ctrl.resRes.Allow = true
|
||||
s.d.LoadBusybox(c)
|
||||
|
||||
tmp, err := ioutil.TempDir("", "test-authz-load-import")
|
||||
c.Assert(err, check.IsNil)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
savedImagePath := filepath.Join(tmp, "save.tar")
|
||||
|
||||
out, err := s.d.Cmd("save", "-o", savedImagePath, "busybox")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
out, err = s.d.Cmd("load", "--input", savedImagePath)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
|
||||
exportedImagePath := filepath.Join(tmp, "export.tar")
|
||||
|
||||
out, err = s.d.Cmd("run", "-d", "--name", "testexport", "busybox")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
out, err = s.d.Cmd("export", "-o", exportedImagePath, "testexport")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
out, err = s.d.Cmd("import", exportedImagePath)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
}
|
||||
|
||||
func (s *DockerAuthzSuite) TestAuthZPluginHeader(c *check.C) {
|
||||
s.d.Start(c, "--debug", "--authorization-plugin="+testAuthZPlugin)
|
||||
s.ctrl.reqRes.Allow = true
|
||||
s.ctrl.resRes.Allow = true
|
||||
s.d.LoadBusybox(c)
|
||||
|
||||
daemonURL, err := url.Parse(s.d.Sock())
|
||||
|
||||
conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10)
|
||||
c.Assert(err, check.IsNil)
|
||||
client := httputil.NewClientConn(conn, nil)
|
||||
req, err := http.NewRequest("GET", "/version", nil)
|
||||
c.Assert(err, check.IsNil)
|
||||
resp, err := client.Do(req)
|
||||
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(resp.Header["Content-Type"][0], checker.Equals, "application/json")
|
||||
}
|
||||
|
||||
// assertURIRecorded verifies that the given URI was sent and recorded in the authz plugin
|
||||
func assertURIRecorded(c *check.C, uris []string, uri string) {
|
||||
var found bool
|
||||
for _, u := range uris {
|
||||
if strings.Contains(u, uri) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
c.Fatalf("Expected to find URI '%s', recorded uris '%s'", uri, strings.Join(uris, ","))
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID0TCCAzqgAwIBAgIJAP2r7GqEJwSnMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD
|
||||
VQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMG
|
||||
A1UEChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTERMA8GA1UEAxMI
|
||||
Y2hhbmdlbWUxETAPBgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWls
|
||||
QGhvc3QuZG9tYWluMB4XDTEzMTIwMzE2NTYzMFoXDTIzMTIwMTE2NTYzMFowgaIx
|
||||
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2Nv
|
||||
MRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYD
|
||||
VQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEW
|
||||
EG1haWxAaG9zdC5kb21haW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALAn
|
||||
0xDw+5y7ZptQacq66pUhRu82JP2WU6IDgo5QUtNU6/CX5PwQATe/OnYTZQFbksxp
|
||||
AU9boG0FCkgxfsgPYXEuZxVEGKI2fxfKHOZZI8mrkWmj6eWU/0cvCjGVc9rTITP5
|
||||
sNQvg+hORyVDdNp2IdsbMJayiB3AQYMFx3vSDOMTAgMBAAGjggELMIIBBzAdBgNV
|
||||
HQ4EFgQUZu7DFz09q0QBa2+ymRm9qgK1NPswgdcGA1UdIwSBzzCBzIAUZu7DFz09
|
||||
q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD
|
||||
QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x
|
||||
ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI
|
||||
Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq
|
||||
hCcEpzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAF8fJKKM+/oOdnNi
|
||||
zEd0M1+PmZOyqvjYQn/2ZR8UHH6Imgc/OPQKZXf0bVE1Txc/DaUNn9Isd1SuCuaE
|
||||
ic3vAIYYU7PmgeNN6vwec48V96T7jr+GAi6AVMhQEc2hHCfVtx11Xx+x6aHDZzJt
|
||||
Zxtf5lL6KSO9Y+EFwM+rju6hm5hW
|
||||
-----END CERTIFICATE-----
|
1
integration-cli/fixtures/https/ca.pem
Symbolic link
1
integration-cli/fixtures/https/ca.pem
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../integration/testdata/https/ca.pem
|
|
@ -1,73 +0,0 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 3 (0x3)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
|
||||
Validity
|
||||
Not Before: Dec 4 14:17:54 2013 GMT
|
||||
Not After : Dec 2 14:17:54 2023 GMT
|
||||
Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=client/name=changeme/emailAddress=mail@host.domain
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:ca:c9:05:d0:09:4e:3e:a4:fc:d5:14:f4:a5:e8:
|
||||
34:d3:6b:51:e3:f3:62:ea:a1:f0:e8:ed:c4:2a:bc:
|
||||
f0:4f:ca:07:df:e3:88:fa:f4:21:99:35:0e:3d:ea:
|
||||
b0:86:e7:c4:d2:8a:83:2b:42:b8:ec:a3:99:62:70:
|
||||
81:46:cc:fc:a5:1d:d2:63:e8:eb:07:25:9a:e2:25:
|
||||
6d:11:56:f2:1a:51:a1:b6:3e:1c:57:32:e9:7b:2c:
|
||||
aa:1b:cc:97:2d:89:2d:b1:c9:5e:35:28:4d:7c:fa:
|
||||
65:31:3e:f7:70:dd:6e:0b:3c:58:af:a8:2e:24:c0:
|
||||
7e:4e:78:7d:0a:9e:8f:42:43
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
Easy-RSA Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
DE:42:EF:2D:98:A3:6C:A8:AA:E0:8C:71:2C:9D:64:23:A9:E2:7E:81
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB
|
||||
DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
|
||||
serial:FD:AB:EC:6A:84:27:04:A7
|
||||
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Client Authentication
|
||||
X509v3 Key Usage:
|
||||
Digital Signature
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
1c:44:26:ea:e1:66:25:cb:e4:8e:57:1c:f6:b9:17:22:62:40:
|
||||
12:90:8f:3b:b2:61:7a:54:94:8f:b1:20:0b:bf:a3:51:e3:fa:
|
||||
1c:a1:be:92:3a:d0:76:44:c0:57:83:ab:6a:e4:1a:45:49:a4:
|
||||
af:39:0d:60:32:fc:3a:be:d7:fb:5d:99:7a:1f:87:e7:d5:ab:
|
||||
84:a2:5e:90:d8:bf:fa:89:6d:32:26:02:5e:31:35:68:7f:31:
|
||||
f5:6b:51:46:bc:af:70:ed:5a:09:7d:ec:b2:48:4f:fe:c5:2f:
|
||||
56:04:ad:f6:c1:d2:2a:e4:6a:c4:87:fe:08:35:c5:38:cb:5e:
|
||||
4a:c4
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEFTCCA36gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv
|
||||
cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l
|
||||
MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv
|
||||
bWFpbjAeFw0xMzEyMDQxNDE3NTRaFw0yMzEyMDIxNDE3NTRaMIGgMQswCQYDVQQG
|
||||
EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE
|
||||
ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEPMA0GA1UEAxMGY2xp
|
||||
ZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0
|
||||
LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyskF0AlOPqT81RT0
|
||||
peg002tR4/Ni6qHw6O3EKrzwT8oH3+OI+vQhmTUOPeqwhufE0oqDK0K47KOZYnCB
|
||||
Rsz8pR3SY+jrByWa4iVtEVbyGlGhtj4cVzLpeyyqG8yXLYktscleNShNfPplMT73
|
||||
cN1uCzxYr6guJMB+Tnh9Cp6PQkMCAwEAAaOCAVkwggFVMAkGA1UdEwQCMAAwLQYJ
|
||||
YIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
|
||||
HQ4EFgQU3kLvLZijbKiq4IxxLJ1kI6nifoEwgdcGA1UdIwSBzzCBzIAUZu7DFz09
|
||||
q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD
|
||||
QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x
|
||||
ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI
|
||||
Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq
|
||||
hCcEpzATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcN
|
||||
AQEFBQADgYEAHEQm6uFmJcvkjlcc9rkXImJAEpCPO7JhelSUj7EgC7+jUeP6HKG+
|
||||
kjrQdkTAV4OrauQaRUmkrzkNYDL8Or7X+12Zeh+H59WrhKJekNi/+oltMiYCXjE1
|
||||
aH8x9WtRRryvcO1aCX3sskhP/sUvVgSt9sHSKuRqxIf+CDXFOMteSsQ=
|
||||
-----END CERTIFICATE-----
|
1
integration-cli/fixtures/https/client-cert.pem
Symbolic link
1
integration-cli/fixtures/https/client-cert.pem
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../integration/testdata/https/client-cert.pem
|
|
@ -1,16 +0,0 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMrJBdAJTj6k/NUU
|
||||
9KXoNNNrUePzYuqh8OjtxCq88E/KB9/jiPr0IZk1Dj3qsIbnxNKKgytCuOyjmWJw
|
||||
gUbM/KUd0mPo6wclmuIlbRFW8hpRobY+HFcy6XssqhvMly2JLbHJXjUoTXz6ZTE+
|
||||
93Ddbgs8WK+oLiTAfk54fQqej0JDAgMBAAECgYBOFEzKp2qbMEexe9ofL2N3rDDh
|
||||
xkrl8OijpzkLA6i78BxMFn4dsnZlWUpciMrjhsYAExkiRRSS+QMMJimAq1jzQqc3
|
||||
FAQV2XGYwkd0cUn7iZGvfNnEPysjsfyYQM+m+sT0ATj4BZjVShC6kkSjTdm1leLN
|
||||
OSvcHdcu3Xxg9ufF0QJBAPYdnNt5sIndt2WECePuRVi+uF4mlxTobFY0fjn26yhC
|
||||
4RsnhhD3Vldygo9gvnkwrAZYaALGSPBewes2InxvjA8CQQDS7erKiNXpwoqz5XiU
|
||||
SVEsIIVTdWzBjGbIqMOu/hUwM5FK4j6JTBks0aTGMyh0YV9L1EzM0X79J29JahCe
|
||||
iQKNAkBKNMOGqTpBV0hko1sYDk96YobUXG5RL4L6uvkUIQ7mJMQam+AgXXL7Ctuy
|
||||
v0iu4a38e8tgisiTMP7nHHtpaXihAkAOiN54/lzfMsykANgCP9scE1GcoqbP34Dl
|
||||
qttxH4kOPT9xzY1JoLjLYdbc4YGUI3GRpBt2sajygNkmUey7P+2xAkBBsVCZFvTw
|
||||
qHvOpPS2kX5ml5xoc/QAHK9N7kR+X7XFYx82RTVSqJEK4lPb+aEWn+CjiIewO4Q5
|
||||
ksDFuNxAzbhl
|
||||
-----END PRIVATE KEY-----
|
1
integration-cli/fixtures/https/client-key.pem
Symbolic link
1
integration-cli/fixtures/https/client-key.pem
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../integration/testdata/https/client-key.pem
|
|
@ -1,76 +0,0 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 4 (0x4)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
|
||||
Validity
|
||||
Not Before: Dec 4 15:01:20 2013 GMT
|
||||
Not After : Dec 2 15:01:20 2023 GMT
|
||||
Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=*/name=changeme/emailAddress=mail@host.domain
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:c1:ff:7d:30:6f:64:4a:b1:92:b1:71:d1:c1:74:
|
||||
e2:1d:db:2d:11:24:e1:00:d4:00:ae:6f:c8:9e:ae:
|
||||
67:b3:4a:bd:f7:e6:9e:57:6d:19:4c:3c:23:94:2d:
|
||||
3d:d6:63:84:d8:fa:76:2b:38:12:c1:ed:20:9d:32:
|
||||
e0:e8:c2:bf:9a:77:70:04:3f:7f:ca:8c:2c:82:d6:
|
||||
3d:25:5c:02:1a:4f:64:93:03:dd:9c:42:97:5e:09:
|
||||
49:af:f0:c2:e1:30:08:0e:21:46:95:d1:13:59:c0:
|
||||
c8:76:be:94:0d:8b:43:67:21:33:b2:08:60:9d:76:
|
||||
a8:05:32:1e:f9:95:09:14:75
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Cert Type:
|
||||
SSL Server
|
||||
Netscape Comment:
|
||||
Easy-RSA Generated Server Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
14:02:FD:FD:DD:13:38:E0:71:EA:D1:BE:C0:0E:89:1A:2D:B6:19:06
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB
|
||||
DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
|
||||
serial:FD:AB:EC:6A:84:27:04:A7
|
||||
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication
|
||||
X509v3 Key Usage:
|
||||
Digital Signature, Key Encipherment
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
40:0f:10:39:c4:b7:0f:0d:2f:bf:d2:16:cc:8e:d3:9a:fb:8b:
|
||||
ce:4b:7b:0d:48:77:ce:f1:fe:d5:8f:ea:b1:71:ed:49:1d:9f:
|
||||
23:3a:16:d4:70:7c:c5:29:bf:e4:90:34:d0:f0:00:24:f4:e4:
|
||||
df:2c:c3:83:01:66:61:c9:a8:ab:29:e7:98:6d:27:89:4a:76:
|
||||
c9:2e:19:8e:fe:6e:d5:f8:99:11:0e:97:67:4b:34:e3:1e:e3:
|
||||
9f:35:00:a5:32:f9:b5:2c:f2:e0:c5:2e:cc:81:bd:18:dd:5c:
|
||||
12:c8:6b:fa:0c:17:74:30:55:f6:6e:20:9a:6c:1e:09:b4:0c:
|
||||
15:42
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEKjCCA5OgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv
|
||||
cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l
|
||||
MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv
|
||||
bWFpbjAeFw0xMzEyMDQxNTAxMjBaFw0yMzEyMDIxNTAxMjBaMIGbMQswCQYDVQQG
|
||||
EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE
|
||||
ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEKMAgGA1UEAxQBKjER
|
||||
MA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21h
|
||||
aW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMH/fTBvZEqxkrFx0cF04h3b
|
||||
LREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y4OjCv5p3
|
||||
cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+lA2LQ2ch
|
||||
M7IIYJ12qAUyHvmVCRR1AgMBAAGjggFzMIIBbzAJBgNVHRMEAjAAMBEGCWCGSAGG
|
||||
+EIBAQQEAwIGQDA0BglghkgBhvhCAQ0EJxYlRWFzeS1SU0EgR2VuZXJhdGVkIFNl
|
||||
cnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUFAL9/d0TOOBx6tG+wA6JGi22GQYw
|
||||
gdcGA1UdIwSBzzCBzIAUZu7DFz09q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJ
|
||||
BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUw
|
||||
EwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQD
|
||||
EwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1h
|
||||
aWxAaG9zdC5kb21haW6CCQD9q+xqhCcEpzATBgNVHSUEDDAKBggrBgEFBQcDATAL
|
||||
BgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAQA8QOcS3Dw0vv9IWzI7TmvuL
|
||||
zkt7DUh3zvH+1Y/qsXHtSR2fIzoW1HB8xSm/5JA00PAAJPTk3yzDgwFmYcmoqynn
|
||||
mG0niUp2yS4Zjv5u1fiZEQ6XZ0s04x7jnzUApTL5tSzy4MUuzIG9GN1cEshr+gwX
|
||||
dDBV9m4gmmweCbQMFUI=
|
||||
-----END CERTIFICATE-----
|
1
integration-cli/fixtures/https/server-cert.pem
Symbolic link
1
integration-cli/fixtures/https/server-cert.pem
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../integration/testdata/https/server-cert.pem
|
|
@ -1,16 +0,0 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMH/fTBvZEqxkrFx
|
||||
0cF04h3bLREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y
|
||||
4OjCv5p3cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+
|
||||
lA2LQ2chM7IIYJ12qAUyHvmVCRR1AgMBAAECgYAmwckb9RUfSwyYgLm8IYLPHiuJ
|
||||
wkllZfVg5Bo7gXJcQnFjZmJ56uTj8xvUjZlODIHM63TSO5ibv6kFXtXKCqZGd2M+
|
||||
wGbhZ0f+2GvKcwMmJERnIQjuoNaYSQLT0tM0VB9Iz0rJlZC+tzPZ+5pPqEumRdsS
|
||||
IzWNXfF42AhcbwAQYQJBAPVXtMYIJc9EZsz86ZcQiMPWUpCX5vnRmtwL8kKyR8D5
|
||||
4KfYeiowyFffSRMMcclwNHq7TgSXN+nIXM9WyzyzwikCQQDKbNA28AgZp9aT54HP
|
||||
WnbeE2pmt+uk/zl/BtxJSoK6H+69Jec+lf7EgL7HgOWYRSNot4uQWu8IhsHLTiUq
|
||||
+0FtAkEAqwlRxRy4/x24bP+D+QRV0/D97j93joFJbE4Hved7jlSlAV4xDGilwlyv
|
||||
HNB4Iu5OJ6Gcaibhm+FKkmD3noHSwQJBAIpu3fokLzX0bS+bDFBU6qO3HXX/47xj
|
||||
+tsfQvkwZrSI8AkU6c8IX0HdVhsz0FBRQAT2ORDQz1XCarfxykNZrwUCQQCGCBIc
|
||||
BBCWzhHlswlGidWJg3HqqO6hPPClEr3B5G87oCsdeYwiO23XT6rUnoJXfJHp6oCW
|
||||
5nCwDu5ZTP+khltg
|
||||
-----END PRIVATE KEY-----
|
1
integration-cli/fixtures/https/server-key.pem
Symbolic link
1
integration-cli/fixtures/https/server-key.pem
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../integration/testdata/https/server-key.pem
|
128
integration/internal/api/container/container.go
Normal file
128
integration/internal/api/container/container.go
Normal file
|
@ -0,0 +1,128 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
// CreateVMemLabel creates a container with the supplied parameters
|
||||
func CreateVMemLabel(client client.APIClient, v []string, memoryMB int, labels map[string]string, img string, cmd []string) (string, error) {
|
||||
ctx := context.Background()
|
||||
config := container.Config{
|
||||
Cmd: cmd,
|
||||
Image: img,
|
||||
Labels: labels,
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: v,
|
||||
Resources: container.Resources{
|
||||
Memory: int64(memoryMB * 1024 * 1024),
|
||||
},
|
||||
}
|
||||
networkingConfig := networktypes.NetworkingConfig{}
|
||||
name := ""
|
||||
response, err := client.ContainerCreate(ctx, &config, &hostConfig, &networkingConfig, name)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.ID, nil
|
||||
}
|
||||
|
||||
// Run runs the command provided in the container image named
|
||||
func Run(client client.APIClient, img string, cmd []string) (string, error) {
|
||||
return RunV(client, []string{}, img, cmd)
|
||||
}
|
||||
|
||||
// RunV runs the command provided in the container image named with
|
||||
// the equivalent of -v bind mounts
|
||||
func RunV(client client.APIClient, v []string, img string, cmd []string) (string, error) {
|
||||
return RunVMem(client, v, 0, img, cmd)
|
||||
}
|
||||
|
||||
// RunVMem runs the command provided in the container image named with
|
||||
// the equivalent of -v bind mounts and a specified memory limit
|
||||
func RunVMem(client client.APIClient, v []string, memoryMB int, img string, cmd []string) (string, error) {
|
||||
return RunVMemLabel(client, v, memoryMB, map[string]string{}, img, cmd)
|
||||
}
|
||||
|
||||
// RunVLabel runs the command provided in the container image named
|
||||
// with the equivalent of -v bind mounts and the specified labels
|
||||
func RunVLabel(client client.APIClient, v []string, labels map[string]string, img string, cmd []string) (string, error) {
|
||||
return RunVMemLabel(client, v, 0, labels, img, cmd)
|
||||
}
|
||||
|
||||
// RunVMemLabel runs the command provided in the container image named
|
||||
// with the equivalent of -v bind mounts, a specified memory limit,
|
||||
// and the specified labels
|
||||
func RunVMemLabel(client client.APIClient, v []string, memoryMB int, labels map[string]string, img string, cmd []string) (string, error) {
|
||||
containerID, err := CreateVMemLabel(client, v, memoryMB, labels, img, cmd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
if err := client.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return containerID, nil
|
||||
}
|
||||
|
||||
// Wait waits until the named container has exited
|
||||
func Wait(client client.APIClient, container string) (int64, error) {
|
||||
resultC, errC := client.ContainerWait(context.Background(), container, "")
|
||||
|
||||
select {
|
||||
case result := <-resultC:
|
||||
return result.StatusCode, nil
|
||||
case err := <-errC:
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the named container
|
||||
func Stop(client client.APIClient, container string) error {
|
||||
timeout := time.Duration(10) * time.Second
|
||||
ctx := context.Background()
|
||||
return client.ContainerStop(ctx, container, &timeout)
|
||||
}
|
||||
|
||||
// Start starts the named container
|
||||
func Start(client client.APIClient, container string) error {
|
||||
ctx := context.Background()
|
||||
return client.ContainerStart(ctx, container, types.ContainerStartOptions{})
|
||||
}
|
||||
|
||||
// Kill kills the named container with SIGKILL
|
||||
func Kill(client client.APIClient, container string) error {
|
||||
ctx := context.Background()
|
||||
return client.ContainerKill(ctx, container, "KILL")
|
||||
}
|
||||
|
||||
// Export exports a container's file system as a tarball
|
||||
func Export(client client.APIClient, path, name string) error {
|
||||
ctx := context.Background()
|
||||
responseReader, err := client.ContainerExport(ctx, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseReader.Close()
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(file, responseReader)
|
||||
return err
|
||||
}
|
66
integration/internal/api/image/image.go
Normal file
66
integration/internal/api/image/image.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
// Save saves an image to a tarball names by path
|
||||
func Save(client client.APIClient, path, image string) error {
|
||||
ctx := context.Background()
|
||||
responseReader, err := client.ImageSave(ctx, []string{image})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseReader.Close()
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(file, responseReader)
|
||||
return err
|
||||
}
|
||||
|
||||
// Load loads an image from a tarball named by path
|
||||
func Load(client client.APIClient, path string) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
quiet := true
|
||||
ctx := context.Background()
|
||||
response, err := client.ImageLoad(ctx, file, quiet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Import imports the contents of a tarball named by path
|
||||
func Import(client client.APIClient, path string) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
options := types.ImageImportOptions{}
|
||||
ref := ""
|
||||
source := types.ImageImportSource{
|
||||
Source: file,
|
||||
SourceName: "-",
|
||||
}
|
||||
ctx := context.Background()
|
||||
responseReader, err := client.ImageImport(ctx, source, ref, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseReader.Close()
|
||||
return nil
|
||||
}
|
59
integration/internal/api/plugin/plugin.go
Normal file
59
integration/internal/api/plugin/plugin.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
// InstallGrantAllPermissions installs the plugin named and grants it
|
||||
// all permissions it may require
|
||||
func InstallGrantAllPermissions(client client.APIClient, name string) error {
|
||||
ctx := context.Background()
|
||||
options := types.PluginInstallOptions{
|
||||
RemoteRef: name,
|
||||
AcceptAllPermissions: true,
|
||||
}
|
||||
responseReader, err := client.PluginInstall(ctx, "", options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseReader.Close()
|
||||
// we have to read the response out here because the client API
|
||||
// actually starts a goroutine which we can only be sure has
|
||||
// completed when we get EOF from reading responseBody
|
||||
_, err = ioutil.ReadAll(responseReader)
|
||||
return err
|
||||
}
|
||||
|
||||
// Enable enables the named plugin
|
||||
func Enable(client client.APIClient, name string) error {
|
||||
ctx := context.Background()
|
||||
options := types.PluginEnableOptions{}
|
||||
return client.PluginEnable(ctx, name, options)
|
||||
}
|
||||
|
||||
// Disable disables the named plugin
|
||||
func Disable(client client.APIClient, name string) error {
|
||||
ctx := context.Background()
|
||||
options := types.PluginDisableOptions{}
|
||||
return client.PluginDisable(ctx, name, options)
|
||||
}
|
||||
|
||||
// Rm removes the named plugin
|
||||
func Rm(client client.APIClient, name string) error {
|
||||
return remove(client, name, false)
|
||||
}
|
||||
|
||||
// RmF forces the removal of the named plugin
|
||||
func RmF(client client.APIClient, name string) error {
|
||||
return remove(client, name, true)
|
||||
}
|
||||
|
||||
func remove(client client.APIClient, name string, force bool) error {
|
||||
ctx := context.Background()
|
||||
options := types.PluginRemoveOptions{Force: force}
|
||||
return client.PluginRemove(ctx, name, options)
|
||||
}
|
45
integration/internal/api/system/system.go
Normal file
45
integration/internal/api/system/system.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Time provides the current time on the daemon host
|
||||
func Time(t *testing.T, client client.APIClient, testEnv *environment.Execution) time.Time {
|
||||
if testEnv.IsLocalDaemon() {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
info, err := client.Info(ctx)
|
||||
require.Nil(t, err)
|
||||
|
||||
dt, err := time.Parse(time.RFC3339Nano, info.SystemTime)
|
||||
require.Nil(t, err, "invalid time format in GET /info response")
|
||||
return dt
|
||||
}
|
||||
|
||||
// Version provides the version of the daemon
|
||||
func Version(client client.APIClient) (types.Version, error) {
|
||||
ctx := context.Background()
|
||||
return client.ServerVersion(ctx)
|
||||
}
|
||||
|
||||
// EventsSince returns event and error streams since a provided time
|
||||
func EventsSince(client client.APIClient, since string) (<-chan events.Message, <-chan error, func()) {
|
||||
eventOptions := types.EventsOptions{
|
||||
Since: since,
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
events, errs := client.Events(ctx, eventOptions)
|
||||
|
||||
return events, errs, cancel
|
||||
}
|
61
integration/internal/api/volume/volume.go
Normal file
61
integration/internal/api/volume/volume.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package volume
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
volumetypes "github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
// Create creates a volume using the named driver with the specified options
|
||||
func Create(client client.APIClient, driver string, opts map[string]string) (string, error) {
|
||||
volReq := volumetypes.VolumesCreateBody{
|
||||
Driver: driver,
|
||||
DriverOpts: opts,
|
||||
Name: "",
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
vol, err := client.VolumeCreate(ctx, volReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return vol.Name, nil
|
||||
}
|
||||
|
||||
// Rm removes the volume named
|
||||
func Rm(client client.APIClient, name string) error {
|
||||
ctx := context.Background()
|
||||
return client.VolumeRemove(ctx, name, false)
|
||||
}
|
||||
|
||||
// Ls lists the volumes available
|
||||
func Ls(client client.APIClient) ([]string, error) {
|
||||
ctx := context.Background()
|
||||
volumes, err := client.VolumeList(ctx, filters.Args{})
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
names := []string{}
|
||||
for _, volume := range volumes.Volumes {
|
||||
names = append(names, volume.Name)
|
||||
}
|
||||
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// Prune removes all volumes not used by at least one container
|
||||
func Prune(client client.APIClient) (types.VolumesPruneReport, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
return client.VolumesPrune(ctx, filters.Args{})
|
||||
}
|
||||
|
||||
// Inspect retrieves detailed information about the named volume
|
||||
func Inspect(client client.APIClient, name string) (types.Volume, error) {
|
||||
ctx := context.Background()
|
||||
return client.VolumeInspect(ctx, name)
|
||||
}
|
370
integration/plugin/authz/authz_plugin_test.go
Normal file
370
integration/plugin/authz/authz_plugin_test.go
Normal file
|
@ -0,0 +1,370 @@
|
|||
// +build !windows
|
||||
|
||||
package authz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
eventtypes "github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/integration/internal/api/container"
|
||||
"github.com/docker/docker/integration/internal/api/image"
|
||||
"github.com/docker/docker/integration/internal/api/system"
|
||||
"github.com/docker/docker/integration/util/request"
|
||||
"github.com/docker/docker/pkg/authorization"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
testAuthZPlugin = "authzplugin"
|
||||
unauthorizedMessage = "User unauthorized authz plugin"
|
||||
errorMessage = "something went wrong..."
|
||||
serverVersionAPI = "/version"
|
||||
)
|
||||
|
||||
var (
|
||||
alwaysAllowed = []string{"/_ping", "/info"}
|
||||
ctrl *authorizationController
|
||||
)
|
||||
|
||||
type authorizationController struct {
|
||||
reqRes authorization.Response // reqRes holds the plugin response to the initial client request
|
||||
resRes authorization.Response // resRes holds the plugin response to the daemon response
|
||||
versionReqCount int // versionReqCount counts the number of requests to the server version API endpoint
|
||||
versionResCount int // versionResCount counts the number of responses from the server version API endpoint
|
||||
requestsURIs []string // requestsURIs stores all request URIs that are sent to the authorization controller
|
||||
reqUser string
|
||||
resUser string
|
||||
}
|
||||
|
||||
func setupTestV1(t *testing.T) func() {
|
||||
ctrl = &authorizationController{}
|
||||
teardown := setupTest(t)
|
||||
|
||||
err := os.MkdirAll("/etc/docker/plugins", 0755)
|
||||
require.Nil(t, err)
|
||||
|
||||
fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", testAuthZPlugin)
|
||||
err = ioutil.WriteFile(fileName, []byte(server.URL), 0644)
|
||||
require.Nil(t, err)
|
||||
|
||||
return func() {
|
||||
err := os.RemoveAll("/etc/docker/plugins")
|
||||
require.Nil(t, err)
|
||||
|
||||
teardown()
|
||||
ctrl = nil
|
||||
}
|
||||
}
|
||||
|
||||
// check for always allowed endpoints to not inhibit test framework functions
|
||||
func isAllowed(reqURI string) bool {
|
||||
for _, endpoint := range alwaysAllowed {
|
||||
if strings.HasSuffix(reqURI, endpoint) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestAuthZPluginAllowRequest(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
ctrl.reqRes.Allow = true
|
||||
ctrl.resRes.Allow = true
|
||||
d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin)
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Ensure command successful
|
||||
id, err := container.Run(client, "busybox", []string{"top"})
|
||||
require.Nil(t, err)
|
||||
|
||||
assertURIRecorded(t, ctrl.requestsURIs, "/containers/create")
|
||||
assertURIRecorded(t, ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", id))
|
||||
|
||||
_, err = system.Version(client)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, ctrl.versionReqCount)
|
||||
require.Equal(t, 1, ctrl.versionResCount)
|
||||
}
|
||||
|
||||
func TestAuthZPluginTLS(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
const (
|
||||
testDaemonHTTPSAddr = "tcp://localhost:4271"
|
||||
cacertPath = "../../testdata/https/ca.pem"
|
||||
serverCertPath = "../../testdata/https/server-cert.pem"
|
||||
serverKeyPath = "../../testdata/https/server-key.pem"
|
||||
clientCertPath = "../../testdata/https/client-cert.pem"
|
||||
clientKeyPath = "../../testdata/https/client-key.pem"
|
||||
)
|
||||
|
||||
d.Start(t,
|
||||
"--authorization-plugin="+testAuthZPlugin,
|
||||
"--tlsverify",
|
||||
"--tlscacert", cacertPath,
|
||||
"--tlscert", serverCertPath,
|
||||
"--tlskey", serverKeyPath,
|
||||
"-H", testDaemonHTTPSAddr)
|
||||
|
||||
ctrl.reqRes.Allow = true
|
||||
ctrl.resRes.Allow = true
|
||||
|
||||
client, err := request.NewTLSAPIClient(t, testDaemonHTTPSAddr, cacertPath, clientCertPath, clientKeyPath)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = system.Version(client)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, "client", ctrl.reqUser)
|
||||
require.Equal(t, "client", ctrl.resUser)
|
||||
}
|
||||
|
||||
func TestAuthZPluginDenyRequest(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
d.Start(t, "--authorization-plugin="+testAuthZPlugin)
|
||||
ctrl.reqRes.Allow = false
|
||||
ctrl.reqRes.Msg = unauthorizedMessage
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Ensure command is blocked
|
||||
_, err = system.Version(client)
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, 1, ctrl.versionReqCount)
|
||||
require.Equal(t, 0, ctrl.versionResCount)
|
||||
|
||||
// Ensure unauthorized message appears in response
|
||||
require.Equal(t, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s", testAuthZPlugin, unauthorizedMessage), err.Error())
|
||||
}
|
||||
|
||||
// TestAuthZPluginAPIDenyResponse validates that when authorization
|
||||
// plugin deny the request, the status code is forbidden
|
||||
func TestAuthZPluginAPIDenyResponse(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
d.Start(t, "--authorization-plugin="+testAuthZPlugin)
|
||||
ctrl.reqRes.Allow = false
|
||||
ctrl.resRes.Msg = unauthorizedMessage
|
||||
|
||||
daemonURL, err := url.Parse(d.Sock())
|
||||
require.Nil(t, err)
|
||||
|
||||
conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10)
|
||||
require.Nil(t, err)
|
||||
client := httputil.NewClientConn(conn, nil)
|
||||
req, err := http.NewRequest("GET", "/version", nil)
|
||||
require.Nil(t, err)
|
||||
resp, err := client.Do(req)
|
||||
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, http.StatusForbidden, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestAuthZPluginDenyResponse(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
d.Start(t, "--authorization-plugin="+testAuthZPlugin)
|
||||
ctrl.reqRes.Allow = true
|
||||
ctrl.resRes.Allow = false
|
||||
ctrl.resRes.Msg = unauthorizedMessage
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Ensure command is blocked
|
||||
_, err = system.Version(client)
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, 1, ctrl.versionReqCount)
|
||||
require.Equal(t, 1, ctrl.versionResCount)
|
||||
|
||||
// Ensure unauthorized message appears in response
|
||||
require.Equal(t, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s", testAuthZPlugin, unauthorizedMessage), err.Error())
|
||||
}
|
||||
|
||||
// TestAuthZPluginAllowEventStream verifies event stream propagates
|
||||
// correctly after request pass through by the authorization plugin
|
||||
func TestAuthZPluginAllowEventStream(t *testing.T) {
|
||||
skip.IfCondition(t, testEnv.DaemonInfo.OSType != "linux")
|
||||
|
||||
defer setupTestV1(t)()
|
||||
ctrl.reqRes.Allow = true
|
||||
ctrl.resRes.Allow = true
|
||||
d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin)
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
startTime := strconv.FormatInt(system.Time(t, client, testEnv).Unix(), 10)
|
||||
events, errs, cancel := system.EventsSince(client, startTime)
|
||||
defer cancel()
|
||||
|
||||
// Create a container and wait for the creation events
|
||||
id, err := container.Run(client, "busybox", []string{"top"})
|
||||
require.Nil(t, err)
|
||||
for i := 0; i < 100; i++ {
|
||||
c, err := client.ContainerInspect(context.Background(), id)
|
||||
require.Nil(t, err)
|
||||
if c.State.Running {
|
||||
break
|
||||
}
|
||||
if i == 99 {
|
||||
t.Fatal("Container didn't run within 10s")
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
created := false
|
||||
started := false
|
||||
for !created && !started {
|
||||
select {
|
||||
case event := <-events:
|
||||
if event.Type == eventtypes.ContainerEventType && event.Actor.ID == id {
|
||||
if event.Action == "create" {
|
||||
created = true
|
||||
}
|
||||
if event.Action == "start" {
|
||||
started = true
|
||||
}
|
||||
}
|
||||
case err := <-errs:
|
||||
if err == io.EOF {
|
||||
t.Fatal("premature end of event stream")
|
||||
}
|
||||
require.Nil(t, err)
|
||||
case <-time.After(30 * time.Second):
|
||||
// Fail the test
|
||||
t.Fatal("event stream timeout")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure both events and container endpoints are passed to the
|
||||
// authorization plugin
|
||||
assertURIRecorded(t, ctrl.requestsURIs, "/events")
|
||||
assertURIRecorded(t, ctrl.requestsURIs, "/containers/create")
|
||||
assertURIRecorded(t, ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", id))
|
||||
}
|
||||
|
||||
func TestAuthZPluginErrorResponse(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
d.Start(t, "--authorization-plugin="+testAuthZPlugin)
|
||||
ctrl.reqRes.Allow = true
|
||||
ctrl.resRes.Err = errorMessage
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Ensure command is blocked
|
||||
_, err = system.Version(client)
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s", testAuthZPlugin, authorization.AuthZApiResponse, errorMessage), err.Error())
|
||||
}
|
||||
|
||||
func TestAuthZPluginErrorRequest(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
d.Start(t, "--authorization-plugin="+testAuthZPlugin)
|
||||
ctrl.reqRes.Err = errorMessage
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Ensure command is blocked
|
||||
_, err = system.Version(client)
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s", testAuthZPlugin, authorization.AuthZApiRequest, errorMessage), err.Error())
|
||||
}
|
||||
|
||||
func TestAuthZPluginEnsureNoDuplicatePluginRegistration(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
d.Start(t, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin)
|
||||
|
||||
ctrl.reqRes.Allow = true
|
||||
ctrl.resRes.Allow = true
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = system.Version(client)
|
||||
require.Nil(t, err)
|
||||
|
||||
// assert plugin is only called once..
|
||||
require.Equal(t, 1, ctrl.versionReqCount)
|
||||
require.Equal(t, 1, ctrl.versionResCount)
|
||||
}
|
||||
|
||||
func TestAuthZPluginEnsureLoadImportWorking(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
ctrl.reqRes.Allow = true
|
||||
ctrl.resRes.Allow = true
|
||||
d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin)
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
tmp, err := ioutil.TempDir("", "test-authz-load-import")
|
||||
require.Nil(t, err)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
savedImagePath := filepath.Join(tmp, "save.tar")
|
||||
|
||||
err = image.Save(client, savedImagePath, "busybox")
|
||||
require.Nil(t, err)
|
||||
err = image.Load(client, savedImagePath)
|
||||
require.Nil(t, err)
|
||||
|
||||
exportedImagePath := filepath.Join(tmp, "export.tar")
|
||||
|
||||
id, err := container.Run(client, "busybox", []string{})
|
||||
require.Nil(t, err)
|
||||
err = container.Export(client, exportedImagePath, id)
|
||||
require.Nil(t, err)
|
||||
err = image.Import(client, exportedImagePath)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestAuthZPluginHeader(t *testing.T) {
|
||||
defer setupTestV1(t)()
|
||||
ctrl.reqRes.Allow = true
|
||||
ctrl.resRes.Allow = true
|
||||
d.StartWithBusybox(t, "--debug", "--authorization-plugin="+testAuthZPlugin)
|
||||
|
||||
daemonURL, err := url.Parse(d.Sock())
|
||||
require.Nil(t, err)
|
||||
|
||||
conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10)
|
||||
require.Nil(t, err)
|
||||
client := httputil.NewClientConn(conn, nil)
|
||||
req, err := http.NewRequest("GET", "/version", nil)
|
||||
require.Nil(t, err)
|
||||
resp, err := client.Do(req)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "application/json", resp.Header["Content-Type"][0])
|
||||
}
|
||||
|
||||
// assertURIRecorded verifies that the given URI was sent and recorded
|
||||
// in the authz plugin
|
||||
func assertURIRecorded(t *testing.T, uris []string, uri string) {
|
||||
var found bool
|
||||
for _, u := range uris {
|
||||
if strings.Contains(u, uri) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Expected to find URI '%s', recorded uris '%s'", uri, strings.Join(uris, ","))
|
||||
}
|
||||
}
|
153
integration/plugin/authz/authz_plugin_v2_test.go
Normal file
153
integration/plugin/authz/authz_plugin_v2_test.go
Normal file
|
@ -0,0 +1,153 @@
|
|||
// +build !windows
|
||||
|
||||
package authz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/integration/internal/api/container"
|
||||
"github.com/docker/docker/integration/internal/api/plugin"
|
||||
"github.com/docker/docker/integration/internal/api/volume"
|
||||
"github.com/docker/docker/integration/util/requirement"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
authzPluginName = "riyaz/authz-no-volume-plugin"
|
||||
authzPluginTag = "latest"
|
||||
authzPluginNameWithTag = authzPluginName + ":" + authzPluginTag
|
||||
authzPluginBadManifestName = "riyaz/authz-plugin-bad-manifest"
|
||||
nonexistentAuthzPluginName = "riyaz/nonexistent-authz-plugin"
|
||||
)
|
||||
|
||||
func setupTestV2(t *testing.T) func() {
|
||||
skip.IfCondition(t, testEnv.DaemonInfo.OSType != "linux")
|
||||
requirement.HasHubConnectivity(t)
|
||||
|
||||
teardown := setupTest(t)
|
||||
|
||||
d.Start(t)
|
||||
|
||||
return teardown
|
||||
}
|
||||
|
||||
func TestAuthZPluginV2AllowNonVolumeRequest(t *testing.T) {
|
||||
skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
|
||||
defer setupTestV2(t)()
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Install authz plugin
|
||||
err = plugin.InstallGrantAllPermissions(client, authzPluginNameWithTag)
|
||||
require.Nil(t, err)
|
||||
// start the daemon with the plugin and load busybox, --net=none build fails otherwise
|
||||
// because it needs to pull busybox
|
||||
d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag)
|
||||
d.LoadBusybox(t)
|
||||
|
||||
// Ensure docker run command and accompanying docker ps are successful
|
||||
id, err := container.Run(client, "busybox", []string{"top"})
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = client.ContainerInspect(context.Background(), id)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestAuthZPluginV2Disable(t *testing.T) {
|
||||
skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
|
||||
defer setupTestV2(t)()
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Install authz plugin
|
||||
err = plugin.InstallGrantAllPermissions(client, authzPluginNameWithTag)
|
||||
require.Nil(t, err)
|
||||
|
||||
d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag)
|
||||
d.LoadBusybox(t)
|
||||
|
||||
_, err = volume.Create(client, "local", map[string]string{})
|
||||
require.NotNil(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
||||
|
||||
// disable the plugin
|
||||
err = plugin.Disable(client, authzPluginNameWithTag)
|
||||
require.Nil(t, err)
|
||||
|
||||
// now test to see if the docker api works.
|
||||
_, err = volume.Create(client, "local", map[string]string{})
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestAuthZPluginV2RejectVolumeRequests(t *testing.T) {
|
||||
skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
|
||||
defer setupTestV2(t)()
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Install authz plugin
|
||||
err = plugin.InstallGrantAllPermissions(client, authzPluginNameWithTag)
|
||||
require.Nil(t, err)
|
||||
|
||||
// restart the daemon with the plugin
|
||||
d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag)
|
||||
|
||||
_, err = volume.Create(client, "local", map[string]string{})
|
||||
require.NotNil(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
||||
|
||||
_, err = volume.Ls(client)
|
||||
require.NotNil(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
||||
|
||||
// The plugin will block the command before it can determine the volume does not exist
|
||||
err = volume.Rm(client, "test")
|
||||
require.NotNil(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
||||
|
||||
_, err = volume.Inspect(client, "test")
|
||||
require.NotNil(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
||||
|
||||
_, err = volume.Prune(client)
|
||||
require.NotNil(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
|
||||
}
|
||||
|
||||
func TestAuthZPluginV2BadManifestFailsDaemonStart(t *testing.T) {
|
||||
skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
|
||||
defer setupTestV2(t)()
|
||||
|
||||
client, err := d.NewClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Install authz plugin with bad manifest
|
||||
err = plugin.InstallGrantAllPermissions(client, authzPluginBadManifestName)
|
||||
require.Nil(t, err)
|
||||
|
||||
// start the daemon with the plugin, it will error
|
||||
err = d.RestartWithError("--authorization-plugin=" + authzPluginBadManifestName)
|
||||
require.NotNil(t, err)
|
||||
|
||||
// restarting the daemon without requiring the plugin will succeed
|
||||
d.Start(t)
|
||||
}
|
||||
|
||||
func TestAuthZPluginV2NonexistentFailsDaemonStart(t *testing.T) {
|
||||
defer setupTestV2(t)()
|
||||
|
||||
// start the daemon with a non-existent authz plugin, it will error
|
||||
err := d.RestartWithError("--authorization-plugin=" + nonexistentAuthzPluginName)
|
||||
require.NotNil(t, err)
|
||||
|
||||
// restarting the daemon without requiring the plugin will succeed
|
||||
d.Start(t)
|
||||
}
|
177
integration/plugin/authz/main_test.go
Normal file
177
integration/plugin/authz/main_test.go
Normal file
|
@ -0,0 +1,177 @@
|
|||
// +build !windows
|
||||
|
||||
package authz
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
"github.com/docker/docker/pkg/authorization"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
)
|
||||
|
||||
var (
|
||||
testEnv *environment.Execution
|
||||
d *daemon.Daemon
|
||||
server *httptest.Server
|
||||
)
|
||||
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testEnv, err = environment.New()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
testEnv.Print()
|
||||
setupSuite()
|
||||
exitCode := m.Run()
|
||||
teardownSuite()
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func setupTest(t *testing.T) func() {
|
||||
environment.ProtectAll(t, testEnv)
|
||||
|
||||
d = daemon.New(t, "", dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
|
||||
})
|
||||
|
||||
return func() {
|
||||
if d != nil {
|
||||
d.Stop(t)
|
||||
}
|
||||
testEnv.Clean(t)
|
||||
}
|
||||
}
|
||||
|
||||
func setupSuite() {
|
||||
mux := http.NewServeMux()
|
||||
server = httptest.NewServer(mux)
|
||||
|
||||
mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
|
||||
b, err := json.Marshal(plugins.Manifest{Implements: []string{authorization.AuthZApiImplements}})
|
||||
if err != nil {
|
||||
panic("could not marshal json for /Plugin.Activate: " + err.Error())
|
||||
}
|
||||
w.Write(b)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/AuthZPlugin.AuthZReq", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
panic("could not read body for /AuthZPlugin.AuthZReq: " + err.Error())
|
||||
}
|
||||
authReq := authorization.Request{}
|
||||
err = json.Unmarshal(body, &authReq)
|
||||
if err != nil {
|
||||
panic("could not unmarshal json for /AuthZPlugin.AuthZReq: " + err.Error())
|
||||
}
|
||||
|
||||
assertBody(authReq.RequestURI, authReq.RequestHeaders, authReq.RequestBody)
|
||||
assertAuthHeaders(authReq.RequestHeaders)
|
||||
|
||||
// Count only server version api
|
||||
if strings.HasSuffix(authReq.RequestURI, serverVersionAPI) {
|
||||
ctrl.versionReqCount++
|
||||
}
|
||||
|
||||
ctrl.requestsURIs = append(ctrl.requestsURIs, authReq.RequestURI)
|
||||
|
||||
reqRes := ctrl.reqRes
|
||||
if isAllowed(authReq.RequestURI) {
|
||||
reqRes = authorization.Response{Allow: true}
|
||||
}
|
||||
if reqRes.Err != "" {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
b, err := json.Marshal(reqRes)
|
||||
if err != nil {
|
||||
panic("could not marshal json for /AuthZPlugin.AuthZReq: " + err.Error())
|
||||
}
|
||||
|
||||
ctrl.reqUser = authReq.User
|
||||
w.Write(b)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/AuthZPlugin.AuthZRes", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
panic("could not read body for /AuthZPlugin.AuthZRes: " + err.Error())
|
||||
}
|
||||
authReq := authorization.Request{}
|
||||
err = json.Unmarshal(body, &authReq)
|
||||
if err != nil {
|
||||
panic("could not unmarshal json for /AuthZPlugin.AuthZRes: " + err.Error())
|
||||
}
|
||||
|
||||
assertBody(authReq.RequestURI, authReq.ResponseHeaders, authReq.ResponseBody)
|
||||
assertAuthHeaders(authReq.ResponseHeaders)
|
||||
|
||||
// Count only server version api
|
||||
if strings.HasSuffix(authReq.RequestURI, serverVersionAPI) {
|
||||
ctrl.versionResCount++
|
||||
}
|
||||
resRes := ctrl.resRes
|
||||
if isAllowed(authReq.RequestURI) {
|
||||
resRes = authorization.Response{Allow: true}
|
||||
}
|
||||
if resRes.Err != "" {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
b, err := json.Marshal(resRes)
|
||||
if err != nil {
|
||||
panic("could not marshal json for /AuthZPlugin.AuthZRes: " + err.Error())
|
||||
}
|
||||
ctrl.resUser = authReq.User
|
||||
w.Write(b)
|
||||
})
|
||||
}
|
||||
|
||||
func teardownSuite() {
|
||||
if server == nil {
|
||||
return
|
||||
}
|
||||
|
||||
server.Close()
|
||||
}
|
||||
|
||||
// assertAuthHeaders validates authentication headers are removed
|
||||
func assertAuthHeaders(headers map[string]string) error {
|
||||
for k := range headers {
|
||||
if strings.Contains(strings.ToLower(k), "auth") || strings.Contains(strings.ToLower(k), "x-registry") {
|
||||
panic(fmt.Sprintf("Found authentication headers in request '%v'", headers))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// assertBody asserts that body is removed for non text/json requests
|
||||
func assertBody(requestURI string, headers map[string]string, body []byte) {
|
||||
if strings.Contains(strings.ToLower(requestURI), "auth") && len(body) > 0 {
|
||||
panic("Body included for authentication endpoint " + string(body))
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
if strings.EqualFold(k, "Content-Type") && strings.HasPrefix(v, "text/") || v == "application/json" {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(body) > 0 {
|
||||
panic(fmt.Sprintf("Body included while it should not (Headers: '%v')", headers))
|
||||
}
|
||||
}
|
23
integration/testdata/https/ca.pem
vendored
Normal file
23
integration/testdata/https/ca.pem
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID0TCCAzqgAwIBAgIJAP2r7GqEJwSnMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD
|
||||
VQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMG
|
||||
A1UEChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTERMA8GA1UEAxMI
|
||||
Y2hhbmdlbWUxETAPBgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWls
|
||||
QGhvc3QuZG9tYWluMB4XDTEzMTIwMzE2NTYzMFoXDTIzMTIwMTE2NTYzMFowgaIx
|
||||
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2Nv
|
||||
MRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYD
|
||||
VQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEW
|
||||
EG1haWxAaG9zdC5kb21haW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALAn
|
||||
0xDw+5y7ZptQacq66pUhRu82JP2WU6IDgo5QUtNU6/CX5PwQATe/OnYTZQFbksxp
|
||||
AU9boG0FCkgxfsgPYXEuZxVEGKI2fxfKHOZZI8mrkWmj6eWU/0cvCjGVc9rTITP5
|
||||
sNQvg+hORyVDdNp2IdsbMJayiB3AQYMFx3vSDOMTAgMBAAGjggELMIIBBzAdBgNV
|
||||
HQ4EFgQUZu7DFz09q0QBa2+ymRm9qgK1NPswgdcGA1UdIwSBzzCBzIAUZu7DFz09
|
||||
q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD
|
||||
QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x
|
||||
ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI
|
||||
Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq
|
||||
hCcEpzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAF8fJKKM+/oOdnNi
|
||||
zEd0M1+PmZOyqvjYQn/2ZR8UHH6Imgc/OPQKZXf0bVE1Txc/DaUNn9Isd1SuCuaE
|
||||
ic3vAIYYU7PmgeNN6vwec48V96T7jr+GAi6AVMhQEc2hHCfVtx11Xx+x6aHDZzJt
|
||||
Zxtf5lL6KSO9Y+EFwM+rju6hm5hW
|
||||
-----END CERTIFICATE-----
|
73
integration/testdata/https/client-cert.pem
vendored
Normal file
73
integration/testdata/https/client-cert.pem
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 3 (0x3)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
|
||||
Validity
|
||||
Not Before: Dec 4 14:17:54 2013 GMT
|
||||
Not After : Dec 2 14:17:54 2023 GMT
|
||||
Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=client/name=changeme/emailAddress=mail@host.domain
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:ca:c9:05:d0:09:4e:3e:a4:fc:d5:14:f4:a5:e8:
|
||||
34:d3:6b:51:e3:f3:62:ea:a1:f0:e8:ed:c4:2a:bc:
|
||||
f0:4f:ca:07:df:e3:88:fa:f4:21:99:35:0e:3d:ea:
|
||||
b0:86:e7:c4:d2:8a:83:2b:42:b8:ec:a3:99:62:70:
|
||||
81:46:cc:fc:a5:1d:d2:63:e8:eb:07:25:9a:e2:25:
|
||||
6d:11:56:f2:1a:51:a1:b6:3e:1c:57:32:e9:7b:2c:
|
||||
aa:1b:cc:97:2d:89:2d:b1:c9:5e:35:28:4d:7c:fa:
|
||||
65:31:3e:f7:70:dd:6e:0b:3c:58:af:a8:2e:24:c0:
|
||||
7e:4e:78:7d:0a:9e:8f:42:43
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
Easy-RSA Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
DE:42:EF:2D:98:A3:6C:A8:AA:E0:8C:71:2C:9D:64:23:A9:E2:7E:81
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB
|
||||
DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
|
||||
serial:FD:AB:EC:6A:84:27:04:A7
|
||||
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Client Authentication
|
||||
X509v3 Key Usage:
|
||||
Digital Signature
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
1c:44:26:ea:e1:66:25:cb:e4:8e:57:1c:f6:b9:17:22:62:40:
|
||||
12:90:8f:3b:b2:61:7a:54:94:8f:b1:20:0b:bf:a3:51:e3:fa:
|
||||
1c:a1:be:92:3a:d0:76:44:c0:57:83:ab:6a:e4:1a:45:49:a4:
|
||||
af:39:0d:60:32:fc:3a:be:d7:fb:5d:99:7a:1f:87:e7:d5:ab:
|
||||
84:a2:5e:90:d8:bf:fa:89:6d:32:26:02:5e:31:35:68:7f:31:
|
||||
f5:6b:51:46:bc:af:70:ed:5a:09:7d:ec:b2:48:4f:fe:c5:2f:
|
||||
56:04:ad:f6:c1:d2:2a:e4:6a:c4:87:fe:08:35:c5:38:cb:5e:
|
||||
4a:c4
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEFTCCA36gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv
|
||||
cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l
|
||||
MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv
|
||||
bWFpbjAeFw0xMzEyMDQxNDE3NTRaFw0yMzEyMDIxNDE3NTRaMIGgMQswCQYDVQQG
|
||||
EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE
|
||||
ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEPMA0GA1UEAxMGY2xp
|
||||
ZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0
|
||||
LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyskF0AlOPqT81RT0
|
||||
peg002tR4/Ni6qHw6O3EKrzwT8oH3+OI+vQhmTUOPeqwhufE0oqDK0K47KOZYnCB
|
||||
Rsz8pR3SY+jrByWa4iVtEVbyGlGhtj4cVzLpeyyqG8yXLYktscleNShNfPplMT73
|
||||
cN1uCzxYr6guJMB+Tnh9Cp6PQkMCAwEAAaOCAVkwggFVMAkGA1UdEwQCMAAwLQYJ
|
||||
YIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
|
||||
HQ4EFgQU3kLvLZijbKiq4IxxLJ1kI6nifoEwgdcGA1UdIwSBzzCBzIAUZu7DFz09
|
||||
q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD
|
||||
QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x
|
||||
ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI
|
||||
Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq
|
||||
hCcEpzATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcN
|
||||
AQEFBQADgYEAHEQm6uFmJcvkjlcc9rkXImJAEpCPO7JhelSUj7EgC7+jUeP6HKG+
|
||||
kjrQdkTAV4OrauQaRUmkrzkNYDL8Or7X+12Zeh+H59WrhKJekNi/+oltMiYCXjE1
|
||||
aH8x9WtRRryvcO1aCX3sskhP/sUvVgSt9sHSKuRqxIf+CDXFOMteSsQ=
|
||||
-----END CERTIFICATE-----
|
16
integration/testdata/https/client-key.pem
vendored
Normal file
16
integration/testdata/https/client-key.pem
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMrJBdAJTj6k/NUU
|
||||
9KXoNNNrUePzYuqh8OjtxCq88E/KB9/jiPr0IZk1Dj3qsIbnxNKKgytCuOyjmWJw
|
||||
gUbM/KUd0mPo6wclmuIlbRFW8hpRobY+HFcy6XssqhvMly2JLbHJXjUoTXz6ZTE+
|
||||
93Ddbgs8WK+oLiTAfk54fQqej0JDAgMBAAECgYBOFEzKp2qbMEexe9ofL2N3rDDh
|
||||
xkrl8OijpzkLA6i78BxMFn4dsnZlWUpciMrjhsYAExkiRRSS+QMMJimAq1jzQqc3
|
||||
FAQV2XGYwkd0cUn7iZGvfNnEPysjsfyYQM+m+sT0ATj4BZjVShC6kkSjTdm1leLN
|
||||
OSvcHdcu3Xxg9ufF0QJBAPYdnNt5sIndt2WECePuRVi+uF4mlxTobFY0fjn26yhC
|
||||
4RsnhhD3Vldygo9gvnkwrAZYaALGSPBewes2InxvjA8CQQDS7erKiNXpwoqz5XiU
|
||||
SVEsIIVTdWzBjGbIqMOu/hUwM5FK4j6JTBks0aTGMyh0YV9L1EzM0X79J29JahCe
|
||||
iQKNAkBKNMOGqTpBV0hko1sYDk96YobUXG5RL4L6uvkUIQ7mJMQam+AgXXL7Ctuy
|
||||
v0iu4a38e8tgisiTMP7nHHtpaXihAkAOiN54/lzfMsykANgCP9scE1GcoqbP34Dl
|
||||
qttxH4kOPT9xzY1JoLjLYdbc4YGUI3GRpBt2sajygNkmUey7P+2xAkBBsVCZFvTw
|
||||
qHvOpPS2kX5ml5xoc/QAHK9N7kR+X7XFYx82RTVSqJEK4lPb+aEWn+CjiIewO4Q5
|
||||
ksDFuNxAzbhl
|
||||
-----END PRIVATE KEY-----
|
76
integration/testdata/https/server-cert.pem
vendored
Normal file
76
integration/testdata/https/server-cert.pem
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 4 (0x4)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
|
||||
Validity
|
||||
Not Before: Dec 4 15:01:20 2013 GMT
|
||||
Not After : Dec 2 15:01:20 2023 GMT
|
||||
Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=*/name=changeme/emailAddress=mail@host.domain
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:c1:ff:7d:30:6f:64:4a:b1:92:b1:71:d1:c1:74:
|
||||
e2:1d:db:2d:11:24:e1:00:d4:00:ae:6f:c8:9e:ae:
|
||||
67:b3:4a:bd:f7:e6:9e:57:6d:19:4c:3c:23:94:2d:
|
||||
3d:d6:63:84:d8:fa:76:2b:38:12:c1:ed:20:9d:32:
|
||||
e0:e8:c2:bf:9a:77:70:04:3f:7f:ca:8c:2c:82:d6:
|
||||
3d:25:5c:02:1a:4f:64:93:03:dd:9c:42:97:5e:09:
|
||||
49:af:f0:c2:e1:30:08:0e:21:46:95:d1:13:59:c0:
|
||||
c8:76:be:94:0d:8b:43:67:21:33:b2:08:60:9d:76:
|
||||
a8:05:32:1e:f9:95:09:14:75
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Cert Type:
|
||||
SSL Server
|
||||
Netscape Comment:
|
||||
Easy-RSA Generated Server Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
14:02:FD:FD:DD:13:38:E0:71:EA:D1:BE:C0:0E:89:1A:2D:B6:19:06
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB
|
||||
DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
|
||||
serial:FD:AB:EC:6A:84:27:04:A7
|
||||
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication
|
||||
X509v3 Key Usage:
|
||||
Digital Signature, Key Encipherment
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
40:0f:10:39:c4:b7:0f:0d:2f:bf:d2:16:cc:8e:d3:9a:fb:8b:
|
||||
ce:4b:7b:0d:48:77:ce:f1:fe:d5:8f:ea:b1:71:ed:49:1d:9f:
|
||||
23:3a:16:d4:70:7c:c5:29:bf:e4:90:34:d0:f0:00:24:f4:e4:
|
||||
df:2c:c3:83:01:66:61:c9:a8:ab:29:e7:98:6d:27:89:4a:76:
|
||||
c9:2e:19:8e:fe:6e:d5:f8:99:11:0e:97:67:4b:34:e3:1e:e3:
|
||||
9f:35:00:a5:32:f9:b5:2c:f2:e0:c5:2e:cc:81:bd:18:dd:5c:
|
||||
12:c8:6b:fa:0c:17:74:30:55:f6:6e:20:9a:6c:1e:09:b4:0c:
|
||||
15:42
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEKjCCA5OgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv
|
||||
cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l
|
||||
MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv
|
||||
bWFpbjAeFw0xMzEyMDQxNTAxMjBaFw0yMzEyMDIxNTAxMjBaMIGbMQswCQYDVQQG
|
||||
EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE
|
||||
ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEKMAgGA1UEAxQBKjER
|
||||
MA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21h
|
||||
aW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMH/fTBvZEqxkrFx0cF04h3b
|
||||
LREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y4OjCv5p3
|
||||
cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+lA2LQ2ch
|
||||
M7IIYJ12qAUyHvmVCRR1AgMBAAGjggFzMIIBbzAJBgNVHRMEAjAAMBEGCWCGSAGG
|
||||
+EIBAQQEAwIGQDA0BglghkgBhvhCAQ0EJxYlRWFzeS1SU0EgR2VuZXJhdGVkIFNl
|
||||
cnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUFAL9/d0TOOBx6tG+wA6JGi22GQYw
|
||||
gdcGA1UdIwSBzzCBzIAUZu7DFz09q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJ
|
||||
BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUw
|
||||
EwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQD
|
||||
EwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1h
|
||||
aWxAaG9zdC5kb21haW6CCQD9q+xqhCcEpzATBgNVHSUEDDAKBggrBgEFBQcDATAL
|
||||
BgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAQA8QOcS3Dw0vv9IWzI7TmvuL
|
||||
zkt7DUh3zvH+1Y/qsXHtSR2fIzoW1HB8xSm/5JA00PAAJPTk3yzDgwFmYcmoqynn
|
||||
mG0niUp2yS4Zjv5u1fiZEQ6XZ0s04x7jnzUApTL5tSzy4MUuzIG9GN1cEshr+gwX
|
||||
dDBV9m4gmmweCbQMFUI=
|
||||
-----END CERTIFICATE-----
|
16
integration/testdata/https/server-key.pem
vendored
Normal file
16
integration/testdata/https/server-key.pem
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMH/fTBvZEqxkrFx
|
||||
0cF04h3bLREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y
|
||||
4OjCv5p3cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+
|
||||
lA2LQ2chM7IIYJ12qAUyHvmVCRR1AgMBAAECgYAmwckb9RUfSwyYgLm8IYLPHiuJ
|
||||
wkllZfVg5Bo7gXJcQnFjZmJ56uTj8xvUjZlODIHM63TSO5ibv6kFXtXKCqZGd2M+
|
||||
wGbhZ0f+2GvKcwMmJERnIQjuoNaYSQLT0tM0VB9Iz0rJlZC+tzPZ+5pPqEumRdsS
|
||||
IzWNXfF42AhcbwAQYQJBAPVXtMYIJc9EZsz86ZcQiMPWUpCX5vnRmtwL8kKyR8D5
|
||||
4KfYeiowyFffSRMMcclwNHq7TgSXN+nIXM9WyzyzwikCQQDKbNA28AgZp9aT54HP
|
||||
WnbeE2pmt+uk/zl/BtxJSoK6H+69Jec+lf7EgL7HgOWYRSNot4uQWu8IhsHLTiUq
|
||||
+0FtAkEAqwlRxRy4/x24bP+D+QRV0/D97j93joFJbE4Hved7jlSlAV4xDGilwlyv
|
||||
HNB4Iu5OJ6Gcaibhm+FKkmD3noHSwQJBAIpu3fokLzX0bS+bDFBU6qO3HXX/47xj
|
||||
+tsfQvkwZrSI8AkU6c8IX0HdVhsz0FBRQAT2ORDQz1XCarfxykNZrwUCQQCGCBIc
|
||||
BBCWzhHlswlGidWJg3HqqO6hPPClEr3B5G87oCsdeYwiO23XT6rUnoJXfJHp6oCW
|
||||
5nCwDu5ZTP+khltg
|
||||
-----END PRIVATE KEY-----
|
|
@ -1,9 +1,15 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -13,3 +19,35 @@ func NewAPIClient(t *testing.T) client.APIClient {
|
|||
require.NoError(t, err)
|
||||
return clt
|
||||
}
|
||||
|
||||
// NewTLSAPIClient returns a docker API client configured with the
|
||||
// provided TLS settings
|
||||
func NewTLSAPIClient(t *testing.T, host, cacertPath, certPath, keyPath string) (client.APIClient, error) {
|
||||
opts := tlsconfig.Options{
|
||||
CAFile: cacertPath,
|
||||
CertFile: certPath,
|
||||
KeyFile: keyPath,
|
||||
ExclusiveRootPools: true,
|
||||
}
|
||||
config, err := tlsconfig.Client(opts)
|
||||
require.Nil(t, err)
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: config,
|
||||
DialContext: (&net.Dialer{
|
||||
KeepAlive: 30 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
}).DialContext,
|
||||
}
|
||||
proto, addr, _, err := client.ParseHost(host)
|
||||
require.Nil(t, err)
|
||||
|
||||
sockets.ConfigureTransport(tr, proto, addr)
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: tr,
|
||||
CheckRedirect: client.CheckRedirect,
|
||||
}
|
||||
verStr := api.DefaultVersion
|
||||
customHeaders := map[string]string{}
|
||||
return client.NewClient(host, verStr, httpClient, customHeaders)
|
||||
}
|
||||
|
|
31
integration/util/requirement/requirement.go
Normal file
31
integration/util/requirement/requirement.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package requirement
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
// HasHubConnectivity checks to see if https://hub.docker.com is
|
||||
// accessible from the present environment
|
||||
func HasHubConnectivity(t *testing.T) {
|
||||
// Set a timeout on the GET at 15s
|
||||
var timeout = 15 * time.Second
|
||||
var url = "https://hub.docker.com"
|
||||
|
||||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
resp, err := client.Get(url)
|
||||
if err != nil && strings.Contains(err.Error(), "use of closed network connection") {
|
||||
t.Fatalf("Timeout for GET request on %s", url)
|
||||
}
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
skip.IfCondition(t, err != nil)
|
||||
}
|
Loading…
Add table
Reference in a new issue