179 lines
3.2 KiB
Go
179 lines
3.2 KiB
Go
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
|
|
}
|