2026-06-10 01:22:14 -07:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
## render-shared-ols-config.sh — assemble httpd_config.conf for the shared-ols
|
|
|
|
|
## tier from the per-site files the WHP panel drops into $SITES_ROOT.
|
|
|
|
|
##
|
|
|
|
|
## WHY THIS EXISTS: OpenLiteSpeed has NO top-level `include` directive (unlike
|
|
|
|
|
## Apache's IncludeOptional that shared-httpd relies on). So we cannot just drop
|
|
|
|
|
## per-vhost files in a dir and have OLS pick them up — the listener `map` lines
|
|
|
|
|
## and the vhost stanzas must live IN httpd_config.conf. This script is the
|
|
|
|
|
## "include" OLS lacks: it concatenates the panel's per-site pieces into one
|
|
|
|
|
## valid httpd_config.conf, then the caller issues `lswsctrl restart`.
|
|
|
|
|
## (Empirically established 2026-06-10 — see the OLS-tier PoC.)
|
|
|
|
|
##
|
|
|
|
|
## Per-site contract — the panel writes, for each site, a directory:
|
2026-06-10 08:34:55 -07:00
|
|
|
## $SITES_ROOT/<vhname>/vhconf.conf (rendered by the WHP panel from its own
|
|
|
|
|
## web-files/configs/shared-ols-vhconf-template.tpl
|
|
|
|
|
## — the single source of truth for vhost detail)
|
|
|
|
|
## $SITES_ROOT/<vhname>/site.meta (VHNAME=, VHROOT=, DOMAINS=a.com,www.a.com)
|
2026-06-10 01:22:14 -07:00
|
|
|
## This script turns each into a `virtualhost {configFile}` stanza + a listener
|
|
|
|
|
## `map` line. A site dir missing either file is skipped (logged).
|
|
|
|
|
##
|
|
|
|
|
## Idempotent: always rebuilds from the stock config, so re-runs never compound.
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
LSWS_CONF=/usr/local/lsws/conf
|
|
|
|
|
TPL_DIR=${TPL_DIR:-/etc/shared-ols-templates}
|
|
|
|
|
SITES_ROOT=${SITES_ROOT:-$LSWS_CONF/shared-sites}
|
|
|
|
|
LSCACHE_ROOT=${LSCACHE_ROOT:-/var/lscache}
|
|
|
|
|
CERT_FILE=${CERT_FILE:-$LSWS_CONF/cert/shared-ols.crt}
|
|
|
|
|
KEY_FILE=${KEY_FILE:-$LSWS_CONF/cert/shared-ols.key}
|
|
|
|
|
export LSCACHE_ROOT
|
|
|
|
|
|
|
|
|
|
OUT="$LSWS_CONF/httpd_config.conf"
|
2026-06-10 08:34:55 -07:00
|
|
|
TMP="$LSWS_CONF/.httpd_config.conf.tmp.$$"
|
2026-06-10 01:22:14 -07:00
|
|
|
STOCK="/usr/local/lsws/.conf/httpd_config.conf"
|
|
|
|
|
|
|
|
|
|
mkdir -p "$SITES_ROOT" "$LSCACHE_ROOT"
|
|
|
|
|
|
2026-06-10 08:34:55 -07:00
|
|
|
## --- SERIALIZE concurrent renders + write ATOMICALLY ---
|
|
|
|
|
## The panel can fire two renders at once (parallel provisioning), and the
|
|
|
|
|
## in-container .htaccess watcher issues `lswsctrl restart` independently. If OLS
|
|
|
|
|
## (re)reads httpd_config.conf while it's half-written, it fails to parse and the
|
|
|
|
|
## whole tier 503s. So: (1) flock so only one render runs at a time; (2) build
|
|
|
|
|
## into $TMP and atomically `mv` into place at the end, so any concurrent OLS
|
|
|
|
|
## restart always sees a COMPLETE config (the old one until the instant of mv).
|
|
|
|
|
exec 9>"$LSWS_CONF/.render.lock"
|
|
|
|
|
flock 9 || { echo "render-shared-ols: could not acquire render lock" >&2; exit 1; }
|
|
|
|
|
trap 'rm -f "$TMP"' EXIT
|
|
|
|
|
## From here on, build into $TMP (not $OUT).
|
|
|
|
|
|
2026-06-10 01:22:14 -07:00
|
|
|
## --- 1. start from a pristine stock config (idempotent) ---
|
|
|
|
|
if [ ! -f "$STOCK" ]; then
|
|
|
|
|
## Some image builds keep the only copy at conf/; snapshot it once so future
|
|
|
|
|
## renders have a clean base to strip.
|
|
|
|
|
mkdir -p "$(dirname "$STOCK")"
|
|
|
|
|
cp "$OUT" "$STOCK"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
## --- 2. strip stock blocks that conflict or would run PHP LOCALLY ---
|
|
|
|
|
## extProcessor lsphp (autoStart 1, uds) + the server scriptHandler are removed
|
|
|
|
|
## so this server NEVER executes PHP itself — all PHP goes to remote sidecars.
|
|
|
|
|
## listener HTTP/HTTPS + vhTemplate docker are removed (we add our own).
|
|
|
|
|
awk '
|
|
|
|
|
/^listener HTTP \{/ { skip=1; next }
|
|
|
|
|
/^listener HTTPS \{/ { skip=1; next }
|
|
|
|
|
/^vhTemplate docker ?\{/ { skip=1; next }
|
|
|
|
|
/^extProcessor lsphp ?\{/{ skip=1; next }
|
|
|
|
|
/^scriptHandler ?\{/ { skip=1; next }
|
|
|
|
|
skip && /^\}/ { skip=0; next }
|
|
|
|
|
!skip { print }
|
2026-06-10 08:34:55 -07:00
|
|
|
' "$STOCK" > "$TMP"
|
2026-06-10 01:22:14 -07:00
|
|
|
|
|
|
|
|
## --- 3. append our server-level base (real-IP, cache module, no local PHP) ---
|
|
|
|
|
{
|
|
|
|
|
echo ""
|
|
|
|
|
envsubst '${LSCACHE_ROOT}' < "$TPL_DIR/httpd_config_base.tpl"
|
2026-06-10 08:34:55 -07:00
|
|
|
} >> "$TMP"
|
2026-06-10 01:22:14 -07:00
|
|
|
|
|
|
|
|
## --- 4. emit per-site vhost stanzas + collect listener map lines ---
|
|
|
|
|
maps=""
|
|
|
|
|
site_count=0
|
|
|
|
|
for meta in "$SITES_ROOT"/*/site.meta; do
|
|
|
|
|
[ -e "$meta" ] || continue
|
|
|
|
|
sdir=$(dirname "$meta")
|
2026-06-10 08:34:55 -07:00
|
|
|
## PARSE site.meta with sed — do NOT `source` it. The panel writes these values
|
|
|
|
|
## (derived from DB domains), so they should be safe, but sourcing paneldata as
|
|
|
|
|
## shell would execute any metacharacters as root in this container if a value
|
|
|
|
|
## ever slipped validation. sed extraction treats them as plain data.
|
|
|
|
|
VHNAME=$(sed -n 's/^VHNAME=//p' "$meta" | head -1)
|
|
|
|
|
VHROOT=$(sed -n 's/^VHROOT=//p' "$meta" | head -1)
|
|
|
|
|
DOMAINS=$(sed -n 's/^DOMAINS=//p' "$meta" | head -1)
|
2026-06-10 01:22:14 -07:00
|
|
|
if [ -z "$VHNAME" ] || [ -z "$VHROOT" ] || [ -z "$DOMAINS" ] || [ ! -f "$sdir/vhconf.conf" ]; then
|
|
|
|
|
echo "render-shared-ols: skipping $sdir (incomplete: VHNAME/VHROOT/DOMAINS/vhconf.conf)" >&2
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
{
|
|
|
|
|
echo ""
|
|
|
|
|
echo "virtualhost ${VHNAME} {"
|
|
|
|
|
echo " vhRoot ${VHROOT}"
|
|
|
|
|
echo " configFile ${sdir}/vhconf.conf"
|
|
|
|
|
echo " allowSymbolLink 1"
|
|
|
|
|
echo " enableScript 1"
|
|
|
|
|
echo " restrained 1"
|
|
|
|
|
echo "}"
|
2026-06-10 08:34:55 -07:00
|
|
|
} >> "$TMP"
|
2026-06-10 01:22:14 -07:00
|
|
|
maps="${maps} map ${VHNAME} ${DOMAINS}"$'\n'
|
|
|
|
|
site_count=$((site_count + 1))
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
## --- 5. ALWAYS add a health vhost mapped to the catch-all so the server is
|
|
|
|
|
## valid with zero customer sites and HAProxy health checks (which hit by IP /
|
|
|
|
|
## unknown Host) get a 200. Exact-domain maps above win over this '*'. ---
|
|
|
|
|
{
|
|
|
|
|
echo ""
|
|
|
|
|
echo "virtualhost _health {"
|
|
|
|
|
echo " vhRoot /usr/local/lsws/shared-ols-health"
|
|
|
|
|
echo " configFile /usr/local/lsws/shared-ols-health/vhconf.conf"
|
|
|
|
|
echo " allowSymbolLink 1"
|
|
|
|
|
echo " enableScript 0"
|
|
|
|
|
echo "}"
|
2026-06-10 08:34:55 -07:00
|
|
|
} >> "$TMP"
|
2026-06-10 01:22:14 -07:00
|
|
|
maps="${maps} map _health *"$'\n'
|
|
|
|
|
|
|
|
|
|
## --- 6. listeners (HTTP :80 + HTTPS :443 self-signed) carrying ALL maps.
|
|
|
|
|
## HAProxy terminates real TLS and connects to this tier on :443 ssl verify
|
|
|
|
|
## none (same as shared-httpd), so :443 needs a cert — self-signed is fine. ---
|
|
|
|
|
{
|
|
|
|
|
echo ""
|
|
|
|
|
echo "listener shared_http {"
|
|
|
|
|
echo " address *:80"
|
|
|
|
|
echo " secure 0"
|
|
|
|
|
printf '%s' "$maps"
|
|
|
|
|
echo "}"
|
|
|
|
|
echo ""
|
|
|
|
|
echo "listener shared_https {"
|
|
|
|
|
echo " address *:443"
|
|
|
|
|
echo " secure 1"
|
|
|
|
|
echo " keyFile ${KEY_FILE}"
|
|
|
|
|
echo " certFile ${CERT_FILE}"
|
|
|
|
|
printf '%s' "$maps"
|
|
|
|
|
echo "}"
|
2026-06-10 08:34:55 -07:00
|
|
|
} >> "$TMP"
|
2026-06-10 01:22:14 -07:00
|
|
|
|
2026-06-10 08:34:55 -07:00
|
|
|
## --- 7. publish atomically. Validate the temp parses as non-empty, then mv into
|
|
|
|
|
## place (rename is atomic on the same filesystem) so a concurrent OLS restart
|
|
|
|
|
## never sees a half-written config. chown only the file we wrote — NOT a
|
|
|
|
|
## recursive chown of the whole conf tree (that was O(N-sites) on every single
|
|
|
|
|
## change; the per-site files are world-readable and owned correctly already). ---
|
|
|
|
|
if [ ! -s "$TMP" ]; then
|
|
|
|
|
echo "render-shared-ols: refusing to publish empty config" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
chown lsadm:nogroup "$TMP" 2>/dev/null || true
|
|
|
|
|
mv -f "$TMP" "$OUT"
|
2026-06-10 01:22:14 -07:00
|
|
|
echo "render-shared-ols: wrote $OUT ($site_count customer vhost(s) + health)"
|