diff --git a/core/openwrt/src/tollgate/mod.rs b/core/openwrt/src/tollgate/mod.rs index 8017f8f0..d92b1c55 100644 --- a/core/openwrt/src/tollgate/mod.rs +++ b/core/openwrt/src/tollgate/mod.rs @@ -48,6 +48,15 @@ pub async fn provision(router: &Router, config: &TollGateConfig) -> Result<()> { nodogsplash::install_and_stop(router, pkg_mgr) .context("install nodogsplash — tollgate-wrt cannot gate clients without it")?; + // Wire NoDogSplash's webroot to TollGate's actual payment portal instead + // of the generic stock splash page it ships with. Confirmed live: without + // this, "click continue" on the stock page authorizes the client via + // NoDogSplash's own built-in handler with zero payment involved. + nodogsplash::install_captive_portal_symlink(router).context( + "wire up TollGate's captive portal — without it NoDogSplash serves its own \ + generic splash page, which authorizes clients on click with no payment", + )?; + config::apply(router, config)?; wifi::provision_ssid(router, config)?; // Must come after provision_ssid (which creates br-tollgate) and before diff --git a/core/openwrt/src/tollgate/nodogsplash.rs b/core/openwrt/src/tollgate/nodogsplash.rs index 887cb144..82bafed4 100644 --- a/core/openwrt/src/tollgate/nodogsplash.rs +++ b/core/openwrt/src/tollgate/nodogsplash.rs @@ -29,6 +29,45 @@ pub fn install_and_stop(router: &Router, pkg_mgr: PkgManager) -> Result<()> { Ok(()) } +/// Point NoDogSplash's webroot at TollGate's own splash page instead of the +/// generic stock one NoDogSplash ships with. +/// +/// Confirmed live: without this, NoDogSplash serves its own bundled +/// click-to-continue splash page — clicking "Continue" calls NDS's built-in +/// auth handler directly and authorizes the client with zero payment +/// involved. TollGate's actual payment UI (a QR/Cashu-token entry SPA) lives +/// at `/etc/tollgate/tollgate-captive-portal-site` — the .ipk's data payload +/// stages it there (see `packaging/files/tollgate-captive-portal-site/` in +/// the upstream repo), it's just never wired up as NoDogSplash's webroot. +/// +/// Mirrors upstream's own `90-tollgate-captive-portal-symlink` uci-defaults +/// script exactly (symlink swap, not a `webroot` UCI override) — confirmed +/// live that setting `option webroot` directly instead causes NoDogSplash to +/// 500 on every request, for reasons not fully understood (worth filing +/// upstream, but the symlink approach is what's actually shipped/tested). +pub fn install_captive_portal_symlink(router: &Router) -> Result<()> { + let (_, exists) = router.run("test -d /etc/tollgate/tollgate-captive-portal-site")?; + if exists != 0 { + anyhow::bail!( + "/etc/tollgate/tollgate-captive-portal-site missing — expected to be staged \ + by the tollgate-wrt package install" + ); + } + + router.run_ok( + "if [ -L /etc/nodogsplash/htdocs ]; then \ + true; \ + else \ + if [ -d /etc/nodogsplash/htdocs ]; then \ + mv /etc/nodogsplash/htdocs /etc/nodogsplash/htdocs.backup; \ + fi; \ + rm -rf /etc/nodogsplash/htdocs; \ + ln -sf /etc/tollgate/tollgate-captive-portal-site /etc/nodogsplash/htdocs; \ + fi" + )?; + Ok(()) +} + /// Configure NoDogSplash to gate the dedicated `br-tollgate` bridge (see /// `wifi::provision_network`), not `br-lan` — the paid SSID here lives on its /// own isolated network/subnet rather than the canonical upstream layout @@ -80,6 +119,14 @@ pub fn configure(router: &Router, cfg: &TollGateConfig) -> Result<()> { router.uci_add_list("nodogsplash.main.users_to_router", "allow tcp port 2121")?; router.uci_add_list("nodogsplash.main.users_to_router", "allow tcp port 2050")?; + // Post-auth (paid) clients get full access — matches the stock package + // default (`list authenticated_users 'allow all'`), which our from-scratch + // named section never carried over. Under this router's default-ACCEPT + // FORWARD policy an empty list happens to behave the same, but that's an + // accident of this specific setup, not something to depend on. + let _ = router.uci_delete("nodogsplash.main.authenticated_users"); + router.uci_add_list("nodogsplash.main.authenticated_users", "allow all")?; + router.uci_commit(Some("nodogsplash"))?; Ok(()) }