mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
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.
185 lines
5.8 KiB
Bash
Executable File
185 lines
5.8 KiB
Bash
Executable File
#!/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
|
|
}
|