SSHGateW is a self-hosted SSH credential gateway. Team members authenticate to the gateway with their own SSH public keys, choose an authorized connection profile in a terminal UI, and receive a transparent interactive shell on the downstream machine. Downstream passwords and private keys stay on the gateway, encrypted at rest.
- Public-key authentication to the gateway on port 2222 by default.
- Optional OIDC device-flow authentication with signed ID-token validation.
- Optional per-user TOTP second-factor authentication with terminal QR enrollment.
- Member and administrator terminal interfaces.
- Per-user and per-group target grants.
- Per-protocol shell, SFTP/SCP, and TCP-forwarding capabilities.
- Password, reusable stored-key, per-target private-key, and restricted forwarded-agent authentication.
- Exact downstream host-key pinning; changed keys are rejected.
- XChaCha20-Poly1305 encrypted credentials with a separate master-key file.
- SQLite persistence, metadata auditing, session limits, and JSON logs.
- Routed
user+targetlogins for direct shells, SFTP, SCP, and restricted local forwarding. - Exact per-target TCP destination allowlists; reverse forwarding remains disabled.
- No arbitrary remote exec, downstream agent exposure, or terminal recording.
Go 1.26 or newer is required.
make build VERSION=0.1.0Place the initial administrator's OpenSSH public key in admin.pub, then run:
SSHGATEW_ADMIN=admin SSHGATEW_VERSION=latest docker compose up -d
docker compose logs sshgatewThe first startup creates the database, encryption master key, gateway host key, configuration, and administrator automatically. The logs print the new gateway host-key fingerprint; verify it before connecting:
ssh -p 2222 admin@docker-host.example.comSet SSHGATEW_PORT to publish a different host port and
SSHGATEW_ADMIN_KEY_FILE if the bootstrap public key is not ./admin.pub.
Configuration and encrypted data live in the sshgatew-config and
sshgatew-data named volumes and survive container replacement. The bootstrap
key file is used only when the configuration volume is empty. The service
process runs as the unprivileged sshgatew user; the entrypoint uses its narrow
startup capabilities only to repair named-volume ownership before dropping
privileges.
SSHGateW can authenticate existing users through an OpenID Connect provider
that advertises the OAuth 2.0 Device Authorization endpoint. Copy
.env.example to an uncommitted .env file and configure the issuer and
client. Docker Compose automatically reads .env next to compose.yaml:
SSHGATEW_OIDC_ENABLED=true
SSHGATEW_OIDC_ISSUER=https://identity.example.com/realms/team
SSHGATEW_OIDC_CLIENT_ID=sshgatew
SSHGATEW_OIDC_CLIENT_SECRET=
SSHGATEW_OIDC_USERNAME_CLAIM=preferred_username
SSHGATEW_OIDC_SCOPES=openid profile email
SSHGATEW_OIDC_LOGIN_TIMEOUT=5mThen recreate the container and explicitly select keyboard-interactive login:
docker compose up -d
ssh -p 2222 \
-o PubkeyAuthentication=no \
-o PreferredAuthentications=keyboard-interactive \
alice@gateway.example.comThe SSH session displays the provider verification URL and one-time code.
Complete login in a browser and return to SSH. OIDC never creates users or
changes grants. Each identity must first be linked to an existing enabled
SSHGateW user. A user can authenticate with a registered gateway SSH key and
press o in the TUI to link their own account. Administrators can select a
user in the Users panel and map an OIDC username; the first successful login
permanently binds that mapping to the provider's signed stable subject. Issuer,
signature, audience, and token expiry are verified from discovery metadata and
JWKS. Local TOTP remains an additional factor when enabled.
To build the image locally, add --build. To upgrade from GHCR without
reinitializing:
SSHGATEW_VERSION=NEW_VERSION docker compose pull
docker compose up -dOn a fresh systemd-based Linux server, download the release binary and run its interactive installer as root:
chmod +x sshgatew
sudo ./sshgatew installThe wizard asks for the administrator username, listen address, and an OpenSSH public key (which may be pasted directly). It then:
- creates the locked
sshgatewservice account; - installs the binary at
/usr/local/bin/sshgatew; - creates
/etc/sshgatewand/var/lib/sshgatewwith protected ownership; - generates the master key and gateway host key;
- initializes SQLite and the administrator identity;
- installs, enables, and starts the hardened systemd service; and
- prints the gateway fingerprint and connection command.
For unattended provisioning:
sudo ./sshgatew install \
--admin admin \
--authorized-key /root/admin.pub \
--listen 0.0.0.0:2222 \
--yes--authorized-key also accepts an inline OpenSSH public key. Use --no-start
to enable the unit without starting it. The installer refuses to overwrite an
existing configuration or database. Open TCP port 2222 in the host or provider
firewall when required.
The following manual procedure remains available for custom layouts or systems without systemd.
Create the service account and protected directories:
sudo useradd --system --home /var/lib/sshgatew --shell /usr/sbin/nologin sshgatew
sudo install -d -o sshgatew -g sshgatew -m 0750 /var/lib/sshgatew
sudo install -d -o sshgatew -g sshgatew -m 0750 /etc/sshgatew
sudo install -o root -g root -m 0755 sshgatew /usr/local/bin/sshgatewInitialize with an existing administrator public key:
sudo -u sshgatew /usr/local/bin/sshgatew \
--config /etc/sshgatew/config.toml \
init --admin admin --authorized-key /path/readable/by/sshgatew/admin.pub
sudo chown root:sshgatew /etc/sshgatew/config.toml /etc/sshgatew
sudo chmod 0640 /etc/sshgatew/config.toml
sudo chmod 0750 /etc/sshgatewInstall deploy/sshgatew.service, open TCP port 2222 in the host firewall, and
start the service:
sudo install -o root -g root -m 0644 deploy/sshgatew.service /etc/systemd/system/sshgatew.service
sudo systemctl daemon-reload
sudo systemctl enable --now sshgatew
sudo systemctl status sshgatewConnect with:
ssh -p 2222 admin@gateway.example.comUsers with TOTP enabled are shown a hidden six-digit challenge after their SSH key is accepted and before the gateway menu opens. Administrators can enroll or remove TOTP through a user's Manage menu. Enrollment displays a scannable QR code and requires a current authenticator code before it takes effect.
Verify the gateway host-key fingerprint printed by sshgatew init before
accepting it in the SSH client.
First verify the downstream server's SSH host fingerprint through a trusted channel. The initial command prints the observed fingerprint but refuses to store it:
sudo -u sshgatew sshgatew targets add \
--name production --host 10.0.0.10 --port 22 --remote-user deploy \
--auth passwordAfter verification, repeat with --accept-host-key. The password is read from
a hidden prompt, never an argument:
sudo -u sshgatew sshgatew targets add \
--name production --host 10.0.0.10 --port 22 --remote-user deploy \
--auth password --accept-host-key
sudo -u sshgatew sshgatew grants add --target production --group operatorsFor private-key authentication, use --auth private_key --key-file PATH.
Encrypted OpenSSH private keys are supported and their passphrases are stored
inside the encrypted credential payload.
Administrators can also manage reusable keys entirely in the TUI. Open the
SSH KEYS tab and press a to import an existing private key or generate a new
Ed25519 key. Its public key and fingerprint remain viewable for installation on
downstream servers, while the private key is encrypted with the gateway master
key. Choose stored_key while adding a target or replacing its credential to
select one of these saved keys. A saved key cannot be deleted while a target
still uses it.
For a FIDO/YubiKey or another local-agent identity, choose forwarded_agent
in the TUI or use --auth forwarded_agent --key-file PUBLIC_KEY. Connect to
the gateway with ssh -A. SSHGateW permits only the configured public key and
closes the agent channel immediately after downstream authentication.
Use USER+TARGET to bypass the target menu. Authorization, pinned host-key
verification, encrypted credentials, session limits, TOTP, and auditing still
apply:
ssh -p 2222 justin+production@gateway.example.com
sftp -P 2222 justin+production@gateway.example.com
scp -P 2222 ./release.tar justin+production:/tmp/
ssh -N -p 2222 -L 5432:127.0.0.1:5432 justin+production@gateway.example.comModern SCP is transported through SFTP. Legacy scp -O is supported through a
strictly parsed downstream SCP command. TCP forwarding requires both the
grant's TCP forwarding capability and an exact destination configured in the
TUI's FORWARDS section. Dynamic forwarding works only for destinations on
that allowlist. Reverse forwarding is intentionally rejected.
Run local administration commands as the sshgatew service user to avoid
creating SQLite WAL files with incompatible ownership.
See the operator guide and security model for the complete command and recovery workflow.