From da0ed0e2ada823db439a9e47e688267cde13ffa9 Mon Sep 17 00:00:00 2001 From: William Mantly Date: Sat, 18 Jul 2026 00:43:07 -0400 Subject: [PATCH] Move install path to /opt/theta42/proxy, secrets to /etc/proxy/secrets.js - ops/install.sh now installs to /opt/theta42/proxy (was /var/www/proxy) and seeds /etc/proxy/secrets.js from secrets.js.example on first run (never overwritten on later runs), instead of requiring a manual nodejs/conf/secrets.js edit inside the repo checkout. - ops/proxy.service points at the new install path and sets CONF_SECRETS=/etc/proxy/secrets.js (requires @simpleworkjs/conf >= 1.2.0, already the pinned version) so the app picks up the secrets file with no symlink into the repo checkout. - install.sh now prints the version it's updating from/to (or "Already up to date") on every run, instead of a silent update. - Updated README/DEPLOYMENT/installation docs to match the new paths. Co-Authored-By: Claude Sonnet 5 --- DEPLOYMENT.md | 19 +++++++++++++----- README.md | 20 +++++++++++++++--- docs/installation.md | 19 +++++++++++++++--- ops/install.sh | 48 +++++++++++++++++++++++++++++++++++++++++--- ops/proxy.service | 5 +++-- secrets.js.example | 11 ++++++---- 6 files changed, 102 insertions(+), 20 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 034ca04..496ec84 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -227,16 +227,25 @@ docker compose logs --tail=200 --since=10m proxy # recent context `ops/install.sh` is an idempotent installer: it installs Node.js 22.x, OpenResty (from openresty.org), Lua modules (luarocks), Redis, force-syncs the repo to -`/var/www/proxy`, symlinks the OpenResty + systemd config from the repo, and -starts `proxy.service`. Re-run it to update. +`/opt/theta42/proxy`, symlinks the OpenResty + systemd config from the repo, and +starts `proxy.service`. Re-run it to update — it prints the version you're +updating from and to (or "Already up to date" if there's nothing new). + +```bash +wget -O - https://raw.githubusercontent.com/theta42/proxy/master/ops/install.sh | sudo bash +``` + +or, if you already have the repo checked out: ```bash sudo ./ops/install.sh ``` -Configuration is file-based: write `nodejs/conf/secrets.js` with the OIDC + -LDAP values (see `nodejs/conf/base.js` for the shape), then -`sudo systemctl restart proxy`. +Configuration is file-based: on first run the installer seeds +`/etc/proxy/secrets.js` from `secrets.js.example` (placeholders you must fill +in — OIDC + LDAP values, see `nodejs/conf/base.js` for the shape). Edit it, +then `sudo systemctl restart proxy`. Later runs never touch an existing +secrets file. --- diff --git a/README.md b/README.md index 0fad571..aebb6e4 100755 --- a/README.md +++ b/README.md @@ -127,10 +127,15 @@ This installer will: - Install and configure Redis - Set up SSL fallback certificates - Install Lua dependencies (lua-resty-auto-ssl, luasocket) -- Clone and install the proxy application +- Clone/update the proxy application at `/opt/theta42/proxy` +- Seed `/etc/proxy/secrets.js` on first run (edit it, then re-run or `systemctl restart proxy`) - Configure systemd service - Start the proxy service +It's idempotent and safe to re-run — re-running it updates the app in place and +prints the version you're updating from and to (e.g. `Updated v1.1.13 -> +v1.1.14`), or `Already up to date` if there's nothing new. + ## Logs (Docker) The all-in-one image runs OpenResty in the foreground and the Node app in the @@ -224,15 +229,24 @@ cp ops/nginx_conf/targetinfo.lua /usr/local/openresty/lualib/targetinfo.lua Clone and install: ```bash -cd /var/www +mkdir -p /opt/theta42 +cd /opt/theta42 git clone https://github.com/theta42/proxy.git cd proxy/nodejs npm install ``` +Configure secrets: +```bash +mkdir -p /etc/proxy +cp ../secrets.js.example /etc/proxy/secrets.js +chmod 600 /etc/proxy/secrets.js +$EDITOR /etc/proxy/secrets.js +``` + Create systemd service: ```bash -cp ops/proxy.service /etc/systemd/system/proxy.service +cp ../ops/proxy.service /etc/systemd/system/proxy.service systemctl daemon-reload systemctl enable proxy.service systemctl start proxy.service diff --git a/docs/installation.md b/docs/installation.md index 4b6e661..7fbacdf 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -146,7 +146,8 @@ openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \ Clone the repository and copy configuration files: ```bash -cd /var/www +mkdir -p /opt/theta42 +cd /opt/theta42 git clone https://github.com/theta42/proxy.git cd proxy @@ -161,14 +162,26 @@ cp ops/nginx_conf/targetinfo.lua /usr/local/openresty/lualib/targetinfo.lua ### Step 7: Install Application ```bash -cd /var/www/proxy/nodejs +cd /opt/theta42/proxy/nodejs npm install ``` +### Step 7b: Configure Secrets + +```bash +mkdir -p /etc/proxy +cp /opt/theta42/proxy/secrets.js.example /etc/proxy/secrets.js +chmod 600 /etc/proxy/secrets.js +$EDITOR /etc/proxy/secrets.js # set oidc.clientId/clientSecret, ldap.bindPassword, ... +``` + +`@simpleworkjs/conf` reads this file via the `CONF_SECRETS` env var, which the +systemd unit below sets to `/etc/proxy/secrets.js`. + ### Step 8: Configure Systemd Service ```bash -cp /var/www/proxy/ops/proxy.service /etc/systemd/system/proxy.service +cp /opt/theta42/proxy/ops/proxy.service /etc/systemd/system/proxy.service systemctl daemon-reload systemctl enable proxy.service systemctl start proxy.service diff --git a/ops/install.sh b/ops/install.sh index 5f4744a..eb88845 100755 --- a/ops/install.sh +++ b/ops/install.sh @@ -9,19 +9,26 @@ # update is just "sync the repo + reload" -- the files under /etc always track # the repo, so there is nothing to re-copy. # +# Secrets live at $SECRETS_FILE (/etc/proxy/secrets.js by default), outside the +# repo checkout so they survive the hard reset below. First run seeds it from +# secrets.js.example (placeholders you must fill in); later runs never touch +# an existing file. +# # Intended to be driven by CI/CD with no human writes on prod: the checkout is # hard-reset to origin/$BRANCH on every run, so the box deterministically mirrors # the repo (any drift on the box is discarded). # -# Usage: sudo ./install.sh (override with REPO_URL=, REPO_DIR=, BRANCH=) +# Usage: sudo ./install.sh (override with REPO_URL=, REPO_DIR=, BRANCH=, +# SECRETS_FILE=) set -euo pipefail # Never block on an interactive git credential prompt in CI. export GIT_TERMINAL_PROMPT=0 REPO_URL="${REPO_URL:-https://github.com/theta42/proxy.git}" -REPO_DIR="${REPO_DIR:-/var/www/proxy}" +REPO_DIR="${REPO_DIR:-/opt/theta42/proxy}" BRANCH="${BRANCH:-master}" NODE_MAJOR=22 +SECRETS_FILE="${SECRETS_FILE:-/etc/proxy/secrets.js}" if [ "$(id -u)" -ne 0 ]; then echo "This script must be run as root (try: sudo $0)" >&2 @@ -34,6 +41,19 @@ link(){ echo "linked $2 -> $1" } +# Read the "version" field out of a package.json without depending on Node +# being installed yet (this runs before the Node.js install step below). +pkg_version(){ + sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' "$1" | head -1 +} + +# Installed version before this run touches anything, for the upgrade banner +# at the end. Empty on a fresh install (no prior checkout). +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 apt-get install -y --no-install-recommends \ @@ -134,6 +154,20 @@ else git clone --branch "$BRANCH" "$REPO_URL" "$REPO_DIR" fi +NEW_VERSION="$(pkg_version "$REPO_DIR/nodejs/package.json")" + +echo "==> Secrets file at ${SECRETS_FILE}" +install -d -m 0750 "$(dirname "$SECRETS_FILE")" +if [ ! -f "$SECRETS_FILE" ]; then + cp "$REPO_DIR/secrets.js.example" "$SECRETS_FILE" + chmod 600 "$SECRETS_FILE" + echo " seeded ${SECRETS_FILE} from secrets.js.example -- EDIT IT before the proxy will work:" + echo " \$EDITOR ${SECRETS_FILE}" + echo " then re-run this script (or: sudo systemctl restart proxy)" +else + echo " ${SECRETS_FILE} already exists, leaving it untouched" +fi + echo "==> Symlink config from the repo" install -d /etc/openresty/sites-enabled /var/log/nginx link "$REPO_DIR/ops/nginx_conf/nginx.conf" /etc/openresty/nginx.conf @@ -162,4 +196,12 @@ else exit 1 fi -echo "==> Done. Update later with: sudo BRANCH=${BRANCH} $0" +echo "==> Done." +if [ -z "$CURRENT_VERSION" ]; then + echo " Installed v${NEW_VERSION}." +elif [ "$CURRENT_VERSION" = "$NEW_VERSION" ]; then + echo " Already up to date (v${NEW_VERSION})." +else + echo " Updated v${CURRENT_VERSION} -> v${NEW_VERSION}." +fi +echo " Update later with: sudo BRANCH=${BRANCH} $0" diff --git a/ops/proxy.service b/ops/proxy.service index 9398f09..fb807ec 100644 --- a/ops/proxy.service +++ b/ops/proxy.service @@ -8,9 +8,10 @@ Type=simple Restart=always RestartSec=1 User=root -WorkingDirectory=/var/www/proxy/nodejs +WorkingDirectory=/opt/theta42/proxy/nodejs Environment="NODE_ENV=production" -ExecStart=/usr/bin/env node /var/www/proxy/nodejs/bin/www +Environment="CONF_SECRETS=/etc/proxy/secrets.js" +ExecStart=/usr/bin/env node /opt/theta42/proxy/nodejs/bin/www [Install] WantedBy=multi-user.target diff --git a/secrets.js.example b/secrets.js.example index bedfc04..0bdc33e 100644 --- a/secrets.js.example +++ b/secrets.js.example @@ -6,13 +6,16 @@ // direct LDAP client for user lookups. This file supplies that wiring. // // Docker / unified stack: place at ./config/proxy-secrets.js and bind-mount -// ./config at /config (see docker-compose.yml); docker-entrypoint.sh symlinks -// it into /app/conf/secrets.js so @simpleworkjs/conf reads it. No app_* env +// ./config at /config (see docker-compose.yml); docker-entrypoint.sh points the +// CONF_SECRETS env var at it so @simpleworkjs/conf reads it. No app_* env // should be passed — app_* env beats this file in @simpleworkjs/conf, so the // file is authoritative only if the matching app_* env is absent. // -// Bare-metal: copy to nodejs/conf/secrets.js and fill in your values. Values -// here override conf/base.js and win over .js. +// Bare-metal: ops/install.sh seeds this file at /etc/proxy/secrets.js on first +// run (with placeholders for the values it can't guess) and points the +// systemd unit's CONF_SECRETS env var at it. Fill in your values, then +// `sudo systemctl restart proxy`. Values here override conf/base.js and win +// over .js. // // Only the keys the app reads are listed below. The `stack` key is read by the // theta-env orchestrator (setup.sh) and ignored by the app.