From 7cbb4518217e350054ea7e72ad52821461d0686f Mon Sep 17 00:00:00 2001 From: Replit Agent Date: Fri, 20 Mar 2026 21:11:10 +0000 Subject: [PATCH] fix: install.sh hardening per code review - Marker-based nginx insertion (BEGIN/END comments) instead of brittle sed; validates against temp file before patching live nginx.conf - Gitea pull token check: warns if /root/.gitea-replit-token missing, prints creation instructions (deploy.sh fails without it) - TLS note in summary output: explains HTTP-only transport, recommends TLS - mkdir -p DEPLOY_DIR; chmod 600 .env --- vps/install.sh | 100 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/vps/install.sh b/vps/install.sh index 9ff27dc..4a913f6 100644 --- a/vps/install.sh +++ b/vps/install.sh @@ -5,15 +5,23 @@ # Run once from the VPS (or from a machine with SSH access): # bash vps/install.sh # OR remotely: -# ssh root@143.198.27.163 'bash -s' < vps/install.sh +# WEBHOOK_SECRET=$(cat .local/deploy-webhook-secret) \ +# ssh root@143.198.27.163 'bash -s' < vps/install.sh # # What it does: # 1. Ensures Node 24 + pnpm are available -# 2. Installs deploy/webhook/health scripts to /opt/timmy-tower/ -# 3. Generates a WEBHOOK_SECRET and adds to .env -# 4. Adds nginx location block for /webhook/deploy -# 5. Enables + starts systemd services: timmy-deploy-hook, timmy-health.timer -# 6. Prints the webhook secret so you can configure the Gitea webhook +# 2. Verifies /root/.gitea-replit-token exists (needed by deploy.sh) +# 3. Installs deploy/webhook/health scripts to /opt/timmy-tower/ +# 4. Generates or applies a WEBHOOK_SECRET and adds to .env +# 5. Adds nginx location block for /webhook/deploy (idempotent marker) +# 6. Enables + starts systemd services: timmy-deploy-hook, timmy-health.timer +# 7. Prints the webhook secret so you can configure the Gitea webhook +# +# Security note: +# The webhook receiver listens on localhost:9000 only. Nginx proxies +# /webhook/deploy from the public interface. HMAC-SHA256 verifies request +# authenticity. For full transport confidentiality, place Hermes behind a +# TLS-terminating proxy or configure nginx with Let's Encrypt. # ============================================================================= set -euo pipefail @@ -21,12 +29,15 @@ DEPLOY_DIR="/opt/timmy-tower" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" NGINX_CONF="/etc/nginx/sites-enabled/default" WEBHOOK_PORT=9000 +NGINX_MARKER_START="# BEGIN timmy-deploy-webhook" +NGINX_MARKER_END="# END timmy-deploy-webhook" log() { echo "[install] $*"; } ok() { echo "[install] ✓ $*"; } +warn(){ echo "[install] ! $*"; } err() { echo "[install] ✗ $*" >&2; exit 1; } -# ── 1. Ensure Node 24 ───────────────────────────────────────────────────────── +# ── 1. Ensure Node.js ───────────────────────────────────────────────────────── log "Checking Node.js..." NODE_VER=$(node --version 2>/dev/null || echo "none") if [[ "$NODE_VER" == none ]]; then @@ -43,19 +54,34 @@ if ! command -v pnpm &>/dev/null; then fi ok "pnpm $(pnpm --version)" -# ── 3. Copy scripts to deploy dir ───────────────────────────────────────────── +# ── 3. Verify Gitea pull credentials ────────────────────────────────────────── +GITEA_TOKEN_FILE="/root/.gitea-replit-token" +if [ ! -f "$GITEA_TOKEN_FILE" ]; then + warn "Gitea pull token not found at $GITEA_TOKEN_FILE" + warn "deploy.sh will fail on first run without it." + warn "Create it with the Gitea admin token:" + warn " echo '' > $GITEA_TOKEN_FILE && chmod 600 $GITEA_TOKEN_FILE" + warn "Continuing install — add the token before triggering a deploy." +else + ok "Gitea pull token present at $GITEA_TOKEN_FILE" +fi + +# ── 4. Copy scripts to deploy dir ───────────────────────────────────────────── log "Copying scripts to $DEPLOY_DIR..." -cp "$SCRIPT_DIR/deploy.sh" "$DEPLOY_DIR/deploy.sh" -cp "$SCRIPT_DIR/webhook.js" "$DEPLOY_DIR/webhook.js" +mkdir -p "$DEPLOY_DIR" +cp "$SCRIPT_DIR/deploy.sh" "$DEPLOY_DIR/deploy.sh" +cp "$SCRIPT_DIR/webhook.js" "$DEPLOY_DIR/webhook.js" cp "$SCRIPT_DIR/health-check.sh" "$DEPLOY_DIR/health-check.sh" chmod +x "$DEPLOY_DIR/deploy.sh" "$DEPLOY_DIR/health-check.sh" ok "Scripts installed." -# ── 4. Set WEBHOOK_SECRET in .env ──────────────────────────────────────────── +# ── 5. Set WEBHOOK_SECRET in .env ──────────────────────────────────────────── # Priority: $WEBHOOK_SECRET env var → existing .env entry → generate new one ENV_FILE="$DEPLOY_DIR/.env" +touch "$ENV_FILE" +chmod 600 "$ENV_FILE" + if [ -n "${WEBHOOK_SECRET:-}" ]; then - # Caller provided secret — write or replace in .env if grep -q "^WEBHOOK_SECRET=" "$ENV_FILE" 2>/dev/null; then sed -i "s|^WEBHOOK_SECRET=.*|WEBHOOK_SECRET=$WEBHOOK_SECRET|" "$ENV_FILE" else @@ -69,10 +95,10 @@ else WEBHOOK_SECRET=$(openssl rand -hex 32) echo "WEBHOOK_SECRET=$WEBHOOK_SECRET" >> "$ENV_FILE" ok "WEBHOOK_SECRET generated and saved to .env" - log "NOTE: Update the Gitea webhook secret to match: $WEBHOOK_SECRET" + warn "Update the Gitea webhook secret to match: $WEBHOOK_SECRET" fi -# ── 5. Install systemd services ─────────────────────────────────────────────── +# ── 6. Install systemd services ─────────────────────────────────────────────── log "Installing systemd units..." cp "$SCRIPT_DIR/timmy-deploy-hook.service" /etc/systemd/system/ cp "$SCRIPT_DIR/timmy-health.service" /etc/systemd/system/ @@ -83,27 +109,42 @@ systemctl enable --now timmy-deploy-hook systemctl enable --now timmy-health.timer ok "Services enabled: timmy-deploy-hook, timmy-health.timer" -# ── 6. Nginx — add /webhook/deploy proxy block ─────────────────────────────── -if grep -q "webhook/deploy" "$NGINX_CONF" 2>/dev/null; then - log "nginx already has /webhook/deploy block — skipping" +# ── 7. Nginx — idempotent marker-based /webhook/deploy proxy block ───────────── +if grep -qF "$NGINX_MARKER_START" "$NGINX_CONF" 2>/dev/null; then + log "nginx /webhook/deploy block already present — skipping" else log "Adding nginx proxy for /webhook/deploy..." BLOCK=" - # Timmy deploy webhook (managed by install.sh) + $NGINX_MARKER_START location /webhook/deploy { proxy_pass http://127.0.0.1:${WEBHOOK_PORT}/deploy; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_read_timeout 10s; - }" + } + $NGINX_MARKER_END" - # Insert before the closing } of the server block - sed -i "s|^}|${BLOCK}\n}|" "$NGINX_CONF" - nginx -t && systemctl reload nginx - ok "nginx updated and reloaded." + # Use a temp file so we can validate before touching the live config + TMP_CONF=$(mktemp) + # Insert the block before the last closing brace of the server block + awk -v block="$BLOCK" ' + /^}$/ && !inserted { print block; inserted=1 } + { print } + ' "$NGINX_CONF" > "$TMP_CONF" + + if nginx -t -c "$TMP_CONF" 2>/dev/null; then + cp "$TMP_CONF" "$NGINX_CONF" + nginx -t && systemctl reload nginx + ok "nginx updated and reloaded." + else + warn "nginx config test failed on temp file — skipping auto-patch." + warn "Add this block manually to the server {} block in $NGINX_CONF:" + echo "$BLOCK" + fi + rm -f "$TMP_CONF" fi -# ── 7. Print summary ───────────────────────────────────────────────────────── +# ── 8. Print summary ───────────────────────────────────────────────────────── echo "" echo "================================================================" echo " Push-to-deploy pipeline installed on Hermes VPS" @@ -112,11 +153,20 @@ echo "" echo " Webhook endpoint: http://143.198.27.163/webhook/deploy" echo " WEBHOOK_SECRET: $WEBHOOK_SECRET" echo "" +echo " NOTE: The webhook runs over plain HTTP. HMAC-SHA256 ensures" +echo " authenticity. For full transport security, configure nginx" +echo " with TLS (Let's Encrypt) on the Hermes public domain." +echo "" echo " Configure this Gitea webhook on admin/timmy-tower:" echo " URL: http://143.198.27.163/webhook/deploy" echo " Secret: $WEBHOOK_SECRET" echo " Events: Push" -echo " Branch: main (filter in webhook.js)" +echo " Branch: main (filtered by webhook.js)" +echo "" +echo " Gitea pull token: $GITEA_TOKEN_FILE" +if [ ! -f "$GITEA_TOKEN_FILE" ]; then + echo " *** NOT FOUND — add before first deploy ***" +fi echo "" echo " Useful commands:" echo " tail -f $DEPLOY_DIR/deploy.log # watch deploy logs"