From 68041a4f7378ac763d26f88d5ac13c87d2d54b0d Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 10 Jul 2021 22:16:47 -0700 Subject: [PATCH] Issue certs using CA KeypairID in NodeupConfig --- nodeup/pkg/model/context.go | 9 ++-- nodeup/pkg/model/kops_controller.go | 1 + nodeup/pkg/model/kube_apiserver.go | 32 +++++++------ .../pkg/model/kube_apiserver_healthcheck.go | 7 +-- nodeup/pkg/model/kubelet.go | 8 ++-- nodeup/pkg/model/networking/cilium.go | 9 ++-- nodeup/pkg/model/secrets.go | 1 + upup/pkg/fi/cloudup/apply_cluster.go | 9 ++-- upup/pkg/fi/nodeup/nodetasks/issue_cert.go | 47 ++++++++++++++++++- 9 files changed, 89 insertions(+), 34 deletions(-) diff --git a/nodeup/pkg/model/context.go b/nodeup/pkg/model/context.go index c0c8df9084..6f74ab5aaf 100644 --- a/nodeup/pkg/model/context.go +++ b/nodeup/pkg/model/context.go @@ -214,10 +214,11 @@ func (c *NodeupModelContext) KubeletKubeConfig() string { // BuildIssuedKubeconfig generates a kubeconfig with a locally issued client certificate. func (c *NodeupModelContext) BuildIssuedKubeconfig(name string, subject nodetasks.PKIXName, ctx *fi.ModelBuilderContext) *fi.TaskDependentResource { issueCert := &nodetasks.IssueCert{ - Name: name, - Signer: fi.CertificateIDCA, - Type: "client", - Subject: subject, + Name: name, + Signer: fi.CertificateIDCA, + KeypairID: c.NodeupConfig.KeypairIDs[fi.CertificateIDCA], + Type: "client", + Subject: subject, } ctx.AddTask(issueCert) certResource, keyResource, caResource := issueCert.GetResources() diff --git a/nodeup/pkg/model/kops_controller.go b/nodeup/pkg/model/kops_controller.go index 8773e0bfce..618bcd01fa 100644 --- a/nodeup/pkg/model/kops_controller.go +++ b/nodeup/pkg/model/kops_controller.go @@ -56,6 +56,7 @@ func (b *KopsControllerBuilder) Build(c *fi.ModelBuilderContext) error { issueCert := &nodetasks.IssueCert{ Name: "kops-controller", Signer: fi.CertificateIDCA, + KeypairID: b.NodeupConfig.KeypairIDs[fi.CertificateIDCA], Type: "server", Subject: nodetasks.PKIXName{CommonName: "kops-controller"}, AlternateNames: []string{"kops-controller.internal." + b.Cluster.ObjectMeta.Name}, diff --git a/nodeup/pkg/model/kube_apiserver.go b/nodeup/pkg/model/kube_apiserver.go index dd5c024a46..33a57cd84f 100644 --- a/nodeup/pkg/model/kube_apiserver.go +++ b/nodeup/pkg/model/kube_apiserver.go @@ -116,9 +116,10 @@ func (b *KubeAPIServerBuilder) Build(c *fi.ModelBuilderContext) error { kubeAPIServer.EtcdCAFile = filepath.Join(pathSrvKAPI, "etcd-ca.crt") issueCert := &nodetasks.IssueCert{ - Name: "etcd-client", - Signer: "etcd-clients-ca", - Type: "client", + Name: "etcd-client", + Signer: "etcd-clients-ca", + KeypairID: b.NodeupConfig.KeypairIDs["etcd-clients-ca"], + Type: "client", Subject: nodetasks.PKIXName{ CommonName: "kube-apiserver", }, @@ -145,9 +146,10 @@ func (b *KubeAPIServerBuilder) Build(c *fi.ModelBuilderContext) error { kubeAPIServer.RequestheaderClientCAFile = filepath.Join(pathSrvKAPI, "apiserver-aggregator-ca.crt") issueCert := &nodetasks.IssueCert{ - Name: "apiserver-aggregator", - Signer: "apiserver-aggregator-ca", - Type: "client", + Name: "apiserver-aggregator", + Signer: "apiserver-aggregator-ca", + KeypairID: b.NodeupConfig.KeypairIDs["apiserver-aggregator-ca"], + Type: "client", // Must match RequestheaderAllowedNames Subject: nodetasks.PKIXName{CommonName: "aggregator"}, } @@ -186,10 +188,11 @@ func (b *KubeAPIServerBuilder) Build(c *fi.ModelBuilderContext) error { } issueCert := &nodetasks.IssueCert{ - Name: "kubelet-api", - Signer: fi.CertificateIDCA, - Type: "client", - Subject: nodetasks.PKIXName{CommonName: "kubelet-api"}, + Name: "kubelet-api", + Signer: fi.CertificateIDCA, + KeypairID: b.NodeupConfig.KeypairIDs[fi.CertificateIDCA], + Type: "client", + Subject: nodetasks.PKIXName{CommonName: "kubelet-api"}, } c.AddTask(issueCert) err := issueCert.AddFileTasks(c, b.PathSrvKubernetes(), "kubelet-api", "", nil) @@ -308,10 +311,11 @@ func (b *KubeAPIServerBuilder) writeAuthenticationConfig(c *fi.ModelBuilderConte { issueCert := &nodetasks.IssueCert{ - Name: id, - Signer: fi.CertificateIDCA, - Type: "server", - Subject: nodetasks.PKIXName{CommonName: id}, + Name: id, + Signer: fi.CertificateIDCA, + KeypairID: b.NodeupConfig.KeypairIDs[fi.CertificateIDCA], + Type: "server", + Subject: nodetasks.PKIXName{CommonName: id}, AlternateNames: []string{ "localhost", "127.0.0.1", diff --git a/nodeup/pkg/model/kube_apiserver_healthcheck.go b/nodeup/pkg/model/kube_apiserver_healthcheck.go index 5091a2dcbf..417d3fb13b 100644 --- a/nodeup/pkg/model/kube_apiserver_healthcheck.go +++ b/nodeup/pkg/model/kube_apiserver_healthcheck.go @@ -82,9 +82,10 @@ func (b *KubeAPIServerBuilder) addHealthcheckSidecarTasks(c *fi.ModelBuilderCont } issueCert := &nodetasks.IssueCert{ - Name: id, - Signer: fi.CertificateIDCA, - Type: "client", + Name: id, + Signer: fi.CertificateIDCA, + KeypairID: b.NodeupConfig.KeypairIDs[fi.CertificateIDCA], + Type: "client", Subject: nodetasks.PKIXName{ CommonName: id, }, diff --git a/nodeup/pkg/model/kubelet.go b/nodeup/pkg/model/kubelet.go index 9012a98293..6b1d37675a 100644 --- a/nodeup/pkg/model/kubelet.go +++ b/nodeup/pkg/model/kubelet.go @@ -552,7 +552,6 @@ func (b *KubeletBuilder) buildKubeletServingCertificate(c *fi.ModelBuilderContex if b.UseKopsControllerForNodeBootstrap() { name := "kubelet-server" dir := b.PathSrvKubernetes() - signer := fi.CertificateIDCA nodeName, err := b.NodeName() if err != nil { @@ -580,9 +579,10 @@ func (b *KubeletBuilder) buildKubeletServingCertificate(c *fi.ModelBuilderContex } else { issueCert := &nodetasks.IssueCert{ - Name: name, - Signer: signer, - Type: "server", + Name: name, + Signer: fi.CertificateIDCA, + KeypairID: b.NodeupConfig.KeypairIDs[fi.CertificateIDCA], + Type: "server", Subject: nodetasks.PKIXName{ CommonName: nodeName, }, diff --git a/nodeup/pkg/model/networking/cilium.go b/nodeup/pkg/model/networking/cilium.go index b0161c1b2b..47ec1e3e02 100644 --- a/nodeup/pkg/model/networking/cilium.go +++ b/nodeup/pkg/model/networking/cilium.go @@ -109,11 +109,12 @@ func (b *CiliumBuilder) buildCiliumEtcdSecrets(c *fi.ModelBuilderContext) error Type: nodetasks.FileType_File, Mode: fi.String("0600"), }) - if b.IsMaster { + if b.HasAPIServer { issueCert := &nodetasks.IssueCert{ - Name: name, - Signer: signer, - Type: "client", + Name: name, + Signer: signer, + KeypairID: b.NodeupConfig.KeypairIDs[signer], + Type: "client", Subject: nodetasks.PKIXName{ CommonName: "cilium", }, diff --git a/nodeup/pkg/model/secrets.go b/nodeup/pkg/model/secrets.go index 6172c10ec2..cabc6342bc 100644 --- a/nodeup/pkg/model/secrets.go +++ b/nodeup/pkg/model/secrets.go @@ -119,6 +119,7 @@ func (b *SecretBuilder) Build(c *fi.ModelBuilderContext) error { issueCert := &nodetasks.IssueCert{ Name: "master", Signer: fi.CertificateIDCA, + KeypairID: b.NodeupConfig.KeypairIDs[fi.CertificateIDCA], Type: "server", Subject: nodetasks.PKIXName{CommonName: "kubernetes-master"}, AlternateNames: alternateNames, diff --git a/upup/pkg/fi/cloudup/apply_cluster.go b/upup/pkg/fi/cloudup/apply_cluster.go index e373497618..b4a5afe26e 100644 --- a/upup/pkg/fi/cloudup/apply_cluster.go +++ b/upup/pkg/fi/cloudup/apply_cluster.go @@ -1284,6 +1284,7 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit useGossip := dns.IsGossipHostname(cluster.Spec.MasterInternalName) isMaster := role == kops.InstanceGroupRoleMaster + hasAPIServer := isMaster || role == kops.InstanceGroupRoleAPIServer config, bootConfig := nodeup.NewConfig(cluster, ig) @@ -1299,7 +1300,7 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit return nil, nil, err } if caTasks["etcd-clients-ca-cilium"] != nil { - if err := getTasksCertificate(caTasks, "etcd-clients-ca-cilium", config, false); err != nil { + if err := getTasksCertificate(caTasks, "etcd-clients-ca-cilium", config, hasAPIServer); err != nil { return nil, nil, err } } @@ -1330,12 +1331,12 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit } } - if isMaster || role == kops.InstanceGroupRoleAPIServer { - if err := getTasksCertificate(caTasks, "apiserver-aggregator-ca", config, false); err != nil { + if hasAPIServer { + if err := getTasksCertificate(caTasks, "apiserver-aggregator-ca", config, true); err != nil { return nil, nil, err } if caTasks["etcd-clients-ca"] != nil { - if err := getTasksCertificate(caTasks, "etcd-clients-ca", config, false); err != nil { + if err := getTasksCertificate(caTasks, "etcd-clients-ca", config, true); err != nil { return nil, nil, err } } diff --git a/upup/pkg/fi/nodeup/nodetasks/issue_cert.go b/upup/pkg/fi/nodeup/nodetasks/issue_cert.go index 003ba4dee7..6efe238c8d 100644 --- a/upup/pkg/fi/nodeup/nodetasks/issue_cert.go +++ b/upup/pkg/fi/nodeup/nodetasks/issue_cert.go @@ -50,6 +50,7 @@ type IssueCert struct { Name string Signer string `json:"signer"` + KeypairID string `json:"keypairID"` Type string `json:"type"` Subject PKIXName `json:"subject"` AlternateNames []string `json:"alternateNames,omitempty"` @@ -152,8 +153,13 @@ func (e *IssueCert) Run(c *fi.Context) error { Validity: time.Hour * time.Duration(validHours), } + keystore, err := newStaticKeystore(e.Signer, e.KeypairID, c.Keystore) + if err != nil { + return err + } + klog.Infof("signing certificate for %q", e.Name) - certificate, privateKey, caCertificate, err := pki.IssueCert(req, c.Keystore) + certificate, privateKey, caCertificate, err := pki.IssueCert(req, keystore) if err != nil { return err } @@ -193,3 +199,42 @@ func (a asBytesResource) Open() (io.Reader, error) { } return bytes.NewReader(data), nil } + +type staticKeystore struct { + keyset string + certificate *pki.Certificate + key *pki.PrivateKey +} + +func (s staticKeystore) FindPrimaryKeypair(name string) (*pki.Certificate, *pki.PrivateKey, error) { + if name != s.keyset { + return nil, nil, fmt.Errorf("wrong signer: expected %q got %q", s.keyset, name) + } + return s.certificate, s.key, nil +} + +func newStaticKeystore(signer string, keypairID string, keystore fi.Keystore) (pki.Keystore, error) { + if signer == "" { + return nil, nil + } + + if keypairID == "" { + return nil, fmt.Errorf("missing keypairID for %s", signer) + } + + keyset, err := keystore.FindKeyset(signer) + if err != nil { + return nil, fmt.Errorf("reading keyset for %s: %v", signer, err) + } + + item := keyset.Items[keypairID] + if item == nil { + return nil, fmt.Errorf("no keypair with id %s for %s", keypairID, signer) + } + + return &staticKeystore{ + keyset: signer, + certificate: item.Certificate, + key: item.PrivateKey, + }, nil +}