- 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
273 lines
10 KiB
Bash
Executable file
273 lines
10 KiB
Bash
Executable file
#!/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
|