diff --git a/packaging/common/fips.yaml b/packaging/common/fips.yaml new file mode 100644 index 0000000..ea757ab --- /dev/null +++ b/packaging/common/fips.yaml @@ -0,0 +1,47 @@ +# FIPS Node Configuration + +node: + identity: + # By default, a new ephemeral keypair is generated on each start. + # Uncomment persistent to keep the same identity across restarts; + # on first start a keypair is saved to fips.key/fips.pub next to + # this config file (mode 0600/0644). + # persistent: true + # + # Or set an explicit key (overrides persistent): + # nsec: "nsec1..." + +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" + + tcp: + # Accepts inbound connections. No static outbound peers. + bind_addr: "0.0.0.0:8443" + + # Ethernet transport — uncomment and set your interface name. + # ethernet: + # interface: "eth0" + # discovery: true + # announce: true + # auto_connect: true + # accept_connections: true + +peers: [] + # Static peers for bootstrapping (UDP or TCP): + # - npub: "npub1..." + # alias: "gateway" + # addresses: + # - transport: udp + # addr: "217.77.8.91:2121" # public FIPS testing node + # connect_policy: auto_connect diff --git a/packaging/systemd/README.install.md b/packaging/systemd/README.install.md new file mode 100644 index 0000000..e8cb132 --- /dev/null +++ b/packaging/systemd/README.install.md @@ -0,0 +1,150 @@ +# FIPS Installation Guide + +## Quick Start + +```bash +tar xzf fips-*-linux-*.tar.gz +cd fips-*-linux-*/ +sudo ./install.sh +``` + +## What Gets Installed + +| File | Location | +|------|----------| +| fips (daemon) | /usr/local/bin/fips | +| fipsctl (CLI) | /usr/local/bin/fipsctl | +| fipstop (TUI) | /usr/local/bin/fipstop | +| Configuration | /etc/fips/fips.yaml | +| Identity key | /etc/fips/fips.key (auto-generated) | +| Public key | /etc/fips/fips.pub (auto-generated) | +| systemd unit | /etc/systemd/system/fips.service | + +A system group `fips` is created for control socket access. + +## Post-Install Configuration + +Edit `/etc/fips/fips.yaml` before starting the service. + +### 1. Identity + +By default, the node generates a new ephemeral identity on each start +for privacy. If the node's npub will be published for others to use as a +static peer, enable a stable identity by uncommenting `persistent: true` +in the identity section: + +```yaml +node: + identity: + persistent: true +``` + +On first start with persistence enabled, a keypair is auto-generated and saved: + +- `/etc/fips/fips.key` (mode 0600) — secret key +- `/etc/fips/fips.pub` (mode 0644) — public key (npub) + +The same identity is reused on subsequent starts. Alternatively, set +`node.identity.nsec` to use a specific key. + +### 2. Ethernet Transport + +If using Ethernet for local mesh discovery, uncomment the ethernet section +and set the interface name: + +```yaml +transports: + ethernet: + interface: "eth0" + discovery: true + announce: true + auto_connect: true + accept_connections: true +``` + +### 3. Static Peers + +For bootstrapping over UDP or TCP, add known peers: + +```yaml +peers: + - npub: "npub1..." + alias: "gateway" + addresses: + - transport: udp + addr: "217.77.8.91:2121" # public FIPS testing node + connect_policy: auto_connect +``` + +### 4. DNS Resolver + +FIPS includes a DNS responder for `.fips` domain names (port 5354). +To integrate with systemd-resolved: + +```bash +sudo resolvectl dns fips0 127.0.0.1:5354 +sudo resolvectl domain fips0 ~fips +``` + +To make this persistent, create a systemd-networkd override or add a +drop-in for the fips0 interface. + +## Firewall Ports + +| Port | Protocol | Purpose | +|------|----------|---------| +| 2121 | UDP | Peer-to-peer mesh traffic | +| 8443 | TCP | Inbound peer connections | + +## Service Management + +```bash +# Start / stop / restart +sudo systemctl start fips +sudo systemctl stop fips +sudo systemctl restart fips + +# View logs +sudo journalctl -u fips -f + +# Switch to debug logging +sudo systemctl set-environment RUST_LOG=debug +sudo systemctl restart fips +``` + +## Monitoring + +```bash +# Quick status +fipsctl show status + +# Interactive dashboard +fipstop + +# Other queries +fipsctl show peers +fipsctl show links +fipsctl show sessions +fipsctl show routing +fipsctl show transports +``` + +## Non-Root Access to fipsctl/fipstop + +Add your user to the `fips` group: + +```bash +sudo usermod -aG fips $USER +``` + +Log out and back in for the group change to take effect. + +## Uninstall + +```bash +# Remove binaries and service, keep configuration +sudo ./uninstall.sh + +# Remove everything including /etc/fips/ and the fips group +sudo ./uninstall.sh --purge +``` diff --git a/packaging/systemd/build-tarball.sh b/packaging/systemd/build-tarball.sh new file mode 100755 index 0000000..1c487da --- /dev/null +++ b/packaging/systemd/build-tarball.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Build FIPS release binaries and create an install tarball. +# +# Usage: ./packaging/build-tarball.sh +# Output: deploy/fips--linux-.tar.gz + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PACKAGING_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +PROJECT_ROOT="$(cd "${PACKAGING_DIR}/.." && pwd)" + +# Extract version from Cargo.toml +VERSION=$(grep '^version' "${PROJECT_ROOT}/Cargo.toml" | head -1 | sed 's/.*"\(.*\)"/\1/') +ARCH=$(uname -m) +TARBALL_NAME="fips-${VERSION}-linux-${ARCH}" +DEPLOY_DIR="${PROJECT_ROOT}/deploy" +STAGING_DIR="${DEPLOY_DIR}/${TARBALL_NAME}" + +echo "Building FIPS v${VERSION} for ${ARCH}..." + +# Build release binaries (tui is a default feature, includes fipstop) +cargo build --release --manifest-path="${PROJECT_ROOT}/Cargo.toml" + +# Create staging directory +rm -rf "${STAGING_DIR}" +mkdir -p "${STAGING_DIR}" + +# Copy binaries +cp "${PROJECT_ROOT}/target/release/fips" "${STAGING_DIR}/" +cp "${PROJECT_ROOT}/target/release/fipsctl" "${STAGING_DIR}/" +cp "${PROJECT_ROOT}/target/release/fipstop" "${STAGING_DIR}/" + +# Strip binaries to reduce size +strip "${STAGING_DIR}/fips" "${STAGING_DIR}/fipsctl" "${STAGING_DIR}/fipstop" + +# Copy packaging files +cp "${SCRIPT_DIR}/install.sh" "${STAGING_DIR}/" +cp "${SCRIPT_DIR}/uninstall.sh" "${STAGING_DIR}/" +cp "${SCRIPT_DIR}/fips.service" "${STAGING_DIR}/" +cp "${SCRIPT_DIR}/fips-dns.service" "${STAGING_DIR}/" +cp "${PACKAGING_DIR}/common/fips.yaml" "${STAGING_DIR}/" +cp "${SCRIPT_DIR}/README.install.md" "${STAGING_DIR}/" + +chmod +x "${STAGING_DIR}/install.sh" "${STAGING_DIR}/uninstall.sh" + +# Create tarball +cd "${DEPLOY_DIR}" +tar czf "${TARBALL_NAME}.tar.gz" "${TARBALL_NAME}/" +rm -rf "${STAGING_DIR}" + +echo "" +echo "Tarball created: deploy/${TARBALL_NAME}.tar.gz" +ls -lh "${TARBALL_NAME}.tar.gz" diff --git a/packaging/systemd/fips-dns.service b/packaging/systemd/fips-dns.service new file mode 100644 index 0000000..459e810 --- /dev/null +++ b/packaging/systemd/fips-dns.service @@ -0,0 +1,12 @@ +[Unit] +Description=Configure DNS routing for .fips domain +After=systemd-resolved.service +Wants=systemd-resolved.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/bash -c 'while ! ip link show fips0 >/dev/null 2>&1; do sleep 1; done; /usr/bin/resolvectl dns fips0 127.0.0.1:5354; /usr/bin/resolvectl domain fips0 ~fips' +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/packaging/systemd/fips.service b/packaging/systemd/fips.service new file mode 100644 index 0000000..93bf08b --- /dev/null +++ b/packaging/systemd/fips.service @@ -0,0 +1,28 @@ +[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 + +# Logging: RUST_LOG controls verbosity. +# Use "info" for production, "debug" for troubleshooting. +Environment=RUST_LOG=info + +# Control socket directory (/run/fips/). +# Group-readable so 'fips' group members can use fipsctl/fipstop. +RuntimeDirectory=fips +RuntimeDirectoryMode=0750 + +# Security hardening (daemon runs as root for TUN and raw sockets) +ProtectHome=yes +PrivateTmp=yes +ProtectKernelModules=yes +ProtectKernelTunables=no + +[Install] +WantedBy=multi-user.target diff --git a/packaging/systemd/install.sh b/packaging/systemd/install.sh new file mode 100755 index 0000000..4afc866 --- /dev/null +++ b/packaging/systemd/install.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# FIPS Install Script +# +# Installs the FIPS mesh network daemon as a systemd service. +# +# Usage: sudo ./install.sh +# +# Files installed: +# /usr/local/bin/fips Daemon binary +# /usr/local/bin/fipsctl CLI query tool +# /usr/local/bin/fipstop TUI monitor +# /etc/fips/fips.yaml Configuration (preserved if exists) +# /etc/systemd/system/fips.service systemd unit +# /etc/systemd/system/fips-dns.service DNS routing for .fips domain + +set -euo pipefail + +INSTALL_PREFIX="/usr/local" +CONFIG_DIR="/etc/fips" +CONFIG_FILE="${CONFIG_DIR}/fips.yaml" +SYSTEMD_DIR="/etc/systemd/system" +FIPS_GROUP="fips" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# --- Preflight checks --- + +if [ "$(id -u)" -ne 0 ]; then + echo "Error: This script must be run as root (use sudo)." >&2 + exit 1 +fi + +if [ ! -f "${SCRIPT_DIR}/fips" ]; then + echo "Error: fips binary not found in ${SCRIPT_DIR}" >&2 + exit 1 +fi + +if ! command -v systemctl &>/dev/null; then + echo "Error: systemctl not found. This script requires systemd." >&2 + exit 1 +fi + +if [ ! -e /dev/net/tun ]; then + echo "Warning: /dev/net/tun not found. TUN support may not work." >&2 + echo " Load the module with: modprobe tun" >&2 +fi + +# --- Create fips group for control socket access --- + +if ! getent group "${FIPS_GROUP}" &>/dev/null; then + groupadd --system "${FIPS_GROUP}" + echo "Created system group '${FIPS_GROUP}'." +fi + +# --- Install binaries --- + +echo "Installing binaries to ${INSTALL_PREFIX}/bin/" +install -m 0755 "${SCRIPT_DIR}/fips" "${INSTALL_PREFIX}/bin/fips" +install -m 0755 "${SCRIPT_DIR}/fipsctl" "${INSTALL_PREFIX}/bin/fipsctl" +if [ -f "${SCRIPT_DIR}/fipstop" ]; then + install -m 0755 "${SCRIPT_DIR}/fipstop" "${INSTALL_PREFIX}/bin/fipstop" +fi + +# --- Install configuration --- + +mkdir -p "${CONFIG_DIR}" + +if [ -f "${CONFIG_FILE}" ]; then + echo "Configuration exists at ${CONFIG_FILE}, not overwriting." + install -m 0644 "${SCRIPT_DIR}/fips.yaml" "${CONFIG_DIR}/fips.yaml.template" + echo " New template installed as ${CONFIG_DIR}/fips.yaml.template" +else + install -m 0600 "${SCRIPT_DIR}/fips.yaml" "${CONFIG_FILE}" + echo "Configuration installed to ${CONFIG_FILE}" +fi + +# --- Install systemd unit --- + +was_active=false +if systemctl is-active --quiet fips.service 2>/dev/null; then + was_active=true + echo "Stopping running fips service..." + systemctl stop fips.service +fi + +dns_was_active=false +if systemctl is-active --quiet fips-dns.service 2>/dev/null; then + dns_was_active=true + echo "Stopping running fips-dns service..." + systemctl stop fips-dns.service +fi + +install -m 0644 "${SCRIPT_DIR}/fips.service" "${SYSTEMD_DIR}/fips.service" +install -m 0644 "${SCRIPT_DIR}/fips-dns.service" "${SYSTEMD_DIR}/fips-dns.service" +systemctl daemon-reload +echo "systemd units installed." + +# --- Configure runtime directory group ownership --- +# systemd creates /run/fips/ with RuntimeDirectory, but we need the +# group set to 'fips' so group members can access the control socket. +# Create a tmpfiles.d entry for this. + +cat > /etc/tmpfiles.d/fips.conf <<'TMPFILES' +d /run/fips 0750 root fips - +TMPFILES +echo "tmpfiles.d entry created for /run/fips/ ownership." + +# --- Enable service --- + +systemctl enable fips.service +systemctl enable fips-dns.service +echo "Services enabled (will start on boot)." + +# Restart if they were running before +if $was_active; then + echo "Restarting fips service..." + systemctl start fips.service +fi +if $dns_was_active; then + echo "Restarting fips-dns service..." + systemctl start fips-dns.service +fi + +echo "" +echo "=== Installation complete ===" +echo "" +echo "Before starting the service, edit ${CONFIG_FILE}:" +echo "" +echo " 1. Set a persistent identity (if publishing npub for static peers)" +echo " Uncomment 'persistent: true' in the identity section." +echo " A keypair will be generated and saved on first start." +echo "" +echo " 2. Configure Ethernet transport interface (if using)" +echo " Uncomment the ethernet section and set the interface name." +echo "" +echo " 3. Add static peers (if bootstrapping over UDP/TCP)" +echo "" +echo "Start the service:" +echo " sudo systemctl start fips" +echo "" +echo "Monitor:" +echo " sudo journalctl -u fips -f" +echo " fipsctl show status" +echo " fipstop" +echo "" +echo "To use fipsctl/fipstop without sudo, add your user to the fips group:" +echo " sudo usermod -aG fips \$USER" +echo " (log out and back in for group membership to take effect)" diff --git a/packaging/systemd/uninstall.sh b/packaging/systemd/uninstall.sh new file mode 100755 index 0000000..60f7b90 --- /dev/null +++ b/packaging/systemd/uninstall.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# FIPS Uninstall Script +# +# Removes the FIPS daemon, service, and optionally configuration. +# +# Usage: sudo ./uninstall.sh [--purge] +# --purge Also remove /etc/fips/ and the fips system group + +set -euo pipefail + +PURGE=false +if [ "${1:-}" = "--purge" ]; then + PURGE=true +fi + +if [ "$(id -u)" -ne 0 ]; then + echo "Error: This script must be run as root (use sudo)." >&2 + exit 1 +fi + +# --- Stop and disable service --- + +if systemctl is-active --quiet fips-dns.service 2>/dev/null; then + echo "Stopping fips-dns service..." + systemctl stop fips-dns.service +fi + +if systemctl is-enabled --quiet fips-dns.service 2>/dev/null; then + systemctl disable fips-dns.service +fi + +if systemctl is-active --quiet fips.service 2>/dev/null; then + echo "Stopping fips service..." + systemctl stop fips.service +fi + +if systemctl is-enabled --quiet fips.service 2>/dev/null; then + systemctl disable fips.service +fi + +# --- Remove systemd units --- + +rm -f /etc/systemd/system/fips.service +rm -f /etc/systemd/system/fips-dns.service +systemctl daemon-reload +echo "systemd units removed." + +# --- Remove tmpfiles.d entry --- + +rm -f /etc/tmpfiles.d/fips.conf + +# --- Remove binaries --- + +rm -f /usr/local/bin/fips /usr/local/bin/fipsctl /usr/local/bin/fipstop +echo "Binaries removed." + +# --- Optionally remove configuration and group --- + +if $PURGE; then + echo "Purging /etc/fips/ (including identity key files)..." + rm -rf /etc/fips/ + + if getent group fips &>/dev/null; then + groupdel fips + echo "System group 'fips' removed." + fi + + echo "Configuration and group removed." +else + echo "Configuration and identity preserved at /etc/fips/" + echo " Use --purge to remove everything (including key files and group)." +fi + +echo "" +echo "Uninstall complete."