Added list command and ability to combine certificate usages.

This commit is contained in:
2025-07-28 18:58:37 +02:00
parent 9b7b995e97
commit 6682be6eb1
2 changed files with 60 additions and 22 deletions

53
ca.go
View File

@@ -465,6 +465,11 @@ func issueSingleCertificate(def CertificateDefinition) error {
return fmt.Errorf("certificate name must be specified and contain only letters, numbers, dash, or underscore")
}
// Check if the certificate is in database, fail if it is.
if caState.FindByName(def.Name, false) != nil {
return fmt.Errorf("certificate %s already exists and is valid.", def.Name)
}
// Initialize Subject if not specified
if def.Subject == "" {
def.Subject = def.Name
@@ -528,19 +533,24 @@ func issueSingleCertificate(def CertificateDefinition) error {
}
}
switch def.Type {
case "client":
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
case "server":
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
case "server-only":
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
case "code-signing":
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}
case "email":
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}
default:
return fmt.Errorf("unknown certificate type. Use one of: client, server, server-only, code-signing, email")
// Split usage types by comma
types := strings.SplitSeq(def.Type, ",")
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{}
// Collect selected usage types
for certType := range types {
switch certType {
case "client":
certTmpl.ExtKeyUsage = append(certTmpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
case "server":
certTmpl.ExtKeyUsage = append(certTmpl.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
case "code-signing":
certTmpl.ExtKeyUsage = append(certTmpl.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
case "email":
certTmpl.ExtKeyUsage = append(certTmpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
default:
return fmt.Errorf("unknown certificate type. Use one of: client, server, code-signing, email")
}
}
certDER, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, &priv.PublicKey, caKey)
@@ -614,25 +624,26 @@ func ProvisionCertificates(filePath string, overwrite bool, dryRun bool, verbose
// Loop through all certificate definitions
// to render templates and fill missing fields from defaults
for i := range certDefs.Certificates {
for _, def := range certDefs.Certificates {
// Fill missing fields from defaults, if provided
certDefs.Certificates[i].FillDefaultValues(certDefs.Defaults)
def.FillDefaultValues(certDefs.Defaults)
// Render templates in the definition using the variables map
// with added definition name.
variables := certDefs.Variables
if variables == nil {
variables = make(map[string]string)
}
variables["Name"] = certDefs.Certificates[i].Name
err = certDefs.Certificates[i].RenderTemplates(variables)
variables["Name"] = def.Name
err = def.RenderTemplates(variables)
if err != nil {
return fmt.Errorf("failed to render templates for certificate %s: %v", certDefs.Certificates[i].Name, err)
return fmt.Errorf("failed to render templates for certificate %s: %v", def.Name, err)
}
}
n := len(certDefs.Certificates)
// No errors so far, now we can issue certificates
for i := range certDefs.Certificates {
fmt.Printf("[%d/%d] Issuing %s... ", i+1, len(certDefs.Certificates), certDefs.Certificates[i].Name)
for i, def := range certDefs.Certificates {
fmt.Printf("[%d/%d] Issuing %s... ", i+1, n, def.Name)
if dryRun {
fmt.Printf("(dry run)\n")
@@ -640,7 +651,7 @@ func ProvisionCertificates(filePath string, overwrite bool, dryRun bool, verbose
continue
}
err = issueSingleCertificate(certDefs.Certificates[i])
err = issueSingleCertificate(def)
if err != nil {
fmt.Printf("error: %v\n", err)
errors++