package main import ( "fmt" "io" "time" "github.com/cloudflare/cfssl/helpers" "github.com/cloudflare/cfssl/log" restful "github.com/emicklei/go-restful" ) var clusterCAs = newClusterSecretKV[CA]("CAs") func wsClusterCAs(req *restful.Request, resp *restful.Response) { clusterName := req.PathParameter("cluster-name") clusterCAs.WsList(resp, clusterName+"/") } func getUsableClusterCA(cluster, name string) (ca CA, err error) { defer func() { if err != nil { err = fmt.Errorf("cluster %s CA %s: %w", cluster, name, err) } }() key := cluster + "/" + name ca, found, err := clusterCAs.Get(key) if err != nil { return } if !found { log.Info("new CA in cluster ", cluster, ": ", name) err = ca.Init() if err != nil { return } err = clusterCAs.Put(key, ca) if err != nil { return } return } checkErr := checkCertUsable(ca.Cert) if checkErr != nil { log.Infof("cluster %s: CA %s: regenerating certificate: %v", cluster, name, checkErr) ca, err = opMutex("renew-ca:"+key, func() (CA, error) { ca, found, err := clusterCAs.Get(key) if err != nil { return ca, err } if !found { return ca, fmt.Errorf("CA not found") } prevCerts, _ := helpers.ParseCertificatesPEM(ca.Cert) err = ca.RenewCert() if err != nil { return ca, fmt.Errorf("renew: %w", err) } now := time.Now() for _, cert := range prevCerts { if cert.NotAfter.Before(now) { continue } certPEM := helpers.EncodeCertificatePEM(cert) ca.Cert = append(ca.Cert, certPEM...) } err = clusterCAs.Put(key, ca) return ca, err }) if err != nil { return ca, err } } return } func caExtraCerts(cluster, name string) (extra string, err error) { cfg, err := readConfig() if err != nil { return } if cfg.ExtraCaCerts != nil { extra = cfg.ExtraCaCerts[cluster+"/"+name] } return } var clusterCASignedKeys = newClusterSecretKV[KeyCert]("CA-signed-keys") func wsClusterCASignedKeys(req *restful.Request, resp *restful.Response) { clusterName := req.PathParameter("cluster-name") caName := req.PathParameter("ca-name") clusterCASignedKeys.WsList(resp, clusterName+"/"+caName+"/") } func wsClusterCASignedKey(req *restful.Request, resp *restful.Response) { clusterName := req.PathParameter("cluster-name") caName := req.PathParameter("ca-name") name := req.PathParameter("signed-name") clusterCASignedKeys.WsGet(resp, clusterName+"/"+caName+"/"+name) } func wsClusterCACertPut(req *restful.Request, resp *restful.Response) { clusterName := req.PathParameter("cluster-name") caName := req.PathParameter("ca-name") key := clusterName + "/" + caName body, err := io.ReadAll(req.Request.Body) if err != nil { wsError(resp, fmt.Errorf("read body: %w", err)) return } certs, err := helpers.ParseCertificatesPEM(body) if err != nil { wsError(resp, fmt.Errorf("invalid certificate(s): %w", err)) return } ca, found, err := clusterCAs.Get(key) if err != nil { wsError(resp, err) return } if !found { wsNotFound(resp) return } caCert, err := ca.ParseCert() if err != nil { wsError(resp, fmt.Errorf("parse CA cert: %w", err)) return } for _, c := range certs { if err := c.CheckSignatureFrom(caCert); err != nil { wsError(resp, fmt.Errorf("certificate not signed by this CA: %w", err)) return } } ca.Cert = body if err := clusterCAs.Put(key, ca); err != nil { wsError(resp, err) return } }