sshd: public exposure contradicts tangled-knot 'tailnet-only' comment; do security pass #103

Open
opened 2026-05-24 18:25:21 +00:00 by coilysiren · 0 comments
Owner

Problem

During mobile-SSH debugging today, auth.log on kai-server showed live brute-force scan attempts from public IPs (2.57.122.177, 89.134.210.182, 213.35.128.24, 161.35.139.3). That contradicts the comment in infrastructure/sshd/tangled-knot.conf which states 'SSH stays tailnet-only.'

Observed state on kai-server (snapshot from this debug session)

  • sshd listening: 0.0.0.0:22 and [::]:22 (all interfaces, including public).
  • systemctl is-active fail2ban sshguard -> both inactive.
  • ufw active, rules not verified in this session (no interactive sudo).
  • Multiple unique public-IP scan attempts logged in the last few minutes - meaning packets are reaching sshd's preauth at minimum.

Likely sources of the gap

  1. Home router port-forwards :22 to kai-server (unintended NAT). Check router config.
  2. sshd was meant to bind to tailscale0 / 127.0.0.1 only and that hardening never landed.
  3. The tangled-knot keyfetch path needs public SSH reachability for git-over-SSH from atproto-registered users (Match User git block). If so, the 'tailnet-only' comment is stale and the security posture relies entirely on the keyfetch denying unknown keys.

Proposed scope for the security pass

Non-exhaustive, ordered by likely payoff:

  • Verify whether public :22 exposure is intentional (tangled-knot git path) or a router misconfig. Document the decision in docs/k3s-deploy-notes.md or a new docs/ssh-exposure.md.
  • If exposure must stay for the git user, restrict the kai user to tailnet-only via a Match Address block + a non-default port for the git user, or split the daemons.
  • Enable fail2ban (or sshguard) with the standard ssh jail. Confirms throttling on bot attempts regardless of upstream answer.
  • Disable PasswordAuthentication globally if not already; pubkey only. (Need to verify current state - not visible without sudo.)
  • Disable PermitRootLogin globally. (Need to verify.)
  • Bind sshd ListenAddress to 100.69.164.66 (tailscale0) + 127.0.0.1 if router forwards aren't required.
  • Cross-reference with docs/k3s-deploy-notes.md section on tailnet-only services and update if reality differs.

Related

  • Followed-on from today's mobile-SSH debug session (tracking doc coilysiren/mobile-ssh-debug.md on Kai's workstation).
  • infrastructure#102 - sshd ClientAlive cleanup (orthogonal but adjacent).

Filed by Claude.

**Problem** During mobile-SSH debugging today, `auth.log` on kai-server showed live brute-force scan attempts from public IPs (`2.57.122.177`, `89.134.210.182`, `213.35.128.24`, `161.35.139.3`). That contradicts the comment in [`infrastructure/sshd/tangled-knot.conf`](https://forgejo.coilysiren.me/coilysiren/infrastructure/src/branch/main/sshd/tangled-knot.conf) which states 'SSH stays tailnet-only.' **Observed state on kai-server (snapshot from this debug session)** - sshd listening: `0.0.0.0:22` and `[::]:22` (all interfaces, including public). - `systemctl is-active fail2ban sshguard` -> both **inactive**. - `ufw` active, rules not verified in this session (no interactive sudo). - Multiple unique public-IP scan attempts logged in the last few minutes - meaning packets are reaching sshd's preauth at minimum. **Likely sources of the gap** 1. Home router port-forwards :22 to kai-server (unintended NAT). Check router config. 2. sshd was meant to bind to tailscale0 / 127.0.0.1 only and that hardening never landed. 3. The tangled-knot keyfetch path needs public SSH reachability for git-over-SSH from atproto-registered users (`Match User git` block). If so, the 'tailnet-only' comment is stale and the security posture relies entirely on the keyfetch denying unknown keys. **Proposed scope for the security pass** Non-exhaustive, ordered by likely payoff: - Verify whether public :22 exposure is intentional (tangled-knot git path) or a router misconfig. Document the decision in `docs/k3s-deploy-notes.md` or a new `docs/ssh-exposure.md`. - If exposure must stay for the `git` user, restrict the `kai` user to tailnet-only via a `Match Address` block + a non-default port for the git user, or split the daemons. - Enable fail2ban (or sshguard) with the standard ssh jail. Confirms throttling on bot attempts regardless of upstream answer. - Disable `PasswordAuthentication` globally if not already; pubkey only. (Need to verify current state - not visible without sudo.) - Disable `PermitRootLogin` globally. (Need to verify.) - Bind sshd `ListenAddress` to `100.69.164.66` (tailscale0) + `127.0.0.1` if router forwards aren't required. - Cross-reference with [`docs/k3s-deploy-notes.md`](https://forgejo.coilysiren.me/coilysiren/infrastructure/src/branch/main/docs/k3s-deploy-notes.md) section on tailnet-only services and update if reality differs. **Related** - Followed-on from today's mobile-SSH debug session (tracking doc `coilysiren/mobile-ssh-debug.md` on Kai's workstation). - [infrastructure#102](https://forgejo.coilysiren.me/coilysiren/infrastructure/issues/102) - sshd ClientAlive cleanup (orthogonal but adjacent). **Filed by Claude.**
Sign in to join this conversation.
No labels
P0
P1
P2
P3
P4
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/infrastructure#103
No description provided.