package main import ( "crypto" "errors" "fmt" "log" "net" "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/csr" "github.com/cloudflare/cfssl/helpers" "github.com/cloudflare/cfssl/initca" "github.com/cloudflare/cfssl/signer" "github.com/cloudflare/cfssl/signer/local" "k8s.io/apimachinery/pkg/util/validation" ) type CA struct { Key []byte Cert []byte Signed map[string]*KeyCert } func (ca *CA) Init() (err error) { req := ca.newReq() cert, _, key, err := initca.New(req) if err != nil { err = fmt.Errorf("initca: %w", err) return } ca.Key = key ca.Cert = cert return } func (ca *CA) RenewCert() (err error) { var signer crypto.Signer signer, err = helpers.ParsePrivateKeyPEM(ca.Key) if err != nil { return } newCert, _, err := initca.NewFromSigner(ca.newReq(), signer) if err != nil { return } ca.Cert = newCert return } func (_ CA) newReq() *csr.CertificateRequest { return &csr.CertificateRequest{ CN: "Direktil Local Server", KeyRequest: &csr.KeyRequest{ A: "ecdsa", S: 521, // 256, 384, 521 }, Names: []csr.Name{ { O: "novit.io", }, }, } } func (ca CA) Signer(policy *config.Signing) (result *local.Signer, err error) { caCert, err := helpers.ParseCertificatePEM(ca.Cert) if err != nil { return } caKey, err := helpers.ParsePrivateKeyPEM(ca.Key) if err != nil { return } return local.NewSigner(caKey, caCert, signer.DefaultSigAlgo(caKey), policy) } func getUsableKeyCert(cluster, caName, name, profile, label string, req *csr.CertificateRequest, cfg *config.Config) (kc KeyCert, err error) { log := log.New(log.Default().Writer(), cluster+": CA "+caName+": ", log.Flags()|log.Lmsgprefix) ca, err := getUsableClusterCA(cluster, caName) if err != nil { return } for _, host := range req.Hosts { if ip := net.ParseIP(host); ip != nil { // valid IP (v4 or v6) continue } if host == "*" { continue } if errs := validation.IsDNS1123Subdomain(host); len(errs) == 0 { continue } if errs := validation.IsWildcardDNS1123Subdomain(host); len(errs) == 0 { continue } err = fmt.Errorf("%q is not an IP or FQDN", host) return } if req.CA != nil { err = errors.New("no CA section allowed here") return } rh := hash(req) key := cluster + "/" + caName + "/" + name kc, found, err := clusterCASignedKeys.Get(key) if err != nil { return } if found { if rh == kc.ReqHash { err = checkCertUsable(kc.Cert) if err == nil { return // all good, no need to create or renew } log.Print("regenerating certificate: ", err) } else { log.Printf("CSR changed for %s: hash=%q previous=%q", name, rh, kc.ReqHash) } } else { log.Print("new CSR for ", name) } sgr, err := ca.Signer(cfg.Signing) if err != nil { return } generator := &csr.Generator{Validator: func(_ *csr.CertificateRequest) error { return nil }} csr, tlsKey, err := generator.ProcessRequest(req) if err != nil { return } signReq := signer.SignRequest{ Request: string(csr), Profile: profile, Label: label, } cert, err := sgr.Sign(signReq) if err != nil { return } kc = KeyCert{ Key: tlsKey, Cert: cert, ReqHash: rh, } err = clusterCASignedKeys.Put(key, kc) return }