double-dealing-character: correct stale setup docs
The disable-password-ssh guard checks only that authorized_keys is
non-empty, which a stale key satisfies while every session silently falls
back to password auth - the state this box is in today.

Assisted-by: Claude Opus 5 via Claude Code
change lzxprqrrnqrnzyllkzrkmzvuyzuuzlsl
commit 490fe9c69ec43426923a4dfbaee3e3b5970d9c75
author Alpha Chen <alpha@kejadlen.dev>
date
parent vpmzoolk
diff --git a/double-dealing-character/README.md b/double-dealing-character/README.md
index 99d28b5..ca6a366 100644
--- a/double-dealing-character/README.md
+++ b/double-dealing-character/README.md
@@ -22,8 +22,19 @@ export SSH_PUBKEY="ssh-ed25519 AAAA... me@laptop"
 - Approve as an exit node and disable key expiry in the [Tailscale console](https://login.tailscale.com/admin/machines)
 - Test SSH from a tailnet peer, then `/tmp/mango-tailscale-setup.sh disable-password-ssh`
 
-`mango-tailscale-setup.sh status` prints install, mount, service, and tailnet
-state in one go.
+Test that step with `ssh -o PasswordAuthentication=no root@double-dealing-character
+true`, not a plain `ssh`. A plain `ssh` succeeds on either method, so a stale key
+plus a silent fallback to password auth looks identical to working key auth. The
+`disable-password-ssh` guard only checks that `authorized_keys` is non-empty,
+which a stale key satisfies, and disabling passwords in that state locks you out
+of a box whose only other door is its own LAN at 192.168.8.1.
+
+`all` runs `configure-routing` as one of its stages, which is what keeps
+exit-node replies inside the tunnel rather than sending them back out the WAN.
+Run it on its own if the exit node stops working after a firmware upgrade.
+
+`mango-tailscale-setup.sh status` prints install, mount, service, tailnet, and
+exit-node return path state in one go.
 
 ## Notes
 
diff --git a/double-dealing-character/mango-tailscale-setup.sh b/double-dealing-character/mango-tailscale-setup.sh
index 809af97..b8bfaab 100755
--- a/double-dealing-character/mango-tailscale-setup.sh
+++ b/double-dealing-character/mango-tailscale-setup.sh
@@ -22,10 +22,17 @@
 #
 # ---- Operating mode --------------------------------------------------------
 #
-# Assumes the Mango is configured as a wifi REPEATER against a home wifi
-# network. The upstream interface is `wwan`, which GL.iNet firmware places
-# in the `wan` firewall zone alongside the physical WAN port. The exit node
-# routes remote tailnet peers' internet traffic through the home connection.
+# Works with either upstream: the physical WAN port (`eth0.2`) or a wifi
+# repeater link (`wwan`). GL.iNet firmware places both in the `wan` firewall
+# zone, so the zone and forwarding rules below are the same either way. The
+# exit node routes remote tailnet peers' internet traffic out whichever one
+# is live.
+#
+# double-dealing-character as deployed runs on the WIRED port: network.wan is
+# eth0.2 on DHCP, and mwan3 reports wan online with wwan offline. Worth knowing
+# before debugging, because the mwan3 return-path bug documented in
+# 2026-08-07-exit-node-return-path.md is expressed as a rule on `iif eth0.2`;
+# on a repeater-mode box the equivalent rule names `wwan` instead.
 #
 # Expected throughput: ~5-10 Mbps. Two compounding effects: the Mango has
 # no AES offload on a single ~580 MHz MIPS core [7], and double-NAT (Mango
@@ -571,9 +578,10 @@ cmd_configure_ssh() {
     fi
     chmod 600 /etc/dropbear/authorized_keys
 
-    log "Test SSH from a tailnet peer BEFORE disabling password auth:"
-    log "    ssh root@$TS_HOSTNAME"
-    log "When that works, run: $0 disable-password-ssh"
+    log "Test SSH from a tailnet peer BEFORE disabling password auth. Force"
+    log "key auth, or you will not notice a silent fallback to password:"
+    log "    ssh -o PasswordAuthentication=no root@$TS_HOSTNAME true"
+    log "When that succeeds, run: $0 disable-password-ssh"
 }
 
 # Lock dropbear down to public-key auth only.
@@ -581,10 +589,24 @@ cmd_configure_ssh() {
 # Refuses to run if authorized_keys is empty - this would otherwise be a
 # foot-cannon: disable passwords with no keys installed and you cannot SSH
 # in by any means.
+#
+# That check is necessary but NOT sufficient, and the gap has bitten this box:
+# a stale or mismatched key leaves the file non-empty while key auth fails and
+# every session quietly falls back to the password. Disabling passwords then
+# locks you out of a box whose only other door is its own LAN. A plain
+# `ssh root@host` does not distinguish the two cases - it succeeds either way -
+# so verify with PasswordAuthentication=no first. Check which method actually
+# won with `logread | grep dropbear`; it logs "Password auth succeeded" or
+# "Public key auth succeeded" per connection.
 cmd_disable_password_ssh() {
     [ -s /etc/dropbear/authorized_keys ] || \
         die "No authorized_keys present - refusing to lock you out"
 
+    warn "authorized_keys is non-empty, but that does not prove key auth works."
+    warn "If you have not confirmed it, interrupt now and run this from a peer:"
+    warn "    ssh -o PasswordAuthentication=no root@$TS_HOSTNAME true"
+    sleep 5
+
     log "Disabling password auth on dropbear"
     uci set dropbear.@dropbear[0].PasswordAuth='off'
     uci set dropbear.@dropbear[0].RootPasswordAuth='off'