e557a94ed0
CI ssh 到 pangolin1 报 Too many authentication failures + 回落密码提示:ssh 把 容器默认/agent key 也一并递上,撞 MaxAuthTries。加 IdentitiesOnly=yes 只用 -i key, BatchMode=yes 纯非交互。影响 deploy-server / backup 等所有走 lib-ssh 的部署。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
3.5 KiB
Bash
Executable File
74 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# lib-ssh.sh — shared SSH deploy-key helpers for the pangolin deploy pipelines
|
|
# (site / server). `source` this from deploy-*.sh.
|
|
#
|
|
# Provides:
|
|
# 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).
|
|
#
|
|
# Target host is hardcoded to the pangolin1 VPS IP (103.119.13.48): the CI
|
|
# runner has no access to the user's local ~/.ssh/config, so the `pangolin1`
|
|
# alias cannot be resolved there — the bare IP is used instead. Override with
|
|
# DEPLOY_HOST / DEPLOY_PORT if a caller needs to retarget.
|
|
#
|
|
# No command substitution ($()) is used anywhere, per repo bash conventions.
|
|
|
|
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
|
|
fi
|
|
|
|
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}"
|
|
|
|
# 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
|
|
|
|
# IdentitiesOnly=yes:只用上面 -i 指定的 key,不把 agent/默认 key 也递上去
|
|
# (否则会触发服务器 MaxAuthTries「Too many authentication failures」)。
|
|
# BatchMode=yes:纯非交互,认证失败即退出,不回落到密码提示卡住。
|
|
_ssh_opts="-i ${SSH_KEY_FILE} -p ${DEPLOY_PORT} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=${SSH_KNOWN_HOSTS_FILE}"
|
|
SSH="ssh ${_ssh_opts}"
|
|
RSYNC_SSH="ssh ${_ssh_opts}"
|
|
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 + known_hosts file.
|
|
teardown_ssh() {
|
|
rm -f "${SSH_KEY_FILE}" "${SSH_KNOWN_HOSTS_FILE}"
|
|
echo "==> teardown_ssh: removed ${SSH_KEY_FILE}"
|
|
}
|