astro-yukunhh-blog/deploy/proxy.sh
yukun-hh 12dd13a76f feat: init Astro blog with wikilinks, KaTeX and local search
- Astro 5 static blog, zero runtime JS
- Obsidian wikilink remark plugin (slug matches Astro content collection)
- KaTeX math rendering, local search, TOC, archives, tags
- 5 posts on math/physics/ML topics
- deploy scripts for nginx + certbot + rsync
2026-08-14 00:19:33 +08:00

273 lines
10 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Yukun's Blog · 通用子域名反代 + HTTPS 脚本
#
# 用法:
# ./deploy/proxy.sh <子域名> <端口> 反代子域名 → http://127.0.0.1:<端口>,自动签 HTTPS
# ./deploy/proxy.sh remove <子域名> 移除该子域名的反代配置(证书保留)
#
# 示例:
# ./deploy/proxy.sh db 7000 → https://db.sausagetoast.cloud → 127.0.0.1:7000
# ./deploy/proxy.sh remove db
#
# 子域名可传前缀(自动拼主域名)或完整域名(含点,直接使用)。
# 前置条件:子域名 DNS 已解析到本机、80 端口可达。幂等可重跑。
set -euo pipefail
# ====== 配置区(与 deploy.sh 保持一致) ======
REMOTE_USER="root" # SSH 用户
REMOTE_HOST="sausagetoast.cloud" # VPS 地址
MAIN_DOMAIN="sausagetoast.cloud" # 主域名(拼接子域名用)
# ============================================
SSH_OPTS="-o StrictHostKeyChecking=accept-new"
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
# 启用 HTTPS 的 awk 程序(与 deploy.sh 共用同一套标记):
# ① 取消 443 段注释 ② 80 段切 301 跳转 ③ 移除 80 段反代块
read -r -d '' ENABLE_HTTPS_AWK <<'AWKPROG' || true
BEGIN { in443=0; inredir=0; inserv=0 }
/@@HTTPS-START@@/ { in443=1; next }
/@@HTTPS-END@@/ { in443=0; next }
/@@HTTPS-REDIRECT-START@@/ { inredir=1; next }
/@@HTTPS-REDIRECT-END@@/ { inredir=0; next }
/@@HTTP-SERVE-START@@/ { inserv=1; next }
/@@HTTP-SERVE-END@@/ { inserv=0; next }
in443 && /^# / { sub(/^# ?/, ""); print; next }
in443 && /^#/ { sub(/^#/, ""); print; next }
inredir && /^[[:space:]]*#.*return 301 https:/ { sub(/^[[:space:]]*#[[:space:]]*/, ""); print; next }
inserv { next }
{ print }
AWKPROG
usage() {
echo "用法: $0 <子域名> <端口> 或 $0 remove <子域名>"
echo " 例: $0 db 7000 → https://db.${MAIN_DOMAIN} → 127.0.0.1:7000"
echo " $0 remove db"
exit 1
}
# 生成反代 location 块80/443 两处共用,缩进 $1
proxy_block() {
local indent="$1"
echo "${indent}location / {"
echo "${indent} proxy_pass http://127.0.0.1:${PORT};"
echo "${indent} proxy_http_version 1.1;"
echo "${indent} proxy_set_header Host \$host;"
echo "${indent} proxy_set_header X-Real-IP \$remote_addr;"
echo "${indent} proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;"
echo "${indent} proxy_set_header X-Forwarded-Proto \$scheme;"
echo "${indent} proxy_set_header Upgrade \$http_upgrade;"
echo "${indent} proxy_set_header Connection \"upgrade\";"
echo "${indent} proxy_connect_timeout 60s;"
echo "${indent} proxy_send_timeout 300s;"
echo "${indent} proxy_read_timeout 300s;"
echo "${indent} proxy_buffering off;"
echo "${indent} client_max_body_size 100m;"
echo "${indent}}"
}
# 本地生成站点配置 → /tmp/yukun-proxy-<FQDN>.conf
gen_conf() {
local f="/tmp/yukun-proxy-${FQDN}.conf"
{
echo "# ${FQDN} · 反代到本地 ${PORT} 端口服务(由 deploy/proxy.sh 生成)"
echo "# 首次部署80 段直接反代HTTP 通);证书签发后自动启用 443 + 80 跳转。"
echo ""
echo "server {"
echo " listen 80;"
echo " listen [::]:80;"
echo " server_name ${FQDN};"
echo ""
echo " # Let's Encrypt 证书验证certbot webroot 用,无论是否启用 HTTPS 都需保留)"
echo " location /.well-known/acme-challenge/ {"
echo " root /var/www/html;"
echo " }"
echo ""
echo " # @@HTTPS-REDIRECT-START@@"
echo " # 启用 HTTPS 后取消下面这行注释,把 80 段变成跳转:"
echo " # location / { return 301 https://\$host\$request_uri; }"
echo " # @@HTTPS-REDIRECT-END@@"
echo ""
echo " # @@HTTP-SERVE-START@@"
echo " # 启用 HTTPS 后,此段会被移除(跳转段接管):"
proxy_block " "
echo " # @@HTTP-SERVE-END@@"
echo "}"
echo ""
echo "# 证书由 proxy.sh 签发到 /etc/letsencrypt/live/${FQDN}/"
echo "# @@HTTPS-START@@"
echo "# server {"
echo "# listen 443 ssl http2;"
echo "# listen [::]:443 ssl http2;"
echo "# server_name ${FQDN};"
echo "#"
echo "# ssl_certificate /etc/letsencrypt/live/${FQDN}/fullchain.pem;"
echo "# ssl_certificate_key /etc/letsencrypt/live/${FQDN}/privkey.pem;"
echo "# ssl_protocols TLSv1.2 TLSv1.3;"
echo "# ssl_ciphers HIGH:!aNULL:!MD5;"
echo "# ssl_prefer_server_ciphers on;"
echo "# ssl_session_cache shared:SSL:10m;"
echo "# ssl_session_timeout 1d;"
echo "#"
proxy_block "# "
echo "# add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains\" always;"
echo "# }"
echo "# @@HTTPS-END@@"
} > "$f"
echo "$f"
}
# 校验并规范化子域名参数
normalize_fqdn() {
local raw="$1"
[[ -n "$raw" ]] || { echo "✗ 缺少子域名参数"; usage; }
[[ "$raw" =~ ^[a-zA-Z0-9.-]+$ ]] && [[ "$raw" != *..* ]] && [[ "$raw" != -* ]] && [[ "$raw" != *- ]] \
|| { echo "✗ 非法子域名: $raw"; exit 1; }
if [[ "$raw" == *.* ]]; then
FQDN="$raw"
else
FQDN="${raw}.${MAIN_DOMAIN}"
fi
}
# ----------------------------------------------------------------
# 子命令:配置反代 + 签证书 + 启用 HTTPS
# ----------------------------------------------------------------
cmd_setup() {
local SUB="$1" PORT="$2"
[[ "$PORT" =~ ^[0-9]+$ ]] && (( PORT >= 1 && PORT <= 65535 )) || { echo "✗ 非法端口: $PORT"; exit 1; }
normalize_fqdn "$SUB"
local CONF_TMP
CONF_TMP="$(gen_conf)"
echo "==> 上传 ${FQDN} 的 nginx 配置(反代 → 127.0.0.1:${PORT})到 ${REMOTE_USER}@${REMOTE_HOST} ..."
scp $SSH_OPTS "$CONF_TMP" "${REMOTE_USER}@${REMOTE_HOST}:/tmp/yukun-proxy.conf"
rm -f "$CONF_TMP"
echo "==> 远端安装配置 + 签证书 + 启用 HTTPS ..."
ssh $SSH_OPTS "${REMOTE_USER}@${REMOTE_HOST}" \
"SITE='$FQDN' PORT='$PORT' ENABLE_HTTPS_AWK=\"$(printf '%s' "$ENABLE_HTTPS_AWK" | base64)\" bash -s" <<'REMOTE'
set -euo pipefail
SUDO=""
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
SITE="$SITE"; PORT="$PORT"
ENABLE_HTTPS_AWK_b64="$ENABLE_HTTPS_AWK"; unset ENABLE_HTTPS_AWK
# 1) 安装站点配置:优先 sites-available(Debian),其次 conf.d(CentOS)
CONF=""
if [ -d /etc/nginx/sites-enabled ] || $SUDO [ -d /etc/nginx/sites-enabled ]; then
AVAIL=/etc/nginx/sites-available; EN=/etc/nginx/sites-enabled
$SUDO mkdir -p "$AVAIL" "$EN"
$SUDO cp /tmp/yukun-proxy.conf "$AVAIL/$SITE"
$SUDO ln -sfn "$AVAIL/$SITE" "$EN/$SITE"
if ! $SUDO grep -q "sites-enabled" /etc/nginx/nginx.conf 2>/dev/null; then
echo " nginx.conf 缺 include sites-enabled/*,自动追加"
$SUDO sed -i '/http {/a\ include /etc/nginx/sites-enabled/*;' /etc/nginx/nginx.conf
fi
CONF="$AVAIL/$SITE"
echo " 站点: $CONF (+软链接 $EN/$SITE)"
else
$SUDO mkdir -p /etc/nginx/conf.d
$SUDO cp /tmp/yukun-proxy.conf "/etc/nginx/conf.d/$SITE.conf"
CONF="/etc/nginx/conf.d/$SITE.conf"
echo " 站点: $CONF"
fi
# 1.5) 清理另一位置的重复配置(防止 conflicting server name 警告)
if [ "$CONF" = "/etc/nginx/sites-available/$SITE" ]; then
if $SUDO [ -f "/etc/nginx/conf.d/$SITE.conf" ]; then
$SUDO rm -f "/etc/nginx/conf.d/$SITE.conf"
echo " 已移除重复配置 /etc/nginx/conf.d/$SITE.conf"
fi
else
if $SUDO [ -f "/etc/nginx/sites-available/$SITE" ]; then
$SUDO rm -f "/etc/nginx/sites-available/$SITE" "/etc/nginx/sites-enabled/$SITE"
echo " 已移除重复配置 sites-available/$SITE"
fi
fi
# 2) 签 HTTPS 证书(已存在则跳过)
CERT="/etc/letsencrypt/live/$SITE/fullchain.pem"
if $SUDO [ -f "$CERT" ]; then
echo "==> 已有证书 $CERT跳过签发"
else
if ! command -v certbot >/dev/null 2>&1; then
echo "==> 安装 certbot ..."
if command -v apt >/dev/null 2>&1; then $SUDO apt update && $SUDO apt install -y certbot python3-certbot-nginx
elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y certbot python3-certbot-nginx
elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y certbot python3-certbot-nginx
else echo "✗ 未识别的包管理器,请手动装 certbot"; exit 1; fi
fi
$SUDO mkdir -p /var/www/html
echo "==> 签发证书 ..."
$SUDO certbot certonly --webroot -w /var/www/html \
-d "$SITE" \
--non-interactive --agree-tos --register-unsafe --email "root@$SITE"
fi
# 3) 启用 HTTPS取消 443 段注释 + 80 段切跳转 + 移除反代块)
echo "==> 启用 HTTPS ..."
printf '%s' "$ENABLE_HTTPS_AWK_b64" | base64 -d > /tmp/enable-https.awk
$SUDO bash -c 'f="$1"; awk -f /tmp/enable-https.awk "$f" > "$f.tmp" && mv "$f.tmp" "$f"' _ "$CONF"
rm -f /tmp/enable-https.awk
echo "==> 测试 nginx 配置 ..."
$SUDO nginx -t
echo "==> reload nginx ..."
$SUDO systemctl reload nginx || $SUDO systemctl restart nginx
rm -f /tmp/yukun-proxy.conf
echo "✓ 完成https://$SITE → http://127.0.0.1:$PORT"
REMOTE
}
# ----------------------------------------------------------------
# 子命令:移除反代配置
# ----------------------------------------------------------------
cmd_remove() {
normalize_fqdn "$1"
echo "==> 远端移除 $FQDN 的反代配置 ..."
ssh $SSH_OPTS "${REMOTE_USER}@${REMOTE_HOST}" "SITE='$FQDN' bash -s" <<'REMOTE'
set -euo pipefail
SUDO=""
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
SITE="$SITE"
removed=0
# sites-enabled 软链接
if $SUDO [ -L "/etc/nginx/sites-enabled/$SITE" ]; then
$SUDO rm -f "/etc/nginx/sites-enabled/$SITE"; removed=1
fi
# sites-available 源文件
if $SUDO [ -f "/etc/nginx/sites-available/$SITE" ]; then
$SUDO rm -f "/etc/nginx/sites-available/$SITE"; removed=1
fi
# conf.d 配置
if $SUDO [ -f "/etc/nginx/conf.d/$SITE.conf" ]; then
$SUDO rm -f "/etc/nginx/conf.d/$SITE.conf"; removed=1
fi
if [ "$removed" -eq 0 ]; then
echo "(未找到 $SITE 的 nginx 配置,可能已移除)"
else
echo "==> 测试 nginx 配置 ..."
$SUDO nginx -t
echo "==> reload nginx ..."
$SUDO systemctl reload nginx || $SUDO systemctl restart nginx
echo "✓ 已移除 $SITE 的反代配置"
fi
if $SUDO [ -d "/etc/letsencrypt/live/$SITE" ]; then
echo " 证书已保留续期任务仍在certbot delete --cert-name $SITE 可清理"
fi
REMOTE
}
# ----------------------------------------------------------------
case "${1:-}" in
remove)
[ $# -eq 2 ] || { echo "✗ 用法: $0 remove <子域名>"; exit 1; }
cmd_remove "$2" ;;
"")
usage ;;
*)
[ $# -eq 2 ] || { echo "✗ 用法: $0 <子域名> <端口>"; exit 1; }
cmd_setup "$1" "$2" ;;
esac