K8s: remove standalone authenticator in favor of providing one through the factory (#85901)

This commit is contained in:
Charandas 2024-04-11 12:30:23 -07:00 committed by GitHub
parent 1031c2f1d2
commit a39fe593c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 12 additions and 186 deletions

View File

@ -180,7 +180,7 @@ If it's not perfectly clear that it's an actual bug, quickly try to reproduce it
1. Add a comment describing detailed steps for how to reproduce it, if applicable.
2. Label the issue `type/bug` and at least one `area/*` or `datasource/*` label.
3. If you know that maintainers wont be able to put any resources into it for some time then label the issue with `help wanted` and optionally `beginner friendly` together with pointers on which code to update to fix the bug. This should signal to the community that we would appreciate any help we can get to resolve this.
3. If you know that maintainers won't be able to put any resources into it for some time then label the issue with `help wanted` and optionally `beginner friendly` together with pointers on which code to update to fix the bug. This should signal to the community that we would appreciate any help we can get to resolve this.
4. Move on to [prioritizing the issue](#4-prioritization-of-issues).
**It can't be reproduced:**

View File

@ -1,71 +0,0 @@
package auth
import (
"net/http"
"strings"
"github.com/grafana/authlib/authn"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
)
const (
headerKeyAccessToken = "X-Access-Token"
headerKeyGrafanaID = "X-Grafana-Id"
extraKeyAccessToken = "access-token"
extraKeyGrafanaID = "id-token"
extraKeyGLSA = "glsa"
)
func NewAccessTokenAuthenticator(config *authn.IDVerifierConfig) authenticator.RequestFunc {
verifier := authn.NewVerifier[CustomClaims](authn.IDVerifierConfig{
SigningKeysURL: config.SigningKeysURL,
AllowedAudiences: config.AllowedAudiences,
})
return getAccessTokenAuthenticatorFunc(&TokenValidator{verifier})
}
func getAccessTokenAuthenticatorFunc(validator *TokenValidator) authenticator.RequestFunc {
return func(req *http.Request) (*authenticator.Response, bool, error) {
accessToken := req.Header.Get(headerKeyAccessToken)
if accessToken == "" {
return nil, false, nil
}
// While the authn token system is in development, we can temporarily use
// service account tokens. Note this does not grant any real permissions/verification,
// it simply allows forwarding the token to the next request
if strings.HasPrefix(accessToken, "glsa_") {
return &authenticator.Response{
Audiences: authenticator.Audiences([]string{}),
User: &user.DefaultInfo{
Name: "glsa-forwarding-request",
UID: "",
Groups: []string{},
Extra: map[string][]string{
extraKeyGLSA: {accessToken},
},
},
}, true, nil
}
result, err := validator.Validate(req.Context(), accessToken)
if err != nil {
return nil, false, err
}
return &authenticator.Response{
Audiences: authenticator.Audiences(result.Claims.Audience),
User: &user.DefaultInfo{
Name: result.Subject,
UID: "",
Groups: []string{},
Extra: map[string][]string{
extraKeyAccessToken: {accessToken},
extraKeyGrafanaID: {req.Header.Get("X-Grafana-Id")}, // this may exist if starting with a user
},
},
}, true, nil
}
}

View File

@ -1,57 +0,0 @@
package auth
import (
"context"
"fmt"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/request/union"
"k8s.io/apiserver/pkg/endpoints/request"
)
func AppendToAuthenticators(newAuthenticator authenticator.RequestFunc, authRequestHandlers ...authenticator.Request) authenticator.Request {
handlers := append([]authenticator.Request{newAuthenticator}, authRequestHandlers...)
return union.New(handlers...)
}
// Get tokens that can be forwarded to the next service
// In the future this will need to create new tokens with a new audience
func GetIDForwardingAuthHeaders(ctx context.Context) (map[string]string, error) {
user, ok := request.UserFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing user")
}
getter := func(key string) string {
vals, ok := user.GetExtra()[key]
if ok && len(vals) == 1 {
return vals[0]
}
return ""
}
token := getter(extraKeyGLSA)
if token != "" {
// Service account tokens get forwarded as auth tokens
// this lets us keep testing the workflows while the ID token system is in dev
return map[string]string{
"Authorization": "Bearer " + token,
}, nil
}
accessToken := getter(extraKeyAccessToken)
if accessToken == "" {
return nil, fmt.Errorf("missing access token in user info")
}
idToken := getter(extraKeyGrafanaID)
if idToken != "" {
return map[string]string{
headerKeyAccessToken: accessToken,
headerKeyGrafanaID: idToken,
}, nil
}
return map[string]string{
headerKeyAccessToken: accessToken,
}, nil
}

View File

@ -1,24 +0,0 @@
package auth
import (
"context"
"github.com/grafana/authlib/authn"
)
type CustomClaims struct {
// Nothing yet
}
type TokenValidator struct {
verifier authn.Verifier[CustomClaims]
}
func (v *TokenValidator) Validate(ctx context.Context, token string) (*authn.Claims[CustomClaims], error) {
customClaims, err := v.verifier.Verify(ctx, token)
if err != nil {
return nil, err
}
return customClaims, nil
}

View File

@ -14,7 +14,6 @@ import (
netutils "k8s.io/utils/net"
"github.com/grafana/grafana/pkg/apiserver/builder"
"github.com/grafana/grafana/pkg/cmd/grafana/apiserver/auth"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
grafanaAPIServer "github.com/grafana/grafana/pkg/services/apiserver"
@ -101,12 +100,11 @@ func (o *APIServerOptions) Config() (*genericapiserver.RecommendedConfig, error)
return nil, fmt.Errorf("failed to apply options to server config: %w", err)
}
// When the ID signing key exists, configure access-token support
if len(o.Options.AuthnOptions.IDVerifierConfig.SigningKeysURL) > 0 {
serverConfig.Authentication.Authenticator = auth.AppendToAuthenticators(
auth.NewAccessTokenAuthenticator(o.Options.AuthnOptions.IDVerifierConfig),
serverConfig.Authentication.Authenticator,
)
if factoryOptions := o.factory.GetOptions(); factoryOptions != nil {
err := factoryOptions.ApplyTo(serverConfig)
if err != nil {
return nil, fmt.Errorf("factory's applyTo func failed: %s", err.Error())
}
}
serverConfig.DisabledPostStartHooks = serverConfig.DisabledPostStartHooks.Insert("generic-apiserver-start-informers")

View File

@ -12,6 +12,7 @@ import (
type OptionsProvider interface {
AddFlags(fs *pflag.FlagSet)
ApplyTo(config *genericapiserver.RecommendedConfig) error
ValidateOptions() []error
}

View File

@ -8,6 +8,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
genericapiserver "k8s.io/apiserver/pkg/server"
"github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
"github.com/grafana/grafana/pkg/apiserver/builder"
@ -62,6 +63,10 @@ func (p *DummyAPIFactory) GetEnabled(runtime []RuntimeConfig) ([]schema.GroupVer
return gv, nil
}
func (p *DummyAPIFactory) ApplyTo(config *genericapiserver.RecommendedConfig) error {
return nil
}
func (p *DummyAPIFactory) MakeAPIServer(tracer tracing.Tracer, gv schema.GroupVersion) (builder.APIGroupBuilder, error) {
if gv.Version != "v0alpha1" {
return nil, fmt.Errorf("only alpha supported now")

View File

@ -1,9 +0,0 @@
package options
import "github.com/grafana/authlib/authn"
func NewAuthnOptions() *AuthnOptions {
return &AuthnOptions{
IDVerifierConfig: &authn.IDVerifierConfig{},
}
}

View File

@ -7,8 +7,6 @@ import (
"k8s.io/apimachinery/pkg/runtime"
genericapiserver "k8s.io/apiserver/pkg/server"
genericoptions "k8s.io/apiserver/pkg/server/options"
"github.com/grafana/authlib/authn"
)
type Options struct {
@ -17,7 +15,6 @@ type Options struct {
RecommendedOptions *genericoptions.RecommendedOptions
TracingOptions *TracingOptions
MetricsOptions *MetricsOptions
AuthnOptions *AuthnOptions
}
func New(logger log.Logger, codec runtime.Codec) *Options {
@ -27,7 +24,6 @@ func New(logger log.Logger, codec runtime.Codec) *Options {
RecommendedOptions: options.NewRecommendedOptions(codec),
TracingOptions: NewTracingOptions(logger),
MetricsOptions: NewMetrcicsOptions(logger),
AuthnOptions: NewAuthnOptions(),
}
}
@ -37,7 +33,6 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
o.RecommendedOptions.AddFlags(fs)
o.TracingOptions.AddFlags(fs)
o.MetricsOptions.AddFlags(fs)
o.AuthnOptions.AddFlags(fs)
}
func (o *Options) Validate() []error {
@ -162,15 +157,3 @@ func (o *Options) ApplyTo(serverConfig *genericapiserver.RecommendedConfig) erro
return nil
}
type AuthnOptions struct {
IDVerifierConfig *authn.IDVerifierConfig
}
func (authOpts *AuthnOptions) AddFlags(fs *pflag.FlagSet) {
prefix := "grafana.authn"
fs.StringVar(&authOpts.IDVerifierConfig.SigningKeysURL, prefix+".signing-keys-url", "", "URL to jwks endpoint")
audience := fs.StringSlice(prefix+".allowed-audiences", []string{}, "Specifies a comma-separated list of allowed audiences.")
authOpts.IDVerifierConfig.AllowedAudiences = *audience
}