Files
jump-host/DEPLOYMENT.md
T
wmantly 36e9d5b0b3 feat: initial jump-host — SSH jump host for the theta42 stack
An SSH jump host that authenticates users against the shared LDAP
directory, authorizes them from the SSO Manager's inventory graph, and
bridges them to downstream hosts — auditing everything.

- Username-grammar routing (uid_-_target@jump) + interactive TUI picker
- Inbound LDAP auth (publickey / password with off|local|all policy)
- Directory-driven access (LDAP groups x /api/discovery/resources?group=)
- Per-user key injection into sshPublicKey, connects downstream as the user
- Shell / exec / SFTP-subsystem bridging (WinSCP works)
- Web UI + HTTP API (:3002) for audit + metrics; LDAP-admin gated
- Packaged like proxy: ops/install.sh + systemd, all-in-one Docker, compose
- Tests: 23 unit + 3 integration (node --test), all green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 15:48:49 -04:00

2.4 KiB

Deployment

Three ways to run the jump host, in increasing manual effort.

1. Unified theta-env stack

Set in theta-env/setup.env:

CFG_JUMP_HOST_ENABLED=true
CFG_JUMP_HOST=jump.example.com
JUMP_SSH_PORT=2222

Re-run ./setup.sh. It builds the submodule, writes config/jump-secrets.js, mints the SSO API token, grants the sshPublicKey write-ACL to the shared cn=ldapclient bind account, registers jump.example.com in the proxy, and seeds a directory entry.

Expose SSH: forward the public host's :22 (or :2222) to the container's published JUMP_SSH_PORT.

2. Standalone Docker

cp secrets.js.example config/jump-secrets.js
$EDITOR config/jump-secrets.js        # LDAP bind (+ sshPublicKey write ACL), SSO url + token
docker compose up -d --build

Host keys persist in the jump-data volume. The web UI is on :3002; front it with your own TLS/proxy.

3. Bare metal

curl -fsSL https://raw.githubusercontent.com/theta42/jump-host/master/ops/install.sh | sudo bash
sudo $EDITOR /etc/jump-host/secrets.js
sudo systemctl restart jump-host
journalctl -u jump-host -f

ops/install.sh installs Node 22 + Redis, hard-resets the checkout at /opt/theta42/jump-host to the remote branch, symlinks the systemd unit, and runs npm ci. Idempotent — re-run to update. Overridable via REPO_DIR=, BRANCH=, SECRETS_FILE=.

The LDAP write-ACL (required)

The bind account must be able to write the sshPublicKey attribute so the jump host can inject its key. In the bundled OpenLDAP (slapd.conf / olc):

access to attrs=sshPublicKey
    by dn.exact="cn=ldapclient,ou=people,dc=example,dc=com" write
    by self write
    by * read

Without it, key injection fails and every bridge attempt is audited key-inject-failed.

Listening on port 22

Default is 2222 (unprivileged). For 22: set ssh.listenPort: 22, and either

  • systemd: uncomment AmbientCapabilities=CAP_NET_BIND_SERVICE in the unit; or
  • Docker: publish 22:22; or
  • firewall: DNAT 22 → 2222.

Verifying

# from a client whose key is in your LDAP sshPublicKey
ssh -p 2222 youruid@jump.example.com          # TUI picker
ssh -p 2222 youruid_-_somehost@jump.example.com
sftp -P 2222 youruid_-_somehost@jump.example.com

curl -s http://localhost:3002/health

Watch journalctl -u jump-host -f (or docker logs -f jump-host) and the audit log at /audit in the web UI.