fix(ci): 部署脚本 —— 私钥清理 trap 前移 + rsync 空目录守卫 + 主机密钥 accept-new(评审 Critical/Important)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,20 @@ set -euo pipefail
|
||||
# shellcheck source=scripts/ci/lib-ssh.sh
|
||||
. scripts/ci/lib-ssh.sh
|
||||
|
||||
# setup_ssh registers the EXIT cleanup trap itself (before writing the key),
|
||||
# so a mid-setup failure still cleans up — see lib-ssh.sh.
|
||||
setup_ssh
|
||||
trap teardown_ssh EXIT
|
||||
|
||||
# Guard against deploying a missing/empty build: `rsync --delete` against an
|
||||
# empty source would wipe the live site.
|
||||
if [ ! -d web/website/dist ]; then
|
||||
echo "==> deploy-site: web/website/dist/ does not exist — refusing to deploy" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! find web/website/dist -mindepth 1 -print -quit | grep -q .; then
|
||||
echo "==> deploy-site: web/website/dist/ is empty — refusing to deploy" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOY_TARGET="root@${DEPLOY_HOST}:/var/www/pangolin-site/"
|
||||
echo "==> deploy-site: rsync web/website/dist/ -> ${DEPLOY_TARGET}"
|
||||
|
||||
+28
-12
@@ -3,10 +3,12 @@
|
||||
# (site / server). `source` this from deploy-*.sh.
|
||||
#
|
||||
# Provides:
|
||||
# setup_ssh -> writes $DEPLOY_SSH_KEY to a temp private key (mode 600),
|
||||
# registers the deploy host in known_hosts, and exports
|
||||
# SSH / RSYNC_SSH (ssh command strings) + SSH_KEY_FILE.
|
||||
# teardown_ssh -> removes the temp private key.
|
||||
# setup_ssh -> registers the EXIT cleanup trap first, then writes
|
||||
# $DEPLOY_SSH_KEY to a temp private key (mode 600),
|
||||
# registers the deploy host in a dedicated known_hosts
|
||||
# file, and exports SSH / RSYNC_SSH (ssh command strings)
|
||||
# + SSH_KEY_FILE.
|
||||
# teardown_ssh -> removes the temp private key + known_hosts file.
|
||||
#
|
||||
# Requires env: DEPLOY_SSH_KEY (PEM content of the deploy private key,
|
||||
# authorized for root on the target host).
|
||||
@@ -21,9 +23,15 @@
|
||||
DEPLOY_HOST="${DEPLOY_HOST:-103.119.13.48}"
|
||||
DEPLOY_PORT="${DEPLOY_PORT:-22}"
|
||||
SSH_KEY_FILE="${SSH_KEY_FILE:-/tmp/pangolin_deploy_key.$$}"
|
||||
SSH_KNOWN_HOSTS_FILE="${SSH_KNOWN_HOSTS_FILE:-/tmp/pangolin_deploy_known_hosts.$$}"
|
||||
|
||||
# setup_ssh — write the deploy key, register known_hosts, export SSH/RSYNC_SSH.
|
||||
setup_ssh() {
|
||||
# Register cleanup FIRST: if anything below fails mid-setup (e.g. a
|
||||
# transient ssh-keyscan error under `set -e`), the private key file must
|
||||
# still be removed on exit rather than leaking.
|
||||
trap teardown_ssh EXIT
|
||||
|
||||
if [ -z "${DEPLOY_SSH_KEY:-}" ]; then
|
||||
echo "==> setup_ssh: DEPLOY_SSH_KEY is empty" >&2
|
||||
return 1
|
||||
@@ -32,22 +40,30 @@ setup_ssh() {
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
|
||||
# Pre-create the key file with restrictive perms *before* writing any key
|
||||
# material into it, so there is no window at the default umask between
|
||||
# file creation and chmod.
|
||||
install -m 600 /dev/null "${SSH_KEY_FILE}"
|
||||
|
||||
# `printf '%s\n'` 末尾补一个换行:Forgejo/Gitea 存 secret 会去掉结尾换行,
|
||||
# 而缺结尾换行的 OpenSSH 格式私钥会被判为 "invalid format" 拒绝加载,
|
||||
# 退化成无密钥 → Permission denied。多补的换行对已含结尾换行的 PEM 无害。
|
||||
printf '%s\n' "${DEPLOY_SSH_KEY}" > "${SSH_KEY_FILE}"
|
||||
chmod 600 "${SSH_KEY_FILE}"
|
||||
|
||||
ssh-keyscan -p "${DEPLOY_PORT}" -H "${DEPLOY_HOST}" >> ~/.ssh/known_hosts 2>/dev/null
|
||||
# Populate a dedicated known_hosts file via TOFU keyscan. This is
|
||||
# belt-and-suspenders: `accept-new` below will pin the host key on first
|
||||
# real connection regardless, so a transient keyscan failure must not
|
||||
# abort the deploy.
|
||||
ssh-keyscan -p "${DEPLOY_PORT}" -H "${DEPLOY_HOST}" >> "${SSH_KNOWN_HOSTS_FILE}" 2>/dev/null || true
|
||||
|
||||
SSH="ssh -i ${SSH_KEY_FILE} -p ${DEPLOY_PORT} -o StrictHostKeyChecking=no"
|
||||
RSYNC_SSH="ssh -i ${SSH_KEY_FILE} -p ${DEPLOY_PORT} -o StrictHostKeyChecking=no"
|
||||
export SSH RSYNC_SSH SSH_KEY_FILE DEPLOY_HOST DEPLOY_PORT
|
||||
echo "==> setup_ssh: key written to ${SSH_KEY_FILE}, known_hosts updated for ${DEPLOY_HOST}:${DEPLOY_PORT}"
|
||||
SSH="ssh -i ${SSH_KEY_FILE} -p ${DEPLOY_PORT} -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=${SSH_KNOWN_HOSTS_FILE}"
|
||||
RSYNC_SSH="ssh -i ${SSH_KEY_FILE} -p ${DEPLOY_PORT} -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=${SSH_KNOWN_HOSTS_FILE}"
|
||||
export SSH RSYNC_SSH SSH_KEY_FILE SSH_KNOWN_HOSTS_FILE DEPLOY_HOST DEPLOY_PORT
|
||||
echo "==> setup_ssh: key written to ${SSH_KEY_FILE}, known_hosts pinned (accept-new) for ${DEPLOY_HOST}:${DEPLOY_PORT}"
|
||||
}
|
||||
|
||||
# teardown_ssh — remove the temp private key.
|
||||
# teardown_ssh — remove the temp private key + known_hosts file.
|
||||
teardown_ssh() {
|
||||
rm -f "${SSH_KEY_FILE}"
|
||||
rm -f "${SSH_KEY_FILE}" "${SSH_KNOWN_HOSTS_FILE}"
|
||||
echo "==> teardown_ssh: removed ${SSH_KEY_FILE}"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user