From d8c7a25487fb3b17111c1da60ff03c27e3c45fe7 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Mon, 27 Jul 2020 07:55:33 -0500 Subject: [PATCH] Extend the REST service to keep a CertIssuer. Signed-off-by: Matt Moyer --- cmd/placeholder-name/app/app.go | 56 +++++++++++-------------------- pkg/apiserver/apiserver.go | 3 +- pkg/registry/loginrequest/rest.go | 10 +++++- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/cmd/placeholder-name/app/app.go b/cmd/placeholder-name/app/app.go index 7b9d726e..99e977da 100644 --- a/cmd/placeholder-name/app/app.go +++ b/cmd/placeholder-name/app/app.go @@ -14,33 +14,27 @@ import ( "encoding/pem" "fmt" "io" - "io/ioutil" - "log" "time" - "k8s.io/apiserver/pkg/server/dynamiccertificates" - + "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" - - "github.com/suzerain-io/placeholder-name/internal/autoregistration" - "github.com/suzerain-io/placeholder-name/internal/certauthority" - "github.com/suzerain-io/placeholder-name/internal/downward" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/util/intstr" genericapiserver "k8s.io/apiserver/pkg/server" + "k8s.io/apiserver/pkg/server/dynamiccertificates" genericoptions "k8s.io/apiserver/pkg/server/options" "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook" "k8s.io/client-go/kubernetes" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" restclient "k8s.io/client-go/rest" + apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" aggregationv1client "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" placeholderv1alpha1 "github.com/suzerain-io/placeholder-name-api/pkg/apis/placeholder/v1alpha1" + "github.com/suzerain-io/placeholder-name/internal/autoregistration" + "github.com/suzerain-io/placeholder-name/internal/certauthority" + "github.com/suzerain-io/placeholder-name/internal/downward" "github.com/suzerain-io/placeholder-name/pkg/apiserver" "github.com/suzerain-io/placeholder-name/pkg/config" ) @@ -79,18 +73,6 @@ func New(ctx context.Context, args []string, stdout, stderr io.Writer) *App { credential from somewhere to an internal credential to be used for authenticating to the Kubernetes API.`, RunE: func(cmd *cobra.Command, args []string) error { - clusterSigningCertificatePEM, err := ioutil.ReadFile(a.clusterSigningCertFilePath) - if err != nil { - return fmt.Errorf("could not read cluster signing certificate: %w", err) - } - clusterSigningPrivateKeyPEM, err := ioutil.ReadFile(a.clusterSigningKeyFilePath) - if err != nil { - return fmt.Errorf("could not read cluster signing private key: %w", err) - } - // TODO: use these value for something useful - _ = clusterSigningCertificatePEM - _ = clusterSigningPrivateKeyPEM - // Load the Kubernetes client configuration (kubeconfig), kubeConfig, err := restclient.InClusterConfig() if err != nil { @@ -169,9 +151,15 @@ func (a *App) run( return fmt.Errorf("could not load config: %w", err) } + // Load the Kubernetes cluster signing CA. + clientCA, err := certauthority.Load(a.clusterSigningCertFilePath, a.clusterSigningKeyFilePath) + if err != nil { + return fmt.Errorf("could not load cluster signing CA: %w", err) + } + webhookTokenAuthenticator, err := config.NewWebhook(cfg.WebhookConfig) if err != nil { - return fmt.Errorf("could create webhook client: %w", err) + return fmt.Errorf("could not create webhook client: %w", err) } podinfo, err := downward.Load(a.downwardAPIPath) @@ -181,19 +169,14 @@ func (a *App) run( // TODO use the postStart hook to generate certs? - ca, err := certauthority.New(pkix.Name{CommonName: "Placeholder CA"}) + apiCA, err := certauthority.New(pkix.Name{CommonName: "Placeholder CA"}) if err != nil { return fmt.Errorf("could not initialize CA: %w", err) } - caBundle, err := ca.Bundle() - if err != nil { - return fmt.Errorf("could not read CA bundle: %w", err) - } - log.Printf("initialized CA bundle:\n%s", string(caBundle)) const serviceName = "placeholder-name-api" - cert, err := ca.Issue( + cert, err := apiCA.Issue( pkix.Name{CommonName: serviceName + "." + podinfo.Namespace + ".svc"}, []string{}, 24*365*time.Hour, @@ -224,7 +207,7 @@ func (a *App) run( Spec: apiregistrationv1.APIServiceSpec{ Group: placeholderv1alpha1.GroupName, Version: placeholderv1alpha1.SchemeGroupVersion.Version, - CABundle: caBundle, + CABundle: apiCA.Bundle(), GroupPriorityMinimum: 2500, // TODO what is the right value? https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#apiservicespec-v1beta1-apiregistration-k8s-io VersionPriority: 10, // TODO what is the right value? https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#apiservicespec-v1beta1-apiregistration-k8s-io }, @@ -239,7 +222,7 @@ func (a *App) run( return fmt.Errorf("could not register API service: %w", err) } - apiServerConfig, err := a.ConfigServer(cert, webhookTokenAuthenticator) + apiServerConfig, err := a.ConfigServer(cert, webhookTokenAuthenticator, clientCA) if err != nil { return err } @@ -252,7 +235,7 @@ func (a *App) run( return server.GenericAPIServer.PrepareRun().Run(ctx.Done()) } -func (a *App) ConfigServer(cert *tls.Certificate, webhookTokenAuthenticator *webhook.WebhookTokenAuthenticator) (*apiserver.Config, error) { +func (a *App) ConfigServer(cert *tls.Certificate, webhookTokenAuthenticator *webhook.WebhookTokenAuthenticator, ca *certauthority.CA) (*apiserver.Config, error) { provider, err := createStaticCertKeyProvider(cert) if err != nil { return nil, fmt.Errorf("could not create static cert key provider: %w", err) @@ -268,6 +251,7 @@ func (a *App) ConfigServer(cert *tls.Certificate, webhookTokenAuthenticator *web GenericConfig: serverConfig, ExtraConfig: apiserver.ExtraConfig{ Webhook: webhookTokenAuthenticator, + Issuer: ca, }, } return apiServerConfig, nil diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 315389ff..1feb1212 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -57,6 +57,7 @@ type Config struct { type ExtraConfig struct { Webhook authenticator.Token + Issuer loginrequest.CertIssuer } type PlaceHolderServer struct { @@ -108,7 +109,7 @@ func (c completedConfig) New() (*PlaceHolderServer, error) { NegotiatedSerializer: Codecs, } - loginRequestStorage := loginrequest.NewREST(c.ExtraConfig.Webhook) + loginRequestStorage := loginrequest.NewREST(c.ExtraConfig.Webhook, c.ExtraConfig.Issuer) v1alpha1Storage, ok := apiGroupInfo.VersionedResourcesStorageMap[gvr.Version] if !ok { diff --git a/pkg/registry/loginrequest/rest.go b/pkg/registry/loginrequest/rest.go index 692b8da1..bc03dd37 100644 --- a/pkg/registry/loginrequest/rest.go +++ b/pkg/registry/loginrequest/rest.go @@ -8,7 +8,9 @@ package loginrequest import ( "context" + "crypto/x509/pkix" "fmt" + "time" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,14 +30,20 @@ var ( _ rest.Storage = &REST{} ) -func NewREST(webhook authenticator.Token) *REST { +type CertIssuer interface { + IssuePEM(subject pkix.Name, dnsNames []string, ttl time.Duration) ([]byte, []byte, error) +} + +func NewREST(webhook authenticator.Token, issuer CertIssuer) *REST { return &REST{ webhook: webhook, + issuer: issuer, } } type REST struct { webhook authenticator.Token + issuer CertIssuer } func (r *REST) New() runtime.Object {