Setup fips in a qube
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
fips/
|
||||
136
README.md
Normal file
136
README.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# sys-fips Setup for Qubes OS
|
||||
|
||||
> Goal: run a **single FIPS node** inside one ProxyVM (`sys-fips`) so any AppVM routed through it can access the FIPS mesh.
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
AppVMs → sys-fips (ProxyVM) → sys-firewall → sys-net → Internet
|
||||
└─ fips0 (TUN) → FIPS mesh (encrypted)
|
||||
```
|
||||
|
||||
This setup is intentionally single-node per VM.
|
||||
If you want another independent FIPS identity/path, clone the VM (for example, `sys-fips2`).
|
||||
|
||||
---
|
||||
|
||||
## Files in this directory
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `README.md` | This guide |
|
||||
| `configs/fips.yaml` | Single-node config template |
|
||||
| `scripts/00-build-fips.sh` | Run in build qube: build `fips`, `fipsctl`, `fipstop` into local `bin/` |
|
||||
| `scripts/01-dom0-create-proxyvm.sh` | Run in dom0: creates `sys-fips` ProxyVM |
|
||||
| `scripts/02-install-fips.sh` | Run in `sys-fips`: installs deps, binaries, and systemd units |
|
||||
| `scripts/03-configure-identity.sh` | Run in `sys-fips`: configures `/etc/fips/fips.yaml`, identity mode |
|
||||
| `scripts/03-generate-node-configs.sh` | Compatibility wrapper to `03-configure-identity.sh` |
|
||||
| `scripts/04-start-fips.sh` | Run in `sys-fips`: starts single FIPS node |
|
||||
| `scripts/04-start-fips-nodes.sh` | Compatibility wrapper to `04-start-fips.sh` |
|
||||
| `scripts/05-stop-fips.sh` | Run in `sys-fips`: stops node + cleanup |
|
||||
| `scripts/05-stop-fips-nodes.sh` | Compatibility wrapper to `05-stop-fips.sh` |
|
||||
| `scripts/06-configure-dns.sh` | Run in `sys-fips`: dnsmasq forwards `.fips` to localhost:5354 and applies Qubes `/32` DNS compatibility patch |
|
||||
| `scripts/07-route-appvms.sh` | Run in `sys-fips`: IPv6/ip6tables forwarding for routed AppVMs |
|
||||
| `scripts/08-add-peer.sh` | Run in `sys-fips`: add static peer to `/etc/fips/fips.yaml` |
|
||||
| `scripts/09-add-known-peers.sh` | Run in `sys-fips`: add two known bootstrap peers and restart FIPS |
|
||||
| `scripts/test-connectivity.sh` | Unified test script |
|
||||
| `scripts/test-local.sh` | Compatibility wrapper to `test-connectivity.sh --local` |
|
||||
| `scripts/test-from-appvm.sh` | Compatibility wrapper to `test-connectivity.sh --appvm` |
|
||||
| `scripts/test-full-suite.sh` | Compatibility wrapper to `test-connectivity.sh --full` |
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
### 1) Create `sys-fips` (run in dom0)
|
||||
|
||||
```bash
|
||||
sudo bash 01-dom0-create-proxyvm.sh [template] [upstream_netvm]
|
||||
```
|
||||
|
||||
Defaults:
|
||||
- template: `debian-12`
|
||||
- upstream netvm: `sys-firewall`
|
||||
|
||||
For VPN chaining, pass `sys-vpn` as upstream:
|
||||
|
||||
```bash
|
||||
sudo bash 01-dom0-create-proxyvm.sh debian-12 sys-vpn
|
||||
```
|
||||
|
||||
### 2) Build in this repo, then copy this entire folder
|
||||
|
||||
From this `fips_setup` repo in your build qube:
|
||||
|
||||
```bash
|
||||
bash ./scripts/00-build-fips.sh
|
||||
qvm-copy-to-vm sys-fips /home/user/anvil/fips_setup/
|
||||
```
|
||||
|
||||
This keeps your update workflow simple: pull latest code, rebuild once in this repo, copy the folder, run install in `sys-fips`.
|
||||
|
||||
### 3) Install/configure/start (inside `sys-fips`)
|
||||
|
||||
```bash
|
||||
cd ~/QubesIncoming/*/fips_setup/scripts
|
||||
sudo bash 02-install-fips.sh
|
||||
bash 03-configure-identity.sh
|
||||
sudo bash 04-start-fips.sh
|
||||
sudo bash 06-configure-dns.sh
|
||||
sudo bash 07-route-appvms.sh
|
||||
```
|
||||
|
||||
### 4) Add at least one external peer
|
||||
|
||||
```bash
|
||||
sudo bash 08-add-peer.sh
|
||||
sudo systemctl restart fips
|
||||
```
|
||||
|
||||
Or add both known bootstrap peers automatically:
|
||||
|
||||
```bash
|
||||
sudo bash 09-add-known-peers.sh
|
||||
```
|
||||
|
||||
### 5) Test
|
||||
|
||||
```bash
|
||||
sudo bash test-connectivity.sh --full
|
||||
```
|
||||
|
||||
### 6) Point AppVMs at `sys-fips` (in dom0)
|
||||
|
||||
```bash
|
||||
qvm-prefs my-appvm netvm sys-fips
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Clone workflow
|
||||
|
||||
```bash
|
||||
# dom0
|
||||
qvm-clone sys-fips sys-fips2
|
||||
qvm-start sys-fips2
|
||||
```
|
||||
|
||||
Inside `sys-fips2`:
|
||||
|
||||
```bash
|
||||
sudo bash 05-stop-fips.sh
|
||||
sudo rm -f /etc/fips/fips.key /etc/fips/fips.pub
|
||||
bash 03-configure-identity.sh
|
||||
sudo bash 04-start-fips.sh
|
||||
```
|
||||
|
||||
Now `sys-fips` and `sys-fips2` are independent FIPS nodes.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- One FIPS node per ProxyVM is the production target.
|
||||
- The previous two-node local loopback approach was useful for local experiments but is not required for normal Qubes deployment.
|
||||
- FIPS mesh encryption is end-to-end. A VPN upstream is optional and can be chained.
|
||||
- `06-configure-dns.sh` removes dnsmasq `--local-service` from Debian's helper to avoid dropping AppVM DNS on Qubes `/32` links.
|
||||
BIN
bin/fipsctl
Executable file
BIN
bin/fipsctl
Executable file
Binary file not shown.
BIN
bin/fipstop
Executable file
BIN
bin/fipstop
Executable file
Binary file not shown.
25
configs/fips-node-a.yaml
Normal file
25
configs/fips-node-a.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
# Deprecated in single-node sys-fips design.
|
||||
#
|
||||
# Use configs/fips.yaml instead.
|
||||
# This file is kept only for backward compatibility with old references.
|
||||
|
||||
node:
|
||||
identity:
|
||||
persistent: true
|
||||
|
||||
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: []
|
||||
25
configs/fips-node-b.yaml
Normal file
25
configs/fips-node-b.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
# Deprecated in single-node sys-fips design.
|
||||
#
|
||||
# Use configs/fips.yaml instead.
|
||||
# This file is kept only for backward compatibility with old references.
|
||||
|
||||
node:
|
||||
identity:
|
||||
persistent: true
|
||||
|
||||
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: []
|
||||
26
configs/fips.yaml
Normal file
26
configs/fips.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
# FIPS single-node config template for sys-fips
|
||||
#
|
||||
# This file is copied to /etc/fips/fips.yaml by 02-install-fips.sh.
|
||||
# Identity is persistent by default so the node keeps the same npub across restarts.
|
||||
|
||||
node:
|
||||
identity:
|
||||
persistent: true
|
||||
|
||||
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
|
||||
|
||||
# Add external peers with scripts/08-add-peer.sh
|
||||
peers: []
|
||||
1
fips
Submodule
1
fips
Submodule
Submodule fips added at 7494ed058d
199
plans/sys-fips-plan.md
Normal file
199
plans/sys-fips-plan.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# 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`
|
||||
36
scripts/00-build-fips.sh
Executable file
36
scripts/00-build-fips.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 00-build-fips.sh
|
||||
#
|
||||
# Build FIPS binaries from the bundled ./fips source tree and stage them into
|
||||
# ./bin so the whole fips_setup directory can be copied to sys-fips.
|
||||
#
|
||||
# Usage:
|
||||
# bash ./scripts/00-build-fips.sh
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
FIPS_DIR="${ROOT_DIR}/fips"
|
||||
OUT_DIR="${ROOT_DIR}/bin"
|
||||
|
||||
if [ ! -f "${FIPS_DIR}/Cargo.toml" ]; then
|
||||
echo "✗ Missing bundled fips source at: ${FIPS_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Building FIPS binaries from ${FIPS_DIR} ==="
|
||||
echo ""
|
||||
|
||||
cd "${FIPS_DIR}"
|
||||
cargo build --release --bin fips --bin fipsctl --bin fipstop
|
||||
|
||||
mkdir -p "${OUT_DIR}"
|
||||
install -m 755 "${FIPS_DIR}/target/release/fips" "${OUT_DIR}/fips"
|
||||
install -m 755 "${FIPS_DIR}/target/release/fipsctl" "${OUT_DIR}/fipsctl"
|
||||
install -m 755 "${FIPS_DIR}/target/release/fipstop" "${OUT_DIR}/fipstop"
|
||||
|
||||
echo ""
|
||||
echo "✓ Staged binaries in ${OUT_DIR}:"
|
||||
ls -lh "${OUT_DIR}/fips" "${OUT_DIR}/fipsctl" "${OUT_DIR}/fipstop"
|
||||
99
scripts/01-dom0-create-proxyvm.sh
Executable file
99
scripts/01-dom0-create-proxyvm.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 01-dom0-create-proxyvm.sh
|
||||
#
|
||||
# Run this script in dom0 to create the sys-fips ProxyVM.
|
||||
#
|
||||
# Usage (in dom0):
|
||||
# sudo bash 01-dom0-create-proxyvm.sh [template] [upstream_netvm]
|
||||
#
|
||||
# Arguments:
|
||||
# template TemplateVM to base on (default: debian-12)
|
||||
# upstream_netvm Upstream NetVM (default: sys-firewall, can be sys-vpn)
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
TEMPLATE="${1:-debian-12}"
|
||||
UPSTREAM_NETVM="${2:-sys-firewall}"
|
||||
VM_NAME="sys-fips"
|
||||
VM_LABEL="blue"
|
||||
|
||||
echo "=== sys-fips ProxyVM Creation ==="
|
||||
echo ""
|
||||
echo " VM Name: $VM_NAME"
|
||||
echo " Template: $TEMPLATE"
|
||||
echo " Upstream NetVM: $UPSTREAM_NETVM"
|
||||
echo " Label: $VM_LABEL"
|
||||
echo ""
|
||||
|
||||
# Check if VM already exists
|
||||
if qvm-check "$VM_NAME" 2>/dev/null; then
|
||||
echo "⚠ VM '$VM_NAME' already exists."
|
||||
echo " To recreate, first remove it:"
|
||||
echo " qvm-shutdown $VM_NAME"
|
||||
echo " qvm-remove $VM_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify template exists
|
||||
if ! qvm-check "$TEMPLATE" 2>/dev/null; then
|
||||
echo "✗ Template '$TEMPLATE' does not exist."
|
||||
echo " Available templates:"
|
||||
qvm-ls --raw-list --fields name,klass | grep TemplateVM | awk '{print " " $1}'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify upstream netvm exists
|
||||
if ! qvm-check "$UPSTREAM_NETVM" 2>/dev/null; then
|
||||
echo "✗ Upstream NetVM '$UPSTREAM_NETVM' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating ProxyVM..."
|
||||
|
||||
# Create as AppVM + provides_network=True => ProxyVM behavior
|
||||
qvm-create "$VM_NAME" \
|
||||
--class AppVM \
|
||||
--template "$TEMPLATE" \
|
||||
--label "$VM_LABEL" \
|
||||
--prop provides_network=True \
|
||||
--prop netvm="$UPSTREAM_NETVM"
|
||||
|
||||
echo "✓ Created $VM_NAME"
|
||||
|
||||
# Memory defaults (lightweight baseline)
|
||||
qvm-prefs "$VM_NAME" memory 512
|
||||
qvm-prefs "$VM_NAME" maxmem 2048
|
||||
echo "✓ Memory: 512MB initial, 2048MB max"
|
||||
|
||||
# Ensure networking prefs
|
||||
qvm-prefs "$VM_NAME" netvm "$UPSTREAM_NETVM"
|
||||
qvm-prefs "$VM_NAME" provides_network True
|
||||
echo "✓ NetVM: $UPSTREAM_NETVM"
|
||||
echo "✓ provides_network: True"
|
||||
|
||||
echo ""
|
||||
echo "=== sys-fips Created Successfully ==="
|
||||
echo ""
|
||||
echo "Network chain examples:"
|
||||
echo " AppVM -> sys-fips -> sys-firewall -> sys-net -> Internet"
|
||||
echo " AppVM -> sys-fips -> sys-vpn -> sys-firewall -> sys-net -> Internet"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo ""
|
||||
echo " 1) Start the VM:"
|
||||
echo " qvm-start $VM_NAME"
|
||||
echo ""
|
||||
echo " 2) Copy FIPS binaries (from your build AppVM):"
|
||||
echo " qvm-copy-to-vm $VM_NAME /home/user/lt/fips/target/release/fips"
|
||||
echo " qvm-copy-to-vm $VM_NAME /home/user/lt/fips/target/release/fipsctl"
|
||||
echo " qvm-copy-to-vm $VM_NAME /home/user/lt/fips/target/release/fipstop"
|
||||
echo ""
|
||||
echo " 3) Copy this setup directory:"
|
||||
echo " qvm-copy-to-vm $VM_NAME /home/user/anvil/fips_setup/"
|
||||
echo ""
|
||||
echo " 4) In sys-fips terminal run:"
|
||||
echo " cd ~/QubesIncoming/*/fips_setup/scripts/"
|
||||
echo " sudo bash 02-install-fips.sh"
|
||||
echo " bash 03-configure-identity.sh"
|
||||
echo " sudo bash 04-start-fips.sh"
|
||||
190
scripts/02-install-fips.sh
Executable file
190
scripts/02-install-fips.sh
Executable file
@@ -0,0 +1,190 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 02-install-fips.sh
|
||||
#
|
||||
# Run inside sys-fips to install FIPS binaries, dependencies, config, and service.
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash 02-install-fips.sh
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
echo "=== sys-fips Installation ==="
|
||||
echo ""
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "✗ This script must be run as root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
# ── Install dependencies ────────────────────────────────────────────────────────
|
||||
echo "Installing system dependencies..."
|
||||
apt-get update -qq
|
||||
apt-get install -y --no-install-recommends \
|
||||
iproute2 iputils-ping dnsutils \
|
||||
dnsmasq iptables \
|
||||
curl python3 jq \
|
||||
openssh-server openssh-client \
|
||||
iperf3 2>/dev/null || {
|
||||
echo "⚠ Some packages may not be available — continuing"
|
||||
}
|
||||
echo "✓ Dependencies installed"
|
||||
|
||||
# ── Ensure control-socket access group exists ──────────────────────────────────
|
||||
if ! getent group fips >/dev/null 2>&1; then
|
||||
groupadd --system fips
|
||||
echo "✓ Created system group: fips"
|
||||
else
|
||||
echo "✓ Group exists: fips"
|
||||
fi
|
||||
|
||||
# ── Locate binaries ─────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "Looking for FIPS binaries..."
|
||||
|
||||
FIPS_BIN=""
|
||||
FIPSCTL_BIN=""
|
||||
FIPSTOP_BIN=""
|
||||
|
||||
LOCAL_BIN_DIR="${ROOT_DIR}/bin"
|
||||
LOCAL_RELEASE_DIR="${ROOT_DIR}/fips/target/release"
|
||||
|
||||
# Preferred: binaries prebuilt in this same repo copy
|
||||
[ -z "$FIPS_BIN" ] && [ -f "${LOCAL_BIN_DIR}/fips" ] && FIPS_BIN="${LOCAL_BIN_DIR}/fips"
|
||||
[ -z "$FIPSCTL_BIN" ] && [ -f "${LOCAL_BIN_DIR}/fipsctl" ] && FIPSCTL_BIN="${LOCAL_BIN_DIR}/fipsctl"
|
||||
[ -z "$FIPSTOP_BIN" ] && [ -f "${LOCAL_BIN_DIR}/fipstop" ] && FIPSTOP_BIN="${LOCAL_BIN_DIR}/fipstop"
|
||||
|
||||
# Fallback: release artifacts in bundled fips source tree
|
||||
[ -z "$FIPS_BIN" ] && [ -f "${LOCAL_RELEASE_DIR}/fips" ] && FIPS_BIN="${LOCAL_RELEASE_DIR}/fips"
|
||||
[ -z "$FIPSCTL_BIN" ] && [ -f "${LOCAL_RELEASE_DIR}/fipsctl" ] && FIPSCTL_BIN="${LOCAL_RELEASE_DIR}/fipsctl"
|
||||
[ -z "$FIPSTOP_BIN" ] && [ -f "${LOCAL_RELEASE_DIR}/fipstop" ] && FIPSTOP_BIN="${LOCAL_RELEASE_DIR}/fipstop"
|
||||
|
||||
# Legacy fallback: individually copied binaries in QubesIncoming
|
||||
for incoming_dir in /home/user/QubesIncoming/*/; do
|
||||
[ -z "$FIPS_BIN" ] && [ -f "${incoming_dir}fips" ] && FIPS_BIN="${incoming_dir}fips"
|
||||
[ -z "$FIPSCTL_BIN" ] && [ -f "${incoming_dir}fipsctl" ] && FIPSCTL_BIN="${incoming_dir}fipsctl"
|
||||
[ -z "$FIPSTOP_BIN" ] && [ -f "${incoming_dir}fipstop" ] && FIPSTOP_BIN="${incoming_dir}fipstop"
|
||||
done
|
||||
|
||||
if [ -z "$FIPS_BIN" ]; then
|
||||
echo "✗ Could not find 'fips' binary in this repo copy."
|
||||
echo " Expected one of:"
|
||||
echo " ${LOCAL_BIN_DIR}/fips"
|
||||
echo " ${LOCAL_RELEASE_DIR}/fips"
|
||||
echo ""
|
||||
echo " Build before copying into sys-fips:"
|
||||
echo " bash ./scripts/00-build-fips.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Found fips: $FIPS_BIN"
|
||||
[ -n "$FIPSCTL_BIN" ] && echo " Found fipsctl: $FIPSCTL_BIN"
|
||||
[ -n "$FIPSTOP_BIN" ] && echo " Found fipstop: $FIPSTOP_BIN"
|
||||
|
||||
# ── Install binaries ────────────────────────────────────────────────────────────
|
||||
install -m 755 "$FIPS_BIN" /usr/local/bin/fips
|
||||
echo "✓ Installed /usr/local/bin/fips"
|
||||
|
||||
if [ -n "$FIPSCTL_BIN" ]; then
|
||||
install -m 755 "$FIPSCTL_BIN" /usr/local/bin/fipsctl
|
||||
echo "✓ Installed /usr/local/bin/fipsctl"
|
||||
fi
|
||||
|
||||
if [ -n "$FIPSTOP_BIN" ]; then
|
||||
install -m 755 "$FIPSTOP_BIN" /usr/local/bin/fipstop
|
||||
echo "✓ Installed /usr/local/bin/fipstop"
|
||||
fi
|
||||
|
||||
# ── Ensure runtime dirs and TUN ─────────────────────────────────────────────────
|
||||
mkdir -p /etc/fips /var/log/fips /var/run/fips
|
||||
|
||||
if [ ! -c /dev/net/tun ]; then
|
||||
echo "Creating /dev/net/tun..."
|
||||
mkdir -p /dev/net
|
||||
mknod /dev/net/tun c 10 200
|
||||
chmod 666 /dev/net/tun
|
||||
fi
|
||||
echo "✓ Runtime directories and TUN are ready"
|
||||
|
||||
# ── Install config ──────────────────────────────────────────────────────────────
|
||||
if [ -f /etc/fips/fips.yaml ]; then
|
||||
echo "⚠ /etc/fips/fips.yaml already exists; preserving existing file"
|
||||
else
|
||||
if [ -f "${ROOT_DIR}/configs/fips.yaml" ]; then
|
||||
install -m 600 "${ROOT_DIR}/configs/fips.yaml" /etc/fips/fips.yaml
|
||||
echo "✓ Installed /etc/fips/fips.yaml"
|
||||
else
|
||||
cat > /etc/fips/fips.yaml << 'EOF'
|
||||
node:
|
||||
identity:
|
||||
persistent: true
|
||||
|
||||
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: []
|
||||
EOF
|
||||
chmod 600 /etc/fips/fips.yaml
|
||||
echo "✓ Installed default /etc/fips/fips.yaml"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Install systemd service (single node) ──────────────────────────────────────
|
||||
cat > /etc/systemd/system/fips.service << 'EOF'
|
||||
[Unit]
|
||||
Description=FIPS Mesh Network Daemon
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/fips --config /etc/fips/fips.yaml
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
RuntimeDirectory=fips
|
||||
RuntimeDirectoryMode=0750
|
||||
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=no
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
echo "✓ Installed /etc/systemd/system/fips.service"
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable fips >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
echo "=== Installation complete ==="
|
||||
echo ""
|
||||
if [ -n "${SUDO_USER:-}" ] && [ "${SUDO_USER}" != "root" ]; then
|
||||
echo "Control socket access tip:"
|
||||
echo " sudo usermod -aG fips ${SUDO_USER}"
|
||||
echo " (then log out/in, or use 'sudo fipsctl ...')"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "Next steps:"
|
||||
echo " sudo bash 03-configure-identity.sh"
|
||||
echo " sudo bash 04-start-fips.sh"
|
||||
echo " sudo bash 06-configure-dns.sh"
|
||||
echo " sudo bash 07-route-appvms.sh"
|
||||
94
scripts/03-configure-identity.sh
Executable file
94
scripts/03-configure-identity.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 03-configure-identity.sh
|
||||
#
|
||||
# Configure identity behavior for single-node sys-fips.
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash 03-configure-identity.sh
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_FILE="/etc/fips/fips.yaml"
|
||||
|
||||
echo "=== FIPS Identity Configuration ==="
|
||||
echo ""
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "✗ Run as root: sudo bash 03-configure-identity.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "✗ $CONFIG_FILE not found"
|
||||
echo " Run: sudo bash 02-install-fips.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /etc/fips/fips.pub ]; then
|
||||
echo "Current identity detected:"
|
||||
echo " npub: $(cat /etc/fips/fips.pub)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "Identity modes:"
|
||||
echo " 1) persistent (recommended): key auto-generated once and reused"
|
||||
echo " 2) explicit nsec: pin node identity to a specific key"
|
||||
echo ""
|
||||
|
||||
read -r -p "Select mode [1/2] (default 1): " MODE
|
||||
MODE="${MODE:-1}"
|
||||
|
||||
set_identity_block() {
|
||||
local identity_line="$1"
|
||||
local tmp
|
||||
tmp=$(mktemp)
|
||||
|
||||
# Replace node.identity block in known config format: node: ... then tun:
|
||||
awk -v ident="$identity_line" '
|
||||
BEGIN { replaced=0; skipping=0 }
|
||||
/^node:[[:space:]]*$/ && replaced==0 {
|
||||
print "node:";
|
||||
print " identity:";
|
||||
print " " ident;
|
||||
replaced=1;
|
||||
skipping=1;
|
||||
next;
|
||||
}
|
||||
skipping==1 {
|
||||
if (/^tun:[[:space:]]*$/) {
|
||||
skipping=0;
|
||||
print $0;
|
||||
}
|
||||
next;
|
||||
}
|
||||
{ print $0 }
|
||||
' "$CONFIG_FILE" > "$tmp"
|
||||
|
||||
mv "$tmp" "$CONFIG_FILE"
|
||||
chmod 600 "$CONFIG_FILE"
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
1)
|
||||
set_identity_block "persistent: true"
|
||||
echo "✓ Set node.identity.persistent=true"
|
||||
;;
|
||||
2)
|
||||
read -r -p "Enter nsec (bech32 or 64-char hex): " NSEC
|
||||
if [ -z "$NSEC" ]; then
|
||||
echo "✗ Empty nsec is not allowed"
|
||||
exit 1
|
||||
fi
|
||||
set_identity_block "nsec: \"$NSEC\""
|
||||
echo "✓ Set node.identity.nsec"
|
||||
;;
|
||||
*)
|
||||
echo "✗ Invalid selection"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "Config updated: $CONFIG_FILE"
|
||||
echo "Next: sudo bash 04-start-fips.sh"
|
||||
6
scripts/03-generate-node-configs.sh
Executable file
6
scripts/03-generate-node-configs.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Compatibility wrapper: old two-node script name -> new single-node identity script
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec bash "${SCRIPT_DIR}/03-configure-identity.sh"
|
||||
6
scripts/04-start-fips-nodes.sh
Executable file
6
scripts/04-start-fips-nodes.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Compatibility wrapper: old two-node script name -> new single-node start script
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec bash "${SCRIPT_DIR}/04-start-fips.sh"
|
||||
81
scripts/04-start-fips.sh
Executable file
81
scripts/04-start-fips.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 04-start-fips.sh
|
||||
#
|
||||
# Start a single FIPS node in sys-fips, wait for fips0, and set route.
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash 04-start-fips.sh
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_FILE="/etc/fips/fips.yaml"
|
||||
ENV_FILE="/etc/fips/node.env"
|
||||
|
||||
echo "=== Starting FIPS (single node) ==="
|
||||
echo ""
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "✗ This script must be run as root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "✗ $CONFIG_FILE not found"
|
||||
echo " Run: sudo bash 02-install-fips.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sysctl -w net.ipv6.conf.all.disable_ipv6=0 >/dev/null 2>&1 || true
|
||||
sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null 2>&1 || true
|
||||
echo "✓ IPv6 + forwarding enabled"
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl restart fips
|
||||
sleep 1
|
||||
|
||||
if ! systemctl is-active --quiet fips; then
|
||||
echo "✗ fips service is not active"
|
||||
echo " Check: journalctl -u fips -n 100 --no-pager"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ fips service active"
|
||||
|
||||
echo "Waiting for fips0..."
|
||||
for i in $(seq 1 30); do
|
||||
if ip link show fips0 >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if ! ip link show fips0 >/dev/null 2>&1; then
|
||||
echo "✗ fips0 did not appear within 30s"
|
||||
echo " Check: journalctl -u fips -n 100 --no-pager"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IPV6=$(ip -6 addr show fips0 scope global 2>/dev/null | awk '/inet6/{print $2}' | cut -d/ -f1 | head -1)
|
||||
NPUB=""
|
||||
|
||||
if [ -f /etc/fips/fips.pub ]; then
|
||||
NPUB=$(cat /etc/fips/fips.pub)
|
||||
elif command -v fipsctl >/dev/null 2>&1; then
|
||||
NPUB=$(fipsctl show status 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin).get("npub",""))' 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
ip -6 route replace fd00::/8 dev fips0 2>/dev/null || true
|
||||
|
||||
cat > "$ENV_FILE" << EOF
|
||||
NPUB=${NPUB}
|
||||
IPV6=${IPV6}
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "✓ fips0 is up"
|
||||
echo " npub: ${NPUB:-unknown}"
|
||||
echo " ipv6: ${IPV6:-unknown}"
|
||||
echo " route: fd00::/8 -> fips0"
|
||||
echo ""
|
||||
echo "Next: sudo bash 06-configure-dns.sh"
|
||||
6
scripts/05-stop-fips-nodes.sh
Executable file
6
scripts/05-stop-fips-nodes.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Compatibility wrapper: old two-node script name -> new single-node stop script
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec bash "${SCRIPT_DIR}/05-stop-fips.sh"
|
||||
31
scripts/05-stop-fips.sh
Executable file
31
scripts/05-stop-fips.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 05-stop-fips.sh
|
||||
#
|
||||
# Stop single FIPS node and clean up route.
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash 05-stop-fips.sh
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
echo "=== Stopping FIPS (single node) ==="
|
||||
echo ""
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "✗ This script must be run as root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
systemctl stop fips 2>/dev/null || true
|
||||
ip -6 route del fd00::/8 dev fips0 2>/dev/null || true
|
||||
|
||||
echo "✓ fips service stopped"
|
||||
if ip link show fips0 >/dev/null 2>&1; then
|
||||
echo " fips0 still present (will disappear when process exits completely)"
|
||||
else
|
||||
echo " fips0 removed"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done"
|
||||
93
scripts/06-configure-dns.sh
Executable file
93
scripts/06-configure-dns.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 06-configure-dns.sh
|
||||
#
|
||||
# Configure dnsmasq in sys-fips so .fips queries are forwarded to local
|
||||
# FIPS DNS resolver on 127.0.0.1:5354, and all other DNS goes upstream.
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash 06-configure-dns.sh
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
echo "=== sys-fips DNS Configuration ==="
|
||||
echo ""
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "✗ This script must be run as root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
systemctl stop dnsmasq 2>/dev/null || true
|
||||
killall dnsmasq 2>/dev/null || true
|
||||
|
||||
UPSTREAM_DNS=$(grep '^nameserver' /etc/resolv.conf | awk '{print $2}' | head -1)
|
||||
UPSTREAM_DNS="${UPSTREAM_DNS:-10.139.1.1}"
|
||||
|
||||
echo " Upstream DNS: $UPSTREAM_DNS"
|
||||
|
||||
mkdir -p /etc/dnsmasq.d
|
||||
cat > /etc/dnsmasq.d/fips.conf << EOF
|
||||
# sys-fips DNS resolution
|
||||
port=53
|
||||
listen-address=0.0.0.0
|
||||
bind-interfaces
|
||||
|
||||
# .fips domains -> FIPS resolver
|
||||
server=/fips/127.0.0.1#5354
|
||||
|
||||
# everything else -> upstream
|
||||
server=$UPSTREAM_DNS
|
||||
|
||||
no-resolv
|
||||
no-hosts
|
||||
# log-queries
|
||||
EOF
|
||||
|
||||
if [ ! -f /etc/dnsmasq.conf ]; then
|
||||
touch /etc/dnsmasq.conf
|
||||
fi
|
||||
if ! grep -q 'conf-dir=/etc/dnsmasq.d/,*.conf' /etc/dnsmasq.conf; then
|
||||
echo 'conf-dir=/etc/dnsmasq.d/,*.conf' >> /etc/dnsmasq.conf
|
||||
fi
|
||||
|
||||
if ! dnsmasq --test >/dev/null 2>&1; then
|
||||
echo "✗ dnsmasq config failed validation"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Qubes AppVMs often use /32 point-to-point addressing.
|
||||
# Debian's dnsmasq systemd-helper adds --local-service by default,
|
||||
# which can reject DNS queries from downstream AppVMs in this topology.
|
||||
DNSMASQ_HELPER="/usr/share/dnsmasq/systemd-helper"
|
||||
if [ -f "$DNSMASQ_HELPER" ] && grep -q -- '--local-service' "$DNSMASQ_HELPER"; then
|
||||
cp "$DNSMASQ_HELPER" "${DNSMASQ_HELPER}.bak"
|
||||
sed -i 's/--local-service//g' "$DNSMASQ_HELPER"
|
||||
echo " Patched dnsmasq helper: removed --local-service for Qubes /32 compatibility"
|
||||
fi
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl restart dnsmasq || true
|
||||
fi
|
||||
|
||||
if ! pgrep -x dnsmasq >/dev/null 2>&1; then
|
||||
dnsmasq
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Verifying DNS..."
|
||||
if dig @127.0.0.1 google.com A +short +time=2 >/dev/null 2>&1; then
|
||||
echo " Upstream DNS: OK"
|
||||
else
|
||||
echo " Upstream DNS: WARN (may be expected in isolated setup)"
|
||||
fi
|
||||
|
||||
STATUS=$(dig @127.0.0.1 test.fips AAAA +time=2 2>&1 | grep 'status:' || true)
|
||||
if echo "$STATUS" | grep -qE 'NXDOMAIN|SERVFAIL|NOERROR'; then
|
||||
echo " .fips forwarding: OK"
|
||||
else
|
||||
echo " .fips forwarding: WARN"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✓ DNS configured"
|
||||
72
scripts/07-route-appvms.sh
Executable file
72
scripts/07-route-appvms.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 07-route-appvms.sh
|
||||
#
|
||||
# Configure IPv6 forwarding and ip6tables rules in sys-fips so AppVMs routed
|
||||
# through sys-fips can reach FIPS fd00::/8 addresses via fips0.
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash 07-route-appvms.sh
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
echo "=== sys-fips AppVM Routing Configuration ==="
|
||||
echo ""
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "✗ This script must be run as root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null
|
||||
sysctl -w net.ipv6.conf.all.proxy_ndp=1 >/dev/null 2>&1 || true
|
||||
sysctl -w net.ipv6.conf.all.disable_ipv6=0 >/dev/null 2>&1 || true
|
||||
echo " IPv6 forwarding enabled"
|
||||
|
||||
if ! ip link show fips0 >/dev/null 2>&1; then
|
||||
echo " WARNING: fips0 not found (start fips first)"
|
||||
fi
|
||||
|
||||
ip -6 route replace fd00::/8 dev fips0 2>/dev/null || true
|
||||
echo " route: fd00::/8 -> fips0"
|
||||
|
||||
# idempotent rule refresh
|
||||
ip6tables -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
|
||||
ip6tables -D FORWARD -d fd00::/8 -o fips0 -j ACCEPT 2>/dev/null || true
|
||||
ip6tables -D FORWARD -s fd00::/8 -i fips0 -j ACCEPT 2>/dev/null || true
|
||||
|
||||
ip6tables -I FORWARD 1 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
ip6tables -I FORWARD 2 -d fd00::/8 -o fips0 -j ACCEPT
|
||||
ip6tables -I FORWARD 3 -s fd00::/8 -i fips0 -j ACCEPT
|
||||
|
||||
echo " ip6tables forwarding rules installed"
|
||||
|
||||
RCLOCAL="/rw/config/rc.local"
|
||||
MARK_START="# === SYS-FIPS ROUTING START ==="
|
||||
MARK_END="# === SYS-FIPS ROUTING END ==="
|
||||
|
||||
if [ -f "$RCLOCAL" ]; then
|
||||
sed -i "/$MARK_START/,/$MARK_END/d" "$RCLOCAL"
|
||||
fi
|
||||
|
||||
cat >> "$RCLOCAL" << 'EOF'
|
||||
# === SYS-FIPS ROUTING START ===
|
||||
sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null 2>&1
|
||||
sysctl -w net.ipv6.conf.all.disable_ipv6=0 >/dev/null 2>&1
|
||||
ip6tables -I FORWARD 1 -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null
|
||||
ip6tables -I FORWARD 2 -d fd00::/8 -o fips0 -j ACCEPT 2>/dev/null
|
||||
ip6tables -I FORWARD 3 -s fd00::/8 -i fips0 -j ACCEPT 2>/dev/null
|
||||
# === SYS-FIPS ROUTING END ===
|
||||
EOF
|
||||
|
||||
chmod +x "$RCLOCAL"
|
||||
|
||||
echo ""
|
||||
echo "Current fd00 routes:"
|
||||
ip -6 route show | grep fd00 || true
|
||||
|
||||
echo ""
|
||||
echo "✓ AppVM routing configured"
|
||||
echo ""
|
||||
echo "In dom0, point an AppVM at sys-fips:"
|
||||
echo " qvm-prefs <appvm-name> netvm sys-fips"
|
||||
88
scripts/08-add-peer.sh
Executable file
88
scripts/08-add-peer.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 08-add-peer.sh
|
||||
#
|
||||
# Add a static UDP peer to /etc/fips/fips.yaml (single-node mode).
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash 08-add-peer.sh [npub] [alias] [host:port]
|
||||
#
|
||||
# If arguments are omitted, prompts interactively.
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_FILE="/etc/fips/fips.yaml"
|
||||
|
||||
echo "=== Add FIPS Peer ==="
|
||||
echo ""
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "✗ This script must be run as root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "✗ $CONFIG_FILE not found"
|
||||
echo " Run: sudo bash 02-install-fips.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NPUB="${1:-}"
|
||||
ALIAS="${2:-}"
|
||||
ADDR="${3:-}"
|
||||
|
||||
if [ -z "$NPUB" ]; then
|
||||
read -r -p "Peer npub: " NPUB
|
||||
fi
|
||||
if [ -z "$ALIAS" ]; then
|
||||
read -r -p "Alias (default peer): " ALIAS
|
||||
ALIAS="${ALIAS:-peer}"
|
||||
fi
|
||||
if [ -z "$ADDR" ]; then
|
||||
read -r -p "UDP address (host:port): " ADDR
|
||||
fi
|
||||
|
||||
if ! echo "$NPUB" | grep -q '^npub1'; then
|
||||
echo "✗ npub must start with 'npub1'"
|
||||
exit 1
|
||||
fi
|
||||
if ! echo "$ADDR" | grep -q ':'; then
|
||||
echo "✗ address must be in host:port format"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmp=$(mktemp)
|
||||
|
||||
if grep -q '^peers:[[:space:]]*\[\][[:space:]]*$' "$CONFIG_FILE"; then
|
||||
sed 's/^peers:[[:space:]]*\[\][[:space:]]*$/peers:/' "$CONFIG_FILE" > "$tmp"
|
||||
cat >> "$tmp" << EOF
|
||||
- npub: "$NPUB"
|
||||
alias: "$ALIAS"
|
||||
addresses:
|
||||
- transport: udp
|
||||
addr: "$ADDR"
|
||||
connect_policy: auto_connect
|
||||
EOF
|
||||
else
|
||||
cp "$CONFIG_FILE" "$tmp"
|
||||
cat >> "$tmp" << EOF
|
||||
- npub: "$NPUB"
|
||||
alias: "$ALIAS"
|
||||
addresses:
|
||||
- transport: udp
|
||||
addr: "$ADDR"
|
||||
connect_policy: auto_connect
|
||||
EOF
|
||||
fi
|
||||
|
||||
mv "$tmp" "$CONFIG_FILE"
|
||||
chmod 600 "$CONFIG_FILE"
|
||||
|
||||
echo ""
|
||||
echo "✓ Peer added"
|
||||
echo " npub: $NPUB"
|
||||
echo " alias: $ALIAS"
|
||||
echo " addr: $ADDR"
|
||||
echo ""
|
||||
echo "Restart FIPS to apply:"
|
||||
echo " sudo systemctl restart fips"
|
||||
65
scripts/09-add-known-peers.sh
Normal file
65
scripts/09-add-known-peers.sh
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# 09-add-known-peers.sh
|
||||
#
|
||||
# Add known public bootstrap peers to /etc/fips/fips.yaml.
|
||||
# Safe to run multiple times (idempotent by npub check).
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash 09-add-known-peers.sh
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_FILE="/etc/fips/fips.yaml"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ADD_PEER_SCRIPT="${SCRIPT_DIR}/08-add-peer.sh"
|
||||
|
||||
PEER1_NPUB="npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98"
|
||||
PEER1_ALIAS="fips-test-node"
|
||||
PEER1_ADDR="217.77.8.91:2121"
|
||||
|
||||
PEER2_NPUB="npub1zv58cn7v83mxvttl70w5fwjwuclfmntv9cnmv5wmz2nzz88u5urqvdx96n"
|
||||
PEER2_ALIAS="fips.v0l.io"
|
||||
PEER2_ADDR="fips.v0l.io:2121"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "✗ This script must be run as root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "✗ $CONFIG_FILE not found"
|
||||
echo " Run: sudo bash 02-install-fips.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$ADD_PEER_SCRIPT" ]; then
|
||||
echo "✗ Missing helper script: $ADD_PEER_SCRIPT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
add_if_missing() {
|
||||
local npub="$1"
|
||||
local alias="$2"
|
||||
local addr="$3"
|
||||
|
||||
if grep -q "$npub" "$CONFIG_FILE"; then
|
||||
echo "• already present: $alias ($npub)"
|
||||
else
|
||||
bash "$ADD_PEER_SCRIPT" "$npub" "$alias" "$addr"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "=== Add Known Bootstrap Peers ==="
|
||||
echo ""
|
||||
|
||||
add_if_missing "$PEER1_NPUB" "$PEER1_ALIAS" "$PEER1_ADDR"
|
||||
add_if_missing "$PEER2_NPUB" "$PEER2_ALIAS" "$PEER2_ADDR"
|
||||
|
||||
echo ""
|
||||
echo "Restarting fips service..."
|
||||
systemctl restart fips
|
||||
|
||||
echo ""
|
||||
echo "✓ Known peers configured"
|
||||
echo "Check with: sudo fipsctl show peers"
|
||||
132
scripts/test-connectivity.sh
Executable file
132
scripts/test-connectivity.sh
Executable file
@@ -0,0 +1,132 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# test-connectivity.sh
|
||||
#
|
||||
# Unified connectivity tests for sys-fips.
|
||||
#
|
||||
# Modes:
|
||||
# --local tests expected to run in sys-fips
|
||||
# --appvm tests expected to run in an AppVM routed via sys-fips
|
||||
# --full run everything reasonably applicable (default)
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
MODE="${1:---full}"
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
SKIPPED=0
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[0;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
pass() { echo -e "${GREEN}PASS${NC}: $1"; PASSED=$((PASSED + 1)); }
|
||||
fail() { echo -e "${RED}FAIL${NC}: $1"; FAILED=$((FAILED + 1)); }
|
||||
skip() { echo -e "${YELLOW}SKIP${NC}: $1"; SKIPPED=$((SKIPPED + 1)); }
|
||||
|
||||
is_sys_fips=false
|
||||
if ip link show fips0 >/dev/null 2>&1 || [ -f /etc/systemd/system/fips.service ]; then
|
||||
is_sys_fips=true
|
||||
fi
|
||||
|
||||
echo "=== FIPS Connectivity Test ($MODE) ==="
|
||||
echo ""
|
||||
|
||||
run_local_tests() {
|
||||
if command -v fips >/dev/null 2>&1; then
|
||||
pass "fips binary installed"
|
||||
else
|
||||
fail "fips binary missing"
|
||||
fi
|
||||
|
||||
if systemctl is-active --quiet fips 2>/dev/null; then
|
||||
pass "fips service active"
|
||||
else
|
||||
fail "fips service not active"
|
||||
fi
|
||||
|
||||
if ip link show fips0 >/dev/null 2>&1; then
|
||||
pass "fips0 exists"
|
||||
IPV6=$(ip -6 addr show fips0 scope global 2>/dev/null | awk '/inet6/{print $2}' | cut -d/ -f1 | head -1)
|
||||
if [ -n "${IPV6:-}" ]; then
|
||||
pass "fips0 has IPv6 (${IPV6})"
|
||||
else
|
||||
fail "fips0 has no global IPv6"
|
||||
fi
|
||||
else
|
||||
fail "fips0 missing"
|
||||
fi
|
||||
|
||||
if ip -6 route show | grep -q 'fd00::/8'; then
|
||||
pass "fd00::/8 route present"
|
||||
else
|
||||
fail "fd00::/8 route missing"
|
||||
fi
|
||||
|
||||
if pgrep -x dnsmasq >/dev/null 2>&1; then
|
||||
pass "dnsmasq running"
|
||||
else
|
||||
fail "dnsmasq not running"
|
||||
fi
|
||||
|
||||
if dig @127.0.0.1 test.fips AAAA +time=2 >/dev/null 2>&1; then
|
||||
pass ".fips DNS query path reachable"
|
||||
else
|
||||
fail ".fips DNS query path failed"
|
||||
fi
|
||||
}
|
||||
|
||||
run_appvm_tests() {
|
||||
GW=$(ip route show default 2>/dev/null | awk '{print $3}' | head -1)
|
||||
if [ -n "$GW" ]; then
|
||||
pass "default gateway present ($GW)"
|
||||
else
|
||||
fail "default gateway missing"
|
||||
fi
|
||||
|
||||
if [ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null || echo 1)" = "0" ]; then
|
||||
pass "IPv6 enabled"
|
||||
else
|
||||
fail "IPv6 disabled"
|
||||
fi
|
||||
|
||||
if command -v dig >/dev/null 2>&1; then
|
||||
STATUS=$(dig test.fips AAAA +time=3 2>&1 | grep 'status:' || true)
|
||||
if echo "$STATUS" | grep -qE 'NXDOMAIN|SERVFAIL|NOERROR'; then
|
||||
pass ".fips DNS forwarding path reachable"
|
||||
else
|
||||
fail ".fips DNS forwarding path unreachable"
|
||||
fi
|
||||
else
|
||||
skip "dig not installed"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
--local)
|
||||
run_local_tests
|
||||
;;
|
||||
--appvm)
|
||||
run_appvm_tests
|
||||
;;
|
||||
--full)
|
||||
if [ "$is_sys_fips" = true ]; then
|
||||
run_local_tests
|
||||
else
|
||||
skip "local sys-fips checks skipped (not in sys-fips)"
|
||||
fi
|
||||
run_appvm_tests
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [--local|--appvm|--full]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "Results: pass=$PASSED fail=$FAILED skip=$SKIPPED"
|
||||
|
||||
if [ "$FAILED" -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
5
scripts/test-from-appvm.sh
Executable file
5
scripts/test-from-appvm.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Compatibility wrapper
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec bash "${SCRIPT_DIR}/test-connectivity.sh" --appvm
|
||||
5
scripts/test-full-suite.sh
Executable file
5
scripts/test-full-suite.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Compatibility wrapper
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec bash "${SCRIPT_DIR}/test-connectivity.sh" --full
|
||||
5
scripts/test-local.sh
Executable file
5
scripts/test-local.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Compatibility wrapper
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec bash "${SCRIPT_DIR}/test-connectivity.sh" --local
|
||||
Reference in New Issue
Block a user