From 2b045027ce32a8f938c83adcf5960f1bffdb3f8f Mon Sep 17 00:00:00 2001 From: Riyaz Faizullabhoy Date: Mon, 31 Oct 2016 13:14:00 -0700 Subject: [PATCH] Check authz plugins are valid on daemon startup, add integration tests Signed-off-by: Riyaz Faizullabhoy --- cmd/dockerd/daemon.go | 28 +++- .../docker_cli_authz_plugin_v2_test.go | 129 ++++++++++++++++++ 2 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 integration-cli/docker_cli_authz_plugin_v2_test.go diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go index 9fd61bf85d..a1ed8bab2a 100644 --- a/cmd/dockerd/daemon.go +++ b/cmd/dockerd/daemon.go @@ -35,6 +35,7 @@ import ( "github.com/docker/docker/pkg/jsonlog" "github.com/docker/docker/pkg/listeners" "github.com/docker/docker/pkg/pidfile" + "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" @@ -287,7 +288,9 @@ func (cli *DaemonCli) start(opts daemonOptions) (err error) { cli.d = d // initMiddlewares needs cli.d to be populated. Dont change this init order. - cli.initMiddlewares(api, serverConfig) + if err := cli.initMiddlewares(api, serverConfig); err != nil { + logrus.Fatalf("Error creating middlewares: %v", err) + } d.SetCluster(c) initRouter(api, d, c) @@ -318,7 +321,11 @@ func (cli *DaemonCli) start(opts daemonOptions) (err error) { func (cli *DaemonCli) reloadConfig() { reload := func(config *daemon.Config) { - // Reload the authorization plugin + // Revalidate and reload the authorization plugins + if err := validateAuthzPlugins(config.AuthorizationPlugins, cli.d.PluginStore); err != nil { + logrus.Fatalf("Error validating authorization plugin: %v", err) + return + } cli.authzMiddleware.SetPlugins(config.AuthorizationPlugins) if err := cli.d.Reload(config); err != nil { @@ -459,7 +466,7 @@ func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) { s.InitRouter(utils.IsDebugEnabled(), routers...) } -func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) { +func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) error { v := cfg.Version exp := middleware.NewExperimentalMiddleware(cli.d.HasExperimental()) @@ -476,6 +483,21 @@ func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config u := middleware.NewUserAgentMiddleware(v) s.UseMiddleware(u) + if err := validateAuthzPlugins(cli.Config.AuthorizationPlugins, cli.d.PluginStore); err != nil { + return fmt.Errorf("Error validating authorization plugin: %v", err) + } cli.authzMiddleware = authorization.NewMiddleware(cli.Config.AuthorizationPlugins, cli.d.PluginStore) s.UseMiddleware(cli.authzMiddleware) + return nil +} + +// validates that the plugins requested with the --authorization-plugin flag are valid AuthzDriver +// plugins present on the host and available to the daemon +func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGetter) error { + for _, reqPlugin := range requestedPlugins { + if _, err := pg.Get(reqPlugin, authorization.AuthZApiImplements, plugingetter.LOOKUP); err != nil { + return err + } + } + return nil } diff --git a/integration-cli/docker_cli_authz_plugin_v2_test.go b/integration-cli/docker_cli_authz_plugin_v2_test.go new file mode 100644 index 0000000000..4a5c17c6a0 --- /dev/null +++ b/integration-cli/docker_cli_authz_plugin_v2_test.go @@ -0,0 +1,129 @@ +// +build !windows + +package main + +import ( + "fmt" + "strings" + + "github.com/docker/docker/pkg/integration/checker" + "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 +} + +func (s *DockerAuthzV2Suite) SetUpTest(c *check.C) { + testRequires(c, DaemonIsLinux, ExperimentalDaemon, Network) + s.d = NewDaemon(c) + c.Assert(s.d.Start(), check.IsNil) +} + +func (s *DockerAuthzV2Suite) TearDownTest(c *check.C) { + s.d.Stop() + s.ds.TearDownTest(c) +} + +func (s *DockerAuthzV2Suite) TestAuthZPluginAllowNonVolumeRequest(c *check.C) { + // 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 + c.Assert(s.d.Restart("--authorization-plugin="+authzPluginNameWithTag), check.IsNil) + c.Assert(s.d.LoadBusybox(), check.IsNil) + + // defer disabling the plugin + defer func() { + c.Assert(s.d.Restart(), check.IsNil) + _, 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 + out, err := s.d.Cmd("run", "-d", "busybox", "top") + c.Assert(err, check.IsNil) + + id := strings.TrimSpace(out) + + out, err = s.d.Cmd("ps") + c.Assert(err, check.IsNil) + c.Assert(assertContainerList(out, []string{id}), check.Equals, true) +} + +func (s *DockerAuthzV2Suite) TestAuthZPluginRejectVolumeRequests(c *check.C) { + // Install authz plugin + _, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", authzPluginNameWithTag) + c.Assert(err, checker.IsNil) + + // restart the daemon with the plugin + c.Assert(s.d.Restart("--authorization-plugin="+authzPluginNameWithTag), check.IsNil) + + // defer disabling the plugin + defer func() { + c.Assert(s.d.Restart(), check.IsNil) + _, 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) { + // 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.Restart("--authorization-plugin="+authzPluginBadManifestName), check.NotNil) + + // restarting the daemon without requiring the plugin will succeed + c.Assert(s.d.Restart(), check.IsNil) +} + +func (s *DockerAuthzV2Suite) TestNonexistentAuthZPluginFailsDaemonStart(c *check.C) { + // start the daemon with a non-existent authz plugin, it will error + c.Assert(s.d.Restart("--authorization-plugin="+nonexistentAuthzPluginName), check.NotNil) + + // restarting the daemon without requiring the plugin will succeed + c.Assert(s.d.Restart(), check.IsNil) +}