36e9d5b0b3
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>
110 lines
3.7 KiB
Bash
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"
|