Skip to content

Certificate Bootstrap & Storage Management #206

@jmgilman

Description

@jmgilman

Certificate Bootstrap & Storage Management

Overview

Implement the certificate bootstrap process and local storage management system. This includes handling the initial manually-provisioned certificate, transitioning to API-managed certificates, and managing the file system operations for certificate storage with proper backup and retention policies.

Requirements

Storage Structure Implementation

Create and manage the following directory structure:

/etc/certs/
├── bootstrap/                 # Bootstrap certificate (manually provisioned)
│   ├── cert.pem              # Bootstrap certificate
│   ├── key.pem               # Bootstrap private key
│   └── ca-chain.pem          # CA certificate chain
├── current/
│   ├── cert.pem              # Current API-managed certificate
│   ├── key.pem               # Current private key
│   ├── ca-chain.pem          # CA certificate chain
│   └── serial                # Serial number for renewal tracking
├── backup/
│   ├── cert-20250901.pem     # Backup certificates (by date)
│   ├── key-20250901.pem      # Backup private keys
│   └── ca-chain-20250901.pem # Backup CA chains
├── pending/
│   ├── cert.pem              # New certificate being validated
│   └── key.pem               # New private key
└── logs/
    └── rotation.log          # Certificate rotation audit log

Module Structure

Implement the following modules:

internal/
├── bootstrap/
│   └── bootstrap.go          # Bootstrap certificate handling
├── storage/
│   ├── filesystem.go         # File system operations
│   └── permissions.go        # Permission management

Bootstrap Process

Implement bootstrap certificate handling in internal/bootstrap/bootstrap.go:

type BootstrapManager struct {
    config     *BootstrapConfig
    storage    *storage.Manager
    logger     *slog.Logger
}

type BootstrapStatus struct {
    HasBootstrap      bool
    HasCurrent        bool
    BootstrapValid    bool
    CurrentValid      bool
    NeedsTransition   bool
}

// Core functions to implement:
func (b *BootstrapManager) ValidateBootstrapCertificate() error
func (b *BootstrapManager) GetBootstrapStatus() (*BootstrapStatus, error)
func (b *BootstrapManager) IsBootstrapCertificate(cert *x509.Certificate) bool
func (b *BootstrapManager) ShouldTransitionFromBootstrap() bool
func (b *BootstrapManager) MarkTransitionComplete() error

Bootstrap logic requirements:

  • Validate bootstrap certificate exists and is valid
  • Detect whether current certificate is bootstrap or API-managed
  • Track transition state (never re-bootstrap once transitioned)
  • Bootstrap certificates are identified by:
    • Location in /etc/certs/bootstrap/
    • Lack of serial number file
    • Not tracked by Certificate API

Storage Manager

Implement storage operations in internal/storage/filesystem.go:

type StorageManager struct {
    basePath    string
    permissions *PermissionConfig
    logger      *slog.Logger
    mutex       sync.RWMutex
}

type Certificate struct {
    Certificate  []byte
    PrivateKey   []byte
    CAChain      []byte
    SerialNumber string
}

// Core functions to implement:
func (s *StorageManager) Initialize() error
func (s *StorageManager) ReadBootstrapCertificate() (*Certificate, error)
func (s *StorageManager) ReadCurrentCertificate() (*Certificate, error)
func (s *StorageManager) WritePendingCertificate(cert *Certificate) error
func (s *StorageManager) PromotePendingToCurrent() error
func (s *StorageManager) BackupCurrentCertificate() error
func (s *StorageManager) CleanupOldBackups(retentionCount int) error
func (s *StorageManager) GetCertificateExpiry(location string) (time.Time, error)

Atomic Certificate Replacement

Implement atomic file operations to prevent partial updates:

func (s *StorageManager) PromotePendingToCurrent() error {
    // 1. Validate pending certificate exists and is valid
    // 2. Create backup of current certificate with timestamp
    // 3. Prepare new certificate files in temporary location
    // 4. Use atomic rename operations for each file
    // 5. Update serial number file
    // 6. Verify successful replacement
    // 7. Clean up temporary files
}

Requirements:

  • Use os.Rename() for atomic operations
  • Never leave system in inconsistent state
  • Rollback on any failure during replacement
  • Maintain audit log of all operations

Permission Management

Implement permission handling in internal/storage/permissions.go:

type PermissionManager struct {
    certFileMode  os.FileMode  // 0644
    keyFileMode   os.FileMode  // 0600
    directoryMode os.FileMode  // 0755
    owner         string       // "root"
    group         string       // "ssl-cert"
}

// Core functions to implement:
func (p *PermissionManager) SetFilePermissions(path string, isPrivateKey bool) error
func (p *PermissionManager) SetDirectoryPermissions(path string) error
func (p *PermissionManager) ValidatePermissions(path string) error
func (p *PermissionManager) SetOwnership(path string) error

Backup and Retention

Implement backup management:

type BackupManager struct {
    storage         *StorageManager
    retentionCount  int
    backupPath      string
}

func (b *BackupManager) CreateBackup(cert *Certificate) error {
    // Generate timestamp-based filename
    // Copy certificate, key, and CA chain to backup directory
    // Maintain permissions on backup files
    // Return error if backup fails
}

func (b *BackupManager) CleanupOldBackups() error {
    // List all backup files
    // Sort by timestamp
    // Keep only the most recent N backups
    // Securely delete old backups (overwrite before deletion)
}

Certificate Validation

Implement certificate validation utilities:

func ValidateCertificate(certPEM []byte) error {
    // Parse PEM-encoded certificate
    // Verify certificate format
    // Check expiry date
    // Validate key usage extensions
    // Return detailed error on validation failure
}

func ValidatePrivateKeyMatch(certPEM, keyPEM []byte) error {
    // Verify private key matches certificate
    // Check key strength (minimum RSA 2048 or ECDSA P-256)
    // Return error if mismatch or weak key
}

func ValidateCAChain(chainPEM []byte) error {
    // Parse CA chain
    // Verify chain validity
    // Check for expired certificates in chain
}

Acceptance Criteria

  1. Storage Initialization

    • Creates all required directories with correct permissions
    • Handles existing directory structures gracefully
    • Validates bootstrap certificate if present
  2. Bootstrap Handling

    • Correctly identifies bootstrap vs API-managed certificates
    • Tracks transition state persistently
    • Never attempts to renew bootstrap certificate
    • Successfully transitions from bootstrap to API-managed cert
  3. File Operations

    • All certificate replacements are atomic
    • No partial updates possible
    • Rollback on failure maintains previous state
    • Proper file permissions set on all operations
  4. Backup Management

    • Creates timestamped backups before replacement
    • Maintains configured number of backups
    • Cleans up old backups automatically
    • Preserves permissions on backup files
  5. Certificate Validation

    • Validates certificate format and expiry
    • Verifies private key matches certificate
    • Checks CA chain validity
    • Provides detailed validation errors

Security Requirements

  • Private keys must have 0600 permissions (readable only by owner)
  • Certificate files must have 0644 permissions
  • Secure deletion of old private keys (overwrite before removal)
  • Audit log for all certificate operations
  • No private keys in logs or error messages

Error Handling

  • Detailed error messages for troubleshooting
  • Distinguish between recoverable and fatal errors
  • Log all operations with outcomes
  • Preserve system state on errors

Testing Requirements

  • Unit tests for all storage operations
  • Test atomic replacement with simulated failures
  • Test permission setting and validation
  • Test backup creation and cleanup
  • Test bootstrap detection and transition logic
  • Integration tests with actual file system operations
  • Test concurrent access scenarios (use mutex)

Dependencies

  • Standard Go crypto/x509 package for certificate handling
  • Standard os package for file operations
  • No external dependencies required for this module

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions