Files
jump-host/ops/install.sh
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

110 lines
3.7 KiB
Bash

#!/usr/bin/env bash
#
# Install / update the Theta42 jump host on a fresh or existing host.
#
# Idempotent: run to install, re-run to update. Installs system dependencies
# (Node, Redis), force-syncs the repo at $REPO_DIR to its remote branch, and
# symlinks the systemd unit straight from the repo so an update is just
# "sync repo + restart".
#
# Secrets live at $SECRETS_FILE (/etc/jump-host/secrets.js by default), outside
# the checkout so they survive the hard reset. First run seeds it from
# secrets.js.example (placeholders you must fill in); later runs never touch it.
#
# Usage: sudo ./install.sh (override with REPO_URL=, REPO_DIR=, BRANCH=,
# SECRETS_FILE=)
set -euo pipefail
export GIT_TERMINAL_PROMPT=0
export DEBIAN_FRONTEND=noninteractive
REPO_URL="${REPO_URL:-https://github.com/theta42/jump-host.git}"
REPO_DIR="${REPO_DIR:-/opt/theta42/jump-host}"
BRANCH="${BRANCH:-master}"
NODE_MAJOR=22
SECRETS_FILE="${SECRETS_FILE:-/etc/jump-host/secrets.js}"
DATA_DIR="${DATA_DIR:-/var/lib/jump-host}"
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root (try: sudo $0)" >&2
exit 1
fi
link(){ ln -sfn "$1" "$2"; echo "linked $2 -> $1"; }
pkg_version(){
sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' "$1" | head -1
}
CURRENT_VERSION=""
if [ -f "$REPO_DIR/nodejs/package.json" ]; then
CURRENT_VERSION="$(pkg_version "$REPO_DIR/nodejs/package.json")"
fi
echo "==> Base packages"
apt-get update -qq
apt-get install -y -qq ca-certificates curl git gnupg redis-server >/dev/null
echo "==> Node.js ${NODE_MAJOR}.x"
if ! command -v node >/dev/null 2>&1 || [ "$(node -v | sed 's/v\([0-9]*\).*/\1/')" -lt "$NODE_MAJOR" ]; then
mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" \
> /etc/apt/sources.list.d/nodesource.list
apt-get update -qq
apt-get install -y -qq nodejs >/dev/null
fi
echo " node $(node -v)"
echo "==> Redis (enable + start)"
systemctl enable --now redis-server >/dev/null 2>&1 || systemctl enable --now redis >/dev/null 2>&1 || true
echo "==> Repo at $REPO_DIR"
if [ -d "$REPO_DIR/.git" ]; then
git -C "$REPO_DIR" fetch --prune origin
git -C "$REPO_DIR" checkout -B "$BRANCH" "origin/$BRANCH"
git -C "$REPO_DIR" reset --hard "origin/$BRANCH"
git -C "$REPO_DIR" clean -fd
else
mkdir -p "$(dirname "$REPO_DIR")"
git clone --branch "$BRANCH" "$REPO_URL" "$REPO_DIR"
fi
NEW_VERSION="$(pkg_version "$REPO_DIR/nodejs/package.json")"
echo "==> Data dir $DATA_DIR (host keys + state)"
mkdir -p "$DATA_DIR/keys"
chmod 700 "$DATA_DIR" "$DATA_DIR/keys"
echo "==> Secrets at $SECRETS_FILE"
if [ ! -f "$SECRETS_FILE" ]; then
mkdir -p "$(dirname "$SECRETS_FILE")"
cp "$REPO_DIR/secrets.js.example" "$SECRETS_FILE"
chmod 600 "$SECRETS_FILE"
echo " seeded from secrets.js.example — EDIT IT before the service will work:"
echo " $SECRETS_FILE"
else
echo " exists — left untouched"
fi
echo "==> systemd unit"
link "$REPO_DIR/ops/jump-host.service" /etc/systemd/system/jump-host.service
echo "==> npm install (production deps)"
( cd "$REPO_DIR/nodejs" && (npm ci --omit=dev 2>/dev/null || npm install --omit=dev) )
echo "==> Start service"
systemctl daemon-reload
systemctl enable --now jump-host.service
systemctl restart jump-host.service
echo
if [ -z "$CURRENT_VERSION" ]; then
echo "Installed jump-host v${NEW_VERSION}."
elif [ "$CURRENT_VERSION" = "$NEW_VERSION" ]; then
echo "Already up to date (v${NEW_VERSION})."
else
echo "Updated jump-host v${CURRENT_VERSION} -> v${NEW_VERSION}."
fi
echo "Re-run this script any time to update: sudo $0"
echo "Logs: journalctl -u jump-host -f"