Skip to content

Commit fdbdfc3

Browse files
author
Amperstrand
committed
rebase onto main (post-#836/#839/#842 merges + cross-family review base): fix(packaging): IPv6 disabled globally until captivity is v6-aware (#783 mitigation)
1 parent c7dbd74 commit fdbdfc3

30 files changed

Lines changed: 2272 additions & 38 deletions

‎.github/workflows/test.yml‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,18 @@ jobs:
314314
# decoration); each was a reader/writer disagreement this table
315315
# would have caught.
316316
bash tests/contract/check-ssid-naming.sh
317+
- name: NDS pre-auth allowlist nft ordering check
318+
run: bash tests/contract/check-nds-preauth-nft.sh
319+
- name: nft guard interface defines check
320+
run: bash tests/contract/check-nft-interface-defs.sh
321+
- name: gatewayinterface adoption check
322+
run: bash tests/contract/check-gatewayinterface-adoption.sh
323+
- name: firewall zone-ref resolution check
324+
run: bash tests/contract/check-firewall-zone-refs.sh
325+
- name: wifi rebind guard check
326+
run: bash tests/contract/check-wifi-rebind-guard.sh
327+
- name: topology self-check contract
328+
run: bash tests/contract/check-topology-selfcheck.sh
317329

318330
deps-and-imports:
319331
runs-on: ubuntu-latest

‎.ngit/act/workflows/test.yml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,16 @@ jobs:
196196
run: bash tests/contract/check-ssid-format.sh
197197
- name: SSID naming contract (the matcher's acceptance table)
198198
run: bash tests/contract/check-ssid-naming.sh
199+
- name: NDS pre-auth allowlist nft ordering check
200+
run: bash tests/contract/check-nds-preauth-nft.sh
201+
- name: nft guard interface defines check
202+
run: bash tests/contract/check-nft-interface-defs.sh
203+
- name: firewall zone-ref resolution check
204+
run: bash tests/contract/check-firewall-zone-refs.sh
205+
- name: wifi rebind guard check
206+
run: bash tests/contract/check-wifi-rebind-guard.sh
207+
- name: topology self-check contract
208+
run: bash tests/contract/check-topology-selfcheck.sh
199209

200210
deps-and-imports:
201211
runs-on: ubuntu-latest
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
- **Setup never binds an AP to a network that does not exist, never silently
2+
renames an operator-chosen SSID, and never silently opens an operator's
3+
AP.** On a split-plane DUT whose config has no `network.lan`, the
4+
unconditional `network=lan` rebind bridged every AP to a ghost network —
5+
clients associate, DHCP dies, the portal is unreachable — and the staged
6+
operator SSID was silently renamed on top (bench-verified on rc1, exactly
7+
the verdict's repro). The binding is now decided: stock routers keep the
8+
byte-identical `lan`; a config without it adopts the network owning the NDS
9+
gatewayinterface (the portal plane); a config with neither preserves the
10+
section's binding or leaves it unbound — never a ghost, every non-stock
11+
decision logged. The SSID follows the one-way discipline of the hostname
12+
(#444): machine-shaped names (empty, the current derivation,
13+
`<anything>-<device code>`) re-derive from the stored code; an operator's
14+
name is preserved and said so — and an adopted operator section keeps its
15+
encryption untouched (the one-way rewrite is about generated names, never
16+
credentials), while the module's own public APs stay open by contract. The
17+
postinst now runs a topology self-check: every enabled AP a member of an
18+
existing bridge, exactly one connected route per portal subnet — the
19+
silent-blackhole signatures — named loudly in syslog
20+
([#786](https://github.com/OpenTollGate/tollgate-module-basic-go/pull/786)).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
- **The NDS pre-auth allowlist actually executes now — rendered into the
2+
enforcement chain, ahead of its first rule.** nodogsplash compiles
3+
`preauthenticated_users` into priority-0 chains, but the package's own
4+
`nds_enforce_forward` chain drops/rejects at priority −1 — the compiled
5+
accepts never ran, so every allowlisted pre-auth flow (the
6+
top-up-before-pay mint access) died (bench-verified on rc1 with
7+
packet-level attribution). `tollgate-nds-preauth-render` now emits
8+
`20-nds-enforce.nft` with the allowlist's accepts as the chain's FIRST
9+
rules — the position the kernel honours, because a base chain's `accept`
10+
verdict ends evaluation in that chain only (a separate earlier-priority
11+
allow chain, this fix's first shape, was refuted live on an nft 1.1.7 rig
12+
and removed). Unparseable, non-tcp/udp, DNS-destination and out-of-range
13+
entries are refused loudly rather than guessed at; the shipped default
14+
file is byte-identical to the renderer's empty-allowlist output by
15+
contract test; convergence runs at install/upgrade/boot and on
16+
nodogsplash config commits (#754, #782).
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
- **IPv6 is disabled globally on the router until captivity is v6-aware.** The rc1
2+
bench caught a pre-auth client fetching the real internet over IPv6 (AAAA) while
3+
the identical IPv4 state was correctly captive (#783): nodogsplash manages only
4+
the IPv4 dimension, and the existing LAN-side disable from #148 (`ra`/`dhcpv6`
5+
off, `ip6assign=0`) does not cover a v6-capable WAN. `setup_disable_ipv6` now
6+
also clears `network.lan.ipv6`, sets `network.wan.ipv6='0'`, and disables the
7+
stock `wan6` section when present (a missing section is adoption, not an
8+
error), re-asserted on every setup pass so upgrades converge too. Proper
9+
v6-aware captivity stays tracked in #783 (post-0.6.0); #794 pins the test axis
10+
([#783](https://github.com/OpenTollGate/tollgate-module-basic-go/issues/783))
11+
([#815](https://github.com/OpenTollGate/tollgate-module-basic-go/pull/815)).
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
- **The nftables guards no longer hardcode the captive bridge's name — a
2+
renamed bridge keeps its API access instead of silently blackholing.**
3+
The shipped guards (20/30/31/32) matched `br-lan` by literal; an operator
4+
renaming the bridge lost the portal→:2121 path with nothing in the log
5+
naming the cause (bench-verified on rc1: the guard's own drop counter
6+
doing the killing). The guards now resolve `$tg_portal_if` /
7+
`$tg_private_if` from `00-tollgate-defs.nft`, rendered from nodogsplash's
8+
`gatewayinterface` and the module-owned private bridge by
9+
`tollgate-nft-interfaces-render` — with a shipped static default so the
10+
defines resolve before the first render, a service-start WARN naming the
11+
expected interface when it does not exist, and every fallback said aloud.
12+
The rendered `20-nds-enforce.nft` (the #754 rescue) carries the same
13+
defines: the renderer's template and the shipped file are byte-identical
14+
by contract test (#757, #784).
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
- **A renamed firewall zone no longer silently un-ships the module's firewall
2+
rules — the zone refs follow the router's own config and a skipped rule
3+
says so in logread.** The package's firewall sections referenced the stock
4+
zone by name; fw4 skips a wrong-name section with exit 0 and nothing in
5+
syslog, so a zone rename left a portal that appeared to work with no rules
6+
(bench-verified on rc1: the rule vanished from the ruleset and a trusted
7+
client lost forwarding with only the zone name differing). The zone that
8+
owns the captive bridge is now resolved from the NDS gatewayinterface
9+
through the network config at section creation and on every setup pass,
10+
with a warned fallback to the stock name when unresolvable — and every
11+
fallback warning reaches both the setup log and syslog; a referenced zone
12+
that does not exist is named on both surfaces too, and the postinst
13+
surfaces fw4's own skip diagnostics instead of discarding them. Only the
14+
captive-side rule (`tollgate_in`) is resolved; the wan-side
15+
`tollgate_protocol` ref is asserted-loud rather than resolved — there is
16+
no authoritative wan-zone source equivalent to the gatewayinterface
17+
(#755, #785).

‎packaging/Makefile‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,14 @@ wait_for_iface br-private || echo "Note: br-private not found — this is expect
144144

145145
echo "Reloading remaining services..."
146146
wifi reload 2>/dev/null || true
147-
/etc/init.d/firewall reload 2>/dev/null || true
147+
/etc/init.d/firewall reload >/tmp/tollgate-fw4-reload.log 2>&1 || true
148+
# fw4 skips a section whose zone/interface ref is wrong — exit 0, stderr
149+
# only, never logread: exactly the silent-skip #755 is about. Surfacing the
150+
# skip diagnostics into syslog is the difference between an operator seeing
151+
# one ERROR line and debugging "the portal works but nothing is enforced".
152+
if grep -q "specifies invalid value\|skipped" /tmp/tollgate-fw4-reload.log 2>/dev/null; then
153+
logger -t tollgate-setup "fw4 reload skipped section(s): $$(grep -E 'specifies invalid value|skipped' /tmp/tollgate-fw4-reload.log | head -n 3 | tr '\n' ' ')"
154+
fi
148155
/etc/init.d/dnsmasq restart 2>/dev/null || true
149156
/etc/init.d/uhttpd restart 2>/dev/null || true
150157
/etc/init.d/nodogsplash restart 2>/dev/null || true
@@ -157,6 +164,13 @@ else
157164
echo "Warning: /etc/init.d/tollgate-wrt not found, skipping service start"
158165
fi
159166

167+
# The #756 verdict's postinst self-check: every enabled AP a member of an
168+
# existing bridge, exactly one connected route per portal subnet. Runs AFTER
169+
# the network/firewall convergence above; assert-style and loud (syslog +
170+
# stdout) but never fails the install — it reports topology damage, not a
171+
# packaging error. Operators can re-run it by hand.
172+
/usr/local/bin/tollgate-topology-selfcheck || true
173+
160174
echo "Post-installation script completed successfully"
161175
exit 0
162176
endef
@@ -214,6 +228,9 @@ define Package/$(PKG_NAME)/install
214228

215229
$(INSTALL_DIR) $(1)/usr/local/bin
216230
$(INSTALL_BIN) $(PKG_MAKEFILE_DIR)files/usr/local/bin/first-login-setup $(1)/usr/local/bin/
231+
$(INSTALL_BIN) $(PKG_MAKEFILE_DIR)files/usr/local/bin/tollgate-nds-preauth-render $(1)/usr/local/bin/
232+
$(INSTALL_BIN) $(PKG_MAKEFILE_DIR)files/usr/local/bin/tollgate-nft-interfaces-render $(1)/usr/local/bin/
233+
$(INSTALL_BIN) $(PKG_MAKEFILE_DIR)files/usr/local/bin/tollgate-topology-selfcheck $(1)/usr/local/bin/
217234

218235
$(INSTALL_DIR) $(1)/etc/tollgate
219236
$(INSTALL_DIR) $(1)/etc/tollgate/ecash
@@ -279,6 +296,7 @@ FILES_$(PKG_NAME) += \
279296
/etc/crontabs/root \
280297
/lib/upgrade/keep.d/tollgate \
281298
/etc/hotplug.d/iface/95-tollgate-restart \
299+
/etc/nftables.d/00-tollgate-defs.nft \
282300
/etc/nftables.d/20-nds-enforce.nft \
283301
/etc/nftables.d/30-backend-firewall.nft \
284302
/etc/nftables.d/31-admin-board-not-guest-reachable.nft \

‎packaging/files/etc/init.d/tollgate-wrt‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,36 @@ start_service() {
3636
procd_set_param respawn 3 5 0
3737

3838
procd_close_instance
39-
39+
40+
# Fail loudly when the guards' interfaces are missing (#757's ask): the
41+
# nft guards silently dropping a renamed bridge's traffic is the exact
42+
# blind spot this replaces — a WARN naming the expected interface turns
43+
# a client-side mystery into one logread line.
44+
_portal_if="$(uci -q get nodogsplash.@nodogsplash[0].gatewayinterface 2>/dev/null || echo br-lan)"
45+
if ! ip link show "$_portal_if" >/dev/null 2>&1; then
46+
logger -s -t tollgate-wrt -p user.warn "WARN: the captive-portal interface '$_portal_if' (nodogsplash gatewayinterface) does not exist on this router — the nft guards and nodogsplash itself cannot serve it; rename follow-up missing? (#757)"
47+
fi
48+
4049
# Log the start for debugging
4150
echo "TollGate basic started with nodogsplash" > /tmp/basic.log
4251
}
4352

53+
# Operator edits to nodogsplash uci (pre-auth allowlist changes among them)
54+
# re-render the priority -2 allow fragment and reload the firewall — WITHOUT
55+
# bouncing the daemon (#754: the allowlist must track uci, and nodogsplash's
56+
# own restart reloads nothing in /etc/nftables.d, which fw4 owns).
57+
service_triggers() {
58+
procd_add_reload_trigger nodogsplash
59+
}
60+
61+
reload_service() {
62+
# Both nft fragments derive from config nodogsplash feeds on: the -2
63+
# pre-auth allowlist (#754) and the interface defines the guards
64+
# resolve (#757). One commit, one converge, no daemon bounce.
65+
/usr/local/bin/tollgate-nds-preauth-render --reload
66+
/usr/local/bin/tollgate-nft-interfaces-render --reload
67+
}
68+
4469
stop() {
4570
# Log the stop for debugging
4671
echo "TollGate basic stopped and nodogsplash disabled" >> /tmp/basic.log
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# The interface defines every /etc/nftables.d guard resolves. This shipped
2+
# file is the STATIC DEFAULT (the shipped bridge names); the renderer
3+
# (/usr/local/bin/tollgate-nft-interfaces-render, run by 99-tollgate-setup
4+
# and the tollgate-wrt reload trigger) overwrites it from the router's own
5+
# config — nodogsplash's gatewayinterface and the module-owned private
6+
# bridge — so a renamed captive bridge keeps the guards matching it (#757).
7+
# The define must always exist: an unresolved $tg_portal_if would fail the
8+
# whole fw4 ruleset load, which is why the default ships rather than being
9+
# generated from nothing. This file sorts first; the guards follow.
10+
define tg_portal_if = "br-lan"
11+
define tg_private_if = "br-private"

0 commit comments

Comments
 (0)