From 4b9e24aa657eb5a9b9fc54696995cc9f12ae08fc Mon Sep 17 00:00:00 2001 From: kaiyou <dev@kaiyou.fr> Date: Sun, 18 Sep 2022 12:00:02 +0200 Subject: [PATCH] Simplify the pki related code --- pkg/cluster/certs.go | 2 +- pkg/cluster/cluster.go | 6 ++-- pkg/cluster/meta.go | 22 ++---------- pkg/cluster/services.go | 35 +++++++++--------- pkg/pekahi/pki.go | 40 +++++++++++++-------- pkg/pki/ca.go | 43 ++++++++-------------- pkg/pki/master.go | 80 ++++++++++++++++++----------------------- pkg/pki/node.go | 34 ++++++++---------- pkg/pki/utils.go | 13 +++++-- 9 files changed, 125 insertions(+), 150 deletions(-) diff --git a/pkg/cluster/certs.go b/pkg/cluster/certs.go index 8e18bf4..8ec91d6 100644 --- a/pkg/cluster/certs.go +++ b/pkg/cluster/certs.go @@ -8,7 +8,7 @@ import ( func (c *Cluster) initCerts() { // Prepare the cluster PKI if c.node.Role == Master { - ca, err := pki.NewClusterPKI("pki") + ca, err := pki.NewClusterCA("pki") if err != nil { logrus.Fatal("could not initialize pki: ", err) } diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 89c3d68..8a7af73 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -3,8 +3,8 @@ package cluster import ( - "forge.tedomum.net/acides/hepto/hepto/pkg/sml" "forge.tedomum.net/acides/hepto/hepto/pkg/pki" + "forge.tedomum.net/acides/hepto/hepto/pkg/sml" "forge.tedomum.net/acides/hepto/hepto/pkg/wg" "github.com/sirupsen/logrus" ) @@ -17,7 +17,7 @@ type Cluster struct { node *NodeSettings certs *pki.NodeCerts masterCerts *pki.MasterCerts - pki *pki.ClusterPKI + pki *pki.ClusterCA services *ClusterServices } @@ -27,7 +27,7 @@ func New(settings *ClusterSettings, node *NodeSettings) *Cluster { node: node, networking: NewClusterNetworking(settings.Name, node.Name), ml: sml.New[HeptoMeta, HeptoState](node.Name, node.IP, node.Port, node.Anchors, settings.Key), - pki: &pki.ClusterPKI{}, + pki: &pki.ClusterCA{}, services: NewClusterServices(), } } diff --git a/pkg/cluster/meta.go b/pkg/cluster/meta.go index 13c268b..04ae143 100644 --- a/pkg/cluster/meta.go +++ b/pkg/cluster/meta.go @@ -2,10 +2,8 @@ package cluster import ( "encoding/json" - "fmt" - "strings" - "forge.tedomum.net/acides/hepto/hepto/pkg/pki" + "forge.tedomum.net/acides/hepto/hepto/pkg/pki" ) // Represents a node metadata @@ -19,7 +17,7 @@ type HeptoMeta struct { // Represents the cluster state type HeptoState struct { // Cluster CAs public certificates - PKI *pki.ClusterPKI `json:"ca"` + PKI *pki.ClusterCA `json:"ca"` // Certificate per node, this should only // be updated by the node itself Certificates map[string]*pki.NodeCerts `json:"nodes"` @@ -46,21 +44,7 @@ func (s *HeptoState) Decode(b []byte) error { } func (s *HeptoState) String() string { - var res string - if s.PKI != nil { - var ca []string - if s.PKI.API != nil { - ca = append(ca, "api") - } - if s.PKI.Kubelet != nil { - ca = append(ca, "kubelet") - } - if s.PKI.Services != nil { - ca = append(ca, "services") - } - res += fmt.Sprintf("ca{%s}", strings.Join(ca, ", ")) - } - return res + return "state" } func (s *HeptoState) Merge(b []byte) (bool, error) { diff --git a/pkg/cluster/services.go b/pkg/cluster/services.go index 50a54dd..d5e5f91 100644 --- a/pkg/cluster/services.go +++ b/pkg/cluster/services.go @@ -42,10 +42,11 @@ func (s *ClusterServices) Update(c *Cluster) { s.startK8sMaster(c.networking, c.pki, c.masterCerts) s.started = true } else if c.node.Role == Node { - if c.pki.Services == nil || c.pki.Services.Cert == nil { + // Bail if certificates are not ready yet + if c.pki.TLS == nil || c.pki.TLS.Cert == nil { return } - if c.certs.API.Cert == nil || c.certs.Service.Cert == nil { + if c.certs.TLS.Cert == nil || c.certs.API.Cert == nil { return } logrus.Debug("looking for master node...") @@ -68,12 +69,12 @@ func (s *ClusterServices) startEtcd() { go s.watch(service) } -func (s *ClusterServices) startK8sMaster(net *ClusterNetworking, ca *pki.ClusterPKI, certs *pki.MasterCerts) { +func (s *ClusterServices) startK8sMaster(net *ClusterNetworking, ca *pki.ClusterCA, certs *pki.MasterCerts) { api, err := wrappers.APIServer(s.ctx, []string{ "--bind-address", net.NodeAddress.IP.String(), "--service-cluster-ip-range", net.ServiceNet.String(), - "--tls-cert-file", certs.Service.CertPath(), - "--tls-private-key-file", certs.Service.KeyPath(), + "--tls-cert-file", certs.TLS.CertPath(), + "--tls-private-key-file", certs.TLS.KeyPath(), "--client-ca-file", ca.API.CertPath(), "--kubelet-certificate-authority", ca.Kubelet.CertPath(), "--kubelet-client-certificate", certs.Kubelet.CertPath(), @@ -90,9 +91,9 @@ func (s *ClusterServices) startK8sMaster(net *ClusterNetworking, ca *pki.Cluster } cmConfig := KubeConfig{ URL: fmt.Sprintf("https://[%s]:6443", net.NodeAddress.IP.String()), - CACert: ca.Services.CertPath(), - ClientCert: certs.ControllersClient.CertPath(), - ClientKey: certs.ControllersClient.KeyPath(), + CACert: ca.TLS.CertPath(), + ClientCert: certs.ControllersAPI.CertPath(), + ClientKey: certs.ControllersAPI.KeyPath(), } cmConfigPath := "/cm.yaml" err = cmConfig.Write(cmConfigPath) @@ -101,8 +102,8 @@ func (s *ClusterServices) startK8sMaster(net *ClusterNetworking, ca *pki.Cluster } cm, err := wrappers.ControllerManager(s.ctx, []string{ "--kubeconfig", cmConfigPath, - "--tls-cert-file", certs.Controllers.CertPath(), - "--tls-private-key-file", certs.Controllers.KeyPath(), + "--tls-cert-file", certs.ControllersTLS.CertPath(), + "--tls-private-key-file", certs.ControllersTLS.KeyPath(), "--service-account-private-key-file", certs.Tokens.KeyPath(), "--use-service-account-credentials", }) @@ -111,9 +112,9 @@ func (s *ClusterServices) startK8sMaster(net *ClusterNetworking, ca *pki.Cluster } schedulerConfig := KubeConfig{ URL: fmt.Sprintf("https://[%s]:6443", net.NodeAddress.IP.String()), - CACert: ca.Services.CertPath(), - ClientCert: certs.SchedulerClient.CertPath(), - ClientKey: certs.SchedulerClient.KeyPath(), + CACert: ca.API.CertPath(), + ClientCert: certs.SchedulerAPI.CertPath(), + ClientKey: certs.SchedulerAPI.KeyPath(), } schedulerConfigPath := "/scheduler.yaml" err = schedulerConfig.Write(schedulerConfigPath) @@ -131,10 +132,10 @@ func (s *ClusterServices) startK8sMaster(net *ClusterNetworking, ca *pki.Cluster go s.watch(scheduler) } -func (s *ClusterServices) startK8sNode(master net.IP, ca *pki.ClusterPKI, certs *pki.NodeCerts) { +func (s *ClusterServices) startK8sNode(master net.IP, ca *pki.ClusterCA, certs *pki.NodeCerts) { kubeletKubeConfig := KubeConfig{ URL: fmt.Sprintf("https://[%s]:6443", master.String()), - CACert: ca.Services.CertPath(), + CACert: ca.TLS.CertPath(), ClientCert: certs.API.CertPath(), ClientKey: certs.API.KeyPath(), } @@ -142,8 +143,8 @@ func (s *ClusterServices) startK8sNode(master net.IP, ca *pki.ClusterPKI, certs kubeletKubeConfig.Write(kubeletKubeConfigPath) kubeletConfig := KubeletConfig{ CACert: ca.Kubelet.CertPath(), - TLSCert: certs.Service.CertPath(), - TLSKey: certs.Service.KeyPath(), + TLSCert: certs.TLS.CertPath(), + TLSKey: certs.TLS.KeyPath(), } kubeletConfigPath := "/kubelet.yaml" kubeletConfig.Write(kubeletConfigPath) diff --git a/pkg/pekahi/pki.go b/pkg/pekahi/pki.go index c9e5dfa..b28213b 100644 --- a/pkg/pekahi/pki.go +++ b/pkg/pekahi/pki.go @@ -1,17 +1,26 @@ package pekahi import ( + "crypto/x509" + "os" "path/filepath" ) -type PKI struct { - *Certificate +type FileBundle struct { Path string } -func GetCertificate(path string) (*Certificate, error) { +func NewFileBundle(path string) (*FileBundle, error) { + err := os.MkdirAll(path, 0755) + if err != nil { + return nil, err + } + return &FileBundle{path}, nil +} + +func (b *FileBundle) GetCertificate(name string) (*Certificate, error) { c := &Certificate{ - IO: FileIO{path}, + IO: FileIO{filepath.Join(b.Path, name)}, } err := c.Load() if err != nil { @@ -24,23 +33,26 @@ func GetCertificate(path string) (*Certificate, error) { return c, nil } -func GetPKI(path string) (*PKI, error) { - c, err := GetCertificate(path) +func (b *FileBundle) GetCertOrCSR(name string, template *x509.Certificate) (*Certificate, error) { + c, err := b.GetCertificate(name) if err != nil { return nil, err } - err = c.SelfSign(NewCATemplate()) + err = c.MakeCSR(template) if err != nil { return nil, err } - return &PKI{ - c, - path, - }, nil + return c, nil } -func (p *PKI) GetCertificate(name string) *Certificate { - return &Certificate{ - IO: FileIO{filepath.Join(p.Path, name)}, +func (b *FileBundle) GetCA(name string) (*Certificate, error) { + c, err := b.GetCertificate(name) + if err != nil { + return nil, err } + err = c.SelfSign(NewCATemplate()) + if err != nil { + return nil, err + } + return c, nil } diff --git a/pkg/pki/ca.go b/pkg/pki/ca.go index f79e8db..4fe3116 100644 --- a/pkg/pki/ca.go +++ b/pkg/pki/ca.go @@ -1,56 +1,43 @@ package pki import ( - "os" - "path/filepath" - "forge.tedomum.net/acides/hepto/hepto/pkg/pekahi" ) -// Cluster PKI is made of three different PKIs -type ClusterPKI struct { +// Cluster PKI is made of three different CAs +type ClusterCA struct { // Signs services exposed over the cluster - Services *pekahi.PKI `json:"services"` + TLS *pekahi.Certificate `json:"tls"` // Signs kubelet client certificates (master) - Kubelet *pekahi.PKI `json:"kubelet"` + Kubelet *pekahi.Certificate `json:"kubelet"` // Signs apiserver client certificates (nodes and controller) - API *pekahi.PKI `json:"api"` + API *pekahi.Certificate `json:"api"` } -func NewClusterPKI(path string) (*ClusterPKI, error) { - err := os.MkdirAll(path, 0755) +func NewClusterCA(path string) (*ClusterCA, error) { + bundle, err := pekahi.NewFileBundle(path) if err != nil { return nil, err } - servicesCA, err := pekahi.GetPKI(filepath.Join(path, "services")) + tlsCA, err := bundle.GetCA("tls") if err != nil { return nil, err } - kubeletCA, err := pekahi.GetPKI(filepath.Join(path, "kubelet")) + kubeletCA, err := bundle.GetCA("kubelet") if err != nil { return nil, err } - apiserverCA, err := pekahi.GetPKI(filepath.Join(path, "api")) + apiserverCA, err := bundle.GetCA("api") if err != nil { return nil, err } - return &ClusterPKI{servicesCA, kubeletCA, apiserverCA}, nil + return &ClusterCA{tlsCA, kubeletCA, apiserverCA}, nil } // Merge PKI -func (n *ClusterPKI) Merge(other *ClusterPKI) bool { - change := false - if n.API == nil && other.API != nil { - n.API = other.API - change = true - } - if n.Services == nil && other.Services != nil { - n.Services = other.Services - change = true - } - if n.Kubelet == nil && other.Kubelet != nil { - n.Kubelet = other.Kubelet - change = true - } +func (n *ClusterCA) Merge(remote *ClusterCA) bool { + change := mergeCert(n.TLS, remote.TLS) + change = change || mergeCert(n.Kubelet, remote.Kubelet) + change = change || mergeCert(n.API, remote.API) return change } diff --git a/pkg/pki/master.go b/pkg/pki/master.go index abf027b..acebc86 100644 --- a/pkg/pki/master.go +++ b/pkg/pki/master.go @@ -2,8 +2,6 @@ package pki import ( "net" - "os" - "path/filepath" "forge.tedomum.net/acides/hepto/hepto/pkg/pekahi" ) @@ -11,88 +9,78 @@ import ( // Master certs type MasterCerts struct { // Certificate for exposing the apiserver - Service *pekahi.Certificate + TLS *pekahi.Certificate // Certificate for signing tokens Tokens *pekahi.Certificate // Certificate for authenticating against kubelets Kubelet *pekahi.Certificate // Service certificate for the controller manager - Controllers *pekahi.Certificate + ControllersTLS *pekahi.Certificate // API client certificate for the controller manager - ControllersClient *pekahi.Certificate + ControllersAPI *pekahi.Certificate // API client certificate for the scheduler - SchedulerClient *pekahi.Certificate + SchedulerAPI *pekahi.Certificate } func NewMasterCerts(path string, ip net.IP) (*MasterCerts, error) { - err := os.MkdirAll(path, 0755) + bundle, err := pekahi.NewFileBundle(path) if err != nil { return nil, err } - // Service certificate - serviceCert, err := pekahi.GetCertificate(filepath.Join(path, "service")) - if err != nil { - return nil, err - } - err = serviceCert.MakeCSR(pekahi.NewServerTemplate([]string{"apiserver"}, []net.IP{ip})) + // TLS certificate + tlsCert, err := bundle.GetCertOrCSR("tls", + pekahi.NewServerTemplate([]string{"apiserver"}, []net.IP{ip}), + ) if err != nil { return nil, err } // Tokens key - tokenKey, err := pekahi.GetCertificate(filepath.Join(path, "tokens")) + tokenKey, err := bundle.GetCertificate("tokens") if err != nil { return nil, err } // Kubelet certificate - kubeletClientCert, err := pekahi.GetCertificate(filepath.Join(path, "kubelet")) - if err != nil { - return nil, err - } - err = kubeletClientCert.MakeCSR(pekahi.NewClientTemplate("apiserver", "")) + kubeletCert, err := bundle.GetCertOrCSR("kubelet", + pekahi.NewClientTemplate("apiserver", ""), + ) if err != nil { return nil, err } // Controller manager certificate - controllersCert, err := pekahi.GetCertificate(filepath.Join(path, "kubelet")) - if err != nil { - return nil, err - } - err = controllersCert.MakeCSR(pekahi.NewServerTemplate([]string{"controllers"}, []net.IP{ip})) + controllersTLSCert, err := bundle.GetCertOrCSR("controllers-tls", + pekahi.NewServerTemplate([]string{"controllers"}, []net.IP{ip}), + ) if err != nil { return nil, err } // Controller manager API client certificate - controllersClientCert, err := pekahi.GetCertificate(filepath.Join(path, "controllers-client")) - if err != nil { - return nil, err - } - err = controllersClientCert.MakeCSR(pekahi.NewClientTemplate("system:kube-controller-manager", "")) + controllersAPICert, err := bundle.GetCertOrCSR("controllers-api", + pekahi.NewClientTemplate("system:kube-controller-manager", ""), + ) if err != nil { return nil, err } // Scheduler API client certificate - schedulerClientCert, err := pekahi.GetCertificate(filepath.Join(path, "scheduler-client")) - if err != nil { - return nil, err - } - err = schedulerClientCert.MakeCSR(pekahi.NewClientTemplate("system:kube-scheduler", "")) + schedulerAPICert, err := bundle.GetCertOrCSR("scheduler-api", + pekahi.NewClientTemplate("system:kube-scheduler", ""), + ) if err != nil { return nil, err } return &MasterCerts{ - Service: serviceCert, - Tokens: tokenKey, - Kubelet: kubeletClientCert, - Controllers: controllersCert, - ControllersClient: controllersClientCert, - SchedulerClient: schedulerClientCert, + TLS: tlsCert, + Tokens: tokenKey, + Kubelet: kubeletCert, + ControllersTLS: controllersTLSCert, + ControllersAPI: controllersAPICert, + SchedulerAPI: schedulerAPICert, }, nil } -func (p *ClusterPKI) SignMasterCerts(m *MasterCerts) { - signCert(p.Services, m.Service, pekahi.NewServerTemplate(m.Service.CSR.DNSNames, m.Service.CSR.IPAddresses)) - signCert(p.Kubelet, m.Kubelet, pekahi.NewClientTemplate(m.Kubelet.CSR.Subject.CommonName, "")) - signCert(p.Services, m.Controllers, pekahi.NewServerTemplate(m.Controllers.CSR.DNSNames, m.Controllers.CSR.IPAddresses)) - signCert(p.API, m.ControllersClient, pekahi.NewClientTemplate(m.ControllersClient.CSR.Subject.CommonName, "")) - signCert(p.API, m.SchedulerClient, pekahi.NewClientTemplate(m.SchedulerClient.CSR.Subject.CommonName, "")) +func (ca *ClusterCA) SignMasterCerts(m *MasterCerts) { + signCert(ca.TLS, m.TLS, pekahi.NewServerTemplate(m.TLS.CSR.DNSNames, m.TLS.CSR.IPAddresses)) + signCert(ca.Kubelet, m.Kubelet, pekahi.NewClientTemplate(m.Kubelet.CSR.Subject.CommonName, "")) + signCert(ca.TLS, m.ControllersTLS, pekahi.NewServerTemplate(m.ControllersTLS.CSR.DNSNames, m.ControllersTLS.CSR.IPAddresses)) + signCert(ca.API, m.ControllersAPI, pekahi.NewClientTemplate(m.ControllersAPI.CSR.Subject.CommonName, "")) + signCert(ca.API, m.SchedulerAPI, pekahi.NewClientTemplate(m.SchedulerAPI.CSR.Subject.CommonName, "")) } diff --git a/pkg/pki/node.go b/pkg/pki/node.go index ba2e9e0..e84cae3 100644 --- a/pkg/pki/node.go +++ b/pkg/pki/node.go @@ -2,8 +2,6 @@ package pki import ( "net" - "os" - "path/filepath" "forge.tedomum.net/acides/hepto/hepto/pkg/pekahi" ) @@ -11,48 +9,44 @@ import ( // Node certs type NodeCerts struct { // Certificate for exposing the kubelet service - Service *pekahi.Certificate `json:"service"` + TLS *pekahi.Certificate `json:"tls"` // Node certificate for accessing the apiserver API *pekahi.Certificate `json:"api"` } func NewNodeCerts(path string, nodeName string) (*NodeCerts, error) { - err := os.MkdirAll(path, 0755) + bundle, err := pekahi.NewFileBundle(path) if err != nil { return nil, err } // Service certificate - serviceCert, err := pekahi.GetCertificate(filepath.Join(path, "service")) - if err != nil { - return nil, err - } - err = serviceCert.MakeCSR(pekahi.NewServerTemplate([]string{nodeName}, []net.IP{})) + tlsCert, err := bundle.GetCertOrCSR("tls", + pekahi.NewServerTemplate([]string{nodeName}, []net.IP{}), + ) if err != nil { return nil, err } // API certificate - apiClientCert, err := pekahi.GetCertificate(filepath.Join(path, "api")) - if err != nil { - return nil, err - } - err = apiClientCert.MakeCSR(pekahi.NewClientTemplate("system:nodes:"+nodeName, "system:nodes")) + apiCert, err := bundle.GetCertOrCSR("api", + pekahi.NewClientTemplate("system:nodes:"+nodeName, "system:nodes"), + ) if err != nil { return nil, err } return &NodeCerts{ - Service: serviceCert, - API: apiClientCert, + TLS: tlsCert, + API: apiCert, }, nil } // Merge node certificates func (n *NodeCerts) Merge(other *NodeCerts) bool { - change := mergeCert(n.Service, other.Service) + change := mergeCert(n.TLS, other.TLS) change = change || mergeCert(n.API, other.API) return change } -func (p *ClusterPKI) SignNodeCerts(name string, n *NodeCerts) { - signCert(p.Services, n.Service, pekahi.NewServerTemplate([]string{name}, []net.IP{})) - signCert(p.API, n.API, pekahi.NewClientTemplate("system:node:"+name, "system:nodes")) +func (ca *ClusterCA) SignNodeCerts(name string, n *NodeCerts) { + signCert(ca.TLS, n.TLS, pekahi.NewServerTemplate([]string{name}, []net.IP{})) + signCert(ca.API, n.API, pekahi.NewClientTemplate("system:node:"+name, "system:nodes")) } diff --git a/pkg/pki/utils.go b/pkg/pki/utils.go index dcd9814..f36ed29 100644 --- a/pkg/pki/utils.go +++ b/pkg/pki/utils.go @@ -10,6 +10,15 @@ import ( // Merge a single node or master certificate func mergeCert(local *pekahi.Certificate, remote *pekahi.Certificate) bool { change := false + // Do not merge nothing + if remote == nil { + return change + } + // Create local certificate if required + if local == nil && remote != nil { + *local = pekahi.Certificate{} + change = true + } // Import CSR to master for signing if local.CSR == nil && remote.CSR != nil { local.CSR = remote.CSR @@ -24,10 +33,10 @@ func mergeCert(local *pekahi.Certificate, remote *pekahi.Certificate) bool { return change } -func signCert(p *pekahi.PKI, c *pekahi.Certificate, template *x509.Certificate) { +func signCert(ca *pekahi.Certificate, c *pekahi.Certificate, template *x509.Certificate) { if c.CSR != nil && c.Cert == nil { logrus.Info("signing certificate ", c.CSR.Subject.String()) - err := p.Sign(c, template) + err := ca.Sign(c, template) if err != nil { logrus.Warnf("cannot sign certificate for %s: %s", c.CSR.Subject.String(), err) } -- GitLab