上一篇 下一篇 分享链接 返回 返回顶部

香港服务器上的CentOS系统如何强化SSH安全策略,防止暴力破解与未授权访问?

发布人:Minchunlin 发布时间:2025-08-19 10:07 阅读量:724


我在葵涌机房的过道里蹲着,抱着一台临时接到 KVM 的 1U 服务器,啃冷掉的叉烧包。走廊尽头空调像老火车一样哐啷作响,指示灯一排排闪。监控里 SSH 暴力破解的告警像倒扣的雨一样刷屏:来自乌克兰的一段 /16、某云厂商的动态出口,甚至还有本地宽带的随机地址。

同一台 CentOS 7,24 小时 18 万次登录尝试。我知道,如果这天不把 SSH 安全策略打到“硬过头”,下次可能就不是告警了。

下面是那一夜直到第二天中午,我在香港机房给 CentOS 7 强化 SSHD 的完整实操记录:包含硬件/网络背景、策略设计、逐步实施、踩坑与复盘。口径尽量详尽,新手照做不翻车,老手也能挑到你要的硬菜。

1. 现场背景 & 目标

硬件与网络

  • 机型:1U 双路,Xeon Silver,128 GB RAM,NVMe 2×1.92 TB(RAID1,系统盘),SATA 4×8 TB(业务数据)。
  • 网卡:2×10 GbE(bond0,LACP),1×1 GbE(独立 IPMI/BMC 带外)。
  • 机房:香港(HK),外网 BGP,多运营商出口,公网 /29。
  • 宿主:物理机 + 若干 KVM 虚拟机(SSHD 强化优先落在宿主与跳板 VM)。

操作系统

  • OS:CentOS 7.9(3.10 内核),systemd。
  • 关键组件:OpenSSH_7.x(官方更新通道),firewalld(iptables 后端),SELinux Enforcing,rsyslog。

威胁模型(SSH 范围)

  • 大规模暴力破解(弱口令、字典、并发扫描)。
  • 凭据撞库(历史泄漏密码)。
  • 扫描器资源耗尽(连接洪泛、MaxStartups 触发)。
  • 横向移动(被攻陷跳板再进来)。
  • 误操作自锁(改端口/禁口令后无应急通道)。

安全目标

必须做到:禁口令、仅密钥、最小暴露面、多因素(PAM 2FA)、速率限制/自动封禁、严密审计与告警、可回退、不影响已有生产会话。

2. 设计蓝图(先想清楚再动手)

层级 措施 目标 风险/注意
网络边界 变更 SSH 端口 + 仅允许固定源段 降低被扫面率、收敛爆破面 需要 SELinux 端口标记、firewalld 永久规则
传输/握手 仅 SSHv2、强算法套件 降低加密降级风险 兼容性需验证旧客户端
账号/认证 禁 root 直登、禁口令、仅密钥、必要时 2FA(PAM) 杜绝凭据泄漏风险 密钥分发、落盘权限、应急账号
连接控制 MaxStartups、MaxAuthTries、LoginGraceTime 压制并发横扫 值过低可能影响正常运维
动态防御 fail2ban + firewalld/ipset 自动拉黑爆破源 正确提取日志、白名单避免误封
审计/可观测 /var/log/secure、auditd、远程 syslog 取证与回放 日志容量/轮转策略
应急/回退 保留会话、带外 KVM、原端口 1 小时灰度 防止自锁 变更窗口 & 同行看护

3. 变更的“安全沙盒”:我这样避免把自己锁在门外

开两条命脉:

  • 打开 两路 SSH:老端口(22)与新端口(例如 2222)并存 1 小时。
  • 两个会话:会话 A(root KVM 控制台),会话 B(普通 SSH)专门用于测试;任何一步失败立即回滚。
  • 确认带外:IPMI 网口可用、虚拟介面 Console 正常(我测试能进 GRUB)。
  • 提前生成并校验密钥:在跳板机 ssh -o PreferredAuthentications=publickey -p 22/2222 均能登录。

4. 实操步骤(可复制粘贴的顺序)

4.1 创建安全运维用户 & 分发密钥

# 1) 新建最小权限账号(wheel 组可 sudo)
useradd -m -s /bin/bash ops
passwd -l ops             # 锁定口令,后续仅用公钥
usermod -aG wheel ops

# 2) 放置公钥(在跳板机生成:ssh-keygen -o -a 64 -t ed25519 -C "ops@hk-bastion")
install -d -m 700 ~ops/.ssh
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..." > ~ops/.ssh/authorized_keys
chown -R ops:ops ~ops/.ssh
chmod 600 ~ops/.ssh/authorized_keys

# 3) 确认 SELinux 上下文没花
restorecon -Rv ~ops/.ssh

实战提示:不要先禁用密码再分发密钥;顺序反了就容易自锁。

4.2 firewalld:加新端口、限来源、保留老端口灰度

# 开机常驻
systemctl enable firewalld --now

# 允许新端口 2222(public 区域)
firewall-cmd --permanent --add-port=2222/tcp

# 仅允许跳板与办公网段(示例)
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port protocol="tcp" port="2222" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" port protocol="tcp" port="2222" accept'

# 过渡期保留 22,但只允许跳板
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port protocol="tcp" port="22" accept'

# 拒绝其他来源对 22/2222
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port protocol="tcp" port="22" drop'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port protocol="tcp" port="2222" drop'

firewall-cmd --reload

坑 1:如果你的主机接口被划到非 public 区域,记得 --zone=xxx 指定,否则规则“加了但不生效”。

4.3 SELinux:为新端口打标

# 安装管理工具
yum install -y policycoreutils-python

# 给 SSHD 新端口 2222 标 ssh_port_t
semanage port -a -t ssh_port_t -p tcp 2222 || semanage port -m -t ssh_port_t -p tcp 2222

# 确认
semanage port -l | grep ssh_port_t

坑 2:很多人改了 sshd_config 和防火墙,却忘了 SELinux;在 Enforcing 下,SSHD 会直接拒绝绑定新端口。

4.4 SSHD 强化配置(灰度双端口)

编辑 /etc/ssh/sshd_config.d/99-hardening.conf(CentOS 7 没这目录也可直接改主文件,建议新建独立片段便于回滚):

# 端口与监听
Port 2222
# 过渡期保留:Port 22
AddressFamily inet
ListenAddress 0.0.0.0

# 协议与算法
Protocol 2
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

# 账户与认证
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
PubkeyAuthentication yes
# 老版本关键字:PubkeyAcceptedKeyTypes ssh-ed25519,ssh-rsa
# 新版本:     PubkeyAcceptedAlgorithms ...

# 登录控制
LoginGraceTime 20
MaxAuthTries 3
MaxSessions 2
MaxStartups 10:30:60
ClientAliveInterval 300
ClientAliveCountMax 2

# 最小化暴露面
AllowUsers ops       # 白名单(配合来源网段更稳)
AllowTcpForwarding no
X11Forwarding no
PermitEmptyPasswords no
UseDNS no
GSSAPIAuthentication no

# 法律横幅(可选)
Banner /etc/issue.net

检查语法并灰度重载:

sshd -t && systemctl reload sshd
sshd -T | egrep 'port|maxauthtries|maxstartups|ciphers|kexalgorithms|macs'

坑 3(兼容性):旧版客户端若不支持 chacha20-poly1305,可暂留 aes256-ctr;若还不行,用 ssh -Q cipher/ssh -Q kex 对双方能力“交集”落地。

4.5 打开 2FA(PAM + Google Authenticator,可选但强烈建议)

yum install -y epel-release
yum install -y google-authenticator qrencode

# 以 ops 用户生成 2FA 秘钥(记录一次性应急码)
sudo -u ops bash -lc 'google-authenticator -t -d -f -r 3 -R 30 -w 3 -Q ANSI'

# 在 /etc/pam.d/sshd 顶部插入(在 auth 子栈前部)
# 注意:确保还保留 pam_unix.so 以配合密钥+2FA (keyboard-interactive)
echo 'auth required pam_google_authenticator.so nullok' | sed -i '1iauth required pam_google_authenticator.so nullok' /etc/pam.d/sshd

# 要求“公钥 + 2FA”
# 在 sshd_config 中加入:
# AuthenticationMethods publickey,keyboard-interactive
# 并 reload

实战权衡:对自动化任务(Ansible、拉管道)可放在跳板机做 2FA,业务节点仅密钥;或针对特定用户/主机豁免 2FA(分组与 PAM 条件)。

4.6 速率限制与自动封禁(fail2ban + firewalld/ipset)

安装与启用:

yum install -y fail2ban fail2ban-systemd
systemctl enable fail2ban --now

过滤器与监狱配置:

/etc/fail2ban/jail.local:

[DEFAULT]
# 封禁时间:首次 15 分钟,递增(bantime.increment = true)
bantime = 15m
findtime = 10m
maxretry = 3
backend = systemd
banaction = firewallcmd-ipset
banaction_allports = firewallcmd-allports

# 白名单(跳板、办公网)
ignoreip = 127.0.0.1/8 203.0.113.10/32 198.51.100.0/24

[sshd]
enabled = true
port    = 2222
filter  = sshd
logpath = /var/log/secure
maxretry = 3
bantime = 30m
findtime = 10m

firewallcmd-ipset 会将恶意源加入 ipset,性能比挨个 rich-rule 好很多。

检查生效:

fail2ban-client reload
fail2ban-client status sshd

4.7 额外的网络层“硬限制”(可选,抗扫很爽)

iptables recent 模块(针对 2222 端口的连接洪泛):

# 注意:使用 firewalld 的场景下,更推荐通过 rich rule + ipset 实现;
# 如确需 raw iptables,可在 direct 规则层加(避免被 firewalld 覆写)
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \
  -p tcp --dport 2222 -m state --state NEW -m recent --set --name SSH

firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 \
  -p tcp --dport 2222 -m state --state NEW -m recent --update --seconds 60 --hitcount 10 --name SSH -j DROP

firewall-cmd --reload

解释:同源 60 秒内新建连接超过 10 次直接丢弃 —— 配合 MaxStartups 进一步降压。

4.8 审计与远程日志

# 开启 sshd 审计更详
yum install -y audit
systemctl enable auditd --now

# 关键:记录失败与成功的认证事件(/var/log/audit/audit.log)
# 也可在 /etc/audit/rules.d/ 追加针对 /etc/ssh/sshd_config 的完整性监控

# rsyslog 远程转发(避免本机被抹)
echo '*.* @@log.example.internal:514' > /etc/rsyslog.d/99-remote.conf
systemctl restart rsyslog

快速检索可疑失败:

grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head

4.9 统一自动化(Ansible 片段)

hardening_ssh.yml:

- hosts: hk_servers
  become: yes
  tasks:
    - name: Ensure firewalld running
      service: { name: firewalld, state: started, enabled: yes }

    - name: Allow new SSH port
      firewalld:
        port: 2222/tcp
        permanent: yes
        state: enabled

    - name: Restrict sources (example)
      firewalld:
        rich_rule: 'rule family="ipv4" source address="203.0.113.10/32" port protocol="tcp" port="2222" accept'
        permanent: yes
        state: enabled

    - name: SELinux port label
      seport:
        ports: 2222
        proto: tcp
        setype: ssh_port_t
        state: present

    - name: Deploy sshd hardening drop-in
      copy:
        dest: /etc/ssh/sshd_config.d/99-hardening.conf
        content: |
          Port 2222
          Protocol 2
          PermitRootLogin no
          PasswordAuthentication no
          PubkeyAuthentication yes
          KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
          Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
          MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
          LoginGraceTime 20
          MaxAuthTries 3
          MaxSessions 2
          MaxStartups 10:30:60
          AllowUsers ops
          UsePAM yes
          X11Forwarding no
          UseDNS no
          GSSAPIAuthentication no

    - name: Reload sshd
      service: { name: sshd, state: reloaded }

    - name: Install fail2ban
      yum: { name: [fail2ban, fail2ban-systemd], state: present }

    - name: Configure jail.local
      copy:
        dest: /etc/fail2ban/jail.local
        content: |
          [DEFAULT]
          bantime = 15m
          findtime = 10m
          maxretry = 3
          backend = systemd
          banaction = firewallcmd-ipset
          ignoreip = 127.0.0.1/8 203.0.113.10/32 198.51.100.0/24
          [sshd]
          enabled = true
          port = 2222
          filter = sshd
          logpath = /var/log/secure
          maxretry = 3
          bantime = 30m
          findtime = 10m

    - name: Enable fail2ban
      service: { name: fail2ban, state: started, enabled: yes }

5. 一眼能抄的“参数表”

5.1 sshd 关键参数

建议值 说明
Port 2222(灰度保留 22) 改端口不是安全本质,但能明显降噪
PermitRootLogin no 禁止 root 直登
PasswordAuthentication no 全面禁口令
AuthenticationMethods publickey,keyboard-interactive 公钥 + 2FA(PAM)
KexAlgorithms curve25519-...优先 安全+性能
Ciphers chacha20-poly1305@openssh.com 现代流加密;保留 aes256-gcm/ctr 做兜底
MACs hmac-sha2-512-etm@openssh.com 优先 防止长度扩展攻击
MaxAuthTries 3 降低爆破窗口
MaxStartups 10:30:60 线性丢弃,抗并发扫
LoginGraceTime 20s 缩短半开等待
AllowUsers ops(或组策略) 最小授权
ClientAliveInterval/CountMax 300/2 清理僵尸连接

5.2 fail2ban(sshd 监狱)

说明
backend systemd 解析 journal/secure
banaction firewallcmd-ipset ipset 封禁更高效
findtime 10m 观察窗口
maxretry 3 触发阈值
bantime 30m(自增) 错误三次封 30 分钟

6. 验证清单(上线前我做了这些)

  1.  本机:sshd -t 语法通过;sshd -T 关键参数正确。
  2.  跳板机:新端口 ssh -p 2222 ops@server 可登,使用公钥。
  3.  旧端口:只对白名单源开放,外网扫描器应直接超时。
  4.  2FA:AuthenticationMethods 生效;误配时能用 KVM 回滚。
  5.  fail2ban:status sshd 能看到被封 IP,ipset 有命中。
  6.  SELinux:semanage port -l | grep 2222 显示 ssh_port_t。
  7.  日志:/var/log/secure 有失败详情;远程 rsyslog 能收到。
  8.  业务:现网自动化任务不依赖密码登录;必要白名单已加。

7. 一些“坑”与我当时的补救

“改了端口就是安全”?

不是。只是降噪。真正的防线是禁口令 + 限源 + 自动封禁 + 多因素。

SELinux 忘了标端口

现象:sshd 启动 OK,但绑定新端口失败或日志报拒绝。

补救:semanage port 标记后 systemctl reload sshd。

AllowUsers 把自己关外面

现象:ops 能登,另一个应急用户登不进。
补救:白名单写组 AllowGroups wheel 更稳;或二者并行。

旧客户机不支持 chacha20

现象:握手失败。

补救:暂留 aes256-ctr;让客户端升级 OpenSSH。

fail2ban 不触发

现象:status sshd 无 Banned,secure 明明有失败。

补救:检查 logpath 与 backend;CentOS 7 多用 /var/log/secure,不是 auth.log。

firewalld 区域错配

现象:规则加了没效果。

补救:firewall-cmd --get-active-zones 看接口在哪个 zone;按 zone 加规则或把接口移到 public。

自动化工单被 2FA 卡死

解决:跳板机做 2FA,人登到跳板后再免密到业务机器;或对某个技术账号禁用 2FA,但严格限源和仅密钥。

8. 进阶思路(视团队成熟度选配)

把 SSH 放到 VPN 后面:如 WireGuard/Tailscale 做组网,SSH 仅监听内网;公网不暴露,爆破直接归零。

端口敲门/单包授权(SPA):knockd 或 fwknopd,在“敲门”正确前,防火墙不开放 2222。

跳板审计:所有 SSH 必须先过堡垒机(录屏/回放/命令审计),业务机完全关闭对公网的 SSH。

配置基线与合规:用 OpenSCAP/自写 Ansible role 定期核查 sshd_config 是否被篡改。

蜜罐端口:22 保留给蜜罐(cowrie 等),收集恶意指标喂给 ipset/威胁情报。

9. 我们真的挡住了吗?(复盘数据)

以下是上线后一周的统计(以一台暴露公网的跳板 VM 为例):

指标 上线前(24h) 上线后(24h) 变化
扫描命中(22 端口 SYN) 180,000 次 9,200 次 ↓ 94.9%
SSH 认证失败 23,500 次 71 次 ↓ 99.7%
fail2ban 封禁条目 0 412 +412
平均握手时延(跳板 → 业务) 62 ms 63 ms
误封恢复次数 - 0 -

解释:改端口+限源大幅降噪;禁口令+fail2ban 把“手动试探”迅速清走;2FA 提高了敏感操作的心理安全阈值。

10. 最后的小抄(一步到位脚本,灰度慎用)

仅用于单机测试环境,生产请按本文分步执行并回滚验证。

#!/bin/bash
set -euo pipefail

NEW_PORT=2222
ALLOW_IP1=203.0.113.10/32
ALLOW_NET1=198.51.100.0/24
USER=ops

yum install -y epel-release policycoreutils-python fail2ban fail2ban-systemd firewalld google-authenticator qrencode

id -u $USER >/dev/null 2>&1 || useradd -m -s /bin/bash $USER
passwd -l $USER || true
usermod -aG wheel $USER

install -d -m 700 /home/$USER/.ssh
[ -f /root/${USER}.pub ] && cat /root/${USER}.pub >> /home/$USER/.ssh/authorized_keys
chown -R $USER:$USER /home/$USER/.ssh
chmod 600 /home/$USER/.ssh/authorized_keys
restorecon -Rv /home/$USER/.ssh

systemctl enable firewalld --now
firewall-cmd --permanent --add-port=${NEW_PORT}/tcp
firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=${ALLOW_IP1} port protocol=tcp port=${NEW_PORT} accept"
firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=${ALLOW_NET1} port protocol=tcp port=${NEW_PORT} accept"
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 port protocol=tcp port=22 drop'
firewall-cmd --permanent --add-rich-rule="rule family=ipv4 port protocol=tcp port=${NEW_PORT} drop"
firewall-cmd --reload

semanage port -a -t ssh_port_t -p tcp ${NEW_PORT} || semanage port -m -t ssh_port_t -p tcp ${NEW_PORT}

mkdir -p /etc/ssh/sshd_config.d
cat >/etc/ssh/sshd_config.d/99-hardening.conf <<EOF
Port ${NEW_PORT}
# Port 22
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
UsePAM yes
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
LoginGraceTime 20
MaxAuthTries 3
MaxSessions 2
MaxStartups 10:30:60
AllowUsers ${USER}
X11Forwarding no
UseDNS no
GSSAPIAuthentication no
EOF
sshd -t && systemctl reload sshd

cat >/etc/fail2ban/jail.local <<EOF
[DEFAULT]
bantime = 15m
findtime = 10m
maxretry = 3
backend = systemd
banaction = firewallcmd-ipset
ignoreip = 127.0.0.1/8 ${ALLOW_IP1} ${ALLOW_NET1}
[sshd]
enabled = true
port = ${NEW_PORT}
filter = sshd
logpath = /var/log/secure
maxretry = 3
bantime = 30m
findtime = 10m
EOF
systemctl enable fail2ban --now
echo "Done. Test SSH on port ${NEW_PORT} with key auth now."

11. 收尾:凌晨三点半的走廊

重载 sshd 的那一刻,Grafana 面板上的红线像被谁按了暂停键,暴力破解的尖刺一个个消失。

我把 22 端口的灰度窗口留到早上 9 点,和同事交接,确认大家都能走跳板用新端口、仅密钥登陆。然后我把 KVM 拔了电,靠在冷气出风口旁边坐了会儿,补完那半块已经硬邦邦的叉烧包。

安全这件事,从来不是一个“配置完成”的终点,而是一整套能经得住凌晨告警的体系:最小化暴露、强认证、动态拉黑、可观测与审计、以及可回退。

如果你也在香港,或者任何一个不眠的机房里和 SSH 打仗,希望这份记录能让你少踩几个坑,多睡几个小时

目录结构
全文