此文档仅适用于 Linux 系统与 Windows 系统无关,用于解决SSH相关异常

由于虚拟化平台的机制在虚拟机 冷迁移、重置密码、网络切换(VPC与经典网络)

时会重置 SSH 相关配置和清空已保存的公钥、比如 SSH 配置偏好、已保存的公钥

注意在虚拟机重置密码、维护后虚拟机密钥发生变化是预期行为,因为触发了初始化

D=/etc/ssh/authorized_keys.d
CONF=/etc/ssh/sshd_config.d/10-sshkey-protect.conf
HEAL=/usr/local/sbin/ssh-authkeys-heal.sh
UNIT=/etc/systemd/system/ssh-authkeys-heal.service

# 1) 建立受保护目录,并把现有公钥搬进去
mkdir -p "$D" /etc/ssh/sshd_config.d
[ -f "$D/root" ] || : > "$D/root"
if [ -f /root/.ssh/authorized_keys ] && [ ! -L /root/.ssh/authorized_keys ]; then
  cat /root/.ssh/authorized_keys >> "$D/root"
fi
sort -u "$D/root" -o "$D/root" 2>/dev/null
chown root:root "$D/root" "$D" 2>/dev/null
chmod 600 "$D/root"; chmod 755 "$D"

# 2) 默认路径改成软链:以后用 ssh-copy-id / >> 追加的密钥都会落到受保护文件里
mkdir -p /root/.ssh; chmod 700 /root/.ssh
rm -f /root/.ssh/authorized_keys
ln -s "$D/root" /root/.ssh/authorized_keys

# 3) sshd 覆盖配置(文件名 10- 必须排在 50- 之前)
rm -f /etc/ssh/sshd_config.d/10-customer.conf
cat > "$CONF" <<'EOF'
PasswordAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u .ssh/authorized_keys
EOF
chmod 644 "$CONF"

# 4) 老发行版的 sshd_config 可能没有 Include 这一行
SC=/etc/ssh/sshd_config
grep -q '^Include[[:space:]]*/etc/ssh/sshd_config\.d/\*\.conf' "$SC" || \
  sed -i '1i Include /etc/ssh/sshd_config.d/*.conf' "$SC"

# 5) 开机自愈:软链被删就重建,并把期间追加到默认路径的密钥吸收进受保护文件
cat > "$HEAL" <<'EOF'
#!/bin/sh
D=/etc/ssh/authorized_keys.d
mkdir -p "$D" /root/.ssh
[ -f "$D/root" ] || : > "$D/root"
if [ ! -L /root/.ssh/authorized_keys ]; then
  if [ -f /root/.ssh/authorized_keys ]; then
    { cat "$D/root"; cat /root/.ssh/authorized_keys; } 2>/dev/null | grep -v '^[[:space:]]*$' | sort -u > "$D/.tmp" && mv "$D/.tmp" "$D/root"
    rm -f /root/.ssh/authorized_keys
  fi
  ln -s "$D/root" /root/.ssh/authorized_keys
fi
chown root:root "$D/root" "$D" 2>/dev/null
chmod 600 "$D/root"; chmod 755 "$D"; chmod 700 /root/.ssh
EOF
chmod 755 "$HEAL"

if command -v systemctl >/dev/null 2>&1; then
cat > "$UNIT" <<'EOF'
[Unit]
Description=Restore protected SSH authorized_keys
After=local-fs.target
Before=cloud-init.service cloud-config.service cloud-final.service ssh.service sshd.service
ConditionPathExists=/etc/ssh/authorized_keys.d

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/ssh-authkeys-heal.sh

[Install]
WantedBy=multi-user.target
EOF
  systemctl daemon-reload >/dev/null 2>&1
  systemctl enable ssh-authkeys-heal.service >/dev/null 2>&1
  systemctl start ssh-authkeys-heal.service >/dev/null 2>&1
elif command -v crontab >/dev/null 2>&1; then
  ( crontab -l 2>/dev/null | grep -v ssh-authkeys-heal; echo "@reboot /usr/local/sbin/ssh-authkeys-heal.sh" ) | crontab - 2>/dev/null
fi

# 6) SELinux 环境尽力修正文件上下文
command -v restorecon >/dev/null 2>&1 && restorecon -RF /etc/ssh /root/.ssh >/dev/null 2>&1

# 7) 生效并校验(sshd -t 不通过则不会重启,避免失联)
"$HEAL"
sshd -t && { systemctl restart sshd 2>/dev/null || systemctl restart ssh 2>/dev/null || \
             service sshd restart 2>/dev/null || service ssh restart 2>/dev/null || true; }
sshd -T 2>/dev/null | grep -iE 'passwordauthentication|pubkeyauthentication|authorizedkeysfile'