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

如何在香港服务器的CentOS 7中通过优化SSH配置和硬化密码策略,防止暴力破解攻击?

发布人:Minchunlin 发布时间:2025-08-24 11:11 阅读量:688

那天是个潮湿的上午,我在香港机房走道里一边吸着冷气一边盯着值班大屏。香港这台对外节点的告警刚刚又跳了一轮:sshd 认证失败尖刺直冲 10k/min。运营同事说晚上就要跑客户演示,我一边把咖啡放到机柜的托盘上,一边通过 iDRAC 进到这台机器,心里就一个念头——先把门(SSH)加固,再谈别的。

1. 资产与环境清单(硬件/系统参数)

项目 参数
机房位置 HK(将军澳 TKO)
服务器 Dell R730(1×Xeon E5-2680 v4,128GB RAM)
存储 2×960GB NVMe(RAID1,用于系统与日志分区)
网卡 2×10GbE(bond0)
外网 /29 公网段,独立 IP
OS CentOS 7.9(3.10 内核)
OpenSSH 7.x(CentOS 7 默认系,后续随补丁)
SELinux Enforcing
防火墙 firewalld + ipset
OOB iDRAC Enterprise(远程 KVM/虚拟介质)

注:确保你有 OOB(带外)入口。任何 SSH 改动前,我都会先验证 iDRAC 控制台可用,避免“锁死在门外”。

2. 攻击态势基线(改造前)

先用几条命令刻下“问题有多糟”:

# 近 10 分钟失败登录 TOP10 IP
awk '/Failed password/ {print $(NF-3)}' /var/log/secure \
  | sort | uniq -c | sort -nr | head

# 每分钟失败次数
grep "Failed password" /var/log/secure \
  | awk '{print $1" "$2" "$3}' \
  | uniq -c | tail

# 当前 sshd 并发
ss -tnp | grep ':22 ' | wc -l

基线数据(摘录)

指标 数值(改造前)
失败认证峰值 ~10,500 次/分钟
高危源段 若干海外云段 & 代理
22 端口并发 2,000+ 半开/短连
CPU 抖动 sshd 占用瞬时 80%+(总量)

3. 总体防护思路(先“关门”,再“设门槛”,再“布捕网”)

  • 最小暴露面:改端口 + 只监听需要的地址 + 允许列表优先。
  • 强身份:禁用密码登录或把密码策略“硬化到痛”,优先用密钥 + 2FA。
  • 限速与惩罚:firewalld / ipset / fail2ban 联动,打+封+记。
  • 日志与回滚:每步变更前验证、sshd -t 语法检查、OOB 待命、每步留痕。

4. 实操步骤(逐步加固)

4.1 新建非 root 维护账号 + 密钥登录

# 1) 创建维护组与账号
groupadd devops
useradd -m -G devops,wheel -s /bin/bash ops

# 2) 配置 sudo 最小化
echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/90-wheel
chmod 440 /etc/sudoers.d/90-wheel

# 3) 生成并部署密钥(在跳板机)
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/hk_ops_ed25519
ssh-copy-id -i ~/.ssh/hk_ops_ed25519.pub ops@<当前服务器IP>

经验:先验证密钥可用,再去改 SSH 配置,避免切断最后一根救命绳。

4.2 变更 SSH 端口 + 仅监听需要的地址(含 SELinux 与防火墙)

SELinux 打开新端口类型

yum install -y policycoreutils-python
semanage port -a -t ssh_port_t -p tcp 2222
semanage port -l | grep ssh_port_t   # 验证

firewalld 允许 2222,保留短期 22 做迁移过渡

# 创建 allowlist
firewall-cmd --permanent --new-ipset=ssh_allowlist --type=hash:ip
firewall-cmd --permanent --ipset=ssh_allowlist --add-entry=<你的跳板IP>
firewall-cmd --permanent --add-rich-rule='rule source ipset=ssh_allowlist port protocol="tcp" port="2222" accept'

# 临时开放公网段(只为切换窗口,后续删除)
firewall-cmd --permanent --add-port=2222/tcp

firewall-cmd --reload

编辑 /etc/ssh/sshd_config(关键项)

Port 2222
# Port 22              # 迁移期保留,确认后删除
AddressFamily inet     # 无 IPv6 需求时减少面暴
ListenAddress <此机公网IPv4>
Protocol 2

# 身份与权限
PermitRootLogin no
PasswordAuthentication no        # 强推密钥;如需临时保留,最后两节再硬化密码策略
PubkeyAuthentication yes
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
UsePAM yes

# 加固算法(CentOS 7/OpenSSH 7.x 兼容)
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256

# 细节
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 20
MaxAuthTries 3
MaxStartups 10:30:60      # 限并发握手
UseDNS no                 # 避免反查卡顿

# 访问白名单(可选:更细颗粒)
# AllowUsers ops

语法检查 & 平滑重载

sshd -t
systemctl reload sshd

在新端口验证登录,确认无误后移除 22

ssh -i ~/.ssh/hk_ops_ed25519 -p 2222 ops@<IP>
# 验证稳定后:
firewall-cmd --permanent --remove-port=22/tcp
firewall-cmd --reload
# 同步删掉 sshd_config 中的 Port 22

坑 1:改了 Port,忘了 semanage 或忘了 firewalld。症状:connection refused 或 No route to host。

解决:OOB 登录,核对 semanage port -l | grep ssh 与 firewall-cmd --list-all。

4.3 失败尝试自动封禁:fail2ban + firewalld/ipset

安装与启用

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

jail.local(/etc/fail2ban/jail.local)

[DEFAULT]
banaction = firewallcmd-ipset
backend = systemd

[sshd]
enabled = true
port = 2222
findtime = 10m
maxretry = 5
bantime = 1h

检查状态

fail2ban-client status sshd

坑 2:后端日志选择不当。CentOS 7 建议 backend=systemd,否则有时抓不全拒绝事件。

4.4 进一步缩口:只允许特定源段(生产强烈建议)

# 扩充允许列表
firewall-cmd --permanent --ipset=ssh_allowlist --add-entry=<你的办公出口IP>
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="<临时远程IP/32>">
  port protocol="tcp" port="2222" accept'
firewall-cmd --reload

策略:先白名单,后黑名单。公网节点尽量不对全网暴露管理口。

4.5 若必须保留密码登录:把密码政策“硬到吓退脚本”

如果业务流程暂时离不开密码(例如跳板未全覆盖),务必开启以下策略;一旦条件允许,还是推荐彻底 PasswordAuthentication no。

pwquality 强度策略(/etc/security/pwquality.conf)

minlen = 14
dcredit = 0
ucredit = 0
lcredit = 0
ocredit = 0
minclass = 4
maxrepeat = 2
dictcheck = 1
usercheck = 1
gecoscheck = 1

PAM 引用(/etc/pam.d/system-auth 与 /etc/pam.d/password-auth)

在 password 段增加(或强化):

password    requisite     pam_pwquality.so retry=3 enforce_for_root local_users_only

登录失败锁定(pam_faillock)

在 auth 段添加:

auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900
auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900
account required pam_faillock.so

密码轮换与过期(/etc/login.defs)

PASS_MAX_DAYS   90
PASS_MIN_DAYS   7
PASS_WARN_AGE   14

对已有用户执行:

chage -M 90 -m 7 -W 14 ops

4.6 选配:SSH + TOTP 二次验证(Google Authenticator)

yum install -y google-authenticator
su - ops -c 'google-authenticator -t -d -f -r 3 -R 30 -w 3'
# 扫码保存应急码

# PAM:/etc/pam.d/sshd
auth required pam_google_authenticator.so nullok

# sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
systemctl reload sshd

小贴士:TOTP 最好只对运维跳板或关键账号启用,通过 Match User ops 分段配置可控范围。

4.7 登录横幅(法律告知)

echo "Authorized access only. All activities may be monitored and recorded." > /etc/issue.net
sed -i 's/^#\?Banner.*/Banner \/etc\/issue.net/' /etc/ssh/sshd_config
systemctl reload sshd

5. 防火墙规则结构(示意)

优先级 规则 说明
1 允许 ssh_allowlist → TCP/2222 固定办公/跳板源
2 fail2ban 动态封禁(ipset) 惩罚阈值命中即封
3 临时例外(短期 IP/32) 运维紧急通道
4 其余拒绝(不暴露 22) 收口

6. 审计与监控要点

配置快照:

sshd -T > /root/sshd.effective.$(date +%F).txt 留档。

日志切割与留存:

确保 /var/log/secure 与 fail2ban 日志落在 NVMe,容量充足。

指标:

  • 每分钟失败登录数
  • fail2ban 封禁数
  • MaxStartups 命中次数
  • 登录成功地域分布(反常告警)

例行检查脚本(摘录):

#!/bin/bash
echo "[Failed/min]"
grep "Failed password" /var/log/secure \
 | awk '{print $1" "$2" "$3}' \
 | uniq -c | tail -n 5

echo "[Fail2ban bans]"
fail2ban-client status sshd | sed -n '1,20p'

7. 实施“前后对比”(实际观测)

指标 改造前 改造后(48h)
失败认证峰值(次/分钟) ~10,500 < 30
22 端口并发 2,000+ 0(已下线)
被封禁 IP(累计) N/A 2,183
有效登录失败率 12% < 1%
SSH 握手耗时 P95 480ms 140ms(禁用反查+白名单)

8. 回滚与应急预案

永远保留 OOB 通道(iDRAC/iLO)。

变更窗口:先开放新端口 2222 → 成功验证 → 再关闭 22。

一键回滚:保留 /etc/ssh/sshd_config.bak,失败时:

cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
semanage port -d -t ssh_port_t -p tcp 2222 || true
firewall-cmd --permanent --remove-port=2222/tcp
firewall-cmd --reload
systemctl restart sshd

9. 常见坑与现场解法(血泪总结)

忘开 SELinux 端口:Permission denied/context not allowed

→ semanage port -a -t ssh_port_t -p tcp 2222

firewalld Zone 搞错:规则加到 public,网卡却在 drop

→ nmcli -f GENERAL.CONNECTION,GENERAL.ZONE dev show <nic>;firewall-cmd --get-active-zones

密钥权限不对:~/.ssh 权限 700、authorized_keys 600;chcon -t ssh_home_t ~/.ssh -R(SELinux)

DNS 反查拖慢登录:UseDNS no、同时 /etc/hosts 固定跳板名

MaxStartups 过严:登录高峰被挤掉

→ MaxStartups 建议 10:30:60 起步,按压测微调

10. 一键化脚本(可读可改)

在 OOB 保驾护航前提下执行,变量按需替换。

#!/bin/bash
set -euo pipefail

NEW_PORT=2222
JUMPHOST_IP="<你的跳板IP>"

yum install -y epel-release policycoreutils-python fail2ban fail2ban-systemd

# SELinux & firewalld
semanage port -a -t ssh_port_t -p tcp ${NEW_PORT} || true
firewall-cmd --permanent --new-ipset=ssh_allowlist --type=hash:ip || true
firewall-cmd --permanent --ipset=ssh_allowlist --add-entry=${JUMPHOST_IP}
firewall-cmd --permanent --add-rich-rule="rule source ipset=ssh_allowlist port protocol=\"tcp\" port=\"${NEW_PORT}\" accept"
firewall-cmd --permanent --add-port=${NEW_PORT}/tcp
firewall-cmd --reload

# 备份 & 修改 sshd_config
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F_%H%M%S)
cat >/etc/ssh/sshd_config <<'CONF'
Port 2222
AddressFamily inet
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 20
MaxAuthTries 3
MaxStartups 10:30:60
UseDNS no
CONF

sshd -t && systemctl reload sshd

# fail2ban
cat >/etc/fail2ban/jail.local <<'JAIL'
[DEFAULT]
banaction = firewallcmd-ipset
backend = systemd

[sshd]
enabled = true
port = 2222
findtime = 10m
maxretry = 5
bantime = 1h
JAIL

systemctl enable --now fail2ban
fail2ban-client status sshd || true

echo "Done. Test SSH: ssh -p 2222 ops@<IP>"

11. 最终清单(Checklist)

  •  已验证跳板/白名单能通 2222
  •  PasswordAuthentication no(或已启用强密码+faillock)
  •  PermitRootLogin no
  •  SELinux 端口已登记(ssh_port_t)
  •  firewalld 仅放行白名单与 2222
  •  fail2ban 生效,banaction=firewallcmd-ipset
  •  UseDNS no,算法/握手参数已收敛
  •  登录横幅与审计策略到位
  •  22 已下线并验证无依赖

12. 从“海啸”到“细雨”

改造完成的第二天夜里,我回到机房,又把咖啡放在了那块托盘上。大屏上 sshd 的日志曲线从锯齿海啸变成了几条细线;fail2ban 偶尔跳一下封禁计数,但很快归于平静。
我知道这不是终点——安全是个过程,但至少今晚,门锁结实、警铃在线、巡逻规律。你得先让门配得上这座机房,剩下的就交给日常的监控和纪律。

FAQ · 我在现场经常被问到的 3 个问题

只改端口有用吗?

仅改端口不是安全策略,但能有效降噪,让惩罚/监控更聚焦;与白名单、密钥、fail2ban 叠加才是闭环。

必须关掉密码登录吗?

能关就关;不能关就把密码策略“硬化到痛”,并配合 pam_faillock 与 2FA。

怕锁自己在外面?

准备 OOB、逐步切换、每步sshd -t、保留回滚脚本。流程比命令更重要。

如果你也在香港维护类似的外网节点,上面的步骤照抄 80% 就能落地。剩下 20%,请结合你的网络边界(云防火墙/硬件 ACL)与团队运维流程去“定制化”。安全不是模板,而是纪律 + 细节。

目录结构
全文