docs: Tailscale SSH ACL set to accept

Tailnet SSH policy changed from the default action:"check" to accept. The
effective policy is now a single rule covering all tailnet nodes, with no
expiry and port forwarding allowed, so the per-device ~12h grants and the
browser re-auth prompt are gone.

Verified unattended with BatchMode=yes: a plain command, a -L forward
returning HTTP 302, and the Herdr fallback leg.

Records the trade-off: check acted as a second factor, so a compromised
tailnet device now reaches root on Unraid without a further step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kaloyan DanchevandClaude Opus 5 committed 2026-11-03 23:03:47 +02:00
1 parent 9a102511ea
commit 27f5a7d5af
3 files changed
+26 -15

No files matched your search

+9 -4
View File
@@ -262,10 +262,15 @@ ssh root@xtrm-unraid.tailf789e1.ts.net
ssh -N -L 18089:127.0.0.1:80 root@100.100.208.69
```
The default tailnet ACL uses `action: "check"`, so the first connection in each
period opens a browser re-auth prompt. Fine interactively, but it will stall an
unattended tunnel; switching that rule to `accept` for `autogroup:self` in the
admin console removes the prompt.
The tailnet SSH ACL was switched from the default `action: "check"` to
`accept` on 2026-11-03, so this needs no browser re-auth and works unattended.
Before the change the policy minted ~12 h grants per device and prompted when
they lapsed. The effective policy is now a single rule: accept for all tailnet
nodes, with local and remote port forwarding and agent forwarding allowed.
The trade-off: `check` acted as a second factor, so a compromised tailnet device
now reaches root on Unraid without a further step. Acceptable here because every
node in the tailnet is the owner's own hardware.
### The other path — via the MikroTik subnet router
+12 -11
View File
@@ -81,22 +81,23 @@ failed first attempt would consume stdin and corrupt the retry. It costs about
245 ms on the success path. `HERDR_VIA_TARGET=lan|ts` forces a leg for testing;
both were verified 2026-11-03.
The fallback leg inherits the tailnet ACL caveat below: with the default
`action: "check"` it works until the grant lapses, then wants a browser
re-auth. Setting that rule to `accept` for `autogroup:self` makes the fallback
fully unattended.
The fallback leg is fully unattended since the tailnet SSH ACL was set to
`accept` on 2026-11-03. Verified the same day with `BatchMode=yes`: a plain
command, a `-L` port forward (HTTP 302 from the Unraid web UI) and the Herdr leg
itself all ran without a prompt.
## Why the LAN address is the primary and not the Tailscale IP
`--via` requires a transport that passes bytes untouched and **asks nothing**.
Tailscale SSH on `100.100.208.69` sits behind the default tailnet ACL
`action: "check"`, which demands browser re-authentication each period — fine
interactively, fatal for a long-lived Herdr connection.
When this was set up, Tailscale SSH sat behind the default tailnet ACL
`action: "check"`, which demanded browser re-authentication every ~12 h — fine
interactively, fatal for a long-lived Herdr connection. That ACL was changed to
`accept` on 2026-11-03, so either address would now serve.
Key auth on `192.168.10.20:422` asks nothing, and from outside the LAN it still
travels over Tailscale via the MikroTik subnet router (see
`docs/09-TAILSCALE-VPN.md`). Changing that ACL rule to `accept` for
`autogroup:self` would make the `100.x` address usable here too.
The LAN address stays primary anyway: key auth on `192.168.10.20:422` depends on
nothing but the SSH key, while Tailscale SSH depends on the tailnet policy
staying permissive. From outside the LAN it still travels over Tailscale via the
MikroTik subnet router (see `docs/09-TAILSCALE-VPN.md`), so there is no loss.
## Reboot persistence
+5
View File
@@ -13,6 +13,11 @@
- **[SERVICE]** Exit node advertised from Unraid as a fallback to the hAP, and approved in the Tailscale admin console the same day. The tailnet now has two exit nodes: `mikrotik-tailscale` (100.74.219.35, primary) and `xtrm-unraid` (100.100.208.69, fallback). Neither has been exercised in anger from the Mac — without `--exit-node-allow-lan-access` an exit node cuts the client's own LAN access.
- Detail, including the root-only `AllowTcpForwarding` behaviour in Unraid's stock sshd config, in `docs/09-TAILSCALE-VPN.md`.
### Network: Tailscale SSH ACL set to accept
- **[NETWORK]** Tailnet SSH policy changed from Tailscale's default `action: "check"` to `accept` for `autogroup:member` -> `autogroup:self`. Before the change the control plane minted ~12 h per-device grants and prompted for browser re-auth when they lapsed, which would stall any unattended connection. Effective policy is now a single rule: accept for all 10 tailnet nodes, no expiry, with local and remote port forwarding and agent forwarding allowed.
- **[NETWORK]** Verified unattended with `BatchMode=yes`: a plain command over Tailscale SSH, a `-L` port forward (HTTP 302 from the Unraid web UI) and the Herdr fallback leg all ran without a prompt.
- Trade-off recorded in the docs: `check` acted as a second factor, so a compromised tailnet device now reaches root on Unraid without a further step. Accepted because every node in the tailnet is owner-controlled hardware.
### Tooling: Unraid registered as a Herdr (hc) machine
- **[SERVICE]** Unraid added to Herdr as the machine `unraid` (`hc` 0.1.17 on the remote). `herdr-core machine add` had been failing with `core did not start within 5 s`: `hc` puts its core socket under `XDG_RUNTIME_DIR`, which on Unraid is `/run/user/0` — a directory elogind creates and destroys per login session, so the socket disappeared the moment the session that started the core ended. The core kept running with an unlinked socket, and `core.log` stayed empty because the failure is client side.
- **[SERVICE]** Fixed from the Mac only, via `~/.local/bin/herdr-via-unraid`, a `--via` transport that exports `XDG_RUNTIME_DIR=/var/run/herdr-rt` on every connection. Nothing on the server was changed. Uses key auth on 192.168.10.20:422 rather than Tailscale SSH, because `--via` needs a transport that asks nothing and the default tailnet ACL `check` would demand periodic browser re-auth.