Add gateway packaging: CI, systemd, Debian, AUR, OpenWrt

Add fips-gateway binary to CI artifact and Docker build. Systemd
service unit with After=fips.service dependency and security
hardening. Debian and AUR package entries.

OpenWrt packaging: procd init script managing dnsmasq forwarding,
proxy NDP, RA route advertisements for the virtual IP pool, and a
global IPv6 prefix on br-lan to work around Android suppressing AAAA
queries on ULA-only networks. Sysctl config for IPv6 forwarding.
Gateway enabled by default in OpenWrt config. Ethernet transport
enabled by default.

Default gateway config section (commented out) in common fips.yaml.
This commit is contained in:
Origami74
2026-04-09 16:53:41 +00:00
committed by Johnathan Corgan
parent 60e5fefb1f
commit 1e4f375dcc
11 changed files with 320 additions and 34 deletions
+4 -1
View File
@@ -65,7 +65,7 @@ jobs:
run: cargo build --release
- name: SHA-256 hashes
run: sha256sum target/release/fips target/release/fipsctl target/release/fipstop
run: sha256sum target/release/fips target/release/fipsctl target/release/fipstop target/release/fips-gateway
# Upload the Linux binary so integration jobs can use it without rebuilding
- name: Upload Linux binary
@@ -77,6 +77,7 @@ jobs:
target/release/fips
target/release/fipsctl
target/release/fipstop
target/release/fips-gateway
retention-days: 1
# ─────────────────────────────────────────────────────────────────────────────
@@ -222,9 +223,11 @@ jobs:
run: |
chmod +x _bin/fips _bin/fipsctl
[ -f _bin/fipstop ] && chmod +x _bin/fipstop || true
[ -f _bin/fips-gateway ] && chmod +x _bin/fips-gateway || true
cp _bin/fips testing/docker/fips
cp _bin/fipsctl testing/docker/fipsctl
[ -f _bin/fipstop ] && cp _bin/fipstop testing/docker/fipstop || true
[ -f _bin/fips-gateway ] && cp _bin/fips-gateway testing/docker/fips-gateway || true
docker build -t fips-test:latest testing/docker
docker build -t fips-test-app:latest -f testing/docker/Dockerfile.app testing/docker
+1 -1
View File
@@ -26,7 +26,7 @@ tarball:
@bash $(PACKAGING_DIR)/systemd/build-tarball.sh
ipk:
@bash $(PACKAGING_DIR)/openwrt/build-ipk.sh
@bash $(PACKAGING_DIR)/openwrt-ipk/build-ipk.sh
aur:
@bash $(PACKAGING_DIR)/aur/build-aur.sh
+2
View File
@@ -45,10 +45,12 @@ package() {
install -Dm0755 target/release/fips "$pkgdir/usr/bin/fips"
install -Dm0755 target/release/fipsctl "$pkgdir/usr/bin/fipsctl"
install -Dm0755 target/release/fipstop "$pkgdir/usr/bin/fipstop"
install -Dm0755 target/release/fips-gateway "$pkgdir/usr/bin/fips-gateway"
# Systemd service files (from packaging/debian/ -- correct /usr/bin/ paths)
install -Dm0644 packaging/debian/fips.service "$pkgdir/usr/lib/systemd/system/fips.service"
install -Dm0644 packaging/debian/fips-dns.service "$pkgdir/usr/lib/systemd/system/fips-dns.service"
install -Dm0644 packaging/debian/fips-gateway.service "$pkgdir/usr/lib/systemd/system/fips-gateway.service"
# Config files (from packaging/common/)
install -Dm0600 packaging/common/fips.yaml "$pkgdir/etc/fips/fips.yaml"
+18
View File
@@ -46,6 +46,24 @@ transports:
# auto_connect: true
# accept_connections: true
# Outbound LAN gateway. Allows non-FIPS hosts on the LAN to reach
# mesh destinations via DNS-allocated virtual IPs and kernel NAT.
# Requires: IPv6 forwarding enabled, fips daemon running with DNS.
# gateway:
# enabled: true
# pool: "fd01::/112"
# lan_interface: "eth0"
# dns:
# listen: "[::]:53"
# upstream: "127.0.0.1:5354"
# ttl: 60
# pool_grace_period: 60
# conntrack:
# tcp_established: 432000
# udp_timeout: 30
# udp_assured: 180
# icmp_timeout: 30
peers: []
# Static peers for bootstrapping (UDP or TCP):
# - npub: "npub1..."
+23
View File
@@ -0,0 +1,23 @@
# Gateway is not enabled by default. Enable with: systemctl enable --now fips-gateway
[Unit]
Description=FIPS Outbound LAN Gateway
After=fips.service
Requires=fips.service
[Service]
Type=simple
ExecStart=/usr/bin/fips-gateway --config /etc/fips/fips.yaml
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
TimeoutStopSec=15
# Security hardening (needs CAP_NET_ADMIN for nftables and proxy NDP)
ProtectHome=yes
PrivateTmp=yes
ProtectKernelModules=yes
ProtectKernelTunables=no
[Install]
WantedBy=multi-user.target
+12 -6
View File
@@ -49,7 +49,8 @@ define Package/fips
CATEGORY:=Network
TITLE:=FIPS Mesh Network Daemon
URL:=https://github.com/jmcorgan/fips
DEPENDS:=+kmod-tun +kmod-br-netfilter
DEPENDS:=+kmod-tun +kmod-br-netfilter +kmod-nft-nat \
+kmod-nf-conntrack +ip-full
endef
define Package/fips/description
@@ -58,10 +59,11 @@ define Package/fips/description
0x2121). Provides a TUN interface (fips0) with ULA IPv6 mesh addressing
and a local DNS responder for .fips name resolution.
Three binaries are installed:
fips — mesh daemon
fipsctl — CLI control and inspection tool
fipstop — live TUI dashboard (requires a terminal)
Four binaries are installed:
fips — mesh daemon
fipsctl — CLI control and inspection tool
fipstop — live TUI dashboard (requires a terminal)
fips-gateway — outbound LAN gateway (not started by default)
endef
# ---------------------------------------------------------------------------
@@ -82,7 +84,8 @@ define Build/Compile
--target $(RUST_TARGET) \
--bin fips \
--bin fipsctl \
--bin fipstop
--bin fipstop \
--bin fips-gateway
endef
define Package/fips/install
@@ -91,10 +94,12 @@ define Package/fips/install
$(INSTALL_BIN) $(RUST_RELEASE_DIR)/fips $(1)/usr/bin/fips
$(INSTALL_BIN) $(RUST_RELEASE_DIR)/fipsctl $(1)/usr/bin/fipsctl
$(INSTALL_BIN) $(RUST_RELEASE_DIR)/fipstop $(1)/usr/bin/fipstop
$(INSTALL_BIN) $(RUST_RELEASE_DIR)/fips-gateway $(1)/usr/bin/fips-gateway
# procd init script
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(CURDIR)/files/etc/init.d/fips $(1)/etc/init.d/fips
$(INSTALL_BIN) $(CURDIR)/files/etc/init.d/fips-gateway $(1)/etc/init.d/fips-gateway
# Default config — installed as CONF so opkg will not overwrite it on upgrade
$(INSTALL_DIR) $(1)/etc/fips
@@ -110,6 +115,7 @@ define Package/fips/install
# sysctl: enable br_netfilter so AF_PACKET sees frames on bridge member ports
$(INSTALL_DIR) $(1)/etc/sysctl.d
$(INSTALL_DATA) $(CURDIR)/files/etc/sysctl.d/fips-bridge.conf $(1)/etc/sysctl.d/fips-bridge.conf
$(INSTALL_DATA) $(CURDIR)/files/etc/sysctl.d/fips-gateway.conf $(1)/etc/sysctl.d/fips-gateway.conf
# Hotplug: apply firewall rules when the FIPS TUN interface comes up
$(INSTALL_DIR) $(1)/etc/hotplug.d/net
+13 -5
View File
@@ -113,13 +113,14 @@ cargo zigbuild \
--features tui \
--bin fips \
--bin fipsctl \
--bin fipstop
--bin fipstop \
--bin fips-gateway
RELEASE_DIR="$PROJECT_ROOT/target/$RUST_TARGET/release"
echo "==> Stripping binaries..."
STRIP="${LLVM_STRIP:-strip}"
for bin in fips fipsctl fipstop; do
for bin in fips fipsctl fipstop fips-gateway; do
"$STRIP" "$RELEASE_DIR/$bin" 2>/dev/null || true
done
@@ -147,9 +148,11 @@ install -d "$DATA_DIR/usr/bin"
install -m 0755 "$RELEASE_DIR/fips" "$DATA_DIR/usr/bin/fips"
install -m 0755 "$RELEASE_DIR/fipsctl" "$DATA_DIR/usr/bin/fipsctl"
install -m 0755 "$RELEASE_DIR/fipstop" "$DATA_DIR/usr/bin/fipstop"
install -m 0755 "$RELEASE_DIR/fips-gateway" "$DATA_DIR/usr/bin/fips-gateway"
install -d "$DATA_DIR/etc/init.d"
install -m 0755 "$FILES_DIR/etc/init.d/fips" "$DATA_DIR/etc/init.d/fips"
install -m 0755 "$FILES_DIR/etc/init.d/fips-gateway" "$DATA_DIR/etc/init.d/fips-gateway"
install -d "$DATA_DIR/etc/fips"
install -m 0600 "$FILES_DIR/etc/fips/fips.yaml" "$DATA_DIR/etc/fips/fips.yaml"
@@ -160,6 +163,7 @@ install -m 0644 "$FILES_DIR/etc/dnsmasq.d/fips.conf" "$DATA_DIR/etc/dnsmasq.d/fi
install -d "$DATA_DIR/etc/sysctl.d"
install -m 0644 "$FILES_DIR/etc/sysctl.d/fips-bridge.conf" "$DATA_DIR/etc/sysctl.d/fips-bridge.conf"
install -m 0644 "$FILES_DIR/etc/sysctl.d/fips-gateway.conf" "$DATA_DIR/etc/sysctl.d/fips-gateway.conf"
install -d "$DATA_DIR/etc/hotplug.d/net"
install -m 0755 "$FILES_DIR/etc/hotplug.d/net/99-fips" "$DATA_DIR/etc/hotplug.d/net/99-fips"
@@ -181,7 +185,7 @@ Architecture: $OPENWRT_ARCH
Maintainer: FIPS Network
Section: net
Priority: optional
Depends: kmod-tun, kmod-br-netfilter
Depends: kmod-tun, kmod-br-netfilter, kmod-nft-nat, kmod-nf-conntrack, ip-full
Description: FIPS Mesh Network Daemon
Distributed, decentralized mesh networking over UDP, TCP, and raw Ethernet.
Provides a TUN interface (fips0) with ULA IPv6 addressing and a DNS
@@ -203,14 +207,18 @@ fi
/etc/init.d/fips enable
/etc/init.d/fips start
/etc/init.d/fips-gateway enable
/etc/init.d/fips-gateway start
exit 0
EOF
chmod 0755 "$CONTROL_DIR/postinst"
cat > "$CONTROL_DIR/prerm" <<'EOF'
#!/bin/sh
/etc/init.d/fips stop 2>/dev/null || true
/etc/init.d/fips disable 2>/dev/null || true
/etc/init.d/fips-gateway stop 2>/dev/null || true
/etc/init.d/fips-gateway disable 2>/dev/null || true
/etc/init.d/fips stop 2>/dev/null || true
/etc/init.d/fips disable 2>/dev/null || true
exit 0
EOF
chmod 0755 "$CONTROL_DIR/prerm"
+39 -20
View File
@@ -34,26 +34,26 @@ transports:
# Use physical port names, NOT bridge names (e.g. eth1, not br-lan).
# Run 'ip link show' on the router to identify port names.
# See: https://github.com/jmcorgan/fips/blob/master/deploy/native/README.md
#
# ethernet:
# wan:
# interface: "eth0" # WAN
# discovery: true
# announce: true
# auto_connect: true
# accept_connections: true
# wwan:
# interface: "phy0-sta0" # WWAN
# discovery: true
# announce: true
# auto_connect: true
# accept_connections: true
# lan:
# interface: "br-lan" # LAN
# discovery: true
# announce: true
# auto_connect: true
# accept_connections: true
ethernet:
wan:
interface: "eth0" # WAN
discovery: true
announce: true
auto_connect: true
accept_connections: true
wwan:
interface: "phy0-sta0" # WWAN
discovery: true
announce: true
auto_connect: true
accept_connections: true
lan:
interface: "br-lan" # LAN
discovery: true
announce: true
auto_connect: true
accept_connections: true
peers: []
# Static peers for bootstrapping (add at least one if using UDP-only):
@@ -62,3 +62,22 @@ peers: []
# addresses:
# - transport: udp
# addr: "1.2.3.4:2121" # IP or hostname
# --- Outbound LAN gateway ---
#
# Allows unmodified LAN hosts to reach FIPS mesh destinations via
# DNS-allocated virtual IPs and kernel nftables NAT.
#
# The gateway DNS listens on port 5353 by default so it does not conflict
# with dnsmasq on port 53. The init script configures dnsmasq to forward
# .fips queries to the gateway automatically.
gateway:
enabled: true
pool: "fd01::/112" # virtual IP range (up to 65535 addresses)
lan_interface: "br-lan" # LAN-facing interface for proxy NDP
dns:
listen: "[::1]:5353" # gateway DNS listener (dnsmasq forwards here)
upstream: "127.0.0.1:5354" # FIPS daemon DNS resolver
ttl: 60 # DNS TTL and mapping lifetime (seconds)
pool_grace_period: 60 # seconds after last session before reclaiming
+184
View File
@@ -0,0 +1,184 @@
#!/bin/sh /etc/rc.common
# FIPS outbound LAN gateway — procd init script for OpenWrt
#
# Not enabled by default. Enable with:
# service fips-gateway enable
# service fips-gateway start
#
# Requires: fips daemon running, gateway section in /etc/fips/fips.yaml,
# IPv6 forwarding enabled.
USE_PROCD=1
START=96
STOP=09
PROG=/usr/bin/fips-gateway
CONFIG=/etc/fips/fips.yaml
# Port the gateway DNS listens on (must match dns.listen in fips.yaml).
GW_DNS_PORT=5353
# Port the FIPS daemon DNS listens on.
DAEMON_DNS_PORT=5354
# Global-scope IPv6 prefix assigned to br-lan so Android/Chrome clients
# believe they have full IPv6 and actually send AAAA queries.
# Uses RFC 5180 (benchmarking) range — not routed on the public internet.
GLOBAL_PREFIX="2001:2:f1b5::1/64"
start_service() {
# Apply gateway sysctls (proxy_ndp, IPv6 forwarding).
sysctl -p /etc/sysctl.d/fips-gateway.conf 2>/dev/null || true
# Load conntrack module for /proc/net/nf_conntrack.
modprobe nf_conntrack 2>/dev/null || true
# Redirect dnsmasq .fips forwarding from daemon (5354) to gateway (5353)
# so LAN clients get virtual IPs instead of raw mesh addresses.
# Done early and synchronously so dnsmasq is ready before the gateway
# starts accepting DNS queries.
dnsmasq_swap_fips_upstream "$GW_DNS_PORT"
sleep 1
# Add a global-scope IPv6 prefix to br-lan so Android/Chrome clients
# send AAAA queries (they suppress AAAA when only ULA addresses exist).
# Also set ra_default=2 so odhcpd advertises a default route even
# without upstream IPv6 — needed for clients to accept the prefix.
gateway_add_global_prefix
# Advertise the virtual IP pool via Router Advertisement so LAN clients
# learn a route to the pool automatically.
gateway_add_ra_route
procd_open_instance
procd_set_param command "$PROG" --config "$CONFIG"
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
stop_service() {
# Restore dnsmasq .fips forwarding back to the daemon.
dnsmasq_swap_fips_upstream "$DAEMON_DNS_PORT"
# Remove the RA route for the virtual IP pool.
gateway_remove_ra_route
# Remove the global prefix and ra_default override.
gateway_remove_global_prefix
}
reload_service() {
restart
}
# Extract the gateway pool CIDR from fips.yaml.
# Looks for "pool:" indented under the top-level "gateway:" block.
gateway_pool_cidr() {
awk '/^gateway:/{found=1; next} found && /^[^ ]/{found=0} found && /pool:/{gsub(/.*pool:[[:space:]]*/, ""); gsub(/["'"'"']/, ""); print; exit}' "$CONFIG"
}
# Add an RA-advertised route for the virtual IP pool so LAN clients
# automatically learn how to reach virtual IPs.
gateway_add_ra_route() {
local pool
pool="$(gateway_pool_cidr)"
[ -z "$pool" ] && return 0
# Add a kernel route on br-lan (needed for RA to advertise it).
ip -6 route replace "$pool" dev br-lan proto static 2>/dev/null || true
# Add a UCI route6 entry for odhcpd to include in Router Advertisements.
# Remove any stale entry first.
gateway_remove_ra_route_uci
uci add dhcp route6 >/dev/null
uci set dhcp.@route6[-1].interface='lan'
uci set dhcp.@route6[-1].target="$pool"
uci commit dhcp
/etc/init.d/odhcpd restart 2>/dev/null || true
}
# Remove the RA route.
gateway_remove_ra_route() {
local pool
pool="$(gateway_pool_cidr)"
[ -z "$pool" ] && return 0
ip -6 route del "$pool" dev br-lan proto static 2>/dev/null || true
gateway_remove_ra_route_uci
/etc/init.d/odhcpd restart 2>/dev/null || true
}
# Remove any existing UCI route6 entries for the pool.
gateway_remove_ra_route_uci() {
local i=0
while uci -q get "dhcp.@route6[$i]" >/dev/null 2>&1; do
if [ "$(uci -q get "dhcp.@route6[$i].target")" = "$(gateway_pool_cidr)" ]; then
uci delete "dhcp.@route6[$i]"
uci commit dhcp
return 0
fi
i=$((i + 1))
done
}
# Add a global-scope IPv6 prefix to br-lan and enable RA default route.
gateway_add_global_prefix() {
# Add the global prefix via UCI (idempotent — remove first).
local current
current="$(uci -q get network.lan.ip6addr 2>/dev/null || echo "")"
if [ "$current" != "$GLOBAL_PREFIX" ]; then
uci set network.lan.ip6addr="$GLOBAL_PREFIX"
uci commit network
fi
# Force odhcpd to advertise a default route even without upstream IPv6.
local ra_default
ra_default="$(uci -q get dhcp.lan.ra_default 2>/dev/null || echo "")"
if [ "$ra_default" != "2" ]; then
uci set dhcp.lan.ra_default='2'
uci commit dhcp
fi
# Apply network change immediately (odhcpd restarted by gateway_add_ra_route).
/etc/init.d/network reload 2>/dev/null || true
}
# Remove the global prefix and ra_default override.
gateway_remove_global_prefix() {
local current
current="$(uci -q get network.lan.ip6addr 2>/dev/null || echo "")"
if [ "$current" = "$GLOBAL_PREFIX" ]; then
uci delete network.lan.ip6addr
uci commit network
/etc/init.d/network reload 2>/dev/null || true
fi
uci -q delete dhcp.lan.ra_default 2>/dev/null || true
uci commit dhcp
/etc/init.d/odhcpd restart 2>/dev/null || true
}
# Swap the dnsmasq .fips forwarding port via UCI and restart dnsmasq.
# $1 = target port number
dnsmasq_swap_fips_upstream() {
local port="$1"
# Remove both possible entries, then add the correct one.
uci -q del_list dhcp.@dnsmasq[0].server="/fips/127.0.0.1#${DAEMON_DNS_PORT}" 2>/dev/null
uci -q del_list dhcp.@dnsmasq[0].server="/fips/127.0.0.1#${GW_DNS_PORT}" 2>/dev/null
# Also handle IPv6 loopback variants.
uci -q del_list dhcp.@dnsmasq[0].server="/fips/::1#${DAEMON_DNS_PORT}" 2>/dev/null
uci -q del_list dhcp.@dnsmasq[0].server="/fips/::1#${GW_DNS_PORT}" 2>/dev/null
uci add_list dhcp.@dnsmasq[0].server="/fips/::1#${port}"
uci commit dhcp
# Update the drop-in config file as well (belt-and-suspenders).
if [ -f /etc/dnsmasq.d/fips.conf ]; then
sed -i "s|^server=/fips/.*|server=/fips/::1#${port}|" /etc/dnsmasq.d/fips.conf
fi
# Restart dnsmasq to pick up the change.
/etc/init.d/dnsmasq restart 2>/dev/null || true
}
@@ -0,0 +1,8 @@
# FIPS Gateway: kernel settings for outbound LAN gateway operation.
#
# Applied by the fips-gateway init script on start.
# proxy_ndp lets the gateway answer Neighbor Solicitation for virtual IPs
# on the LAN interface so clients can resolve their link-layer address.
# IPv6 forwarding is required for routing LAN traffic through fips0.
net.ipv6.conf.all.proxy_ndp=1
net.ipv6.conf.all.forwarding=1
@@ -64,9 +64,24 @@ uci commit firewall
# regeneration (e.g. after a firmware upgrade that rebuilds dnsmasq.conf).
uci -q del_list dhcp.@dnsmasq[0].server="/fips/127.0.0.1#5354" 2>/dev/null || true
uci add_list dhcp.@dnsmasq[0].server="/fips/127.0.0.1#5354"
uci -q del_list dhcp.@dnsmasq[0].server="/fips/::1#5354" 2>/dev/null || true
uci add_list dhcp.@dnsmasq[0].server="/fips/::1#5354"
uci -q del_list dhcp.@dnsmasq[0].rebind_domain="fips" 2>/dev/null || true
uci add_list dhcp.@dnsmasq[0].rebind_domain="fips"
uci commit dhcp
# ---------------------------------------------------------------------------
# 4. Gateway prerequisites
# ---------------------------------------------------------------------------
# Load conntrack module (needed for /proc/net/nf_conntrack session counting).
modprobe nf_conntrack 2>/dev/null || true
grep -qxF 'nf_conntrack' /etc/modules.d/nf-conntrack 2>/dev/null || \
echo "nf_conntrack" > /etc/modules.d/nf-conntrack
# Apply gateway sysctls (proxy_ndp + IPv6 forwarding).
# These are harmless even if the gateway is not enabled — forwarding is
# typically already on for a router, and proxy_ndp has no effect until
# proxy NDP entries are actually added.
sysctl -p /etc/sysctl.d/fips-gateway.conf 2>/dev/null || true
exit 0