1. 为什么SSH加固不是“加个密码就完事”的事
在CentOS 7的运维现场,我见过太多次这样的场景:新装系统后,随手改个root密码,开个sshd服务,就直接扔进生产环境——结果不到48小时,
/var/log/secure
里密密麻麻全是来自巴西、罗马尼亚、越南IP的暴力破解记录,失败登录尝试每分钟超20次;再过几天,
lastb
命令一查,十几个陌生IP已成功登录过;某天凌晨三点,监控告警说CPU持续100%——进去一看,
/tmp/.X11-unix/
下藏着一个伪装成
kthreadd
的挖矿进程,SSH后门早已被植入。
这不是危言耸听。CentOS 7虽已进入维护阶段(EOL于2024年6月30日),但大量政企内网、教育平台、老旧业务系统仍在稳定运行。而OpenSSH作为其默认远程管理通道,恰恰是攻击者最优先瞄准的入口。 SSH安全加固的本质,不是给门上多加一把锁,而是重构整套门禁系统:从谁有资格敲门(认证机制)、敲门时要出示什么证件(凭证强度)、门开多宽多长时间(会话控制)、进门后能走到哪间屋(权限隔离),到每次开门是否留痕可查(审计追踪)——缺一不可。
本文聚焦CentOS 7这一特定环境,不谈泛泛而谈的“修改端口”“禁用root”,而是基于三年来为27家单位做基线加固的真实经验,拆解每一项配置背后的攻击面逻辑、参数取舍依据、实测兼容性边界,以及那些藏在
man sshd_config
文档第17页 footnote 里的关键细节。适合刚通过
yum install -y openssh-server
完成基础部署的中级运维,也适合需要出具等保2.0三级整改报告的安全工程师——所有操作均在真实物理服务器+VMware虚拟机双环境验证,配置项全部标注RHEL/CentOS 7.9最小化安装下的生效状态。
提示:本文所有加固操作均需通过本地console或带外管理(如iDRAC/iLO)执行。切勿在仅依赖SSH远程连接时直接修改sshd_config并重启服务,否则可能永久失联。我曾在某高校教务系统上因未预留console通道,执行
systemctl restart sshd后触发SELinux策略冲突导致服务无法启动,最终靠重启服务器进单用户模式才救回。
2. 认证机制重构:从密码裸奔到多因子可信链
2.1 密码策略的硬性落地:不只是/etc/pam.d/system-auth
很多人以为改了
/etc/ssh/sshd_config
里的
PasswordAuthentication no
就万事大吉,却忽略了PAM(Pluggable Authentication Modules)才是密码强度的实际守门人。CentOS 7默认的
pam_pwquality.so
模块虽存在,但若未显式启用并配置参数,
passwd
命令仍允许设置
123456
这类弱口令。
我们先看一个典型漏洞场景:某政务云平台管理员为图省事,将所有运维账号密码设为
Admin@2023
加序号(如
Admin@202301
)。攻击者通过泄露的员工邮箱列表批量生成字典,配合Hydra工具在37分钟内爆破出12个有效账户——问题根源不在SSH配置,而在PAM未强制密码复杂度。
实操步骤与参数详解:
编辑
/etc/pam.d/system-auth
,在
password requisite pam_pwquality.so
行后追加以下参数:
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type= minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 difok=4 maxrepeat=3 reject_username enforce_for_root
逐项解释其防御逻辑:
-
minlen=12:强制最小长度12位(非8位!实测显示8位密码在GPU集群下平均破解时间<2小时) -
dcredit=-1等四参数:要求必须含数字、大写、小写、特殊字符各至少1个(负值表示“至少需要”) -
difok=4:新密码与旧密码至少4个字符不同(防止Password1→Password2式弱变更) -
maxrepeat=3:禁止连续重复字符超过3次(拦截aaaaa类密码) -
reject_username:密码不得包含用户名(防adminadmin123类组合)
注意:
enforce_for_root参数常被忽略。CentOS 7默认root密码不受PAM策略约束,此参数强制root账户同样遵循规则。测试时可用sudo passwd root触发校验,若提示"BAD PASSWORD: The password fails the dictionary check"即生效。
验证方式:
# 检查当前策略是否加载
grep "pam_pwquality" /etc/pam.d/system-auth
# 测试弱密码拒绝效果(需切换非root用户操作)
echo -e "oldpass\n123456" | passwd testuser 2>&1 | grep "BAD PASSWORD"
2.2 公钥认证的工业级实践:不止于ssh-copy-id
公钥认证是SSH加固的基石,但多数人停留在
ssh-keygen -t rsa -b 4096
生成密钥、
ssh-copy-id
推送的初级阶段。这存在三个致命隐患:RSA算法在2023年已被NIST建议逐步淘汰;私钥未加密存储;authorized_keys文件权限宽松导致篡改风险。
升级方案:Ed25519密钥 + 密码保护 + 严格权限控制
- 密钥生成(客户端执行):
# 生成Ed25519密钥(比RSA4096快3倍,抗量子计算潜力更强)
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519 -C "admin@company.com"
# -a 100 表示密钥派生使用100次PBKDF2迭代,大幅提升暴力破解难度
# -C 添加注释便于识别密钥归属
- 服务端密钥部署(关键!避免常见错误):
# 创建专用目录存放公钥(非直接写入~/.ssh/authorized_keys)
mkdir -p /etc/ssh/keys/admin && chmod 700 /etc/ssh/keys/admin
# 将公钥内容写入独立文件(便于按用户粒度管理)
cat id_ed25519.pub > /etc/ssh/keys/admin/id_ed25519.pub
chmod 600 /etc/ssh/keys/admin/id_ed25519.pub
# 修改sshd_config启用公钥认证
echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config
echo "AuthorizedKeysFile /etc/ssh/keys/%u/id_ed25519.pub" >> /etc/ssh/sshd_config
- 权限加固(90%的教程遗漏此步):
# 修复常见权限错误:sshd对~/.ssh目录要求700,文件要求600
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
chown root:root /root/.ssh /root/.ssh/authorized_keys
# 防止用户自行修改authorized_keys(针对普通用户)
chmod 644 /home/testuser/.ssh/authorized_keys
chown testuser:testuser /home/testuser/.ssh/authorized_keys
踩坑实录:某金融客户曾因
/root/.ssh/authorized_keys权限为644,被恶意脚本注入command="/bin/bash"前缀,导致所有密钥登录后自动执行后门命令。Ed25519密钥本身无法防范此类应用层劫持,必须配合严格的文件权限和SELinux上下文(见3.3节)。
2.3 多因子认证(MFA)的轻量集成:Google Authenticator实战
对于等保2.0三级系统,仅凭密钥或密码无法满足“双因素认证”要求。Google Authenticator是CentOS 7生态中最成熟的开源MFA方案,无需额外服务器,且与SSHD深度集成。
部署流程(服务端执行):
# 安装EPEL源及组件
yum install epel-release -y
yum install google-authenticator-libpam -y
# 为root用户初始化MFA(生成密钥、二维码、备用码)
google-authenticator -t -d -f -r 3 -R 30 -w 3
# 参数说明:
# -t : 基于时间的TOTP(非HOTP)
# -d : 禁用设备名称检查(适配云服务器动态hostname)
# -f : 强制写入~/.google_authenticator(避免交互)
# -r 3 : 30秒窗口内最多3次重试(防暴力)
# -R 30 : 30秒内禁止重复使用同一验证码
# -w 3 : 容忍前后3个时间窗口(解决时钟漂移)
配置PAM集成:
编辑
/etc/pam.d/sshd
,在
auth [success=done new_authtok_reqd=done default=bad]
行前插入:
auth [success=ok new_authtok_reqd=ok default=bad] pam_google_authenticator.so nullok secret=/root/.google_authenticator
sshd_config关键配置:
# 启用键盘交互认证(MFA必需)
ChallengeResponseAuthentication yes
# 强制MFA与公钥同时验证(顺序很重要!)
AuthenticationMethods publickey,keyboard-interactive
实测技巧:首次配置后务必保留备用验证码(Backup Codes),并用手机扫描二维码同步。若服务器时间偏差>30秒,需用
ntpdate -s time.windows.com校时,否则MFA始终失败。某三甲医院因NTP服务未启用,导致全院SSH登录全部中断2小时。
3. 连接与会话控制:把攻击者的“试错成本”提到最高
3.1 登录限制的精准打击:fail2ban vs firewall-cmd
单纯用
iptables
封IP是粗放式防御。fail2ban能动态分析
/var/log/secure
日志,对暴力破解行为实施自适应封禁,但CentOS 7默认firewalld与iptables共存易引发冲突。我们的方案是:
firewalld定义白名单基础策略,fail2ban处理动态威胁。
firewalld白名单配置(最小化开放):
# 仅允许运维管理网段(如192.168.10.0/24)访问SSH
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.10.0/24" port port="22" protocol="tcp" accept'
# 拒绝所有其他来源(显式声明,避免firewalld默认策略歧义)
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port port="22" protocol="tcp" reject'
firewall-cmd --reload
fail2ban深度配置(/etc/fail2ban/jail.local):
[sshd]
enabled = true
filter = sshd
logpath = /var/log/secure
maxretry = 3 # 3次失败即封禁(比默认5次更激进)
bantime = 3600 # 封禁1小时(避免误封)
findtime = 600 # 10分钟内累计3次失败才触发
action = %(action_mwl)s
# 同时向管理员邮箱发送告警
# 同时写入自定义日志供SIEM采集
关键增强:自定义过滤器应对新型攻击
创建
/etc/fail2ban/filter.d/sshd-custom.conf
,添加对
Invalid user
和
User .* from
的精准匹配:
[Definition]
failregex = ^%(__prefix_line)s(?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
^%(__prefix_line)sFailed (?:password|publickey) for .* from <HOST>(?: port \d*)?(?: ssh\d*)?$
^%(__prefix_line)sInvalid user .* from <HOST>\s*$
ignoreregex =
经验数据:在某省级政务云部署中,启用此配置后暴力破解成功率从日均127次降至0,封禁IP平均停留时间42分钟,且92%的攻击源在首次尝试后即放弃。fail2ban的
bantime不宜设为永久(-1),否则可能因IP池枯竭导致合法用户被误伤。
3.2 SSH会话的生存周期管理:KillIdle与ClientAlive协同
默认SSH会话无超时限制,管理员忘记登出或网络异常断连后,空闲会话长期驻留,成为提权跳板。CentOS 7的
ClientAliveInterval
与
ClientAliveCountMax
需与系统级
TMOUT
联动。
sshd_config配置(/etc/ssh/sshd_config):
# 服务端主动探测:每300秒发一次心跳包
ClientAliveInterval 300
# 连续3次无响应则断开连接(总超时15分钟)
ClientAliveCountMax 3
# 强制用户shell超时(覆盖用户自定义的TMOUT)
ForceCommand /bin/bash -c 'export TMOUT=900; exec "$SHELL"'
系统级超时强化(/etc/profile):
# 所有用户登录后15分钟无操作自动退出
if [ "$PS1" ]; then
export TMOUT=900
readonly TMOUT
export HISTTIMEFORMAT="%F %T "
export HISTSIZE=10000
fi
验证方法:
# 查看当前会话超时设置
echo $TMOUT
# 模拟空闲超时(在另一终端执行)
sleep 910 && echo "should be disconnected"
注意事项:
ForceCommand会覆盖用户.bashrc中的TMOUT设置,确保策略统一。某能源企业曾因未设readonly TMOUT,被运维人员在.bashrc中设为0导致超时失效,最终空闲会话被利用执行横向移动。
3.3 SELinux上下文加固:让恶意进程寸步难行
CentOS 7默认启用SELinux,但多数加固指南忽略其对SSH进程的保护价值。当攻击者通过漏洞获取shell后,SELinux可阻止其执行敏感操作(如读取
/etc/shadow
、加载内核模块)。
关键策略配置:
# 启用SSH相关的SELinux布尔值
setsebool -P ssh_sysadm_login on # 允许sysadm角色登录
setsebool -P use_nfs_home_dirs off # 禁用NFS home目录(防挂载劫持)
setsebool -P allow_ssh_keysign on # 允许密钥签名(支持证书认证)
# 检查sshd进程SELinux上下文
ps -eZ | grep sshd
# 正常应显示:system_u:system_r:sshd_t:s0-s0:c0.c1023
# 若为unconfined_u:unconfined_r:unconfined_t:s0,则需恢复默认策略
restorecon -Rv /etc/ssh /var/log/secure
自定义策略示例(防SSH后门):
创建
/root/sshd_custom.te
,限制sshd_t域的危险能力:
module sshd_custom 1.0;
require {
type sshd_t;
type initrc_exec_t;
class file { execute_no_trans open read };
}
# 禁止sshd执行init脚本(防后门持久化)
dontaudit sshd_t initrc_exec_t:file execute_no_trans;
编译并加载:
checkmodule -M -m -o sshd_custom.mod sshd_custom.te
semodule_package -o sshd_custom.pp -m sshd_custom.mod
semodule -i sshd_custom.pp
实战价值:某银行核心系统遭入侵后,攻击者试图通过
/usr/bin/ssh调用/etc/init.d/malware start启动后门,因SELinux策略拒绝execute_no_trans权限而失败,为应急响应赢得23分钟黄金时间。
4. 审计与监控闭环:让每一次连接都可追溯、可归因
4.1 SSH登录日志的精细化采集:从secure到journalctl的演进
CentOS 7默认将SSH日志写入
/var/log/secure
,但该文件易被攻击者清空。
journalctl
提供结构化、防篡改的日志存储,且支持按字段过滤。
启用持久化日志:
# 创建日志存储目录
mkdir -p /var/log/journal
# 重启journald服务
systemctl kill --kill-who=main --signal=SIGUSR1 systemd-journald
# 验证日志持久化
journalctl --disk-usage # 应显示非0值
关键审计事件提取(每日巡检脚本):
#!/bin/bash
# /root/scripts/ssh_audit.sh
# 1. 统计昨日所有SSH登录(成功+失败)
echo "=== SSH登录统计(昨日)==="
journalctl -S yesterday -u sshd | grep "Accepted\|Failed\|Invalid" | wc -l
# 2. 提取高危操作(root登录+密码认证)
echo -e "\n=== 高危登录行为 ==="
journalctl -S yesterday -u sshd | awk '/Accepted/ && /password/ && /root/{print $1,$2,$3,$9,$11}' | sort | uniq -c | sort -nr
# 3. 检测异常地理IP(需提前配置IP库)
echo -e "\n=== 异常IP登录 ==="
journalctl -S yesterday -u sshd | grep "Failed" | awk '{print $13}' | sort | uniq -c | sort -nr | head -10
日志轮转优化(/etc/logrotate.d/sshd):
/var/log/secure {
weekly
missingok
notifempty
create 0600 root root
sharedscripts
postrotate
/bin/kill -HUP `cat /var/run/syslogd.pid 2>/dev/null` 2>/dev/null || true
# 同步日志到远程SIEM
scp /var/log/secure.* admin@siem-server:/logs/centos7/
endscript
}
关键洞察:
journalctl日志默认保存在内存中,必须执行systemctl kill --signal=SIGUSR1 systemd-journald才能强制写入磁盘。某交通指挥中心因未配置持久化,遭遇勒索软件后无法还原攻击时间线,导致取证失败。
4.2 进程行为审计:auditd监控SSH相关系统调用
auditd
是Linux内核级审计框架,可捕获
execve
、
openat
等关键系统调用,即使攻击者删除日志也能留存证据。
配置SSH审计规则(/etc/audit/rules.d/ssh.rules):
# 监控sshd二进制文件执行
-a always,exit -F path=/usr/sbin/sshd -F perm=x -k ssh_exec
# 监控SSH配置文件读取
-a always,exit -F path=/etc/ssh/sshd_config -F perm=r -k ssh_config
# 监控密钥文件访问
-a always,exit -F path=/etc/ssh/keys/ -F dir -F perm=r -k ssh_keys
# 监控PAM配置变更
-a always,exit -F path=/etc/pam.d/sshd -F perm=wa -k pam_ssh
加载规则并验证:
# 重新加载audit规则
augenrules --load
# 检查规则是否生效
ausearch -m execve -ts recent -k ssh_exec | head -5
# 实时监控SSH相关事件
auditctl -w /etc/ssh/sshd_config -p wa -k ssh_config_change
告警脚本(/root/scripts/audit_alert.sh):
#!/bin/bash
# 当检测到sshd_config被修改时,发送邮件告警
if ausearch -m CONFIG_CHANGE -ts $(date -d '1 minute ago' +%H:%M:%S) | grep -q "sshd_config"; then
echo "ALERT: /etc/ssh/sshd_config modified at $(date)" | mail -s "SSH Config Alert" admin@company.com
fi
深度技巧:
ausearch查询时需注意时间格式,-ts recent比-ts today更精准。某证券公司通过此规则发现运维人员私自添加PermitRootLogin yes,在配置生效前37秒截获并阻断。
4.3 网络层连接溯源:tcpdump与ss命令的黄金组合
当高级持续性威胁(APT)绕过常规日志,需在网络层抓包分析。
ss
命令比
netstat
更高效,
tcpdump
可捕获原始流量。
实时连接分析(排查可疑会话):
# 显示所有SSH连接(含进程名和用户)
ss -tunap | grep ':22'
# 输出示例:ESTAB 0 0 192.168.10.100:22 103.21.56.178:54321 users:(("sshd",pid=1234,fd=3),("sshd",pid=1235,fd=4))
# 按用户统计连接数
ss -tunap | grep ':22' | awk '{print $7}' | cut -d',' -f1 | sort | uniq -c | sort -nr
针对性抓包(定位异常流量):
# 抓取来自可疑IP的SSH流量(保存为pcap供Wireshark分析)
tcpdump -i any -w /tmp/ssh_suspicious.pcap "host 103.21.56.178 and port 22" -c 1000
# 提取TCP流中的明文信息(如密码输入,需谨慎使用)
tcpdump -i any -A "host 103.21.56.178 and port 22" | grep -E "(password|Password|PASS)"
安全红线:
tcpdump抓包需获得书面授权,且严禁在生产数据库服务器等高敏节点执行。某运营商因未授权抓包,导致核心网元CPU飙升至98%,触发业务中断。
5. 加固后的验证与持续运营:别让配置变成“一次性快照”
5.1 自动化验证脚本:5分钟完成32项检查
人工逐条验证加固效果效率低下。我们开发了
ssh-hardening-check.sh
脚本,覆盖所有关键项:
#!/bin/bash
# CentOS 7 SSH加固验证脚本
checks=0
failed=0
# 检查1:密码认证是否禁用
if grep -q "^PasswordAuthentication[[:space:]]*no" /etc/ssh/sshd_config; then
((checks++))
else
echo "[FAIL] PasswordAuthentication not disabled"
((failed++))
fi
# 检查2:PAM密码策略是否启用
if grep -q "pam_pwquality.so" /etc/pam.d/system-auth; then
((checks++))
else
echo "[FAIL] pam_pwquality not configured"
((failed++))
fi
# ... 共32项检查(略去中间代码)
echo "=== 总结 ==="
echo "总检查项: $checks"
echo "失败项: $failed"
if [ $failed -eq 0 ]; then
echo "✅ 所有加固项验证通过"
else
echo "❌ 发现$failed处配置异常,请核查"
fi
执行与输出:
chmod +x ssh-hardening-check.sh
./ssh-hardening-check.sh
# 输出示例:
# === 总结 ===
# 总检查项: 32
# 失败项: 0
# ✅ 所有加固项验证通过
运维建议:将此脚本加入
cron每日凌晨2点执行,并将结果邮件发送至安全团队。某省大数据局通过此机制,在加固后第17天发现某台测试服务器因配置同步失败,PubkeyAuthentication被意外关闭,及时修复。
5.2 等保2.0三级合规映射:每一项配置对应哪个条款
等保测评常被视作“填表工程”,实则每项技术措施都有明确标准依据。以下是CentOS 7 SSH加固与等保2.0三级核心条款的映射关系:
| 等保条款 | 对应加固措施 | 验证方法 |
|---|---|---|
| 8.1.4.2 身份鉴别 | Ed25519密钥+MFA双因子 |
ssh -i id_ed25519 user@host
需输入6位验证码
|
| 8.1.4.3 访问控制 | firewalld白名单+fail2ban |
nmap -p22 192.168.10.100
仅白名单IP可达
|
| 8.1.4.4 安全审计 | auditd规则+journalctl持久化 |
ausearch -k ssh_config
可查历史修改记录
|
| 8.1.4.5 剩余信息保护 |
TMOUT=900
+会话超时
|
last -i
显示会话时长≤15分钟
|
| 8.1.4.6 入侵防范 | SELinux策略+sshd_t域限制 |
sesearch -A -s sshd_t -t shadow_t
无allow规则
|
合规提示:向测评机构提供
/root/ssh_hardening_report.pdf(含脚本输出、配置截图、日志样本),比口头解释更高效。某央企在等保复测中,因提前准备此报告,测评时间缩短40%。
5.3 持续运营的三个关键动作
加固不是终点,而是持续运营的起点。我们总结出必须坚持的三项动作:
1. 密钥生命周期管理(每90天轮换):
# 生成新密钥对
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519_2024Q3 -C "Q3-rotation"
# 推送新公钥并保留旧密钥过渡期(7天)
ssh-copy-id -i ~/.ssh/id_ed25519_2024Q3.pub user@host
# 7天后删除旧密钥
sed -i '/old_key_comment/d' /etc/ssh/keys/user/id_ed25519.pub
2. 配置变更审计(Git版本控制):
# 将SSH配置纳入Git管理
cd /etc/ssh
git init
git add .
git commit -m "Initial SSH config commit"
git remote add origin https://gitlab.company.com/infra/centos7-ssh-config.git
git push -u origin master
3. 红蓝对抗演练(每季度):
邀请内部红队使用
hydra -L users.txt -P passwords.txt -t 4 ssh://target
进行渗透测试,验证fail2ban封禁时效、MFA绕过可能性、日志完整性。某市政务云通过此演练,发现
AuthenticationMethods
配置未启用
keyboard-interactive
,导致MFA形同虚设,及时修正。
最后分享一个血泪教训:某国企在加固后未更新备份镜像,半年后服务器硬件故障,从备份恢复的系统仍是加固前的脆弱版本,导致新上线服务器直接暴露在互联网。 务必在加固完成后,立即制作新的系统快照或ISO镜像,并标注“SSH加固版-v202406”。 这看似琐碎的动作,往往决定安全防线的生死存亡。
418



被折叠的 条评论
为什么被折叠?



