Added state file location defintion to the CA configuration. Added more certificate properties to certificate database.
This commit is contained in:
34
ca.go
34
ca.go
@@ -24,6 +24,7 @@ import (
|
|||||||
type Paths struct {
|
type Paths struct {
|
||||||
Certificates string `hcl:"certificates"`
|
Certificates string `hcl:"certificates"`
|
||||||
PrivateKeys string `hcl:"private_keys"`
|
PrivateKeys string `hcl:"private_keys"`
|
||||||
|
StatePath string `hcl:"state_file"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CAConfig struct {
|
type CAConfig struct {
|
||||||
@@ -143,15 +144,23 @@ func (c *Certificates) LoadFromFile(path string) error {
|
|||||||
|
|
||||||
// Global CA configuration and state variables
|
// Global CA configuration and state variables
|
||||||
var caConfigPath string
|
var caConfigPath string
|
||||||
var caState *CAState
|
|
||||||
var caConfig *CAConfig
|
var caConfig *CAConfig
|
||||||
|
|
||||||
|
var caStatePath string
|
||||||
|
var caState *CAState
|
||||||
|
|
||||||
var caKey *rsa.PrivateKey
|
var caKey *rsa.PrivateKey
|
||||||
var caCert *x509.Certificate
|
var caCert *x509.Certificate
|
||||||
|
|
||||||
// LoadCAConfig parses and validates the CA config from the given path and stores it in the CAConfig global variable
|
// LoadCAConfig parses and validates the CA config from the given path and stores it in the CAConfig global variable
|
||||||
func LoadCAConfig() error {
|
func LoadCAConfig() error {
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Printf("Loading CA config from \"%s\"", caConfigPath)
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("The current working dirctory: \"%s\"\n", cwd)
|
||||||
|
fmt.Printf("Loading CA config from \"%s\"... ", caConfigPath)
|
||||||
}
|
}
|
||||||
parser := hclparse.NewParser()
|
parser := hclparse.NewParser()
|
||||||
file, diags := parser.ParseHCLFile(caConfigPath)
|
file, diags := parser.ParseHCLFile(caConfigPath)
|
||||||
@@ -172,6 +181,14 @@ func LoadCAConfig() error {
|
|||||||
if err := config.CA.Validate(); err != nil {
|
if err := config.CA.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the state file is specified as an absolute path, use it directly.
|
||||||
|
if filepath.IsAbs(config.CA.Paths.StatePath) {
|
||||||
|
caStatePath = config.CA.Paths.StatePath
|
||||||
|
} else {
|
||||||
|
caStatePath = filepath.Join(filepath.Dir(caConfigPath), config.CA.Paths.StatePath)
|
||||||
|
}
|
||||||
|
|
||||||
caConfig = &config.CA
|
caConfig = &config.CA
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -329,6 +346,7 @@ func InitCA() error {
|
|||||||
|
|
||||||
err = LoadCAConfig()
|
err = LoadCAConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,12 +550,8 @@ func issueSingleCertificate(def CertificateDefinition) error {
|
|||||||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
||||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||||
|
|
||||||
basename := def.Name
|
certFile := filepath.Join(caConfig.Paths.Certificates, def.Name+".crt.pem")
|
||||||
if basename == "" {
|
keyFile := filepath.Join(caConfig.Paths.PrivateKeys, def.Name+".key.pem")
|
||||||
basename = def.Subject
|
|
||||||
}
|
|
||||||
certFile := filepath.Join(caConfig.Paths.Certificates, basename+".crt.pem")
|
|
||||||
keyFile := filepath.Join(caConfig.Paths.PrivateKeys, basename+".key.pem")
|
|
||||||
if err := SavePEM(certFile, certPEM, false); err != nil {
|
if err := SavePEM(certFile, certPEM, false); err != nil {
|
||||||
return fmt.Errorf("error saving certificate: %v", err)
|
return fmt.Errorf("error saving certificate: %v", err)
|
||||||
}
|
}
|
||||||
@@ -562,7 +576,9 @@ Certificate:
|
|||||||
}
|
}
|
||||||
caState.UpdateCAStateAfterIssue(
|
caState.UpdateCAStateAfterIssue(
|
||||||
caConfig.SerialType,
|
caConfig.SerialType,
|
||||||
basename,
|
def.Name,
|
||||||
|
def.Subject,
|
||||||
|
def.Type,
|
||||||
serialNumber,
|
serialNumber,
|
||||||
validityDur,
|
validityDur,
|
||||||
)
|
)
|
||||||
|
24
certdb.go
24
certdb.go
@@ -10,7 +10,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,6 +25,8 @@ type CAState struct {
|
|||||||
// CertificateRecord represents a single certificate record in the CA state
|
// CertificateRecord represents a single certificate record in the CA state
|
||||||
type CertificateRecord struct {
|
type CertificateRecord struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Type string `json:"type"`
|
||||||
Issued string `json:"issued"`
|
Issued string `json:"issued"`
|
||||||
Expires string `json:"expires"`
|
Expires string `json:"expires"`
|
||||||
Serial string `json:"serial"`
|
Serial string `json:"serial"`
|
||||||
@@ -33,15 +34,14 @@ type CertificateRecord struct {
|
|||||||
RevokeReason int `json:"revokeReason,omitempty"`
|
RevokeReason int `json:"revokeReason,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func caStatePath() string {
|
// func caStatePath() string {
|
||||||
return filepath.Join(filepath.Dir(caConfigPath), caConfig.GetStateFileName())
|
// return filepath.Join(filepath.Dir(caConfigPath), caConfig.GetStateFileName())
|
||||||
}
|
// }
|
||||||
|
|
||||||
// LoadCAState loads the CA state from a JSON file
|
// LoadCAState loads the CA state from a JSON file
|
||||||
func LoadCAState() error {
|
func LoadCAState() error {
|
||||||
path := caStatePath()
|
fmt.Printf("Loading CA state from %s\n", caStatePath)
|
||||||
fmt.Printf("Loading CA state from %s\n", path)
|
f, err := os.Open(caStatePath)
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ func LoadCAState() error {
|
|||||||
// SaveCAState saves the CA state to a JSON file
|
// SaveCAState saves the CA state to a JSON file
|
||||||
func SaveCAState() error {
|
func SaveCAState() error {
|
||||||
caState.UpdatedAt = time.Now().UTC().Format(time.RFC3339)
|
caState.UpdatedAt = time.Now().UTC().Format(time.RFC3339)
|
||||||
f, err := os.Create(caStatePath())
|
f, err := os.Create(caStatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ func SaveCAState() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCAStateAfterIssue updates the CA state JSON after issuing a certificate
|
// UpdateCAStateAfterIssue updates the CA state JSON after issuing a certificate
|
||||||
func (s *CAState) UpdateCAStateAfterIssue(serialType, basename string, serialNumber any, validity time.Duration) error {
|
func (s *CAState) UpdateCAStateAfterIssue(serialType, name string, subject string, certType string, serialNumber any, validity time.Duration) error {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
fmt.Fprintf(os.Stderr, "FATAL: CAState is nil in UpdateCAStateAfterIssue. This indicates a programming error.\n")
|
fmt.Fprintf(os.Stderr, "FATAL: CAState is nil in UpdateCAStateAfterIssue. This indicates a programming error.\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -84,17 +84,19 @@ func (s *CAState) UpdateCAStateAfterIssue(serialType, basename string, serialNum
|
|||||||
default:
|
default:
|
||||||
serialStr = fmt.Sprintf("%v", serialNumber)
|
serialStr = fmt.Sprintf("%v", serialNumber)
|
||||||
}
|
}
|
||||||
s.AddCertificate(basename, issued, expires, serialStr)
|
s.AddCertificate(name, subject, certType, issued, expires, serialStr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CAState) AddCertificate(name, issued, expires, serial string) {
|
func (s *CAState) AddCertificate(name, subject, certType, issued, expires, serial string) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
fmt.Fprintf(os.Stderr, "FATAL: CAState is nil in AddCertificate. This indicates a programming error.\n")
|
fmt.Fprintf(os.Stderr, "FATAL: CAState is nil in AddCertificate. This indicates a programming error.\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
rec := CertificateRecord{
|
rec := CertificateRecord{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
Subject: subject,
|
||||||
|
Type: certType,
|
||||||
Issued: issued,
|
Issued: issued,
|
||||||
Expires: expires,
|
Expires: expires,
|
||||||
Serial: serial,
|
Serial: serial,
|
||||||
|
@@ -9,5 +9,6 @@ ca "example_ca" {
|
|||||||
paths {
|
paths {
|
||||||
certificates = "certs"
|
certificates = "certs"
|
||||||
private_keys = "private"
|
private_keys = "private"
|
||||||
|
state_file = "ca_state.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
main.go
2
main.go
@@ -48,7 +48,7 @@ func main() {
|
|||||||
rootCmd.PersistentFlags().BoolVar(&overwrite, "overwrite", false, "Allow overwriting existing files")
|
rootCmd.PersistentFlags().BoolVar(&overwrite, "overwrite", false, "Allow overwriting existing files")
|
||||||
rootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "Print detailed information about each processed certificate")
|
rootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "Print detailed information about each processed certificate")
|
||||||
rootCmd.PersistentFlags().BoolVar(&dryRun, "dry-run", false, "Validate and show what would be created, but do not write files (batch mode)")
|
rootCmd.PersistentFlags().BoolVar(&dryRun, "dry-run", false, "Validate and show what would be created, but do not write files (batch mode)")
|
||||||
rootCmd.PersistentFlags().StringVar(&caConfigPath, "config-path", "ca_config.hcl", "Path to CA configuration file")
|
rootCmd.PersistentFlags().StringVar(&caConfigPath, "config", "ca_config.hcl", "Path to CA configuration file")
|
||||||
|
|
||||||
// lab-ca initca command
|
// lab-ca initca command
|
||||||
var initCmd = &cobra.Command{
|
var initCmd = &cobra.Command{
|
||||||
|
Reference in New Issue
Block a user