新增 nginx 整站 Basic Auth(deploy.sh auth 子命令)
This commit is contained in:
87
deploy.sh
87
deploy.sh
@@ -8,6 +8,9 @@
|
||||
# bash deploy.sh cookie 'cna=…' # 直接以参数提供 Cookie(会出现在 shell 历史,优先用交互方式)
|
||||
# bash deploy.sh probe # 更新探针脚本与 systemd 单元,立即试跑
|
||||
# bash deploy.sh nginx # 安装/刷新 nginx 站点配置并 reload
|
||||
# bash deploy.sh auth # 交互式设置 Basic Auth 用户名/密码(不回显)并开启整站认证
|
||||
# bash deploy.sh auth user pwd # 非交互设置(密码会进 shell 历史,优先用交互方式)
|
||||
# bash deploy.sh auth off # 关闭 Basic Auth(删除 htpasswd 并 reload)
|
||||
# bash deploy.sh status # 查看定时器、最新快照与 HTTP 自检
|
||||
# bash deploy.sh all # nginx + web + probe(不动 Cookie)
|
||||
#
|
||||
@@ -27,6 +30,7 @@ PROBE_BIN="$PROBE_DIR/bailian-probe.py"
|
||||
RESET_CARD_BIN="$PROBE_DIR/reset-card-server.py"
|
||||
RESET_CARD_PORT="${RESET_CARD_PORT:-18082}"
|
||||
SNAPSHOT="$REMOTE_DIR/www/data/bailian.json"
|
||||
HTPASSWD=/etc/nginx/token-list.htpasswd
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -216,17 +220,87 @@ REMOTE
|
||||
ok "探针已更新,定时器每 5 分钟跑一次"
|
||||
}
|
||||
|
||||
# ---------- auth ----------
|
||||
|
||||
# 在远端用 openssl apr1 生成 htpasswd 行(Debian/Ubumtu 的 openssl 带 -apr1)
|
||||
cmd_auth() {
|
||||
if [ "${2:-}" = "off" ]; then
|
||||
log "关闭 Basic Auth"
|
||||
ssh_r "rm -f '$HTPASSWD'"
|
||||
cmd_nginx
|
||||
ok "Basic Auth 已关闭"
|
||||
return
|
||||
fi
|
||||
|
||||
local user password
|
||||
if [ "$#" -ge 3 ]; then
|
||||
user="$2"; password="$3"
|
||||
else
|
||||
printf 'Basic Auth 用户名:' >&2
|
||||
IFS= read -r user </dev/tty || true
|
||||
printf '密码(输入不回显):' >&2
|
||||
IFS= read -r -s password </dev/tty || true
|
||||
printf '\n' >&2
|
||||
fi
|
||||
[ -n "$user" ] || die "用户名不能为空"
|
||||
[ -n "$password" ] || die "密码不能为空"
|
||||
|
||||
# 密码经 600 临时文件透传,不出现在远端命令行/进程列表
|
||||
local tmp; tmp="$(mktemp -t token-htpasswd-XXXXXX)"
|
||||
chmod 600 "$tmp"
|
||||
printf '%s' "$password" >"$tmp"
|
||||
scp_r -q "$tmp" "$REMOTE_HOST:/run/.token-htpasswd.in"
|
||||
rm -f "$tmp"
|
||||
|
||||
log "写入 $HTPASSWD 并刷新 nginx"
|
||||
# stdout 输出除本次用户外仍保留的账号,用于提示旧账号仍可登录
|
||||
local others
|
||||
others="$(ssh_r 'bash -s' "$HTPASSWD" "$user" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
HTPASSWD="$1"; USER_NAME="$2"
|
||||
command -v openssl >/dev/null || { echo "✗ 远端没有 openssl,无法生成 apr1 哈希" >&2; exit 1; }
|
||||
[ -s /run/.token-htpasswd.in ] || { echo "✗ 密码临时文件缺失" >&2; exit 1; }
|
||||
hash=$(openssl passwd -apr1 -in /run/.token-htpasswd.in)
|
||||
rm -f /run/.token-htpasswd.in
|
||||
mkdir -p "$(dirname "$HTPASSWD")"
|
||||
# 同名用户覆盖更新,其余用户保留
|
||||
if [ -f "$HTPASSWD" ]; then
|
||||
grep -v "^${USER_NAME}:" "$HTPASSWD" > "$HTPASSWD.new" || true
|
||||
else
|
||||
: > "$HTPASSWD.new"
|
||||
fi
|
||||
printf '%s:%s\n' "$USER_NAME" "$hash" >> "$HTPASSWD.new"
|
||||
install -m 640 "$HTPASSWD.new" "$HTPASSWD"
|
||||
rm -f "$HTPASSWD.new"
|
||||
chown root:www-data "$HTPASSWD" 2>/dev/null || chown root:nginx "$HTPASSWD" 2>/dev/null || true
|
||||
cut -d: -f1 "$HTPASSWD" | grep -v "^${USER_NAME}$" || true
|
||||
REMOTE
|
||||
)"
|
||||
cmd_nginx >/dev/null
|
||||
ok "Basic Auth 已开启:用户 $user(浏览器访问整站时输入)"
|
||||
if [ -n "$others" ]; then
|
||||
printf '\033[1;33m! htpasswd 里还有其他账号仍可登录:%s\n' "$(printf '%s' "$others" | tr '\n' ' ')" >&2
|
||||
printf ' 如要停用旧账号:ssh %s "sed -i \x27/^<用户名>:/d\x27 %s"\033[0m\n' "$REMOTE_HOST" "$HTPASSWD" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------- nginx ----------
|
||||
|
||||
cmd_nginx() {
|
||||
log "安装 nginx 站点(端口 $SITE_PORT,反代 $SUB2API_UPSTREAM)"
|
||||
# 先看远端 htpasswd 是否存在:存在就在整站 server 块开 Basic Auth
|
||||
local auth_block=""
|
||||
if ssh_r "test -s '$HTPASSWD'"; then
|
||||
auth_block="$(printf ' auth_basic "token-list-share";\n auth_basic_user_file %s;\n' "$HTPASSWD")"
|
||||
log "检测到 $HTPASSWD,开启 Basic Auth"
|
||||
fi
|
||||
local tmp; tmp="$(mktemp -t token-nginx-XXXXXX.conf)"
|
||||
cat >"$tmp" <<EOF
|
||||
server {
|
||||
listen $SITE_PORT;
|
||||
listen [::]:$SITE_PORT;
|
||||
server_name _;
|
||||
|
||||
$auth_block
|
||||
location /api/ {
|
||||
proxy_pass $SUB2API_UPSTREAM;
|
||||
proxy_http_version 1.1;
|
||||
@@ -270,9 +344,9 @@ REMOTE
|
||||
# ---------- status ----------
|
||||
|
||||
cmd_status() {
|
||||
ssh_r 'bash -s' "$SITE_PORT" "$SNAPSHOT" <<'REMOTE'
|
||||
ssh_r 'bash -s' "$SITE_PORT" "$SNAPSHOT" "$HTPASSWD" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
PORT="$1"; SNAPSHOT="$2"
|
||||
PORT="$1"; SNAPSHOT="$2"; HTPASSWD="$3"
|
||||
echo "== timer =="
|
||||
systemctl list-timers bailian-probe.timer --no-pager | head -3
|
||||
echo "== snapshot =="
|
||||
@@ -287,6 +361,10 @@ else
|
||||
echo "(快照不存在)"
|
||||
fi
|
||||
echo "== http =="
|
||||
# 若开了 Basic Auth,无凭证应统一 401(哈希无法回放密码,带凭证 200 需手工 curl -u 验证)
|
||||
if [ -s "$HTPASSWD" ]; then
|
||||
echo "(Basic Auth 已开启:下面无凭证请求预期均为 401)"
|
||||
fi
|
||||
for path in / /list.html /groups.html /data/bailian.json /api/v1/admin/groups/all; do
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT$path")
|
||||
printf ' %s %s\n' "$code" "$path"
|
||||
@@ -301,7 +379,8 @@ case "${1:-web}" in
|
||||
cookie) cmd_cookie "$@" ;;
|
||||
probe) cmd_probe ;;
|
||||
nginx) cmd_nginx ;;
|
||||
auth) cmd_auth "$@" ;;
|
||||
status) cmd_status ;;
|
||||
all) cmd_nginx; cmd_web; cmd_probe ;;
|
||||
*) die "未知子命令:$1(支持 web | cookie | probe | nginx | status | all)" ;;
|
||||
*) die "未知子命令:$1(支持 web | cookie | probe | nginx | auth | status | all)" ;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user