1
0
mirror of https://github.com/rancher/steve.git synced 2025-04-27 02:51:10 +00:00
steve/pkg/auth/filter.go
Darren Shepherd 8b42d0aff8 Refactor
2020-01-30 22:37:59 -07:00

147 lines
3.4 KiB
Go

package auth
import (
"io/ioutil"
"net/http"
"strings"
"time"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/token/cache"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
type Authenticator interface {
Authenticate(req *http.Request) (user.Info, bool, error)
}
type AuthenticatorFunc func(req *http.Request) (user.Info, bool, error)
func (a AuthenticatorFunc) Authenticate(req *http.Request) (user.Info, bool, error) {
return a(req)
}
type Middleware func(http.ResponseWriter, *http.Request, http.Handler)
func (m Middleware) Wrap(handler http.Handler) http.Handler {
if m == nil {
return handler
}
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
m(rw, req, handler)
})
}
func WebhookConfigForURL(url string) (string, error) {
config := clientcmdapi.Config{
Clusters: map[string]*clientcmdapi.Cluster{
"local": {
Server: url,
InsecureSkipTLSVerify: true,
},
},
Contexts: map[string]*clientcmdapi.Context{
"Default": {
Cluster: "local",
AuthInfo: "user",
Namespace: "default",
},
},
AuthInfos: map[string]*clientcmdapi.AuthInfo{
"user": {},
},
CurrentContext: "Default",
}
tmpFile, err := ioutil.TempFile("", "webhook-config")
if err != nil {
return "", err
}
if err := tmpFile.Close(); err != nil {
return "", err
}
return tmpFile.Name(), clientcmd.WriteToFile(config, tmpFile.Name())
}
func NewWebhookAuthenticator(cacheTTL time.Duration, kubeConfigFile string) (Authenticator, error) {
wh, err := webhook.New(kubeConfigFile, "v1", nil)
if err != nil {
return nil, err
}
if cacheTTL > 0 {
return &webhookAuth{
auth: cache.New(wh, false, cacheTTL, cacheTTL),
}, nil
}
return &webhookAuth{
auth: wh,
}, nil
}
func NewWebhookMiddleware(cacheTTL time.Duration, kubeConfigFile string) (Middleware, error) {
auth, err := NewWebhookAuthenticator(cacheTTL, kubeConfigFile)
if err != nil {
return nil, err
}
return ToMiddleware(auth), nil
}
type webhookAuth struct {
auth authenticator.Token
}
func (w *webhookAuth) Authenticate(req *http.Request) (user.Info, bool, error) {
token := req.Header.Get("Authorization")
if strings.HasPrefix(token, "Bearer ") {
token = strings.TrimPrefix(token, "Bearer ")
} else {
token = ""
}
if token == "" {
cookie, err := req.Cookie("R_SESS")
if err != nil && err != http.ErrNoCookie {
return nil, false, err
} else if err != http.ErrNoCookie && len(cookie.Value) > 0 {
token = "cookie://" + cookie.Value
}
}
if token == "" {
return nil, false, nil
}
resp, ok, err := w.auth.AuthenticateToken(req.Context(), token)
if resp == nil {
return nil, ok, err
}
return resp.User, ok, err
}
func ToMiddleware(auth Authenticator) func(rw http.ResponseWriter, req *http.Request, next http.Handler) {
return func(rw http.ResponseWriter, req *http.Request, next http.Handler) {
info, ok, err := auth.Authenticate(req)
if err != nil {
rw.WriteHeader(http.StatusServiceUnavailable)
rw.Write([]byte(err.Error()))
return
}
if !ok {
rw.WriteHeader(http.StatusUnauthorized)
return
}
ctx := request.WithUser(req.Context(), info)
req = req.WithContext(ctx)
next.ServeHTTP(rw, req)
}
}