From fae904af02a184833d2cd5ce9fdd61a4083707c7 Mon Sep 17 00:00:00 2001 From: Anusha Ragunathan Date: Wed, 26 Oct 2016 16:29:48 -0700 Subject: [PATCH] Update authz plugin list on failure. When daemon fails to load an authz plugin, it should be removed from the plugin list. Else the plugin is retried on every request and response, resulting in undesired behavior (eg. daemon panic) Signed-off-by: Anusha Ragunathan --- pkg/authorization/authz.go | 14 ++++++++++++-- pkg/authorization/middleware.go | 11 +++++++++++ pkg/authorization/plugin.go | 10 +++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/pkg/authorization/authz.go b/pkg/authorization/authz.go index 04eef64b67..9080249b5e 100644 --- a/pkg/authorization/authz.go +++ b/pkg/authorization/authz.go @@ -52,6 +52,8 @@ type Ctx struct { } // AuthZRequest authorized the request to the docker daemon using authZ plugins +// Side effect: If the authz plugin is invalid, then update ctx.plugins, so that +// the caller(middleware) can update its list and stop retrying with invalid plugins. func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error { var body []byte if sendBody(ctx.requestURI, r.Header) && r.ContentLength > 0 && r.ContentLength < maxBodySize { @@ -76,11 +78,14 @@ func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error { RequestHeaders: headers(r.Header), } - for _, plugin := range ctx.plugins { + for i, plugin := range ctx.plugins { logrus.Debugf("AuthZ request using plugin %s", plugin.Name()) authRes, err := plugin.AuthZRequest(ctx.authReq) if err != nil { + if err == ErrInvalidPlugin { + ctx.plugins = append(ctx.plugins[:i], ctx.plugins[i+1:]...) + } return fmt.Errorf("plugin %s failed with error: %s", plugin.Name(), err) } @@ -93,6 +98,8 @@ func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error { } // AuthZResponse authorized and manipulates the response from docker daemon using authZ plugins +// Side effect: If the authz plugin is invalid, then update ctx.plugins, so that +// the caller(middleware) can update its list and stop retrying with invalid plugins. func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error { ctx.authReq.ResponseStatusCode = rm.StatusCode() ctx.authReq.ResponseHeaders = headers(rm.Header()) @@ -101,11 +108,14 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error { ctx.authReq.ResponseBody = rm.RawBody() } - for _, plugin := range ctx.plugins { + for i, plugin := range ctx.plugins { logrus.Debugf("AuthZ response using plugin %s", plugin.Name()) authRes, err := plugin.AuthZResponse(ctx.authReq) if err != nil { + if err == ErrInvalidPlugin { + ctx.plugins = append(ctx.plugins[:i], ctx.plugins[i+1:]...) + } return fmt.Errorf("plugin %s failed with error: %s", plugin.Name(), err) } diff --git a/pkg/authorization/middleware.go b/pkg/authorization/middleware.go index 52890dd360..ab8e810efc 100644 --- a/pkg/authorization/middleware.go +++ b/pkg/authorization/middleware.go @@ -2,6 +2,7 @@ package authorization import ( "net/http" + "strings" "sync" "github.com/Sirupsen/logrus" @@ -59,6 +60,11 @@ func (m *Middleware) WrapHandler(handler func(ctx context.Context, w http.Respon if err := authCtx.AuthZRequest(w, r); err != nil { logrus.Errorf("AuthZRequest for %s %s returned error: %s", r.Method, r.RequestURI, err) + if strings.Contains(err.Error(), ErrInvalidPlugin.Error()) { + m.mu.Lock() + m.plugins = authCtx.plugins + m.mu.Unlock() + } return err } @@ -72,6 +78,11 @@ func (m *Middleware) WrapHandler(handler func(ctx context.Context, w http.Respon if err := authCtx.AuthZResponse(rw, r); errD == nil && err != nil { logrus.Errorf("AuthZResponse for %s %s returned error: %s", r.Method, r.RequestURI, err) + if strings.Contains(err.Error(), ErrInvalidPlugin.Error()) { + m.mu.Lock() + m.plugins = authCtx.plugins + m.mu.Unlock() + } return err } diff --git a/pkg/authorization/plugin.go b/pkg/authorization/plugin.go index 4b1c71bd4b..5c3431d5cf 100644 --- a/pkg/authorization/plugin.go +++ b/pkg/authorization/plugin.go @@ -1,12 +1,20 @@ package authorization import ( + "errors" "sync" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" ) +var ( + // ErrInvalidPlugin indicates that the plugin cannot be used. This is + // because the plugin was not found or does not implement necessary + // functionality + ErrInvalidPlugin = errors.New("invalid plugin") +) + // Plugin allows third party plugins to authorize requests and responses // in the context of docker API type Plugin interface { @@ -102,7 +110,7 @@ func (a *authorizationPlugin) initPlugin() error { plugin, e = plugins.Get(a.name, AuthZApiImplements) } if e != nil { - err = e + err = ErrInvalidPlugin return } a.plugin = plugin.Client()