Linux 优化SSH安全配置

下面是优化后的版本,支持:

  • 自动备份 sshd_config
  • 自动写入公钥,避免重复追加
  • 修改 SSH 端口
  • 禁用密码登录
  • 启用公钥登录
  • 配置防火墙
  • 安装并启用 fail2ban
  • 配置 fail2ban 保护新 SSH 端口

运行前建议保留当前 SSH 会话不要断开,新开一个终端测试 ssh -p 25701 user@server 成功后再关闭旧连接。


生成 SSH 密钥

1、 以管理员身份打开 Windows PowerShell

2、 使用以下命令生成 SSH 密钥对:

ssh-keygen -t ed25519 -C "your_email@example.com"

如果 VPS 较老不支持 ed25519,使用 RSA 算法:

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

3、 设置保存路径

Enter file in which to save the key (/c/Users/你的用户名/.ssh/id_ed25519):

直接按回车使用默认路径:C:\Users\你的用户名\.ssh\

4、 设置密钥的密码(可选,可直接留空):

设置密码会增加安全性,但每次使用密钥时都需要输入密码。如果追求便利性,可以留空。

5、 密钥生成完成后,会在指定目录创建两个文件:

  • 私钥文件 : id_ed25519
  • 公钥文件 : id_ed25519.pub

需要公钥文件上传到 VPS 服务器


操作方法

SSH 登录

ssh root@你的服务器IP
  • 然后执行
nano harden-ssh.sh
  • 把完整脚本粘贴进去,把公钥:ssh-ed25519...hotmail.com替换你的
#!/usr/bin/env bash
set -Eeuo pipefail

SSH_PORT="25701"
TARGET_USER="root"
PUBLIC_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAPvwkcejp5Ck088PyCTsRhJM+LEph1mhDAFQjx/r82/ guboysky@hotmail.com"

FAIL2BAN_MAXRETRY="3"
FAIL2BAN_FINDTIME="10m"
FAIL2BAN_BANTIME="24h"

if [[ "$EUID" -ne 0 ]]; then
  echo "请使用 root 或 sudo 运行"
  exit 1
fi

if ! [[ "$SSH_PORT" =~ ^[0-9]+$ ]] || (( SSH_PORT < 1 || SSH_PORT > 65535 )); then
  echo "SSH_PORT 必须是 1-65535"
  exit 1
fi

if [[ -z "$PUBLIC_KEY" ]]; then
  echo "缺少 PUBLIC_KEY"
  exit 1
fi

if ! getent passwd "$TARGET_USER" >/dev/null; then
  echo "用户不存在:$TARGET_USER"
  exit 1
fi

USER_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
USER_GROUP="$(id -gn "$TARGET_USER")"

echo "==> 写入 SSH 公钥到 $TARGET_USER"

install -d -m 700 -o "$TARGET_USER" -g "$USER_GROUP" "$USER_HOME/.ssh"
touch "$USER_HOME/.ssh/authorized_keys"
chown "$TARGET_USER:$USER_GROUP" "$USER_HOME/.ssh/authorized_keys"
chmod 600 "$USER_HOME/.ssh/authorized_keys"

if ! grep -qxF "$PUBLIC_KEY" "$USER_HOME/.ssh/authorized_keys"; then
  echo "$PUBLIC_KEY" >> "$USER_HOME/.ssh/authorized_keys"
fi

echo "==> 备份并加固 sshd"

SSHD_CONFIG="/etc/ssh/sshd_config"
BACKUP_FILE="${SSHD_CONFIG}.bak.$(date +%F-%H%M%S)"
cp -a "$SSHD_CONFIG" "$BACKUP_FILE"

mkdir -p /etc/ssh/sshd_config.d

if ! grep -Eq '^[[:space:]]*Include[[:space:]]+/etc/ssh/sshd_config\.d/\*\.conf' "$SSHD_CONFIG"; then
  sed -i '1i Include /etc/ssh/sshd_config.d/*.conf' "$SSHD_CONFIG"
fi

sed -i -E \
  -e 's/^[[:space:]]*(Port[[:space:]]+)/# managed by harden-ssh: \1/' \
  -e 's/^[[:space:]]*(PasswordAuthentication[[:space:]]+)/# managed by harden-ssh: \1/' \
  -e 's/^[[:space:]]*(KbdInteractiveAuthentication[[:space:]]+)/# managed by harden-ssh: \1/' \
  -e 's/^[[:space:]]*(ChallengeResponseAuthentication[[:space:]]+)/# managed by harden-ssh: \1/' \
  -e 's/^[[:space:]]*(PermitRootLogin[[:space:]]+)/# managed by harden-ssh: \1/' \
  -e 's/^[[:space:]]*(PubkeyAuthentication[[:space:]]+)/# managed by harden-ssh: \1/' \
  -e 's/^[[:space:]]*(X11Forwarding[[:space:]]+)/# managed by harden-ssh: \1/' \
  -e 's/^[[:space:]]*(MaxAuthTries[[:space:]]+)/# managed by harden-ssh: \1/' \
  "$SSHD_CONFIG"

cat > /etc/ssh/sshd_config.d/99-hardening.conf <<EOF
Port ${SSH_PORT}
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PermitRootLogin prohibit-password
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
AllowUsers ${TARGET_USER}
ClientAliveInterval 300
ClientAliveCountMax 2
UseDNS no
EOF

mkdir -p /run/sshd

if ! sshd -t -f "$SSHD_CONFIG"; then
  echo "sshd 配置错误,已备份:$BACKUP_FILE"
  exit 1
fi

echo "==> 配置防火墙"

if command -v ufw >/dev/null 2>&1; then
  ufw allow "${SSH_PORT}/tcp"
  ufw reload || true
elif command -v firewall-cmd >/dev/null 2>&1; then
  firewall-cmd --permanent --add-port="${SSH_PORT}/tcp"
  firewall-cmd --reload
else
  echo "未检测到 ufw/firewalld,请手动放行端口 ${SSH_PORT}/tcp"
fi

echo "==> 安装 fail2ban"

if ! command -v fail2ban-client >/dev/null 2>&1; then
  if command -v apt-get >/dev/null 2>&1; then
    apt-get update
    DEBIAN_FRONTEND=noninteractive apt-get install -y fail2ban
  elif command -v dnf >/dev/null 2>&1; then
    dnf install -y fail2ban
  elif command -v yum >/dev/null 2>&1; then
    yum install -y epel-release || true
    yum install -y fail2ban
  else
    echo "无法自动安装 fail2ban,请手动安装"
  fi
fi

echo "==> 配置 fail2ban"

mkdir -p /etc/fail2ban/jail.d

if command -v journalctl >/dev/null 2>&1; then
  FAIL2BAN_BACKEND="systemd"
else
  FAIL2BAN_BACKEND="auto"
fi

cat > /etc/fail2ban/jail.d/sshd.local <<EOF
[sshd]
enabled = true
port = ${SSH_PORT}
filter = sshd
backend = ${FAIL2BAN_BACKEND}
maxretry = ${FAIL2BAN_MAXRETRY}
findtime = ${FAIL2BAN_FINDTIME}
bantime = ${FAIL2BAN_BANTIME}
ignoreip = 127.0.0.1/8 ::1
EOF

systemctl enable --now fail2ban || true
systemctl restart fail2ban || true

echo "==> 重启 SSH"

if systemctl list-unit-files | grep -q '^ssh.service'; then
  systemctl restart ssh
elif systemctl list-unit-files | grep -q '^sshd.service'; then
  systemctl restart sshd
else
  service ssh restart || service sshd restart
fi

echo
echo "加固完成"
echo
echo "测试登录:"
echo "ssh -p ${SSH_PORT} ${TARGET_USER}@你的服务器IP"
echo
echo "查看 SSH 监听:"
echo "ss -lntp | grep ${SSH_PORT}"
echo
echo "查看 fail2ban:"
echo "fail2ban-client status sshd"
echo
echo "原配置备份:$BACKUP_FILE"

  • 保存后执行:
chmod +x harden-ssh.sh
./harden-ssh.sh