feat: dedicated jumpbox user for SSH key delegation#89
Merged
Conversation
Pre-fix, ReconcileAuthorizedKeys appended every Gopher-managed public key
into ~/.ssh/authorized_keys of the OS user the dashboard runs as
(typically gopher). That user has NOPASSWD sudo for iptables / fail2ban,
read access to gopher.db (every per-machine SSH private key, every token,
the password hash, TOTP secrets), and write access to /opt/gopher/gopher.
A leaked Gopher SSH key gave the holder a full path to root + DB
exfiltration + every-client compromise.
Fix: dedicated `gopher-jump` system user, no shell, no sudo, no homedir
contents the dashboard cares about. Each authorized_keys line is wrapped
with `restrict,permitopen="127.0.0.1:*",permitopen="localhost:*"` so even
a stolen key can only forward to localhost ports on the VPS — which is
exactly what the jumpbox flow legitimately needs. `restrict` also
disables shell, X11, agent forwarding, env, and ~/.ssh/rc.
Auto-migration: the next ReconcileAuthorizedKeys call after `gopher-jump`
exists writes Gopher keys into ~gopher-jump/.ssh/authorized_keys with the
new options and scrubs them from the dashboard user's authorized_keys.
Matching is by type+keydata, so any operator-added non-Gopher keys
survive untouched. Replace-in-place semantics on existing matching lines
mean re-runs upgrade no-options entries to restrict/permitopen without
duplicating.
User creation lives in two places:
- cmd/server/install.go: ensureSystemUser(gopher-jump, /var/lib/gopher-jump)
runs as part of `gopher install`. New deployments get it for free.
- scripts/reinstall.sh: same idempotent useradd. Existing deployments
using reinstall.sh as their upgrade path also get the safer config
without an extra step.
Frontend: /api/local/status now exposes `jumpbox_user`. SSH commands on
Tunnels and Machines pages use it instead of os_user. Empty string
(legacy install hasn't created the user yet) falls back to vps.username
or os_user with the same UX the operator had before — so the upgrade
isn't visible until they re-run install/reinstall.
Threat model after: a leaked Gopher SSH key only gives the attacker the
ability to forward TCP to localhost ports they were already authorized
to reach via the jumpbox. No shell. No DB read. No iptables. No binary
replacement. The blast radius collapses from "full system + every client
compromise" to "what they could already do with the same key."
Tests cover the keydata-token parser (the heart of the migration logic):
plain keys, options-prefixed keys, options containing quoted commas,
malformed input, blank/comment lines.
|
Unit tests run: 281 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
All Gopher-managed SSH public keys now live under a dedicated, restricted system user (gopher-jump) instead of the dashboard's service account. Closes a privilege escalation path where a leaked SSH key gave the holder root-equivalent access to the VPS and read access to the dashboard database