Use Tailscale and OpenSSH for Private Pi Administration

This procedure explicitly assumes Starlink’s default IPv4 CGNAT, a Netgate 2100 LAN4 DMZ at 192.168.60.0/24, and Raspberry Pi 5 192.168.60.10. Nginx listens on loopback, cloudflared provides public web only, and Tailscale provides private administration. There is no WAN SSH forward and no SSH through the Cloudflare public web hostname.

Use two independent recovery paths. Tailscale is the normal private path; a documented trusted-LAN host or local console is break glass. Keep both key-controlled and audited. Do not turn the whole tailnet, LAN, or public web hostname into an administrator network.

Enroll identities deliberately

  1. Patch the Pi and administrator devices, verify clocks, and enable MFA on the tailnet identity provider. Remove lost or replaced devices before enrollment.
  2. Install Tailscale using the current official repository or package instructions for the Pi’s actual OS and architecture. Check tailscale version and read the installed command help; package commands change.
  3. Prefer interactive enrollment. If automation requires an auth key, scope it to the intended tags, set an expiry, store it as a secret, and revoke it after enrollment. Never put it in a unit file or shell transcript.
  4. Use normal OpenSSH keys or short-lived OpenSSH certificates for the Unix account. Tailscale membership does not authorize an operating-system login.
tailscale version
sudo tailscale up
tailscale status
tailscale ip

Only run tailscale up after reviewing its flags for the installed release. Do not add --advertise-exit-node or --advertise-routes for a one-host administration role. A subnet router would increase the reachable surface and requires its own approval and policy.

Write a least-privilege grants or ACL policy

Name the administrator group, a Pi tag or device identity, and TCP/22 only. Grants are preferred for current policy syntax; legacy ACL syntax may be required by an existing tailnet. Validate policy in the admin console and retain the previous version for rollback:

{
  "grants": [
    {
      "src": ["group:ssh-admins"],
      "dst": ["tag:pi-dmz"],
      "ip": ["tcp:22"]
    }
  ]
}

Replace the sample group and tag with real, reviewed identities. Do not grant *:*, the whole 192.168.60.0/24, or all tailnet users. If the policy uses ACL syntax rather than grants, express the same source, destination, and TCP/22 constraint and run the policy validator. Keep device approval, key expiry, tags, group membership, and administrative events under review.

Restrict the Pi host firewall and sshd

Preserve the existing loopback, established/related, ICMP, and service rules. Add a rule to the existing nftables inet input chain for only TCP/22 from tailscale0:

ct state established,related accept
iifname "tailscale0" tcp dport 22 accept

Never replace it with a broad iifname "tailscale0" accept. Configure OpenSSH with key-only authorization, no root login, and no password authentication according to the distribution’s supported policy. Use a drop-in, validate it, and reload while a second session remains available:

# /etc/ssh/sshd_config.d/30-private-admin.conf
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no

sudo sshd -t
sudo systemctl reload ssh || sudo systemctl reload sshd

These values may conflict with an approved console or PAM policy; inspect sshd -T and use the account’s documented recovery method. Keep host-key verification enabled. For certificate users, configure the CA and principals as described in Use OpenSSH Certificates and Key Revocation.

Optional direct transport, always private SSH

Tailscale may use a direct encrypted WireGuard path or an encrypted DERP relay. UDP/41641 is optional direct transport, not an SSH listener. If the Pi’s LAN4 interface is confirmed as eth0, the separate host-input rule is:

iifname "eth0" udp dport 41641 accept

Use this only when its scan and resource-exhaustion trade-off is accepted. Starlink CGNAT may still prevent direct connectivity. Allow Tailscale’s documented outbound control and relay traffic (commonly TCP/443, UDP/3478, and UDP/41641) through the Netgate LAN4 egress policy as needed; these are stateful outbound flows. Do not add a WAN pass rule or port forward, and do not change the SSH rule from tailscale0 to eth0 or WAN.

tailscale netcheck
tailscale ping pi-dmz
ssh -o ConnectTimeout=10 pi-admin@<tailnet-address-or-name>

Record whether the peer is direct or relayed, but do not reduce authorization for a DERP path. A successful SSH connection must still satisfy tailnet policy, the host firewall, the SSH key or certificate, the account, and the host key.

Retain trusted-LAN break glass

Choose one management host or a small source alias on the trusted LAN and permit only its TCP/22 connection to 192.168.60.10. Keep it separate from the tailscale0 rule. Store the source, account, key fingerprint, console owner, and use procedure in the recovery record. Test with Tailscale stopped: the named host succeeds, another trusted-LAN host fails, and all public sources fail. Do not permit the whole trusted LAN, and do not expose the Netgate WebConfigurator or private storage from the DMZ.

Rotate, back up, and test

  • Rotate a user key before expiry and immediately after loss, copying, or device repair. For certificates, issue a replacement with a short validity and revoke the old serial or key ID.
  • Review and remove stale Tailscale devices, expired auth keys, old tags, unused groups, and former administrators. Rotate the Tailscale policy when the role changes.
  • Back up the Pi’s SSH drop-ins, authorized principals, CA public key, KRL, host keys, nftables rules, Tailscale policy, Netgate configuration, cloudflared configuration, and recovery notes. Encrypt backups; never include private keys unless separately protected and required.
TestExpected result
Approved administrator over TailscaleTCP/22 succeeds; Nginx origin, Netgate GUI, and unlisted ports remain unavailable.
Unauthorized tailnet device or userGrant/ACL and host policy deny TCP/22.
UDP/41641 allowedDirect path may appear; SSH remains on tailscale0.
UDP/41641 blockedEncrypted DERP fallback works, or the failure is clearly recorded; authorization is unchanged.
Tailscale stoppedOnly the documented trusted-LAN break-glass host can SSH.
Outside IPv4 sourceNo WAN SSH forward and no SSH via Cloudflare web hostname.

Rollback policy edits by restoring the prior validated grants or ACL. Roll back host changes by restoring the reviewed SSH and nftables files, running syntax checks, and reloading through the open session or console. Remove temporary UDP/41641 and break-glass exceptions only after the normal and recovery tests pass.

For the complete network boundary, read SSH Across Starlink CGNAT and a Netgate 2100 DMZ and Restrict Netgate 2100 DMZ Egress.

Official references