Added list command and ability to combine certificate usages.
This commit is contained in:
43
ca.go
43
ca.go
@@ -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")
|
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
|
// Initialize Subject if not specified
|
||||||
if def.Subject == "" {
|
if def.Subject == "" {
|
||||||
def.Subject = def.Name
|
def.Subject = def.Name
|
||||||
@@ -528,19 +533,24 @@ func issueSingleCertificate(def CertificateDefinition) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch def.Type {
|
// 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":
|
case "client":
|
||||||
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
|
certTmpl.ExtKeyUsage = append(certTmpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
|
||||||
case "server":
|
case "server":
|
||||||
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
|
certTmpl.ExtKeyUsage = append(certTmpl.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
|
||||||
case "server-only":
|
|
||||||
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
|
||||||
case "code-signing":
|
case "code-signing":
|
||||||
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}
|
certTmpl.ExtKeyUsage = append(certTmpl.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
|
||||||
case "email":
|
case "email":
|
||||||
certTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}
|
certTmpl.ExtKeyUsage = append(certTmpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown certificate type. Use one of: client, server, server-only, code-signing, email")
|
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)
|
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
|
// Loop through all certificate definitions
|
||||||
// to render templates and fill missing fields from defaults
|
// 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
|
// 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
|
// Render templates in the definition using the variables map
|
||||||
// with added definition name.
|
// with added definition name.
|
||||||
variables := certDefs.Variables
|
variables := certDefs.Variables
|
||||||
if variables == nil {
|
if variables == nil {
|
||||||
variables = make(map[string]string)
|
variables = make(map[string]string)
|
||||||
}
|
}
|
||||||
variables["Name"] = certDefs.Certificates[i].Name
|
variables["Name"] = def.Name
|
||||||
err = certDefs.Certificates[i].RenderTemplates(variables)
|
err = def.RenderTemplates(variables)
|
||||||
if err != nil {
|
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
|
// No errors so far, now we can issue certificates
|
||||||
for i := range certDefs.Certificates {
|
for i, def := range certDefs.Certificates {
|
||||||
fmt.Printf("[%d/%d] Issuing %s... ", i+1, len(certDefs.Certificates), certDefs.Certificates[i].Name)
|
fmt.Printf("[%d/%d] Issuing %s... ", i+1, n, def.Name)
|
||||||
|
|
||||||
if dryRun {
|
if dryRun {
|
||||||
fmt.Printf("(dry run)\n")
|
fmt.Printf("(dry run)\n")
|
||||||
@@ -640,7 +651,7 @@ func ProvisionCertificates(filePath string, overwrite bool, dryRun bool, verbose
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = issueSingleCertificate(certDefs.Certificates[i])
|
err = issueSingleCertificate(def)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("error: %v\n", err)
|
fmt.Printf("error: %v\n", err)
|
||||||
errors++
|
errors++
|
||||||
|
29
main.go
29
main.go
@@ -16,6 +16,9 @@ var verbose bool
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
// list command flags
|
||||||
|
var listRevoked bool
|
||||||
|
|
||||||
// issue command flags
|
// issue command flags
|
||||||
var name string
|
var name string
|
||||||
var subject string
|
var subject string
|
||||||
@@ -60,6 +63,29 @@ func main() {
|
|||||||
}
|
}
|
||||||
rootCmd.AddCommand(initCmd)
|
rootCmd.AddCommand(initCmd)
|
||||||
|
|
||||||
|
// lab-ca list command
|
||||||
|
var listCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "List issued certificates",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := LoadCA()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
for _, certDef := range caState.Certificates {
|
||||||
|
if certDef.RevokedAt != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("Certificate %s\n", certDef.Name)
|
||||||
|
fmt.Printf("\tSubject: %s\n\tType: %s\n\tIssued at: %s\n",
|
||||||
|
certDef.Subject, certDef.Type, certDef.Issued)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
listCmd.Flags().BoolVar(&listRevoked, "revoked", false, "List all certificates, including revoked ones")
|
||||||
|
rootCmd.AddCommand(listCmd)
|
||||||
|
|
||||||
// lab-ca issue command
|
// lab-ca issue command
|
||||||
var issueCmd = &cobra.Command{
|
var issueCmd = &cobra.Command{
|
||||||
Use: "issue",
|
Use: "issue",
|
||||||
@@ -82,7 +108,8 @@ func main() {
|
|||||||
|
|
||||||
issueCmd.Flags().StringVar(&name, "name", "", "Name for the certificate and key files (used as subject if --subject is omitted)")
|
issueCmd.Flags().StringVar(&name, "name", "", "Name for the certificate and key files (used as subject if --subject is omitted)")
|
||||||
issueCmd.Flags().StringVar(&subject, "subject", "", "Subject Common Name for the certificate (optional, defaults to --name)")
|
issueCmd.Flags().StringVar(&subject, "subject", "", "Subject Common Name for the certificate (optional, defaults to --name)")
|
||||||
issueCmd.Flags().StringVar(&certType, "type", "server", "Certificate type: client, server, server-only, code-signing, email")
|
issueCmd.Flags().StringVar(&certType, "type", "server",
|
||||||
|
"Certificate type: client, server, code-signing, email.\nCombine by specifying more than one separated by comma.")
|
||||||
issueCmd.Flags().StringArrayVar(&san, "san", nil,
|
issueCmd.Flags().StringArrayVar(&san, "san", nil,
|
||||||
"Subject Alternative Name (SAN). Use multiple times for multiple values.\nFormat: dns:example.com, ip:1.2.3.4, email:user@example.com")
|
"Subject Alternative Name (SAN). Use multiple times for multiple values.\nFormat: dns:example.com, ip:1.2.3.4, email:user@example.com")
|
||||||
issueCmd.Flags().StringVar(&validity, "validity", "1y", "Certificate validity (e.g. 2y, 6m, 30d). Overrides config file for this certificate.")
|
issueCmd.Flags().StringVar(&validity, "validity", "1y", "Certificate validity (e.g. 2y, 6m, 30d). Overrides config file for this certificate.")
|
||||||
|
Reference in New Issue
Block a user