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
348 lines
13 KiB
Markdown
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? |