aa3b3ed515
The web management UI was a bespoke minimal theme with LDAP-bind login.
Rebuild it to match the SSO Manager and Proxy — same stack, same look/feel,
same auth model. The SSH bridge, audit, metrics, and access logic are
unchanged; this is purely the web layer.
Frontend (mirrors proxy/sso):
- Express + EJS with the shared top.ejs/bottom.ejs shell, Bootstrap 5,
jQuery, jq-repeat, FontAwesome, Socket.IO, and the shared app-base.js /
val.js client framework (copied verbatim). Vendor libs served from
node_modules via /static-modules; app assets via /static.
- Dashboard / Sessions / Audit pages render in the common look/feel,
loading data through the authenticated /api/* endpoints.
Auth (mirrors proxy):
- OIDC against the SSO (utils/oidc.js + routes/auth.js + models/oidc_state)
plus a local anti-lockout admin (models/user_redis.js, bootstrapped from
auth.adminUsers[0] / auth.localAdminPass). AuthToken sessions carry the
group snapshot; middleware gates the data API on adminGroups or the local
admin. New config: oidc{} + auth.adminUsers/localAdminPass.
- /api/user/me drives the client login state; "Log in with SSO" hidden when
oidc.enabled is false.
Verified end to end: local admin login -> token -> /api/user/me isAdmin,
metrics/sessions/audit 200 with token / 401 without / 401 bad password;
static + page shells serve; 26 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
111 lines
3.8 KiB
Markdown
111 lines
3.8 KiB
Markdown
---
|
|
layout: default
|
|
title: Installation
|
|
description: Install the jump host three ways — bundled in the theta-env stack, standalone Docker, or bare metal — plus the required LDAP write-ACL and port-22 options.
|
|
---
|
|
|
|
# Installation
|
|
|
|
Three ways to run the jump host, in increasing manual effort. All read their
|
|
config through [@simpleworkjs/conf](https://www.npmjs.com/package/@simpleworkjs/conf)
|
|
(`conf/base.js` < `conf/<NODE_ENV>.js` < the `CONF_SECRETS` file < `app_*` env).
|
|
|
|
## Requirements
|
|
|
|
- The [SSO Manager](https://theta42.github.io/sso-manager-node/) (OpenLDAP
|
|
directory + `/api/discovery`), v1.3.0 or newer.
|
|
- Downstream hosts joined via
|
|
[ldap-client](https://github.com/theta42/ldap-client) (SSSD +
|
|
`AuthorizedKeysCommand`).
|
|
- An LDAP bind account with **write access to the `sshPublicKey` attribute** on
|
|
user entries (see below).
|
|
- An SSO API token (`sso_…`) for the directory queries.
|
|
|
|
## 1. Unified theta-env stack (recommended)
|
|
|
|
Enable it in `theta-env/setup.env`:
|
|
|
|
```bash
|
|
CFG_JUMP_HOST_ENABLED=true
|
|
CFG_JUMP_HOST=jump.example.com
|
|
JUMP_SSH_PORT=2222
|
|
```
|
|
|
|
Re-run `./setup.sh`. The stack builds the submodule (behind the `jump-host`
|
|
compose profile), mints the directory API token, writes
|
|
`./config/jump-secrets.js`, grants the `sshPublicKey` write-ACL, registers the
|
|
jump host in the proxy, and seeds a directory entry. Forward the public host's
|
|
`:22` (or `:2222`) to the container's published `JUMP_SSH_PORT`.
|
|
|
|
## 2. Standalone Docker
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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 jump host injects its public key into each user's `sshPublicKey`, so its
|
|
bind account must be able to **write** that attribute. In the bundled OpenLDAP:
|
|
|
|
```
|
|
access to attrs=sshPublicKey
|
|
by dn.exact="cn=ldapclient,ou=people,dc=example,dc=com" write
|
|
by self write
|
|
by * read
|
|
```
|
|
|
|
In the theta-env bundle this is handled for you (the jump host binds as the LDAP
|
|
admin). For a hardened standalone deployment, use a dedicated bind account with
|
|
exactly this attribute-scoped ACL. Without write access, key injection fails and
|
|
every bridge attempt is audited `key-inject-failed`.
|
|
|
|
## Listening on port 22
|
|
|
|
The default SSH port is **2222** so the service needs no privilege. To listen on
|
|
22, set `ssh.listenPort: 22` and either:
|
|
|
|
- **systemd:** uncomment `AmbientCapabilities=CAP_NET_BIND_SERVICE` in the unit;
|
|
- **Docker:** publish `22:22`; or
|
|
- **firewall:** DNAT `22 → 2222`.
|
|
|
|
## Configuration reference
|
|
|
|
Every key is documented in
|
|
[`secrets.js.example`](https://github.com/theta42/jump-host/blob/master/secrets.js.example):
|
|
`ldap` (bind + bases + TLS), `sso` (url + apiToken), `ssh`
|
|
(`listenPort`, `passwordAuth`, `allowRawIPs`, `keyComment`, timeouts,
|
|
`maxSessions`), `web.port`, `oidc` (web-UI SSO login), `auth`
|
|
(`adminGroups` / `adminUsers` / `localAdminPass`), and `redis`.
|
|
|
|
## Verifying
|
|
|
|
```bash
|
|
ssh -p 2222 youruid@jump.example.com # TUI picker
|
|
ssh -p 2222 youruid_-_somehost@jump.example.com # direct
|
|
sftp -P 2222 youruid_-_somehost@jump.example.com # WinSCP path
|
|
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.
|