commit a351883b67eeb465d34e6c04ff0426ff242167e0 Author: Laan Tungir Date: Sat Apr 11 14:20:42 2026 -0400 Setup fips in a qube diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ebca51 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +fips/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f0b8f42 --- /dev/null +++ b/README.md @@ -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. diff --git a/bin/fips b/bin/fips new file mode 100755 index 0000000..632e6c1 Binary files /dev/null and b/bin/fips differ diff --git a/bin/fipsctl b/bin/fipsctl new file mode 100755 index 0000000..fd2ac8f Binary files /dev/null and b/bin/fipsctl differ diff --git a/bin/fipstop b/bin/fipstop new file mode 100755 index 0000000..4f06e55 Binary files /dev/null and b/bin/fipstop differ diff --git a/configs/fips-node-a.yaml b/configs/fips-node-a.yaml new file mode 100644 index 0000000..e824eae --- /dev/null +++ b/configs/fips-node-a.yaml @@ -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: [] diff --git a/configs/fips-node-b.yaml b/configs/fips-node-b.yaml new file mode 100644 index 0000000..e824eae --- /dev/null +++ b/configs/fips-node-b.yaml @@ -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: [] diff --git a/configs/fips.yaml b/configs/fips.yaml new file mode 100644 index 0000000..1f443fb --- /dev/null +++ b/configs/fips.yaml @@ -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: [] diff --git a/fips b/fips new file mode 160000 index 0000000..7494ed0 --- /dev/null +++ b/fips @@ -0,0 +1 @@ +Subproject commit 7494ed058dd577b52d49120120b4b2c9b97c930a diff --git a/plans/sys-fips-plan.md b/plans/sys-fips-plan.md new file mode 100644 index 0000000..082ccde --- /dev/null +++ b/plans/sys-fips-plan.md @@ -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` diff --git a/scripts/00-build-fips.sh b/scripts/00-build-fips.sh new file mode 100755 index 0000000..042ece0 --- /dev/null +++ b/scripts/00-build-fips.sh @@ -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" diff --git a/scripts/01-dom0-create-proxyvm.sh b/scripts/01-dom0-create-proxyvm.sh new file mode 100755 index 0000000..990d84a --- /dev/null +++ b/scripts/01-dom0-create-proxyvm.sh @@ -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" diff --git a/scripts/02-install-fips.sh b/scripts/02-install-fips.sh new file mode 100755 index 0000000..ea09672 --- /dev/null +++ b/scripts/02-install-fips.sh @@ -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" diff --git a/scripts/03-configure-identity.sh b/scripts/03-configure-identity.sh new file mode 100755 index 0000000..5a3c2fd --- /dev/null +++ b/scripts/03-configure-identity.sh @@ -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" diff --git a/scripts/03-generate-node-configs.sh b/scripts/03-generate-node-configs.sh new file mode 100755 index 0000000..831b6be --- /dev/null +++ b/scripts/03-generate-node-configs.sh @@ -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" diff --git a/scripts/04-start-fips-nodes.sh b/scripts/04-start-fips-nodes.sh new file mode 100755 index 0000000..0c05d1b --- /dev/null +++ b/scripts/04-start-fips-nodes.sh @@ -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" diff --git a/scripts/04-start-fips.sh b/scripts/04-start-fips.sh new file mode 100755 index 0000000..3ef8456 --- /dev/null +++ b/scripts/04-start-fips.sh @@ -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" diff --git a/scripts/05-stop-fips-nodes.sh b/scripts/05-stop-fips-nodes.sh new file mode 100755 index 0000000..2c07a9b --- /dev/null +++ b/scripts/05-stop-fips-nodes.sh @@ -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" diff --git a/scripts/05-stop-fips.sh b/scripts/05-stop-fips.sh new file mode 100755 index 0000000..e759526 --- /dev/null +++ b/scripts/05-stop-fips.sh @@ -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" diff --git a/scripts/06-configure-dns.sh b/scripts/06-configure-dns.sh new file mode 100755 index 0000000..fec80b4 --- /dev/null +++ b/scripts/06-configure-dns.sh @@ -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" diff --git a/scripts/07-route-appvms.sh b/scripts/07-route-appvms.sh new file mode 100755 index 0000000..c536004 --- /dev/null +++ b/scripts/07-route-appvms.sh @@ -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 netvm sys-fips" diff --git a/scripts/08-add-peer.sh b/scripts/08-add-peer.sh new file mode 100755 index 0000000..b2c532d --- /dev/null +++ b/scripts/08-add-peer.sh @@ -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" diff --git a/scripts/09-add-known-peers.sh b/scripts/09-add-known-peers.sh new file mode 100644 index 0000000..4be5f52 --- /dev/null +++ b/scripts/09-add-known-peers.sh @@ -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" diff --git a/scripts/test-connectivity.sh b/scripts/test-connectivity.sh new file mode 100755 index 0000000..f74863d --- /dev/null +++ b/scripts/test-connectivity.sh @@ -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 diff --git a/scripts/test-from-appvm.sh b/scripts/test-from-appvm.sh new file mode 100755 index 0000000..64001d7 --- /dev/null +++ b/scripts/test-from-appvm.sh @@ -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 diff --git a/scripts/test-full-suite.sh b/scripts/test-full-suite.sh new file mode 100755 index 0000000..1537447 --- /dev/null +++ b/scripts/test-full-suite.sh @@ -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 diff --git a/scripts/test-local.sh b/scripts/test-local.sh new file mode 100755 index 0000000..591e9a9 --- /dev/null +++ b/scripts/test-local.sh @@ -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