ca.crt restore endpoint

This commit is contained in:
Mikaël Cluseau
2026-07-17 13:24:41 +02:00
parent 88f97496be
commit 4d1deeb773
4 changed files with 94 additions and 18 deletions
+7 -2
View File
@@ -2,13 +2,14 @@ package main
import ( import (
"crypto/ed25519" "crypto/ed25519"
"crypto/x509"
"encoding/json" "encoding/json"
"encoding/pem"
"errors" "errors"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
"github.com/cloudflare/cfssl/certinfo"
"github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/helpers/derhelpers" "github.com/cloudflare/cfssl/helpers/derhelpers"
"github.com/cloudflare/cfssl/log" "github.com/cloudflare/cfssl/log"
@@ -61,7 +62,11 @@ func loadSecretData(config *config.Config) (sd *SecretData, err error) {
} }
func checkCertUsable(certPEM []byte) error { func checkCertUsable(certPEM []byte) error {
cert, err := certinfo.ParseCertificatePEM(certPEM) block, _ := pem.Decode(certPEM)
if block == nil {
return errors.New("no PEM block found")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil { if err != nil {
return err return err
} }
+6 -1
View File
@@ -76,7 +76,12 @@ func (ca CA) ParseKey() (key crypto.Signer, err error) {
return helpers.ParsePrivateKeyPEM(ca.Key) return helpers.ParsePrivateKeyPEM(ca.Key)
} }
func (ca CA) ParseCert() (cert *x509.Certificate, err error) { func (ca CA) ParseCert() (cert *x509.Certificate, err error) {
return helpers.ParseCertificatePEM(ca.Cert) block, _ := pem.Decode(ca.Cert)
if block == nil {
err = errors.New("no PEM block found")
return
}
return x509.ParseCertificate(block.Bytes)
} }
func (ca CA) Signer(policy *config.Signing) (result *local.Signer, err error) { func (ca CA) Signer(policy *config.Signing) (result *local.Signer, err error) {
+65 -2
View File
@@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"io"
"time" "time"
"github.com/cloudflare/cfssl/helpers" "github.com/cloudflare/cfssl/helpers"
@@ -50,16 +51,25 @@ func getUsableClusterCA(cluster, name string) (ca CA, err error) {
if checkErr != nil { if checkErr != nil {
log.Infof("cluster %s: CA %s: regenerating certificate: %v", cluster, name, checkErr) 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) prevCerts, _ := helpers.ParseCertificatesPEM(ca.Cert)
err = ca.RenewCert() err = ca.RenewCert()
if err != nil { if err != nil {
err = fmt.Errorf("renew: %w", err) return ca, fmt.Errorf("renew: %w", err)
} }
now := time.Now() now := time.Now()
for _, cert := range prevCerts { for _, cert := range prevCerts {
if cert.NotAfter.After(now) { if cert.NotAfter.Before(now) {
continue continue
} }
certPEM := helpers.EncodeCertificatePEM(cert) certPEM := helpers.EncodeCertificatePEM(cert)
@@ -67,6 +77,11 @@ func getUsableClusterCA(cluster, name string) (ca CA, err error) {
} }
err = clusterCAs.Put(key, ca) err = clusterCAs.Put(key, ca)
return ca, err
})
if err != nil {
return ca, err
}
} }
return return
@@ -98,3 +113,51 @@ func wsClusterCASignedKey(req *restful.Request, resp *restful.Response) {
clusterCASignedKeys.WsGet(resp, clusterName+"/"+caName+"/"+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
}
}
+3
View File
@@ -132,6 +132,9 @@ func registerWS(rest *restful.Container) {
cluster(GET, "/CAs/{ca-name}/certificate").To(wsClusterCACert). cluster(GET, "/CAs/{ca-name}/certificate").To(wsClusterCACert).
Produces(mime.CACERT). Produces(mime.CACERT).
Doc("Get cluster CA's certificate"), Doc("Get cluster CA's certificate"),
cluster(PUT, "/CAs/{ca-name}/certificate").To(wsClusterCACertPut).
Consumes(mime.CACERT).
Doc("Set cluster CA's certificate(s)"),
cluster(GET, "/CAs/{ca-name}/signed").To(wsClusterSignedCert). cluster(GET, "/CAs/{ca-name}/signed").To(wsClusterSignedCert).
Produces(mime.CERT). Produces(mime.CERT).
Param(ws.QueryParameter("name", "signed reference name").Required(true)). Param(ws.QueryParameter("name", "signed reference name").Required(true)).