certauth/docs/PLAN.md
Jarian Cottingham 4bd538b217 fix: YubiKey CA signing, remove inline API copies, env-driven config
Critical: build_leaf_cert self-signed leaves with the leaf key instead of
the YubiKey-held Intermediate CA key. Now extracts TBS, signs via
pkcs11-tool (ECDSA-SHA384), reassembles, and verifies against the
intermediate CA public key before returning.

- setup-certauth.sh: ~1100 lines of stale inline api/ copies replaced with
  copy-from-repo (single source of truth); writes private
  /etc/certauth/certauth.env (0600); DB init loads env, no more
  swallowed errors; systemd unit gets EnvironmentFile=
- config.py: YubiKey serials no longer hard-coded (env, fail closed);
  aarch64-only PKCS#11 path replaced with arch-neutral default; all
  paths env-overridable (CERTAUTH_*)
- main.py: removed dead fastapi.security.CSRFProtection import (crashed
  startup); module-relative static/templates dirs; created_by resolved
  from the authenticated user instead of hard-coded 1; unclosed file
  handles fixed; domain_id 0 stored as NULL (FK bug)
- models.py: certificates.domain_id FK pointed at users(id), now domains(id)
- login: CSRF token now actually sent and validated
- tests: 23 tests (auth, API flows, DER helpers, signing pipeline)
- README, LICENSE, requirements.txt, pyproject.toml
2026-08-20 22:03:01 +00:00

348 lines
13 KiB
Markdown

# Certificate Authority Infrastructure Plan
## Overview
Build a secure, self-contained Certificate Authority (CA) infrastructure on an isolated Ubuntu machine (`192.168.8.248`) using two YubiKeys for hardware-secured key storage, with a web-based Key Vault UI for certificate management.
---
## Architecture
```
┌─────────────────────────────────────────────────────┐
│ certauth Machine (192.168.8.248) │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ YubiKey #1 │ │ YubiKey #2 │ │
│ │ Root CA Key │ │ Intermediate │ │
│ │ (PIN+Touch) │ │ CA Key │ │
│ └──────┬───────┘ │ (PIN+Touch) │ │
│ │ └──────┬───────┘ │
│ │ │ │
│ Root Certificate Intermediate Cert │
│ (signed on YK1) (signed by Root) │
│ │ │
│ ┌─────────▼─────────┐ │
│ │ Certificate API │ │
│ │ + Key Vault UI │ │
│ └─────────┬─────────┘ │
│ │ │
│ ┌─────────▼─────────┐ │
│ │ Certificate DB │ │
│ │ (SQLite/Postgres) │ │
│ └───────────────────┘ │
│ │
│ Firewall: Only local network (192.168.8.0/24) │
│ No outbound internet except Ubuntu package updates │
└─────────────────────────────────────────────────────┘
```
---
## Phase 1: Machine Hardening
### 1.1 Base System Configuration
- Fresh Ubuntu Server installation verification
- Create dedicated `certauth` user with sudo privileges
- Disable root login, enforce key-based SSH
- Configure SSH to only accept connections from local network (192.168.8.0/24)
- Set up UFW firewall:
- Allow SSH (port 22) from 192.168.8.0/24 only
- Allow HTTPS (port 443) for Key Vault UI from 192.168.8.0/24 only
- Allow HTTP (port 80) for redirect from 192.168.8.0/24 only
- Deny all other inbound traffic
- Deny all outbound traffic except:
- Ubuntu package repositories (archive.ubuntu.com, security.ubuntu.com)
- DNS (port 53)
- NTP (port 123)
- Local network traffic (192.168.8.0/24)
### 1.2 System Hardening
- Automatic security updates enabled
- Fail2ban for SSH protection
- Auditd for security auditing
- Remove unnecessary packages and services
- ConfigureAppArmor profiles
- Set up encrypted swap
- Configure secure kernel parameters (sysctl)
### 1.3 YubiKey Preparation
- Install `yubikey-manager`, `pcscd`, `opensc`, `gnutls-bin`
- Verify YubiKey connectivity and PIV applet
- Test both YubiKeys are recognized
---
## Phase 2: YubiKey #1 - Root CA
### 2.1 YubiKey Configuration
- Set PIV PIN (strong, 9-16 chars)
- Set PIV PUK (for PIN reset)
- Set management key
- Configure touch policy to `callback` or `fixed-on` for the signing key slot
- Generate RSA 4096-bit (or EC P-384) key pair **on the YubiKey** (never leaves device)
- Key stored in PIV slot `9c` (signing)
### 2.2 Root Certificate Generation
- Generate self-signed Root CA certificate on YubiKey
- Validity: 20-30 years
- Key usage: Certificate Signing, CRL Signing
- Basic constraints: CA:TRUE, pathlen:1
- Subject: `CN=certauth Root CA, O=Home, C=US` (customizable)
- Store Root CA certificate in `/etc/ssl/ca/root/`
### 2.3 Security
- Root CA certificate is public and stored on disk
- Root private key **never** leaves YubiKey #1
- YubiKey #1 can be physically removed and stored offline when not in use
- All signing operations require PIN + physical touch
---
## Phase 3: YubiKey #2 - Intermediate CA
### 3.1 YubiKey Configuration
- Set PIV PIN (strong, 9-16 chars, different from YK1)
- Set PIV PUK
- Set management key
- Configure touch policy to `callback` or `fixed-on` for signing key slot
- Generate RSA 4096-bit (or EC P-384) key pair **on the YubiKey**
- Key stored in PIV slot `9c` (signing)
### 3.2 Intermediate Certificate Generation
- Generate CSR on YubiKey #2
- Sign CSR using YubiKey #1 (Root CA) — requires PIN + touch on YK1
- Validity: 10-15 years
- Key usage: Certificate Signing, CRL Signing
- Basic constraints: CA:TRUE, pathlen:0
- Subject: `CN=certauth Intermediate CA, O=Home, C=US`
- Store Intermediate CA certificate in `/etc/ssl/ca/intermediate/`
### 3.3 Security
- Intermediate private key **never** leaves YubiKey #2
- All signing operations require PIN + physical touch
- Certificate chain: Root CA → Intermediate CA → Leaf Certificates
---
## Phase 4: Certificate Management API & Key Vault UI
### 4.1 Technology Stack
- **Backend**: Python FastAPI (lightweight, async, good OpenAPI support)
- **Database**: SQLite (simple, file-based, sufficient for this scale)
- **Frontend**: HTMX + Tailwind CSS (minimal JS, server-rendered, fast)
- **Reverse Proxy**: Caddy (automatic HTTPS, simple config)
- **Process Manager**: systemd
### 4.2 API Endpoints
```
Authentication:
POST /api/auth/login - Admin login (username + password + TOTP)
POST /api/auth/logout - Logout
GET /api/auth/verify - Verify API key
Domain Management:
POST /api/domains - Register a new domain (e.g., *.example.com)
GET /api/domains - List all registered domains
GET /api/domains/{id} - Get domain details
DELETE /api/domains/{id} - Revoke domain registration
Certificate Management:
POST /api/certs/request - Request a new certificate for a domain
GET /api/certs - List all certificates
GET /api/certs/{id} - Get certificate details
GET /api/certs/{id}/download - Download certificate + chain
POST /api/certs/{id}/revoke - Revoke a certificate
POST /api/certs/{id}/renew - Renew a certificate
Health & Status:
GET /api/health - Health check
GET /api/yubikeys - YubiKey status (connected, slots)
```
### 4.3 Key Vault UI Features
- Dashboard showing certificate inventory, expiring certs, YubiKey status
- Domain registration form with validation
- Certificate request workflow:
1. Select registered domain
2. Specify SANs (Subject Alternative Names)
3. Choose validity period
4. Confirm request (triggers signing workflow)
- Certificate signing workflow:
1. System generates CSR locally
2. Admin inserts YubiKey #2, enters PIN, touches YubiKey
3. Certificate is signed and stored
4. Certificate + chain available for download
- Certificate download page with PEM files
- Audit log viewer
- Settings page for CA configuration
### 4.4 Signing Workflow (Human-in-the-Loop)
Since YubiKey requires physical touch + PIN, the signing process is semi-interactive:
```
1. Admin requests certificate via UI/API
2. System generates CSR and stores pending request
3. Admin runs: certauth sign --request <id>
- This prompts for YubiKey #2 PIN
- Admin touches YubiKey #2 when prompted
- Certificate is signed by Intermediate CA
4. Signed certificate stored in database
5. Status updated to "issued"
6. Certificate available for download
```
Alternative: Web-based signing using WebAuthn/CTAP2 (future enhancement)
### 4.5 API Key Authentication
- Admin generates API keys via UI
- API keys are scoped (read-only, sign, admin)
- Services authenticate with API key header: `X-API-Key: <key>`
- API keys stored hashed in database
---
## Phase 5: Security Hardening
### 5.1 Application Security
- All API endpoints require authentication
- Rate limiting on API endpoints
- Input validation and sanitization
- SQL injection prevention (parameterized queries)
- CSRF protection for UI
- Content Security Policy headers
- Secure cookie flags (HttpOnly, Secure, SameSite)
### 5.2 Data Protection
- Database encrypted at rest (LUKS encrypted partition)
- Certificate files stored with restricted permissions (root:certauth, 0640)
- No secrets in plaintext logs
- API keys hashed (bcrypt)
- Passwords hashed (bcrypt)
### 5.3 Network Security
- Caddy configured with strong TLS settings
- Only binds to local network interfaces
- No services exposed to internet
- Outbound connections restricted by firewall
### 5.4 Operational Security
- Audit logging of all certificate operations
- Regular backup of certificates and configuration (encrypted)
- Monitoring for failed authentication attempts
- Log rotation and retention policy
---
## Phase 6: Setup Script
### 6.1 Automated Setup Script (`setup-certauth.sh`)
A single script that can provision a fresh Ubuntu machine:
- Validates prerequisites (Ubuntu version, YubiKeys present)
- Runs all hardening steps
- Configures YubiKeys (interactive prompts for PINs)
- Generates CA certificates
- Installs and configures the Certificate API
- Sets up firewall rules
- Creates systemd services
- Outputs configuration summary and credentials
### 6.2 Configuration File (`certauth.conf`)
All configurable values in one file:
- CA subject information
- Certificate validity periods
- Network configuration
- Admin credentials
- YubiKey PIN policies
### 6.3 Documentation
- README with architecture overview
- OPERATIONS.md with daily usage instructions
- TROUBLESHOOTING.md with common issues
- BACKUP.md with backup and restore procedures
---
## Directory Structure
```
certauth/
├── setup-certauth.sh # Main setup script
├── certauth.conf # Configuration file
├── README.md # Project documentation
├── OPERATIONS.md # Operations guide
├── scripts/
│ ├── harden-system.sh # System hardening
│ ├── configure-yubikey.sh # YubiKey setup
│ ├── generate-ca.sh # CA certificate generation
│ ├── install-api.sh # API installation
│ └── configure-firewall.sh # Firewall setup
├── api/
│ ├── main.py # FastAPI application
│ ├── models.py # Database models
│ ├── auth.py # Authentication
│ ├── signing.py # Certificate signing logic
│ ├── templates/ # HTML templates
│ ├── static/ # Static assets
│ └── requirements.txt # Python dependencies
├── systemd/
│ ├── certauth-api.service # API service unit
│ └── certauth-sign.timer # Signing timer unit
├── caddy/
│ └── Caddyfile # Caddy configuration
├── ssl/
│ └── openssl.cnf # OpenSSL configuration
└── tests/
└── test_api.py # API tests
```
---
## Implementation Order
1. **Plan Review** — You review and approve this plan
2. **Machine Hardening** — SSH into machine, verify state, apply hardening
3. **YubiKey Setup** — Configure both YubiKeys with Root and Intermediate CAs
4. **API Development** — Build the Certificate Management API and UI
5. **Integration** — Connect API to YubiKeys for signing
6. **Testing** — End-to-end testing of certificate issuance
7. **Setup Script** — Create reproducible setup script
8. **Documentation** — Final documentation and handover
---
## Security Considerations & Trade-offs
### YubiKey Touch Policy
- `callback`: Prompts user to touch (most flexible, requires polling)
- `fixed-on`: Always requires touch (most secure, slightly slower UX)
- **Recommendation**: `fixed-on` for both keys
### Key Algorithm
- RSA 4096: Wider compatibility, larger keys/certs
- EC P-384: Smaller, faster, modern
- **Recommendation**: RSA 4096 for maximum compatibility with all services/browsers
### Signing Workflow
- Fully automated signing is **not possible** with YubiKey touch requirement
- Admin must be present to touch YubiKey and enter PIN
- This is a **feature**, not a limitation — it provides human-in-the-loop security
- For bulk operations, a CLI tool handles batch signing requests
### Offline Root CA
- YubiKey #1 (Root) can be removed after Intermediate CA is created
- Root is only needed if Intermediate CA key is compromised
- This provides true offline root CA capability
---
## Questions for Confirmation
1. **Key algorithm**: RSA 4096 vs EC P-384? (RSA 4096 recommended for compatibility)
2. **Certificate validity**: Root: 25 years, Intermediate: 15 years, Leaf: configurable per-request?
3. **TOTP for admin login**: Enable two-factor authentication?
4. **Backup strategy**: Encrypted backups to local disk only, or also to network share?
5. **Additional domains**: Any specific domains to pre-register beyond `*.example.com`?
6. **CRL/OCSP**: Do we need Certificate Revocation List or OCSP responder? (Adds complexity)
7. **Admin username**: Should the admin account be `certauth` or a different username?