Serve sparkmessenger/sparkmsg on shared Nginx SNI with deploy script and Let's Encrypt. Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.1 KiB
Bash
121 lines
3.1 KiB
Bash
#!/usr/bin/env bash
|
|
# Deploy Spark Messenger website to the Singapore ECS host.
|
|
# Layout on host:
|
|
# /var/www/sparkmessenger.wyrs365.com static site
|
|
# /etc/nginx/conf.d/sparkmessenger.conf TLS vhost (loopback :8443)
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
HOST="${SPARK_SSH_HOST:-root@47.236.66.9}"
|
|
KEY="${SPARK_SSH_KEY:-$HOME/.ssh/wyrs_shanghai_deploy.pem}"
|
|
SITE_SRC="$ROOT/website"
|
|
SITE_DST="/var/www/sparkmessenger.wyrs365.com"
|
|
DOMAINS=(sparkmessenger.wyrs365.com sparkmsg.wyrs365.com)
|
|
PRIMARY="${DOMAINS[0]}"
|
|
SSH=(ssh -i "$KEY" -o BatchMode=yes -o StrictHostKeyChecking=accept-new "$HOST")
|
|
SCP=(scp -i "$KEY" -o BatchMode=yes -o StrictHostKeyChecking=accept-new)
|
|
|
|
need() {
|
|
command -v "$1" >/dev/null || {
|
|
echo "need $1 on this machine" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
need ssh
|
|
need scp
|
|
need rsync
|
|
|
|
if [[ ! -f "$KEY" ]]; then
|
|
echo "missing SSH key: $KEY" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$SITE_SRC/index.html" ]]; then
|
|
echo "missing $SITE_SRC/index.html" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> upload site + nginx conf"
|
|
"${SSH[@]}" "mkdir -p '$SITE_DST' /var/www/certbot /tmp/spark-nginx-host"
|
|
rsync -az --delete -e "ssh -i $KEY -o BatchMode=yes -o StrictHostKeyChecking=accept-new" \
|
|
"$SITE_SRC/" "$HOST:$SITE_DST/"
|
|
"${SCP[@]}" "$ROOT/deploy/nginx/host/sparkmessenger.conf" "$HOST:/tmp/spark-nginx-host/sparkmessenger.conf"
|
|
|
|
echo "==> ensure TLS certificates"
|
|
"${SSH[@]}" bash -s -- "$PRIMARY" "${DOMAINS[@]}" <<'REMOTE'
|
|
set -euo pipefail
|
|
PRIMARY=$1
|
|
shift
|
|
DOMAINS=("$@")
|
|
|
|
cert_ok() {
|
|
[[ -f "/etc/letsencrypt/live/$1/fullchain.pem" ]]
|
|
}
|
|
|
|
need_issue=0
|
|
if ! cert_ok "$PRIMARY"; then
|
|
need_issue=1
|
|
else
|
|
# Ensure SAN covers all website domains; re-issue if missing.
|
|
for d in "${DOMAINS[@]}"; do
|
|
if ! openssl x509 -in "/etc/letsencrypt/live/$PRIMARY/fullchain.pem" -noout -text 2>/dev/null | grep -q "$d"; then
|
|
need_issue=1
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ "$need_issue" -eq 1 ]]; then
|
|
mkdir -p /var/www/certbot
|
|
cat > /etc/nginx/conf.d/sparkmessenger-acme.conf <<EOF
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name ${DOMAINS[*]};
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/certbot;
|
|
}
|
|
|
|
location / {
|
|
return 404;
|
|
}
|
|
}
|
|
EOF
|
|
nginx -t
|
|
systemctl reload nginx
|
|
|
|
certbot_args=(certonly --webroot -w /var/www/certbot --non-interactive --agree-tos --keep-until-expiring --cert-name "$PRIMARY")
|
|
# Use a placeholder email if register is needed; prefer existing account.
|
|
if [[ -n "${CERTBOT_EMAIL:-}" ]]; then
|
|
certbot_args+=(--email "$CERTBOT_EMAIL")
|
|
else
|
|
certbot_args+=(--register-unsafely-without-email)
|
|
fi
|
|
for d in "${DOMAINS[@]}"; do
|
|
certbot_args+=(-d "$d")
|
|
done
|
|
certbot "${certbot_args[@]}"
|
|
rm -f /etc/nginx/conf.d/sparkmessenger-acme.conf
|
|
fi
|
|
|
|
if ! cert_ok "$PRIMARY"; then
|
|
echo "certificate missing for $PRIMARY" >&2
|
|
exit 1
|
|
fi
|
|
|
|
cp /tmp/spark-nginx-host/sparkmessenger.conf /etc/nginx/conf.d/sparkmessenger.conf
|
|
nginx -t
|
|
systemctl reload nginx
|
|
echo "website nginx ready"
|
|
REMOTE
|
|
|
|
echo "==> verify"
|
|
for d in "${DOMAINS[@]}"; do
|
|
code=$("${SSH[@]}" "curl -fsS -o /dev/null -w '%{http_code}' --resolve ${d}:443:127.0.0.1 https://${d}/" || true)
|
|
echo "$d -> HTTP $code"
|
|
done
|
|
|
|
echo "done"
|