151 lines
5.2 KiB
Bash
151 lines
5.2 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# ═══════════════════════════════════════════════════════════════════
|
||
|
|
# B3: ECS 部署用户隔离配置脚本
|
||
|
|
# 用途: 在阿里云 ECS 上创建受限的 deploy 用户,替代 root 直接部署
|
||
|
|
#
|
||
|
|
# 在 ECS 上以 root 身份执行:
|
||
|
|
# bash setup-deploy-user.sh
|
||
|
|
#
|
||
|
|
# 安全设计:
|
||
|
|
# - 专用 deployer 用户,无交互登录 shell
|
||
|
|
# - authorized_keys command= 限制只能执行白名单脚本
|
||
|
|
# - 应用目录权限隔离
|
||
|
|
# - sudo 仅允许 pm2 和指定部署操作
|
||
|
|
# ═══════════════════════════════════════════════════════════════════
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
DEPLOY_USER="deployer"
|
||
|
|
APP_DIR="/var/www/app"
|
||
|
|
DEPLOY_SCRIPT="${APP_DIR}/scripts/deploy.sh"
|
||
|
|
DEPLOY_KEY_PUB="" # 需要填入部署公钥
|
||
|
|
|
||
|
|
echo "=== Bookworm ECS 部署用户配置 ==="
|
||
|
|
|
||
|
|
# 1. 创建 deployer 用户
|
||
|
|
if id "${DEPLOY_USER}" &>/dev/null; then
|
||
|
|
echo "[OK] 用户 ${DEPLOY_USER} 已存在"
|
||
|
|
else
|
||
|
|
useradd -r -m -s /usr/sbin/nologin -d /home/${DEPLOY_USER} ${DEPLOY_USER}
|
||
|
|
echo "[OK] 已创建用户 ${DEPLOY_USER}"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 2. 配置 SSH 目录
|
||
|
|
mkdir -p /home/${DEPLOY_USER}/.ssh
|
||
|
|
chmod 700 /home/${DEPLOY_USER}/.ssh
|
||
|
|
|
||
|
|
# 3. 生成部署专用密钥对 (如果本地还没有)
|
||
|
|
if [[ ! -f /home/${DEPLOY_USER}/.ssh/authorized_keys ]]; then
|
||
|
|
# 生成限制性 authorized_keys
|
||
|
|
# command= 限制: 只允许执行 deploy-wrapper.sh
|
||
|
|
cat > /home/${DEPLOY_USER}/.ssh/authorized_keys <<'AKEOF'
|
||
|
|
# Bookworm 自动部署密钥 — 仅允许执行 deploy-wrapper.sh
|
||
|
|
# 如需添加密钥,请保持 command= 前缀
|
||
|
|
# command="/home/deployer/deploy-wrapper.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding <PUBLIC_KEY_HERE>
|
||
|
|
AKEOF
|
||
|
|
echo "[WARN] 请编辑 authorized_keys 添加部署公钥并启用 command= 限制"
|
||
|
|
fi
|
||
|
|
|
||
|
|
chmod 600 /home/${DEPLOY_USER}/.ssh/authorized_keys
|
||
|
|
chown -R ${DEPLOY_USER}:${DEPLOY_USER} /home/${DEPLOY_USER}/.ssh
|
||
|
|
|
||
|
|
# 4. 创建 deploy-wrapper.sh (白名单脚本)
|
||
|
|
cat > /home/${DEPLOY_USER}/deploy-wrapper.sh <<'WEOF'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
# 部署白名单包装器 — 仅允许预定义操作
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
# 从 SSH_ORIGINAL_COMMAND 获取请求的命令
|
||
|
|
CMD="${SSH_ORIGINAL_COMMAND:-}"
|
||
|
|
LOG="/var/log/deploy-audit.log"
|
||
|
|
|
||
|
|
log_audit() {
|
||
|
|
echo "$(date -Iseconds) | deployer | $1 | $2" >> "${LOG}"
|
||
|
|
}
|
||
|
|
|
||
|
|
case "${CMD}" in
|
||
|
|
"deploy")
|
||
|
|
log_audit "ALLOW" "deploy"
|
||
|
|
cd /var/www/app && bash scripts/deploy.sh 2>&1
|
||
|
|
;;
|
||
|
|
"deploy-status")
|
||
|
|
log_audit "ALLOW" "deploy-status"
|
||
|
|
pm2 status 2>&1
|
||
|
|
;;
|
||
|
|
"health-check")
|
||
|
|
log_audit "ALLOW" "health-check"
|
||
|
|
curl -sf http://localhost:3000/api/health 2>&1 || echo "HEALTH_CHECK_FAILED"
|
||
|
|
;;
|
||
|
|
"rollback "*)
|
||
|
|
# rollback <commit-hash> — 仅允许 40 字符 hex commit hash
|
||
|
|
COMMIT="${CMD#rollback }"
|
||
|
|
if [[ "${COMMIT}" =~ ^[0-9a-f]{40}$ ]]; then
|
||
|
|
log_audit "ALLOW" "rollback to ${COMMIT}"
|
||
|
|
cd /var/www/app && git checkout "${COMMIT}" -- . && pm2 reload all 2>&1
|
||
|
|
else
|
||
|
|
log_audit "DENY" "invalid commit hash: ${COMMIT}"
|
||
|
|
echo "ERROR: 无效的 commit hash" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
;;
|
||
|
|
"pm2-reload")
|
||
|
|
log_audit "ALLOW" "pm2-reload"
|
||
|
|
pm2 reload all 2>&1
|
||
|
|
;;
|
||
|
|
"logs")
|
||
|
|
log_audit "ALLOW" "logs"
|
||
|
|
pm2 logs --lines 50 --nostream 2>&1
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
log_audit "DENY" "blocked command: ${CMD}"
|
||
|
|
echo "ERROR: 不允许的操作。允许的命令: deploy, deploy-status, health-check, rollback <hash>, pm2-reload, logs" >&2
|
||
|
|
exit 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
WEOF
|
||
|
|
chmod 755 /home/${DEPLOY_USER}/deploy-wrapper.sh
|
||
|
|
|
||
|
|
# 5. 配置 sudoers (仅 pm2 操作)
|
||
|
|
cat > /etc/sudoers.d/deployer <<'SEOF'
|
||
|
|
# Bookworm deployer — 最小权限 pm2 操作 (git 由 deployer 直接执行,无需 sudo)
|
||
|
|
deployer ALL=(ALL) NOPASSWD: /usr/bin/pm2 reload *, /usr/bin/pm2 status, /usr/bin/pm2 logs *
|
||
|
|
SEOF
|
||
|
|
chmod 440 /etc/sudoers.d/deployer
|
||
|
|
|
||
|
|
# 6. 应用目录权限
|
||
|
|
if [[ -d "${APP_DIR}" ]]; then
|
||
|
|
chown -R ${DEPLOY_USER}:${DEPLOY_USER} "${APP_DIR}"
|
||
|
|
echo "[OK] 已将 ${APP_DIR} 所有权转移到 ${DEPLOY_USER}"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 7. 创建审计日志
|
||
|
|
touch /var/log/deploy-audit.log
|
||
|
|
chown ${DEPLOY_USER}:${DEPLOY_USER} /var/log/deploy-audit.log
|
||
|
|
|
||
|
|
# 8. 创建部署前签名校验脚本
|
||
|
|
cat > /home/${DEPLOY_USER}/verify-deploy.sh <<'VEOF'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
# 部署前 Git commit 签名校验
|
||
|
|
set -euo pipefail
|
||
|
|
cd /var/www/app
|
||
|
|
|
||
|
|
CURRENT_COMMIT=$(git rev-parse HEAD)
|
||
|
|
REMOTE_COMMIT=$(git ls-remote origin HEAD | cut -f1)
|
||
|
|
|
||
|
|
# 确保本地和远程 commit 一致
|
||
|
|
if [[ "${CURRENT_COMMIT}" != "${REMOTE_COMMIT}" ]]; then
|
||
|
|
echo "VERIFY_FAIL: 本地 commit (${CURRENT_COMMIT:0:8}) != 远程 (${REMOTE_COMMIT:0:8})"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "VERIFY_OK: commit ${CURRENT_COMMIT:0:8}"
|
||
|
|
VEOF
|
||
|
|
chmod 755 /home/${DEPLOY_USER}/verify-deploy.sh
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "=== 配置完成 ==="
|
||
|
|
echo "下一步:"
|
||
|
|
echo " 1. 在本地生成部署专用密钥对: ssh-keygen -t ed25519 -f ~/.ssh/id_deploy -N ''"
|
||
|
|
echo " 2. 将公钥添加到 /home/${DEPLOY_USER}/.ssh/authorized_keys (保持 command= 前缀)"
|
||
|
|
echo " 3. 更新 loop-controller.sh 中的 DEPLOY_HOST 为 deployer@<DEPLOY_SERVER_IP>"
|
||
|
|
echo " 4. 测试: ssh deployer@<DEPLOY_SERVER_IP> deploy-status"
|