Forráskód Böngészése

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 <anusha@docker.com>
Anusha Ragunathan 8 éve
szülő
commit
fae904af02

+ 12 - 2
pkg/authorization/authz.go

@@ -52,6 +52,8 @@ type Ctx struct {
 }
 }
 
 
 // AuthZRequest authorized the request to the docker daemon using authZ plugins
 // 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 {
 func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
 	var body []byte
 	var body []byte
 	if sendBody(ctx.requestURI, r.Header) && r.ContentLength > 0 && r.ContentLength < maxBodySize {
 	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),
 		RequestHeaders:  headers(r.Header),
 	}
 	}
 
 
-	for _, plugin := range ctx.plugins {
+	for i, plugin := range ctx.plugins {
 		logrus.Debugf("AuthZ request using plugin %s", plugin.Name())
 		logrus.Debugf("AuthZ request using plugin %s", plugin.Name())
 
 
 		authRes, err := plugin.AuthZRequest(ctx.authReq)
 		authRes, err := plugin.AuthZRequest(ctx.authReq)
 		if err != nil {
 		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)
 			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
 // 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 {
 func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
 	ctx.authReq.ResponseStatusCode = rm.StatusCode()
 	ctx.authReq.ResponseStatusCode = rm.StatusCode()
 	ctx.authReq.ResponseHeaders = headers(rm.Header())
 	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()
 		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())
 		logrus.Debugf("AuthZ response using plugin %s", plugin.Name())
 
 
 		authRes, err := plugin.AuthZResponse(ctx.authReq)
 		authRes, err := plugin.AuthZResponse(ctx.authReq)
 		if err != nil {
 		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)
 			return fmt.Errorf("plugin %s failed with error: %s", plugin.Name(), err)
 		}
 		}
 
 

+ 11 - 0
pkg/authorization/middleware.go

@@ -2,6 +2,7 @@ package authorization
 
 
 import (
 import (
 	"net/http"
 	"net/http"
+	"strings"
 	"sync"
 	"sync"
 
 
 	"github.com/Sirupsen/logrus"
 	"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 {
 		if err := authCtx.AuthZRequest(w, r); err != nil {
 			logrus.Errorf("AuthZRequest for %s %s returned error: %s", r.Method, r.RequestURI, err)
 			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
 			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 {
 		if err := authCtx.AuthZResponse(rw, r); errD == nil && err != nil {
 			logrus.Errorf("AuthZResponse for %s %s returned error: %s", r.Method, r.RequestURI, err)
 			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
 			return err
 		}
 		}
 
 

+ 9 - 1
pkg/authorization/plugin.go

@@ -1,12 +1,20 @@
 package authorization
 package authorization
 
 
 import (
 import (
+	"errors"
 	"sync"
 	"sync"
 
 
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugins"
 	"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
 // Plugin allows third party plugins to authorize requests and responses
 // in the context of docker API
 // in the context of docker API
 type Plugin interface {
 type Plugin interface {
@@ -102,7 +110,7 @@ func (a *authorizationPlugin) initPlugin() error {
 				plugin, e = plugins.Get(a.name, AuthZApiImplements)
 				plugin, e = plugins.Get(a.name, AuthZApiImplements)
 			}
 			}
 			if e != nil {
 			if e != nil {
-				err = e
+				err = ErrInvalidPlugin
 				return
 				return
 			}
 			}
 			a.plugin = plugin.Client()
 			a.plugin = plugin.Client()