A complete, variable-driven toolkit for deploying a two-tier PKI (Root CA → Intermediate CA) inside a systemd-nspawn container on a UniFi Dream Machine Pro running UniFi OS 3.x+.
Since UniFi OS 3.0, Ubiquiti removed Docker/Podman support. This project uses systemd-nspawn (the community-supported alternative via unifios-utilities) to run an isolated Debian container that hosts your internal PKI.
UDM Pro (Host)
├── systemd-nspawn container (isolated VLAN)
│ └── /root/ca/
│ ├── Root CA (offline-capable, 10-year validity)
│ ├── Intermediate CA (day-to-day issuing, 5-year validity)
│ └── Leaf certificates (server, RADIUS, VPN, client, code signing)
├── On-boot persistence (survives firmware updates)
└── macvlan bridge (host ↔ container communication)
| Type | Script | Key | Validity | EKU |
|---|---|---|---|---|
| Internal HTTPS | issue-server-cert.sh |
2048-bit | 825 days | serverAuth |
| RADIUS/EAP-TLS | issue-radius-cert.sh |
2048-bit | 825 days | serverAuth, clientAuth, EAP over LAN |
| VPN Server | issue-vpn-cert.sh server |
2048-bit | 825 days | serverAuth |
| VPN Client | issue-vpn-cert.sh client |
2048-bit | 825 days | clientAuth |
| 802.1X Client | issue-client-cert.sh |
2048-bit | 365 days | clientAuth |
| Code Signing | issue-codesigning-cert.sh |
4096-bit | 3 years | codeSigning |
cp .env.example .env
vim .env # Fill in your org name, IPs, VLAN, CA names, etc.Create the PKI VLAN in Settings → Networks → Create New Network with the VLAN ID and subnet defined in your .env.
scp -r . root@<UDM_IP>:/tmp/udm-nspawn-pki/
ssh root@<UDM_IP>
cd /tmp/udm-nspawn-pki
bash scripts/01-setup-container.shmachinectl shell <container_name>
cd /tmp/udm-nspawn-pki
bash scripts/02-setup-pki.sh
exitbash scripts/03-export-rootca.sh
# Root CA files appear in /tmp/ — SCP to your workstationbash scripts/04-preflight-check.sh.
├── .env.example # Template — copy to .env and configure
├── .gitignore # Excludes .env, private keys, databases
├── README.md
├── LICENSE
├── docs/
│ ├── deployment-guide.md # Full step-by-step deployment reference
│ ├── operations.md # Certificate lifecycle, CRL, backup procedures
│ └── firewall.md # VLAN isolation and firewall rule guidance
└── scripts/
├── 00-env-helper.sh # Shared environment loader (sources .env)
├── 01-setup-container.sh # Creates nspawn container on UDM host
├── 02-setup-pki.sh # Builds Root CA + Intermediate CA inside container
├── 03-export-rootca.sh # Exports Root CA to UDM host /tmp/
├── 04-preflight-check.sh # Comprehensive validation of entire deployment
├── issue-server-cert.sh # Issue internal HTTPS/TLS certificates
├── issue-radius-cert.sh # Issue RADIUS/EAP-TLS certificates
├── issue-vpn-cert.sh # Issue VPN server or client certificates
├── issue-client-cert.sh # Issue 802.1X client auth certs (with .p12)
├── issue-codesigning-cert.sh # Issue code signing certificates
├── revoke-cert.sh # Revoke a certificate and regenerate CRL
├── verify-cert.sh # Verify any certificate against the chain
└── backup-ca.sh # Encrypted backup of the entire CA
- All site-specific data (IPs, org names, VLAN IDs, CA names) lives in
.env— never hardcoded .envis gitignored — your configuration never enters version control- Private keys are gitignored —
.key.pem,.p12,.pfxfiles are excluded - Root CA key should be taken offline after generating the Intermediate CA (see
docs/operations.md) - The container is isolated on its own VLAN with minimal network exposure
- UniFi Dream Machine Pro (or SE) running UniFi OS 3.x+
- SSH access with root
- A dedicated VLAN for PKI isolation
- ~2 GB free space on
/data
- unifios-utilities nspawn-container — Community container guide for UniFi OS 3.x+
- systemd-nspawn man page
- OID 1.3.6.1.5.5.7.3.14 (EAP over LAN)
- perplexity-windows-xpc — Perplexity AI for Windows — PowerShell, system tray, Office integration
- perplexity-xpc — PerplexityXPC broker service, tray app, MCP server management
- perplexity-connector — Perplexity Sonar API connector — CLI, streaming, async, structured output
- atera-dashboard — Atera RMM NOC dashboard — React + Vite + Tailwind + Recharts
- atera-connector — Atera RMM API v3 connector — Python CLI with full CRUD support
- synology-connector — Synology DSM Web API connector — 40 CLI actions across 10 modules
- wireguard-vpn-spk — WireGuard VPN Tunnel SPK for Synology DS220+ (userspace wireguard-go)
- nas-git-sync — Automated GitHub to Synology NAS repo sync script
- Ubiquiti UniFi OS — The platform this project runs on
- systemd-nspawn — Lightweight container runtime used for PKI isolation
- OpenSSL — Core cryptographic engine for certificate generation
- Bash — Shell scripting runtime powering all automation scripts
Brian Vicente — Network Coordinator & Cybersecurity Admin
Built with Perplexity Computer
GitHub: @brianatalliance
MIT