# sys-fips: Single-Node FIPS ProxyVM for Qubes OS ## Overview Simplify the current `fips_setup` to run **one FIPS node** in a Qubes ProxyVM called `sys-fips`. This VM slots into the Qubes network chain just like `sys-vpn`: ``` AppVM → sys-fips → sys-firewall → sys-net → Internet ↘ FIPS Mesh (encrypted overlay) ``` To get a second independent FIPS node, simply **clone** `sys-fips` in dom0 — each clone gets its own identity and peer config. ## Architecture ```mermaid graph LR A[AppVM-1] --> SF[sys-fips] B[AppVM-2] --> SF SF -->|regular traffic| FW[sys-firewall] SF -->|fips0 TUN| MESH[FIPS Mesh] FW --> NET[sys-net] NET --> INET[Internet] ``` ### What changes from the current plan | Current plan | Revised plan | |---|---| | Two FIPS nodes per VM - fips0 + fips1 | **One FIPS node** per VM - fips0 only | | Two configs - node-a.yaml, node-b.yaml | **One config** - fips.yaml | | Two keypairs generated | **One keypair** - persistent identity | | Localhost peering between nodes | No local peering needed | | Complex start/stop for two processes | **Single systemd service** | | Clone not considered | **Clone-friendly** design | ### Network flow inside sys-fips ```mermaid graph TD subgraph sys-fips ProxyVM VIF[vif* - from AppVMs] -->|fd00::/8 traffic| FW6[ip6tables FORWARD] FW6 --> FIPS0[fips0 TUN interface] VIF -->|regular traffic| ETH0[eth0 - to sys-firewall] DNS53[dnsmasq :53] -->|.fips queries| DNS5354[FIPS DNS :5354] DNS53 -->|other queries| UPSTREAM[upstream DNS] end FIPS0 --> MESH[FIPS Mesh Network] ETH0 --> SYSFW[sys-firewall] ``` ## Revised file structure ``` fips_setup/ ├── README.md # Updated overview and instructions ├── configs/ │ └── fips.yaml # Single node config template ├── scripts/ │ ├── 01-dom0-create-sys-fips.sh # Create sys-fips ProxyVM in dom0 │ ├── 02-install-fips.sh # Install binary + deps in sys-fips │ ├── 03-configure-identity.sh # Generate or import keypair │ ├── 04-start-fips.sh # Start single FIPS node + routing │ ├── 05-stop-fips.sh # Stop FIPS node + cleanup │ ├── 06-configure-dns.sh # dnsmasq for .fips resolution │ ├── 07-route-appvms.sh # ip6tables forwarding for AppVMs │ ├── 08-add-peer.sh # Add an external peer to config │ └── test-connectivity.sh # Unified test script └── plans/ └── sys-fips-plan.md # This file ``` ## Implementation plan ### 1. Simplify config to single node Replace `fips-node-a.yaml` and `fips-node-b.yaml` with a single `fips.yaml`: ```yaml node: identity: persistent: true # Auto-generates keypair on first run, reuses on restart tun: enabled: true name: fips0 mtu: 1280 dns: enabled: true bind_addr: "127.0.0.1" port: 5354 transports: udp: bind_addr: "0.0.0.0:2121" mtu: 1472 peers: [] ``` Key difference: `persistent: true` instead of explicit `nsec`. FIPS auto-generates `/etc/fips/fips.key` and `/etc/fips/fips.pub` on first run. When you clone the VM, the clone starts fresh and generates its own identity. ### 2. Rewrite dom0 script - 01-dom0-create-sys-fips.sh - Rename VM from `fips-proxy` to `sys-fips` to follow Qubes naming convention - Accept optional `netvm` parameter - defaults to `sys-firewall` but supports chaining through `sys-vpn` - Same `provides_network=True` setup ### 3. Simplify install script - 02-install-fips.sh - Same as current but also installs the upstream systemd service files from FIPS packaging - Installs `fips.service` and `fips-dns.service` from the FIPS repo packaging directory - Copies the single `fips.yaml` config template ### 4. New identity script - 03-configure-identity.sh - If `/etc/fips/fips.key` exists, show current npub and ask to keep or regenerate - If not, let FIPS generate on first start via `persistent: true` - Option to import an existing nsec for a known identity - This is what makes cloning work: after cloning, run this script to get a fresh identity ### 5. Simplify start/stop - 04-start-fips.sh / 05-stop-fips.sh - Start: `systemctl start fips` — uses the upstream systemd unit - Enable IPv6 forwarding - Add `fd00::/8 → fips0` route - Wait for `fips0` TUN to appear - Show npub and IPv6 address - Stop: `systemctl stop fips` + cleanup routes ### 6. DNS - 06-configure-dns.sh - Same dnsmasq approach but simplified — only one FIPS DNS backend on port 5354 - No changes needed from current plan ### 7. Routing - 07-route-appvms.sh - Same ip6tables forwarding rules but only for `fips0` — no `fips1` - Persist via `/rw/config/rc.local` ### 8. New peer management - 08-add-peer.sh - Interactive script to add a peer to `/etc/fips/fips.yaml` - Takes npub, alias, and address as arguments - Appends to the peers list in the YAML config - Restarts FIPS to pick up the new peer ### 9. Unified test script - test-connectivity.sh - Merge `test-local.sh`, `test-from-appvm.sh`, and `test-full-suite.sh` into one - Auto-detects whether running inside sys-fips or an AppVM - Tests: binary installed, fips0 up, IPv6 address, DNS resolution, peer connectivity ## Cloning workflow Once `sys-fips` is working: ```bash # In dom0: qvm-clone sys-fips sys-fips-2 # Start sys-fips-2, open terminal: sudo bash 03-configure-identity.sh # Generates new keypair sudo systemctl restart fips # Now you have two independent FIPS nodes: # sys-fips → identity A, peers X # sys-fips-2 → identity B, peers Y # Point AppVMs at whichever you want: qvm-prefs work-vm netvm sys-fips qvm-prefs personal-vm netvm sys-fips-2 ``` ## Qubes network chaining sys-fips can chain through a VPN qube: ``` AppVM → sys-fips → sys-vpn → sys-firewall → sys-net → Internet ``` Set this up in dom0: ```bash qvm-prefs sys-fips netvm sys-vpn ``` FIPS UDP traffic exits through the VPN tunnel, adding another layer of transport privacy. The FIPS mesh encryption is independent of the VPN — even if the VPN is compromised, FIPS traffic remains encrypted end-to-end. ## What gets deleted - `configs/fips-node-a.yaml` — replaced by single `configs/fips.yaml` - `configs/fips-node-b.yaml` — no longer needed - `scripts/03-generate-node-configs.sh` — replaced by `03-configure-identity.sh` - `scripts/test-local.sh` — merged into `test-connectivity.sh` - `scripts/test-from-appvm.sh` — merged into `test-connectivity.sh` - `scripts/test-full-suite.sh` — merged into `test-connectivity.sh`