0b300cbe42
To make an impersonation request, first make a TokenCredentialRequest to get a certificate. That cert will either be issued by the Kube API server's CA or by a new CA specific to the impersonator. Either way, you can then make a request to the impersonator and present that client cert for auth and the impersonator will accept it and make the impesonation call on your behalf. The impersonator http handler now borrows some Kube library code to handle request processing. This will allow us to more closely mimic the behavior of a real API server, e.g. the client cert auth will work exactly like the real API server. Signed-off-by: Monis Khan <mok@vmware.com>
125 lines
4.0 KiB
Go
125 lines
4.0 KiB
Go
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package apiserver
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/apimachinery/pkg/util/errors"
|
|
"k8s.io/apiserver/pkg/registry/rest"
|
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
|
"k8s.io/client-go/pkg/version"
|
|
|
|
"go.pinniped.dev/internal/issuer"
|
|
"go.pinniped.dev/internal/plog"
|
|
"go.pinniped.dev/internal/registry/credentialrequest"
|
|
"go.pinniped.dev/internal/registry/whoamirequest"
|
|
)
|
|
|
|
type Config struct {
|
|
GenericConfig *genericapiserver.RecommendedConfig
|
|
ExtraConfig ExtraConfig
|
|
}
|
|
|
|
type ExtraConfig struct {
|
|
Authenticator credentialrequest.TokenCredentialRequestAuthenticator
|
|
Issuer issuer.CertIssuer
|
|
StartControllersPostStartHook func(ctx context.Context)
|
|
Scheme *runtime.Scheme
|
|
NegotiatedSerializer runtime.NegotiatedSerializer
|
|
LoginConciergeGroupVersion schema.GroupVersion
|
|
IdentityConciergeGroupVersion schema.GroupVersion
|
|
}
|
|
|
|
type PinnipedServer struct {
|
|
GenericAPIServer *genericapiserver.GenericAPIServer
|
|
}
|
|
|
|
type completedConfig struct {
|
|
GenericConfig genericapiserver.CompletedConfig
|
|
ExtraConfig *ExtraConfig
|
|
}
|
|
|
|
type CompletedConfig struct {
|
|
// Embed a private pointer that cannot be instantiated outside of this package.
|
|
*completedConfig
|
|
}
|
|
|
|
// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
|
|
func (c *Config) Complete() CompletedConfig {
|
|
completedCfg := completedConfig{
|
|
c.GenericConfig.Complete(),
|
|
&c.ExtraConfig,
|
|
}
|
|
|
|
versionInfo := version.Get()
|
|
completedCfg.GenericConfig.Version = &versionInfo
|
|
|
|
return CompletedConfig{completedConfig: &completedCfg}
|
|
}
|
|
|
|
// New returns a new instance of AdmissionServer from the given config.
|
|
func (c completedConfig) New() (*PinnipedServer, error) {
|
|
genericServer, err := c.GenericConfig.New("pinniped-concierge", genericapiserver.NewEmptyDelegate()) // completion is done in Complete, no need for a second time
|
|
if err != nil {
|
|
return nil, fmt.Errorf("completion error: %w", err)
|
|
}
|
|
|
|
s := &PinnipedServer{
|
|
GenericAPIServer: genericServer,
|
|
}
|
|
|
|
var errs []error //nolint: prealloc
|
|
for _, f := range []func() (schema.GroupVersionResource, rest.Storage){
|
|
func() (schema.GroupVersionResource, rest.Storage) {
|
|
tokenCredReqGVR := c.ExtraConfig.LoginConciergeGroupVersion.WithResource("tokencredentialrequests")
|
|
tokenCredStorage := credentialrequest.NewREST(c.ExtraConfig.Authenticator, c.ExtraConfig.Issuer, tokenCredReqGVR.GroupResource())
|
|
return tokenCredReqGVR, tokenCredStorage
|
|
},
|
|
func() (schema.GroupVersionResource, rest.Storage) {
|
|
whoAmIReqGVR := c.ExtraConfig.IdentityConciergeGroupVersion.WithResource("whoamirequests")
|
|
whoAmIStorage := whoamirequest.NewREST(whoAmIReqGVR.GroupResource())
|
|
return whoAmIReqGVR, whoAmIStorage
|
|
},
|
|
} {
|
|
gvr, storage := f()
|
|
errs = append(errs,
|
|
s.GenericAPIServer.InstallAPIGroup(
|
|
&genericapiserver.APIGroupInfo{
|
|
PrioritizedVersions: []schema.GroupVersion{gvr.GroupVersion()},
|
|
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{gvr.Version: {gvr.Resource: storage}},
|
|
OptionsExternalVersion: &schema.GroupVersion{Version: "v1"},
|
|
Scheme: c.ExtraConfig.Scheme,
|
|
ParameterCodec: metav1.ParameterCodec,
|
|
NegotiatedSerializer: c.ExtraConfig.NegotiatedSerializer,
|
|
},
|
|
),
|
|
)
|
|
}
|
|
if err := errors.NewAggregate(errs); err != nil {
|
|
return nil, fmt.Errorf("could not install API groups: %w", err)
|
|
}
|
|
|
|
s.GenericAPIServer.AddPostStartHookOrDie("start-controllers",
|
|
func(postStartContext genericapiserver.PostStartHookContext) error {
|
|
plog.Debug("start-controllers post start hook starting")
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go func() {
|
|
<-postStartContext.StopCh
|
|
cancel()
|
|
}()
|
|
c.ExtraConfig.StartControllersPostStartHook(ctx)
|
|
|
|
return nil
|
|
},
|
|
)
|
|
|
|
return s, nil
|
|
}
|