Signed-off-by: Antonio Murdaca <runcom@redhat.com>
| ... | ... |
@@ -26,7 +26,13 @@ import ( |
| 26 | 26 |
// For response manipulation, the response from each plugin is piped between plugins. Plugin execution order |
| 27 | 27 |
// is determined according to daemon parameters |
| 28 | 28 |
func NewCtx(authZPlugins []Plugin, user, userAuthNMethod, requestMethod, requestURI string) *Ctx {
|
| 29 |
- return &Ctx{plugins: authZPlugins, user: user, userAuthNMethod: userAuthNMethod, requestMethod: requestMethod, requestURI: requestURI}
|
|
| 29 |
+ return &Ctx{
|
|
| 30 |
+ plugins: authZPlugins, |
|
| 31 |
+ user: user, |
|
| 32 |
+ userAuthNMethod: userAuthNMethod, |
|
| 33 |
+ requestMethod: requestMethod, |
|
| 34 |
+ requestURI: requestURI, |
|
| 35 |
+ } |
|
| 30 | 36 |
} |
| 31 | 37 |
|
| 32 | 38 |
// Ctx stores a a single request-response interaction context |
| ... | ... |
@@ -41,27 +47,26 @@ type Ctx struct {
|
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 | 43 |
// AuthZRequest authorized the request to the docker daemon using authZ plugins |
| 44 |
-func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) (err error) {
|
|
| 45 |
- |
|
| 44 |
+func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
|
|
| 46 | 45 |
var body []byte |
| 47 | 46 |
if sendBody(a.requestURI, r.Header) {
|
| 48 |
- var drainedBody io.ReadCloser |
|
| 47 |
+ var ( |
|
| 48 |
+ err error |
|
| 49 |
+ drainedBody io.ReadCloser |
|
| 50 |
+ ) |
|
| 49 | 51 |
drainedBody, r.Body, err = drainBody(r.Body) |
| 50 | 52 |
if err != nil {
|
| 51 | 53 |
return err |
| 52 | 54 |
} |
| 53 |
- body, err = ioutil.ReadAll(drainedBody) |
|
| 54 | 55 |
defer drainedBody.Close() |
| 55 |
- |
|
| 56 |
+ body, err = ioutil.ReadAll(drainedBody) |
|
| 56 | 57 |
if err != nil {
|
| 57 | 58 |
return err |
| 58 | 59 |
} |
| 59 | 60 |
} |
| 60 | 61 |
|
| 61 | 62 |
var h bytes.Buffer |
| 62 |
- err = r.Header.Write(&h) |
|
| 63 |
- |
|
| 64 |
- if err != nil {
|
|
| 63 |
+ if err := r.Header.Write(&h); err != nil {
|
|
| 65 | 64 |
return err |
| 66 | 65 |
} |
| 67 | 66 |
|
| ... | ... |
@@ -74,9 +79,7 @@ func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) (err error) {
|
| 74 | 74 |
RequestHeaders: headers(r.Header)} |
| 75 | 75 |
|
| 76 | 76 |
for _, plugin := range a.plugins {
|
| 77 |
- |
|
| 78 | 77 |
authRes, err := plugin.AuthZRequest(a.authReq) |
| 79 |
- |
|
| 80 | 78 |
if err != nil {
|
| 81 | 79 |
return err |
| 82 | 80 |
} |
| ... | ... |
@@ -91,7 +94,6 @@ func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) (err error) {
|
| 91 | 91 |
|
| 92 | 92 |
// AuthZResponse authorized and manipulates the response from docker daemon using authZ plugins |
| 93 | 93 |
func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
| 94 |
- |
|
| 95 | 94 |
a.authReq.ResponseStatusCode = rm.StatusCode() |
| 96 | 95 |
a.authReq.ResponseHeaders = headers(rm.Header()) |
| 97 | 96 |
|
| ... | ... |
@@ -100,9 +102,7 @@ func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
| 100 | 100 |
} |
| 101 | 101 |
|
| 102 | 102 |
for _, plugin := range a.plugins {
|
| 103 |
- |
|
| 104 | 103 |
authRes, err := plugin.AuthZResponse(a.authReq) |
| 105 |
- |
|
| 106 | 104 |
if err != nil {
|
| 107 | 105 |
return err |
| 108 | 106 |
} |
| ... | ... |
@@ -119,12 +119,12 @@ func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
| 119 | 119 |
|
| 120 | 120 |
// drainBody dump the body, it reads the body data into memory and |
| 121 | 121 |
// see go sources /go/src/net/http/httputil/dump.go |
| 122 |
-func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
|
|
| 122 |
+func drainBody(b io.ReadCloser) (io.ReadCloser, io.ReadCloser, error) {
|
|
| 123 | 123 |
var buf bytes.Buffer |
| 124 |
- if _, err = buf.ReadFrom(b); err != nil {
|
|
| 124 |
+ if _, err := buf.ReadFrom(b); err != nil {
|
|
| 125 | 125 |
return nil, nil, err |
| 126 | 126 |
} |
| 127 |
- if err = b.Close(); err != nil {
|
|
| 127 |
+ if err := b.Close(); err != nil {
|
|
| 128 | 128 |
return nil, nil, err |
| 129 | 129 |
} |
| 130 | 130 |
return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil |
| ... | ... |
@@ -132,7 +132,6 @@ func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
|
| 132 | 132 |
|
| 133 | 133 |
// sendBody returns true when request/response body should be sent to AuthZPlugin |
| 134 | 134 |
func sendBody(url string, header http.Header) bool {
|
| 135 |
- |
|
| 136 | 135 |
// Skip body for auth endpoint |
| 137 | 136 |
if strings.HasSuffix(url, "/auth") {
|
| 138 | 137 |
return false |
| ... | ... |
@@ -2,10 +2,6 @@ package authorization |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 |
- "fmt" |
|
| 6 |
- "github.com/docker/docker/pkg/plugins" |
|
| 7 |
- "github.com/docker/docker/pkg/tlsconfig" |
|
| 8 |
- "github.com/gorilla/mux" |
|
| 9 | 5 |
"io/ioutil" |
| 10 | 6 |
"log" |
| 11 | 7 |
"net" |
| ... | ... |
@@ -15,12 +11,15 @@ import ( |
| 15 | 15 |
"path" |
| 16 | 16 |
"reflect" |
| 17 | 17 |
"testing" |
| 18 |
+ |
|
| 19 |
+ "github.com/docker/docker/pkg/plugins" |
|
| 20 |
+ "github.com/docker/docker/pkg/tlsconfig" |
|
| 21 |
+ "github.com/gorilla/mux" |
|
| 18 | 22 |
) |
| 19 | 23 |
|
| 20 | 24 |
const pluginAddress = "authzplugin.sock" |
| 21 | 25 |
|
| 22 | 26 |
func TestAuthZRequestPlugin(t *testing.T) {
|
| 23 |
- |
|
| 24 | 27 |
server := authZPluginTestServer{t: t}
|
| 25 | 28 |
go server.start() |
| 26 | 29 |
defer server.stop() |
| ... | ... |
@@ -40,7 +39,6 @@ func TestAuthZRequestPlugin(t *testing.T) {
|
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 | 42 |
actualResponse, err := authZPlugin.AuthZRequest(&request) |
| 43 |
- |
|
| 44 | 43 |
if err != nil {
|
| 45 | 44 |
t.Fatalf("Failed to authorize request %v", err)
|
| 46 | 45 |
} |
| ... | ... |
@@ -54,7 +52,6 @@ func TestAuthZRequestPlugin(t *testing.T) {
|
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 | 56 |
func TestAuthZResponsePlugin(t *testing.T) {
|
| 57 |
- |
|
| 58 | 57 |
server := authZPluginTestServer{t: t}
|
| 59 | 58 |
go server.start() |
| 60 | 59 |
defer server.stop() |
| ... | ... |
@@ -71,7 +68,6 @@ func TestAuthZResponsePlugin(t *testing.T) {
|
| 71 | 71 |
} |
| 72 | 72 |
|
| 73 | 73 |
actualResponse, err := authZPlugin.AuthZResponse(&request) |
| 74 |
- |
|
| 75 | 74 |
if err != nil {
|
| 76 | 75 |
t.Fatalf("Failed to authorize request %v", err)
|
| 77 | 76 |
} |
| ... | ... |
@@ -85,7 +81,6 @@ func TestAuthZResponsePlugin(t *testing.T) {
|
| 85 | 85 |
} |
| 86 | 86 |
|
| 87 | 87 |
func TestResponseModifier(t *testing.T) {
|
| 88 |
- |
|
| 89 | 88 |
r := httptest.NewRecorder() |
| 90 | 89 |
m := NewResponseModifier(r) |
| 91 | 90 |
m.Header().Set("h1", "v1")
|
| ... | ... |
@@ -105,7 +100,6 @@ func TestResponseModifier(t *testing.T) {
|
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 | 107 |
func TestResponseModifierOverride(t *testing.T) {
|
| 108 |
- |
|
| 109 | 108 |
r := httptest.NewRecorder() |
| 110 | 109 |
m := NewResponseModifier(r) |
| 111 | 110 |
m.Header().Set("h1", "v1")
|
| ... | ... |
@@ -137,18 +131,12 @@ func TestResponseModifierOverride(t *testing.T) {
|
| 137 | 137 |
// createTestPlugin creates a new sample authorization plugin |
| 138 | 138 |
func createTestPlugin(t *testing.T) *authorizationPlugin {
|
| 139 | 139 |
plugin := &plugins.Plugin{Name: "authz"}
|
| 140 |
- var err error |
|
| 141 | 140 |
pwd, err := os.Getwd() |
| 142 | 141 |
if err != nil {
|
| 143 |
- fmt.Println(err) |
|
| 144 |
- os.Exit(1) |
|
| 145 |
- } |
|
| 146 |
- if err != nil {
|
|
| 147 | 142 |
log.Fatal(err) |
| 148 | 143 |
} |
| 149 | 144 |
|
| 150 | 145 |
plugin.Client, err = plugins.NewClient("unix:///"+path.Join(pwd, pluginAddress), tlsconfig.Options{InsecureSkipVerify: true})
|
| 151 |
- |
|
| 152 | 146 |
if err != nil {
|
| 153 | 147 |
t.Fatalf("Failed to create client %v", err)
|
| 154 | 148 |
} |
| ... | ... |
@@ -186,9 +174,7 @@ func (t *authZPluginTestServer) start() {
|
| 186 | 186 |
|
| 187 | 187 |
// stop stops the test server that implements the plugin |
| 188 | 188 |
func (t *authZPluginTestServer) stop() {
|
| 189 |
- |
|
| 190 | 189 |
os.Remove(pluginAddress) |
| 191 |
- |
|
| 192 | 190 |
if t.listener != nil {
|
| 193 | 191 |
t.listener.Close() |
| 194 | 192 |
} |
| ... | ... |
@@ -196,9 +182,7 @@ func (t *authZPluginTestServer) stop() {
|
| 196 | 196 |
|
| 197 | 197 |
// auth is a used to record/replay the authentication api messages |
| 198 | 198 |
func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) {
|
| 199 |
- |
|
| 200 | 199 |
t.recordedRequest = Request{}
|
| 201 |
- |
|
| 202 | 200 |
defer r.Body.Close() |
| 203 | 201 |
body, err := ioutil.ReadAll(r.Body) |
| 204 | 202 |
json.Unmarshal(body, &t.recordedRequest) |
| ... | ... |
@@ -207,11 +191,9 @@ func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) {
|
| 207 | 207 |
log.Fatal(err) |
| 208 | 208 |
} |
| 209 | 209 |
w.Write(b) |
| 210 |
- |
|
| 211 | 210 |
} |
| 212 | 211 |
|
| 213 | 212 |
func (t *authZPluginTestServer) activate(w http.ResponseWriter, r *http.Request) {
|
| 214 |
- |
|
| 215 | 213 |
b, err := json.Marshal(plugins.Manifest{Implements: []string{AuthZApiImplements}})
|
| 216 | 214 |
if err != nil {
|
| 217 | 215 |
log.Fatal(err) |
| ... | ... |
@@ -8,12 +8,11 @@ import ( |
| 8 | 8 |
// Plugin allows third party plugins to authorize requests and responses |
| 9 | 9 |
// in the context of docker API |
| 10 | 10 |
type Plugin interface {
|
| 11 |
- |
|
| 12 | 11 |
// AuthZRequest authorize the request from the client to the daemon |
| 13 |
- AuthZRequest(authReq *Request) (authRes *Response, err error) |
|
| 12 |
+ AuthZRequest(*Request) (*Response, error) |
|
| 14 | 13 |
|
| 15 | 14 |
// AuthZResponse authorize the response from the daemon to the client |
| 16 |
- AuthZResponse(authReq *Request) (authRes *Response, err error) |
|
| 15 |
+ AuthZResponse(*Request) (*Response, error) |
|
| 17 | 16 |
} |
| 18 | 17 |
|
| 19 | 18 |
// NewPlugins constructs and initialize the authorization plugins based on plugin names |
| ... | ... |
@@ -35,38 +34,30 @@ func newAuthorizationPlugin(name string) Plugin {
|
| 35 | 35 |
return &authorizationPlugin{name: name}
|
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 |
-func (a *authorizationPlugin) AuthZRequest(authReq *Request) (authRes *Response, err error) {
|
|
| 39 |
- |
|
| 38 |
+func (a *authorizationPlugin) AuthZRequest(authReq *Request) (*Response, error) {
|
|
| 40 | 39 |
logrus.Debugf("AuthZ requset using plugins %s", a.name)
|
| 41 | 40 |
|
| 42 |
- err = a.initPlugin() |
|
| 43 |
- if err != nil {
|
|
| 41 |
+ if err := a.initPlugin(); err != nil {
|
|
| 44 | 42 |
return nil, err |
| 45 | 43 |
} |
| 46 | 44 |
|
| 47 |
- authRes = &Response{}
|
|
| 48 |
- err = a.plugin.Client.Call(AuthZApiRequest, authReq, authRes) |
|
| 49 |
- |
|
| 50 |
- if err != nil {
|
|
| 45 |
+ authRes := &Response{}
|
|
| 46 |
+ if err := a.plugin.Client.Call(AuthZApiRequest, authReq, authRes); err != nil {
|
|
| 51 | 47 |
return nil, err |
| 52 | 48 |
} |
| 53 | 49 |
|
| 54 | 50 |
return authRes, nil |
| 55 | 51 |
} |
| 56 | 52 |
|
| 57 |
-func (a *authorizationPlugin) AuthZResponse(authReq *Request) (authRes *Response, err error) {
|
|
| 58 |
- |
|
| 53 |
+func (a *authorizationPlugin) AuthZResponse(authReq *Request) (*Response, error) {
|
|
| 59 | 54 |
logrus.Debugf("AuthZ response using plugins %s", a.name)
|
| 60 | 55 |
|
| 61 |
- err = a.initPlugin() |
|
| 62 |
- if err != nil {
|
|
| 56 |
+ if err := a.initPlugin(); err != nil {
|
|
| 63 | 57 |
return nil, err |
| 64 | 58 |
} |
| 65 | 59 |
|
| 66 |
- authRes = &Response{}
|
|
| 67 |
- err = a.plugin.Client.Call(AuthZApiResponse, authReq, authRes) |
|
| 68 |
- |
|
| 69 |
- if err != nil {
|
|
| 60 |
+ authRes := &Response{}
|
|
| 61 |
+ if err := a.plugin.Client.Call(AuthZApiResponse, authReq, authRes); err != nil {
|
|
| 70 | 62 |
return nil, err |
| 71 | 63 |
} |
| 72 | 64 |
|
| ... | ... |
@@ -74,10 +65,10 @@ func (a *authorizationPlugin) AuthZResponse(authReq *Request) (authRes *Response |
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 | 76 |
// initPlugin initialize the authorization plugin if needed |
| 77 |
-func (a *authorizationPlugin) initPlugin() (err error) {
|
|
| 78 |
- |
|
| 77 |
+func (a *authorizationPlugin) initPlugin() error {
|
|
| 79 | 78 |
// Lazy loading of plugins |
| 80 | 79 |
if a.plugin == nil {
|
| 80 |
+ var err error |
|
| 81 | 81 |
a.plugin, err = plugins.Get(a.name, AuthZApiImplements) |
| 82 | 82 |
if err != nil {
|
| 83 | 83 |
return err |
| ... | ... |
@@ -81,9 +81,7 @@ func (rm *responseModifier) OverrideStatusCode(statusCode int) {
|
| 81 | 81 |
// Override replace the headers of the HTTP reply |
| 82 | 82 |
func (rm *responseModifier) OverrideHeader(b []byte) error {
|
| 83 | 83 |
header := http.Header{}
|
| 84 |
- err := json.Unmarshal(b, &header) |
|
| 85 |
- |
|
| 86 |
- if err != nil {
|
|
| 84 |
+ if err := json.Unmarshal(b, &header); err != nil {
|
|
| 87 | 85 |
return err |
| 88 | 86 |
} |
| 89 | 87 |
rm.header = header |
| ... | ... |
@@ -103,8 +101,7 @@ func (rm *responseModifier) RawBody() []byte {
|
| 103 | 103 |
|
| 104 | 104 |
func (rm *responseModifier) RawHeaders() ([]byte, error) {
|
| 105 | 105 |
var b bytes.Buffer |
| 106 |
- err := rm.header.Write(&b) |
|
| 107 |
- if err != nil {
|
|
| 106 |
+ if err := rm.header.Write(&b); err != nil {
|
|
| 108 | 107 |
return nil, err |
| 109 | 108 |
} |
| 110 | 109 |
return b.Bytes(), nil |
| ... | ... |
@@ -121,7 +118,6 @@ func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
| 121 | 121 |
|
| 122 | 122 |
// Flush flushes all data to the HTTP response |
| 123 | 123 |
func (rm *responseModifier) Flush() error {
|
| 124 |
- |
|
| 125 | 124 |
// Copy the status code |
| 126 | 125 |
if rm.statusCode > 0 {
|
| 127 | 126 |
rm.rw.WriteHeader(rm.statusCode) |