Browse code

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 authored on 2016/10/27 08:29:48
Showing 3 changed files
... ...
@@ -52,6 +52,8 @@ type Ctx struct {
52 52
 }
53 53
 
54 54
 // AuthZRequest authorized the request to the docker daemon using authZ plugins
55
+// Side effect: If the authz plugin is invalid, then update ctx.plugins, so that
56
+// the caller(middleware) can update its list and stop retrying with invalid plugins.
55 57
 func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
56 58
 	var body []byte
57 59
 	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 {
76 76
 		RequestHeaders:  headers(r.Header),
77 77
 	}
78 78
 
79
-	for _, plugin := range ctx.plugins {
79
+	for i, plugin := range ctx.plugins {
80 80
 		logrus.Debugf("AuthZ request using plugin %s", plugin.Name())
81 81
 
82 82
 		authRes, err := plugin.AuthZRequest(ctx.authReq)
83 83
 		if err != nil {
84
+			if err == ErrInvalidPlugin {
85
+				ctx.plugins = append(ctx.plugins[:i], ctx.plugins[i+1:]...)
86
+			}
84 87
 			return fmt.Errorf("plugin %s failed with error: %s", plugin.Name(), err)
85 88
 		}
86 89
 
... ...
@@ -93,6 +98,8 @@ func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
93 93
 }
94 94
 
95 95
 // AuthZResponse authorized and manipulates the response from docker daemon using authZ plugins
96
+// Side effect: If the authz plugin is invalid, then update ctx.plugins, so that
97
+// the caller(middleware) can update its list and stop retrying with invalid plugins.
96 98
 func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
97 99
 	ctx.authReq.ResponseStatusCode = rm.StatusCode()
98 100
 	ctx.authReq.ResponseHeaders = headers(rm.Header())
... ...
@@ -101,11 +108,14 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
101 101
 		ctx.authReq.ResponseBody = rm.RawBody()
102 102
 	}
103 103
 
104
-	for _, plugin := range ctx.plugins {
104
+	for i, plugin := range ctx.plugins {
105 105
 		logrus.Debugf("AuthZ response using plugin %s", plugin.Name())
106 106
 
107 107
 		authRes, err := plugin.AuthZResponse(ctx.authReq)
108 108
 		if err != nil {
109
+			if err == ErrInvalidPlugin {
110
+				ctx.plugins = append(ctx.plugins[:i], ctx.plugins[i+1:]...)
111
+			}
109 112
 			return fmt.Errorf("plugin %s failed with error: %s", plugin.Name(), err)
110 113
 		}
111 114
 
... ...
@@ -2,6 +2,7 @@ package authorization
2 2
 
3 3
 import (
4 4
 	"net/http"
5
+	"strings"
5 6
 	"sync"
6 7
 
7 8
 	"github.com/Sirupsen/logrus"
... ...
@@ -59,6 +60,11 @@ func (m *Middleware) WrapHandler(handler func(ctx context.Context, w http.Respon
59 59
 
60 60
 		if err := authCtx.AuthZRequest(w, r); err != nil {
61 61
 			logrus.Errorf("AuthZRequest for %s %s returned error: %s", r.Method, r.RequestURI, err)
62
+			if strings.Contains(err.Error(), ErrInvalidPlugin.Error()) {
63
+				m.mu.Lock()
64
+				m.plugins = authCtx.plugins
65
+				m.mu.Unlock()
66
+			}
62 67
 			return err
63 68
 		}
64 69
 
... ...
@@ -72,6 +78,11 @@ func (m *Middleware) WrapHandler(handler func(ctx context.Context, w http.Respon
72 72
 
73 73
 		if err := authCtx.AuthZResponse(rw, r); errD == nil && err != nil {
74 74
 			logrus.Errorf("AuthZResponse for %s %s returned error: %s", r.Method, r.RequestURI, err)
75
+			if strings.Contains(err.Error(), ErrInvalidPlugin.Error()) {
76
+				m.mu.Lock()
77
+				m.plugins = authCtx.plugins
78
+				m.mu.Unlock()
79
+			}
75 80
 			return err
76 81
 		}
77 82
 
... ...
@@ -1,12 +1,20 @@
1 1
 package authorization
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"sync"
5 6
 
6 7
 	"github.com/docker/docker/pkg/plugingetter"
7 8
 	"github.com/docker/docker/pkg/plugins"
8 9
 )
9 10
 
11
+var (
12
+	// ErrInvalidPlugin indicates that the plugin cannot be used. This is
13
+	// because the plugin was not found or does not implement necessary
14
+	// functionality
15
+	ErrInvalidPlugin = errors.New("invalid plugin")
16
+)
17
+
10 18
 // Plugin allows third party plugins to authorize requests and responses
11 19
 // in the context of docker API
12 20
 type Plugin interface {
... ...
@@ -102,7 +110,7 @@ func (a *authorizationPlugin) initPlugin() error {
102 102
 				plugin, e = plugins.Get(a.name, AuthZApiImplements)
103 103
 			}
104 104
 			if e != nil {
105
-				err = e
105
+				err = ErrInvalidPlugin
106 106
 				return
107 107
 			}
108 108
 			a.plugin = plugin.Client()