pkg: authorization: cleanup
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
207e5e455a
commit
fb77ffd682
4 changed files with 35 additions and 67 deletions
|
@ -26,7 +26,13 @@ import (
|
||||||
// For response manipulation, the response from each plugin is piped between plugins. Plugin execution order
|
// For response manipulation, the response from each plugin is piped between plugins. Plugin execution order
|
||||||
// is determined according to daemon parameters
|
// is determined according to daemon parameters
|
||||||
func NewCtx(authZPlugins []Plugin, user, userAuthNMethod, requestMethod, requestURI string) *Ctx {
|
func NewCtx(authZPlugins []Plugin, user, userAuthNMethod, requestMethod, requestURI string) *Ctx {
|
||||||
return &Ctx{plugins: authZPlugins, user: user, userAuthNMethod: userAuthNMethod, requestMethod: requestMethod, requestURI: requestURI}
|
return &Ctx{
|
||||||
|
plugins: authZPlugins,
|
||||||
|
user: user,
|
||||||
|
userAuthNMethod: userAuthNMethod,
|
||||||
|
requestMethod: requestMethod,
|
||||||
|
requestURI: requestURI,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ctx stores a a single request-response interaction context
|
// Ctx stores a a single request-response interaction context
|
||||||
|
@ -41,27 +47,26 @@ 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
|
||||||
func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) (err error) {
|
func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
|
||||||
var body []byte
|
var body []byte
|
||||||
if sendBody(a.requestURI, r.Header) {
|
if sendBody(a.requestURI, r.Header) {
|
||||||
var drainedBody io.ReadCloser
|
var (
|
||||||
|
err error
|
||||||
|
drainedBody io.ReadCloser
|
||||||
|
)
|
||||||
drainedBody, r.Body, err = drainBody(r.Body)
|
drainedBody, r.Body, err = drainBody(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
body, err = ioutil.ReadAll(drainedBody)
|
|
||||||
defer drainedBody.Close()
|
defer drainedBody.Close()
|
||||||
|
body, err = ioutil.ReadAll(drainedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var h bytes.Buffer
|
var h bytes.Buffer
|
||||||
err = r.Header.Write(&h)
|
if err := r.Header.Write(&h); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,9 +79,7 @@ func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) (err error) {
|
||||||
RequestHeaders: headers(r.Header)}
|
RequestHeaders: headers(r.Header)}
|
||||||
|
|
||||||
for _, plugin := range a.plugins {
|
for _, plugin := range a.plugins {
|
||||||
|
|
||||||
authRes, err := plugin.AuthZRequest(a.authReq)
|
authRes, err := plugin.AuthZRequest(a.authReq)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -91,7 +94,6 @@ func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) (err 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
|
||||||
func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
||||||
|
|
||||||
a.authReq.ResponseStatusCode = rm.StatusCode()
|
a.authReq.ResponseStatusCode = rm.StatusCode()
|
||||||
a.authReq.ResponseHeaders = headers(rm.Header())
|
a.authReq.ResponseHeaders = headers(rm.Header())
|
||||||
|
|
||||||
|
@ -100,9 +102,7 @@ func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, plugin := range a.plugins {
|
for _, plugin := range a.plugins {
|
||||||
|
|
||||||
authRes, err := plugin.AuthZResponse(a.authReq)
|
authRes, err := plugin.AuthZResponse(a.authReq)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -119,12 +119,12 @@ func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
||||||
|
|
||||||
// drainBody dump the body, it reads the body data into memory and
|
// drainBody dump the body, it reads the body data into memory and
|
||||||
// see go sources /go/src/net/http/httputil/dump.go
|
// see go sources /go/src/net/http/httputil/dump.go
|
||||||
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
|
func drainBody(b io.ReadCloser) (io.ReadCloser, io.ReadCloser, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if _, err = buf.ReadFrom(b); err != nil {
|
if _, err := buf.ReadFrom(b); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if err = b.Close(); err != nil {
|
if err := b.Close(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
|
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) {
|
||||||
|
|
||||||
// sendBody returns true when request/response body should be sent to AuthZPlugin
|
// sendBody returns true when request/response body should be sent to AuthZPlugin
|
||||||
func sendBody(url string, header http.Header) bool {
|
func sendBody(url string, header http.Header) bool {
|
||||||
|
|
||||||
// Skip body for auth endpoint
|
// Skip body for auth endpoint
|
||||||
if strings.HasSuffix(url, "/auth") {
|
if strings.HasSuffix(url, "/auth") {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -2,10 +2,6 @@ package authorization
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"github.com/docker/docker/pkg/plugins"
|
|
||||||
"github.com/docker/docker/pkg/tlsconfig"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
@ -15,12 +11,15 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/plugins"
|
||||||
|
"github.com/docker/docker/pkg/tlsconfig"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pluginAddress = "authzplugin.sock"
|
const pluginAddress = "authzplugin.sock"
|
||||||
|
|
||||||
func TestAuthZRequestPlugin(t *testing.T) {
|
func TestAuthZRequestPlugin(t *testing.T) {
|
||||||
|
|
||||||
server := authZPluginTestServer{t: t}
|
server := authZPluginTestServer{t: t}
|
||||||
go server.start()
|
go server.start()
|
||||||
defer server.stop()
|
defer server.stop()
|
||||||
|
@ -40,7 +39,6 @@ func TestAuthZRequestPlugin(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
actualResponse, err := authZPlugin.AuthZRequest(&request)
|
actualResponse, err := authZPlugin.AuthZRequest(&request)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to authorize request %v", err)
|
t.Fatalf("Failed to authorize request %v", err)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +52,6 @@ func TestAuthZRequestPlugin(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthZResponsePlugin(t *testing.T) {
|
func TestAuthZResponsePlugin(t *testing.T) {
|
||||||
|
|
||||||
server := authZPluginTestServer{t: t}
|
server := authZPluginTestServer{t: t}
|
||||||
go server.start()
|
go server.start()
|
||||||
defer server.stop()
|
defer server.stop()
|
||||||
|
@ -71,7 +68,6 @@ func TestAuthZResponsePlugin(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
actualResponse, err := authZPlugin.AuthZResponse(&request)
|
actualResponse, err := authZPlugin.AuthZResponse(&request)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to authorize request %v", err)
|
t.Fatalf("Failed to authorize request %v", err)
|
||||||
}
|
}
|
||||||
|
@ -85,7 +81,6 @@ func TestAuthZResponsePlugin(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResponseModifier(t *testing.T) {
|
func TestResponseModifier(t *testing.T) {
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
r := httptest.NewRecorder()
|
||||||
m := NewResponseModifier(r)
|
m := NewResponseModifier(r)
|
||||||
m.Header().Set("h1", "v1")
|
m.Header().Set("h1", "v1")
|
||||||
|
@ -105,7 +100,6 @@ func TestResponseModifier(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResponseModifierOverride(t *testing.T) {
|
func TestResponseModifierOverride(t *testing.T) {
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
r := httptest.NewRecorder()
|
||||||
m := NewResponseModifier(r)
|
m := NewResponseModifier(r)
|
||||||
m.Header().Set("h1", "v1")
|
m.Header().Set("h1", "v1")
|
||||||
|
@ -137,18 +131,12 @@ func TestResponseModifierOverride(t *testing.T) {
|
||||||
// createTestPlugin creates a new sample authorization plugin
|
// createTestPlugin creates a new sample authorization plugin
|
||||||
func createTestPlugin(t *testing.T) *authorizationPlugin {
|
func createTestPlugin(t *testing.T) *authorizationPlugin {
|
||||||
plugin := &plugins.Plugin{Name: "authz"}
|
plugin := &plugins.Plugin{Name: "authz"}
|
||||||
var err error
|
|
||||||
pwd, err := os.Getwd()
|
pwd, err := os.Getwd()
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.Client, err = plugins.NewClient("unix:///"+path.Join(pwd, pluginAddress), tlsconfig.Options{InsecureSkipVerify: true})
|
plugin.Client, err = plugins.NewClient("unix:///"+path.Join(pwd, pluginAddress), tlsconfig.Options{InsecureSkipVerify: true})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create client %v", err)
|
t.Fatalf("Failed to create client %v", err)
|
||||||
}
|
}
|
||||||
|
@ -186,9 +174,7 @@ func (t *authZPluginTestServer) start() {
|
||||||
|
|
||||||
// stop stops the test server that implements the plugin
|
// stop stops the test server that implements the plugin
|
||||||
func (t *authZPluginTestServer) stop() {
|
func (t *authZPluginTestServer) stop() {
|
||||||
|
|
||||||
os.Remove(pluginAddress)
|
os.Remove(pluginAddress)
|
||||||
|
|
||||||
if t.listener != nil {
|
if t.listener != nil {
|
||||||
t.listener.Close()
|
t.listener.Close()
|
||||||
}
|
}
|
||||||
|
@ -196,9 +182,7 @@ func (t *authZPluginTestServer) stop() {
|
||||||
|
|
||||||
// auth is a used to record/replay the authentication api messages
|
// auth is a used to record/replay the authentication api messages
|
||||||
func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) {
|
func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
t.recordedRequest = Request{}
|
t.recordedRequest = Request{}
|
||||||
|
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
json.Unmarshal(body, &t.recordedRequest)
|
json.Unmarshal(body, &t.recordedRequest)
|
||||||
|
@ -207,11 +191,9 @@ func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
w.Write(b)
|
w.Write(b)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *authZPluginTestServer) activate(w http.ResponseWriter, r *http.Request) {
|
func (t *authZPluginTestServer) activate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
b, err := json.Marshal(plugins.Manifest{Implements: []string{AuthZApiImplements}})
|
b, err := json.Marshal(plugins.Manifest{Implements: []string{AuthZApiImplements}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -8,12 +8,11 @@ import (
|
||||||
// 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 {
|
||||||
|
|
||||||
// AuthZRequest authorize the request from the client to the daemon
|
// AuthZRequest authorize the request from the client to the daemon
|
||||||
AuthZRequest(authReq *Request) (authRes *Response, err error)
|
AuthZRequest(*Request) (*Response, error)
|
||||||
|
|
||||||
// AuthZResponse authorize the response from the daemon to the client
|
// AuthZResponse authorize the response from the daemon to the client
|
||||||
AuthZResponse(authReq *Request) (authRes *Response, err error)
|
AuthZResponse(*Request) (*Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPlugins constructs and initialize the authorization plugins based on plugin names
|
// NewPlugins constructs and initialize the authorization plugins based on plugin names
|
||||||
|
@ -35,38 +34,30 @@ func newAuthorizationPlugin(name string) Plugin {
|
||||||
return &authorizationPlugin{name: name}
|
return &authorizationPlugin{name: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *authorizationPlugin) AuthZRequest(authReq *Request) (authRes *Response, err error) {
|
func (a *authorizationPlugin) AuthZRequest(authReq *Request) (*Response, error) {
|
||||||
|
|
||||||
logrus.Debugf("AuthZ requset using plugins %s", a.name)
|
logrus.Debugf("AuthZ requset using plugins %s", a.name)
|
||||||
|
|
||||||
err = a.initPlugin()
|
if err := a.initPlugin(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
authRes = &Response{}
|
authRes := &Response{}
|
||||||
err = a.plugin.Client.Call(AuthZApiRequest, authReq, authRes)
|
if err := a.plugin.Client.Call(AuthZApiRequest, authReq, authRes); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return authRes, nil
|
return authRes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *authorizationPlugin) AuthZResponse(authReq *Request) (authRes *Response, err error) {
|
func (a *authorizationPlugin) AuthZResponse(authReq *Request) (*Response, error) {
|
||||||
|
|
||||||
logrus.Debugf("AuthZ response using plugins %s", a.name)
|
logrus.Debugf("AuthZ response using plugins %s", a.name)
|
||||||
|
|
||||||
err = a.initPlugin()
|
if err := a.initPlugin(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
authRes = &Response{}
|
authRes := &Response{}
|
||||||
err = a.plugin.Client.Call(AuthZApiResponse, authReq, authRes)
|
if err := a.plugin.Client.Call(AuthZApiResponse, authReq, authRes); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,10 +65,10 @@ func (a *authorizationPlugin) AuthZResponse(authReq *Request) (authRes *Response
|
||||||
}
|
}
|
||||||
|
|
||||||
// initPlugin initialize the authorization plugin if needed
|
// initPlugin initialize the authorization plugin if needed
|
||||||
func (a *authorizationPlugin) initPlugin() (err error) {
|
func (a *authorizationPlugin) initPlugin() error {
|
||||||
|
|
||||||
// Lazy loading of plugins
|
// Lazy loading of plugins
|
||||||
if a.plugin == nil {
|
if a.plugin == nil {
|
||||||
|
var err error
|
||||||
a.plugin, err = plugins.Get(a.name, AuthZApiImplements)
|
a.plugin, err = plugins.Get(a.name, AuthZApiImplements)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -81,9 +81,7 @@ func (rm *responseModifier) OverrideStatusCode(statusCode int) {
|
||||||
// Override replace the headers of the HTTP reply
|
// Override replace the headers of the HTTP reply
|
||||||
func (rm *responseModifier) OverrideHeader(b []byte) error {
|
func (rm *responseModifier) OverrideHeader(b []byte) error {
|
||||||
header := http.Header{}
|
header := http.Header{}
|
||||||
err := json.Unmarshal(b, &header)
|
if err := json.Unmarshal(b, &header); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rm.header = header
|
rm.header = header
|
||||||
|
@ -103,8 +101,7 @@ func (rm *responseModifier) RawBody() []byte {
|
||||||
|
|
||||||
func (rm *responseModifier) RawHeaders() ([]byte, error) {
|
func (rm *responseModifier) RawHeaders() ([]byte, error) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
err := rm.header.Write(&b)
|
if err := rm.header.Write(&b); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
|
@ -121,7 +118,6 @@ func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
|
||||||
// Flush flushes all data to the HTTP response
|
// Flush flushes all data to the HTTP response
|
||||||
func (rm *responseModifier) Flush() error {
|
func (rm *responseModifier) Flush() error {
|
||||||
|
|
||||||
// Copy the status code
|
// Copy the status code
|
||||||
if rm.statusCode > 0 {
|
if rm.statusCode > 0 {
|
||||||
rm.rw.WriteHeader(rm.statusCode)
|
rm.rw.WriteHeader(rm.statusCode)
|
||||||
|
|
Loading…
Reference in a new issue