Extend the REST service to keep a CertIssuer.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
Matt Moyer 2020-07-27 07:55:33 -05:00
parent 07a71236aa
commit d8c7a25487
3 changed files with 31 additions and 38 deletions

View File

@ -14,33 +14,27 @@ import (
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log"
"time" "time"
"k8s.io/apiserver/pkg/server/dynamiccertificates" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/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" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/spf13/cobra"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
genericoptions "k8s.io/apiserver/pkg/server/options" genericoptions "k8s.io/apiserver/pkg/server/options"
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook" "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
restclient "k8s.io/client-go/rest" 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" aggregationv1client "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
placeholderv1alpha1 "github.com/suzerain-io/placeholder-name-api/pkg/apis/placeholder/v1alpha1" 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/apiserver"
"github.com/suzerain-io/placeholder-name/pkg/config" "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 credential from somewhere to an internal credential to be used for
authenticating to the Kubernetes API.`, authenticating to the Kubernetes API.`,
RunE: func(cmd *cobra.Command, args []string) error { 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), // Load the Kubernetes client configuration (kubeconfig),
kubeConfig, err := restclient.InClusterConfig() kubeConfig, err := restclient.InClusterConfig()
if err != nil { if err != nil {
@ -169,9 +151,15 @@ func (a *App) run(
return fmt.Errorf("could not load config: %w", err) 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) webhookTokenAuthenticator, err := config.NewWebhook(cfg.WebhookConfig)
if err != nil { 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) podinfo, err := downward.Load(a.downwardAPIPath)
@ -181,19 +169,14 @@ func (a *App) run(
// TODO use the postStart hook to generate certs? // 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 { if err != nil {
return fmt.Errorf("could not initialize CA: %w", err) 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" const serviceName = "placeholder-name-api"
cert, err := ca.Issue( cert, err := apiCA.Issue(
pkix.Name{CommonName: serviceName + "." + podinfo.Namespace + ".svc"}, pkix.Name{CommonName: serviceName + "." + podinfo.Namespace + ".svc"},
[]string{}, []string{},
24*365*time.Hour, 24*365*time.Hour,
@ -224,7 +207,7 @@ func (a *App) run(
Spec: apiregistrationv1.APIServiceSpec{ Spec: apiregistrationv1.APIServiceSpec{
Group: placeholderv1alpha1.GroupName, Group: placeholderv1alpha1.GroupName,
Version: placeholderv1alpha1.SchemeGroupVersion.Version, 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 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 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) 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 { if err != nil {
return err return err
} }
@ -252,7 +235,7 @@ func (a *App) run(
return server.GenericAPIServer.PrepareRun().Run(ctx.Done()) 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) provider, err := createStaticCertKeyProvider(cert)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not create static cert key provider: %w", err) 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, GenericConfig: serverConfig,
ExtraConfig: apiserver.ExtraConfig{ ExtraConfig: apiserver.ExtraConfig{
Webhook: webhookTokenAuthenticator, Webhook: webhookTokenAuthenticator,
Issuer: ca,
}, },
} }
return apiServerConfig, nil return apiServerConfig, nil

View File

@ -57,6 +57,7 @@ type Config struct {
type ExtraConfig struct { type ExtraConfig struct {
Webhook authenticator.Token Webhook authenticator.Token
Issuer loginrequest.CertIssuer
} }
type PlaceHolderServer struct { type PlaceHolderServer struct {
@ -108,7 +109,7 @@ func (c completedConfig) New() (*PlaceHolderServer, error) {
NegotiatedSerializer: Codecs, NegotiatedSerializer: Codecs,
} }
loginRequestStorage := loginrequest.NewREST(c.ExtraConfig.Webhook) loginRequestStorage := loginrequest.NewREST(c.ExtraConfig.Webhook, c.ExtraConfig.Issuer)
v1alpha1Storage, ok := apiGroupInfo.VersionedResourcesStorageMap[gvr.Version] v1alpha1Storage, ok := apiGroupInfo.VersionedResourcesStorageMap[gvr.Version]
if !ok { if !ok {

View File

@ -8,7 +8,9 @@ package loginrequest
import ( import (
"context" "context"
"crypto/x509/pkix"
"fmt" "fmt"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -28,14 +30,20 @@ var (
_ rest.Storage = &REST{} _ 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{ return &REST{
webhook: webhook, webhook: webhook,
issuer: issuer,
} }
} }
type REST struct { type REST struct {
webhook authenticator.Token webhook authenticator.Token
issuer CertIssuer
} }
func (r *REST) New() runtime.Object { func (r *REST) New() runtime.Object {