1
0
mirror of https://github.com/rancher/steve.git synced 2025-04-27 02:51:10 +00:00
steve/pkg/auth/filter.go
Michael Bolot b73cc57b20 Adding validate phase to the CI
Adds a validate phase to the CI which runs a linter. Also fixes
linter issues discovered during the initial run
2022-10-14 15:21:17 -05:00

208 lines
4.8 KiB
Go

package auth
import (
"io/ioutil"
"net/http"
"strings"
"time"
"k8s.io/apimachinery/pkg/util/wait"
"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/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/transport"
)
var (
// Default value taken from DefaultAuthWebhookRetryBackoff
WebhookBackoff = wait.Backoff{
Duration: 500 * time.Millisecond,
Factor: 1.5,
Jitter: 0.2,
Steps: 5,
}
)
var ExistingContext = ToMiddleware(AuthenticatorFunc(func(req *http.Request) (user.Info, bool, error) {
user, ok := request.UserFrom(req.Context())
return user, ok, nil
}))
const CattleAuthFailed = "X-API-Cattle-Auth-Failed"
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(next http.Handler) http.Handler
func (m Middleware) Chain(middleware Middleware) Middleware {
return func(next http.Handler) http.Handler {
return m(middleware(next))
}
}
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, kubeConfig *rest.Config) (Authenticator, error) {
wh, err := webhook.New(kubeConfig, "v1", nil, WebhookBackoff)
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, kubeConfig *rest.Config) (Middleware, error) {
auth, err := NewWebhookAuthenticator(cacheTTL, kubeConfig)
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) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
info, ok, err := auth.Authenticate(req)
ctx := req.Context()
if err != nil {
info = &user.DefaultInfo{
Name: "system:cattle:error",
UID: "system:cattle:error",
Groups: []string{
"system:unauthenticated",
"system:cattle:error",
},
}
ctx = request.WithValue(ctx, CattleAuthFailed, "true")
} else if !ok {
info = &user.DefaultInfo{
Name: "system:unauthenticated",
UID: "system:unauthenticated",
Groups: []string{
"system:unauthenticated",
},
}
}
ctx = request.WithUser(ctx, info)
req = req.WithContext(ctx)
next.ServeHTTP(rw, req)
})
}
}
func AlwaysAdmin(req *http.Request) (user.Info, bool, error) {
return &user.DefaultInfo{
Name: "admin",
UID: "admin",
Groups: []string{
"system:masters",
"system:authenticated",
},
}, true, nil
}
func Impersonation(req *http.Request) (user.Info, bool, error) {
userName := req.Header.Get(transport.ImpersonateUserHeader)
if userName == "" {
return nil, false, nil
}
result := user.DefaultInfo{
Name: userName,
Groups: req.Header[transport.ImpersonateGroupHeader],
Extra: map[string][]string{},
}
for k, v := range req.Header {
if strings.HasPrefix(k, transport.ImpersonateUserExtraHeaderPrefix) {
result.Extra[k[len(transport.ImpersonateUserExtraHeaderPrefix):]] = v
}
}
return &result, true, nil
}