1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
package authorization
import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"net/http"
"github.com/docker/go-plugins-helpers/sdk"
)
const (
// AuthZApiRequest is the url for daemon request authorization
AuthZApiRequest = "AuthZPlugin.AuthZReq"
// AuthZApiResponse is the url for daemon response authorization
AuthZApiResponse = "AuthZPlugin.AuthZRes"
// AuthZApiImplements is the name of the interface all AuthZ plugins implement
AuthZApiImplements = "authz"
manifest = `{"Implements": ["` + AuthZApiImplements + `"]}`
reqPath = "/" + AuthZApiRequest
resPath = "/" + AuthZApiResponse
)
// PeerCertificate is a wrapper around x509.Certificate which provides a sane
// encoding/decoding to/from PEM format and JSON.
type PeerCertificate x509.Certificate
// MarshalJSON returns the JSON encoded pem bytes of a PeerCertificate.
func (pc *PeerCertificate) MarshalJSON() ([]byte, error) {
b := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: pc.Raw})
return json.Marshal(b)
}
// UnmarshalJSON populates a new PeerCertificate struct from JSON data.
func (pc *PeerCertificate) UnmarshalJSON(b []byte) error {
var buf []byte
if err := json.Unmarshal(b, &buf); err != nil {
return err
}
derBytes, _ := pem.Decode(buf)
c, err := x509.ParseCertificate(derBytes.Bytes)
if err != nil {
return err
}
*pc = PeerCertificate(*c)
return nil
}
// Request holds data required for authZ plugins
type Request struct {
// User holds the user extracted by AuthN mechanism
User string `json:"User,omitempty"`
// UserAuthNMethod holds the mechanism used to extract user details (e.g., krb)
UserAuthNMethod string `json:"UserAuthNMethod,omitempty"`
// RequestMethod holds the HTTP method (GET/POST/PUT)
RequestMethod string `json:"RequestMethod,omitempty"`
// RequestUri holds the full HTTP uri (e.g., /v1.21/version)
RequestURI string `json:"RequestUri,omitempty"`
// RequestBody stores the raw request body sent to the docker daemon
RequestBody []byte `json:"RequestBody,omitempty"`
// RequestHeaders stores the raw request headers sent to the docker daemon
RequestHeaders map[string]string `json:"RequestHeaders,omitempty"`
// RequestPeerCertificates stores the request's TLS peer certificates in PEM format
RequestPeerCertificates []*PeerCertificate `json:"RequestPeerCertificates,omitempty"`
// ResponseStatusCode stores the status code returned from docker daemon
ResponseStatusCode int `json:"ResponseStatusCode,omitempty"`
// ResponseBody stores the raw response body sent from docker daemon
ResponseBody []byte `json:"ResponseBody,omitempty"`
// ResponseHeaders stores the response headers sent to the docker daemon
ResponseHeaders map[string]string `json:"ResponseHeaders,omitempty"`
}
// Response represents authZ plugin response
type Response struct {
// Allow indicating whether the user is allowed or not
Allow bool `json:"Allow"`
// Msg stores the authorization message
Msg string `json:"Msg,omitempty"`
// Err stores a message in case there's an error
Err string `json:"Err,omitempty"`
}
// Plugin represent the interface a plugin must fulfill.
type Plugin interface {
AuthZReq(Request) Response
AuthZRes(Request) Response
}
// Handler forwards requests and responses between the docker daemon and the plugin.
type Handler struct {
plugin Plugin
sdk.Handler
}
// NewHandler initializes the request handler with a plugin implementation.
func NewHandler(plugin Plugin) *Handler {
h := &Handler{plugin, sdk.NewHandler(manifest)}
h.initMux()
return h
}
func (h *Handler) initMux() {
h.handle(reqPath, func(req Request) Response {
return h.plugin.AuthZReq(req)
})
h.handle(resPath, func(req Request) Response {
return h.plugin.AuthZRes(req)
})
}
type actionHandler func(Request) Response
func (h *Handler) handle(name string, actionCall actionHandler) {
h.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
var (
req Request
d = json.NewDecoder(r.Body)
)
d.UseNumber()
if err := d.Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
res := actionCall(req)
sdk.EncodeResponse(w, res, res.Err != "")
})
}
|