第10章 安全加固实战
第10章 安全加固实战
10.1 SSH 安全加固清单
| 优先级 | 措施 | 影响 |
|---|---|---|
| 🔴 必须 | 禁用密码认证 | 极高 |
| 🔴 必须 | 禁止 root 密码登录 | 高 |
| 🔴 必须 | 使用强加密算法 | 高 |
| 🟡 强烈推荐 | 安装 Fail2Ban | 高 |
| 🟡 强烈推荐 | 限制用户/组访问 | 中 |
| 🟢 推荐 | 更改默认端口 | 低 |
| 🟢 推荐 | 端口敲门 | 中 |
| 🟢 推荐 | 使用 SSH 证书 | 高 |
10.2 禁用密码认证
前提条件
在禁用密码认证之前,必须确保:
- ✅ 已为所有管理员部署 SSH 公钥
- ✅ 已测试密钥认证可以正常登录
- ✅ 有带外访问方式(控制台/VNC)以防万一
配置步骤
# 1. 验证密钥认证可用
ssh -o PreferredAuthentications=publickey -i ~/.ssh/id_ed25519 admin@server "echo OK"
# 确认输出 OK
# 2. 修改配置
# /etc/ssh/sshd_config
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
# 3. 检查语法
sudo sshd -t
# 4. 重新加载(不要重启,保持当前连接)
sudo systemctl reload sshd
# 5. 在**新的终端**测试密钥登录
ssh -i ~/.ssh/id_ed25519 admin@server
⚠️ 警告: 切勿在断开当前 SSH 会话前修改密码认证设置。始终保持一个活动的 SSH 连接作为备用。
10.3 Fail2Ban 防暴力破解
安装
# Debian/Ubuntu
sudo apt install fail2ban
# RHEL/CentOS
sudo yum install epel-release
sudo yum install fail2ban
基本配置
# 创建本地配置文件(不要直接修改 jail.conf)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# 或创建更简洁的本地配置
sudo vi /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local
[DEFAULT]
# 忽略的 IP(白名单)
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24
# 封禁时间(秒)
bantime = 3600
# 查找时间窗口(秒)
findtime = 600
# 最大失败次数
maxretry = 5
# 封禁动作
banaction = iptables-multiport
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 7200
findtime = 300
使用 systemd 日志(推荐)
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = systemd
maxretry = 3
bantime = 7200
findtime = 300
高级配置
# /etc/fail2ban/jail.local
[DEFAULT]
# 使用 firewalld(RHEL/CentOS)
# banaction = firewallcmd-ipset
# 使用 nftables
# banaction = nftables-multiport
# 发送邮件通知
destemail = [email protected]
sender = [email protected]
mta = sendmail
action = %(action_mwl)s
# 多次封禁递增时间
# 第一次:1小时,第二次:1天,第三次:1周
bantime.increment = true
bantime.factor = 24
bantime.maxtime = 604800
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 3
bantime = 3600
# 针对特定攻击模式
[sshd-ddos]
enabled = true
port = ssh
filter = sshd-ddos
logpath = %(sshd_log)s
maxretry = 10
bantime = 3600
# 保护其他服务
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
Fail2Ban 管理命令
# 启动/停止/重启
sudo systemctl start fail2ban
sudo systemctl stop fail2ban
sudo systemctl restart fail2ban
sudo systemctl status fail2ban
# 查看状态
sudo fail2ban-client status
sudo fail2ban-client status sshd
# 手动封禁 IP
sudo fail2ban-client set sshd banip 192.168.1.100
# 手动解封 IP
sudo fail2ban-client set sshd unbanip 192.168.1.100
# 查看封禁日志
sudo journalctl -u fail2ban | grep Ban
# 查看所有被封禁的 IP
sudo fail2ban-client banned
自定义过滤规则
# /etc/fail2ban/filter.d/custom-sshd.conf
[Definition]
failregex = ^.*Failed password for .* from <HOST>.*$
^.*Failed keyboard-interactive for .* from <HOST>.*$
^.*Invalid user .* from <HOST>.*$
^.*Connection closed by authenticating user .* <HOST>.*\[preauth\]$
ignoreregex =
10.4 更改默认端口
# /etc/ssh/sshd_config
Port 2222
# 更新 SELinux(RHEL/CentOS)
sudo semanage port -a -t ssh_port_t -p tcp 2222
# 更新防火墙
# UFW
sudo ufw allow 2222/tcp
sudo ufw delete allow ssh
# firewalld
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload
注意: 更改端口只是减少噪音日志,不能替代真正的安全措施。自动化扫描器会扫描所有端口。
10.5 端口敲门(Port Knocking)
什么是端口敲门?
端口敲门是一种通过按特定顺序访问关闭的端口来临时打开 SSH 端口的技术。
步骤 1: 访问 7000 端口
步骤 2: 访问 8000 端口
步骤 3: 访护 9000 端口
→ SSH 端口 22 自动打开
→ 连接完成后自动关闭
使用 knockd 实现
# 安装 knockd
sudo apt install knockd
# 编辑配置
sudo vi /etc/knockd.conf
# /etc/knockd.conf
[options]
logfile = /var/log/knockd.log
UseSyslog
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 10
command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 10
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
# 启动 knockd
sudo systemctl enable knockd
sudo systemctl start knockd
# 默认接口配置
# /etc/default/knockd
START_KNOCKD=1
KNOCKD_OPTS="-i eth0"
客户端使用
# 安装 knock 客户端
sudo apt install knockd
# 敲门(打开 SSH)
knock server-ip 7000 8000 9000
# 等待 1 秒
sleep 1
# 连接 SSH
ssh user@server-ip
# 敲门(关闭 SSH)
knock server-ip 9000 8000 7000
一键脚本
#!/bin/bash
# knock-ssh.sh
SERVER=$1
USER=$2
SEQUENCE="7000 8000 9000"
if [ -z "$SERVER" ] || [ -z "$USER" ]; then
echo "Usage: $0 <server> <user>"
exit 1
fi
echo "Knocking on $SERVER..."
knock $SERVER $SEQUENCE
sleep 2
echo "Connecting..."
ssh $USER@$SERVER
echo "Closing port..."
knock $SERVER 9000 8000 7000
使用 iptables 实现(不依赖 knockd)
# 使用 iptables recent 模块
# 只允许在"敲门"后连接
iptables -A INPUT -p tcp --dport 7000 -m recent --set --name KNOCK1
iptables -A INPUT -p tcp --dport 8000 -m recent --set --name KNOCK2 -m recent --rcheck --name KNOCK1
iptables -A INPUT -p tcp --dport 9000 -m recent --set --name KNOCK3 -m recent --rcheck --name KNOCK2
iptables -A INPUT -p tcp --dport 22 -m recent --rcheck --seconds 30 --name KNOCK3 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
10.6 iptables / nftables 防火墙规则
iptables 限制 SSH 访问
# 只允许特定 IP 访问 SSH
sudo iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j DROP
# 限制连接速率(每分钟最多 3 个新连接)
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 使用 hashlimit(更灵活)
sudo iptables -A INPUT -p tcp --dport 22 -m hashlimit --hashlimit-above 3/min --hashlimit-burst 3 --hashlimit-mode srcip --hashlimit-name ssh -j DROP
nftables 配置
#!/usr/sbin/nft -f
# /etc/nftables.conf
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# 允许已建立的连接
ct state established,related accept
# 允许本地回环
iif lo accept
# SSH 速率限制
tcp dport 22 ct state new limit rate 3/minute accept
tcp dport 22 ct state new drop
# 其他允许的服务
tcp dport { 80, 443 } accept
icmp type echo-request accept
}
}
10.7 AllowUsers / AllowGroups
# /etc/ssh/sshd_config
# 只允许特定用户
AllowUsers admin deploy monitoring
# 只允许特定组
AllowGroups ssh-users admins
# 组合条件
AllowUsers [email protected].* [email protected].*
# 禁止特定用户
DenyUsers guest test
DenyGroups visitors
10.8 安全审计与监控
实时监控 SSH 登录
# 实时查看认证日志
sudo tail -f /var/log/auth.log | grep ssh
# 查看失败的登录尝试
sudo grep "Failed password" /var/log/auth.log | tail -20
# 统计失败次数(按 IP)
sudo grep "Failed password" /var/log/auth.log | \
awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -20
# 查看成功的登录
sudo grep "Accepted" /var/log/auth.log | tail -20
# 查看当前登录用户
who
w
last
设置登录通知
# /etc/ssh/sshrc
# 每次 SSH 登录时发送通知
IP=$(echo $SSH_CONNECTION | awk '{print $1}')
HOSTNAME=$(hostname)
USER=$(whoami)
DATE=$(date '+%Y-%m-%d %H:%M:%S')
# 发送邮件通知
echo "SSH login from $IP to $HOSTNAME as $USER at $DATE" | \
mail -s "SSH Login Alert: $HOSTNAME" [email protected]
# 或发送到 Slack/Telegram
# curl -X POST -H 'Content-type: application/json' \
# --data '{"text":"SSH login from '$IP' to '$HOSTNAME' as '$USER'"}' \
# https://hooks.slack.com/services/xxx/yyy/zzz
使用 wtmp/lastlog 审计
# 查看登录历史
last -i | head -30
# 查看失败登录
lastb | head -30
# 查看最后一次登录
lastlog | grep -v "Never"
# 使用 ausearch 查看审计
sudo ausearch -m USER_LOGIN --start today
10.9 安全加固脚本
#!/bin/bash
# ssh-hardening.sh - SSH 安全加固脚本
set -e
SSHD_CONFIG="/etc/ssh/sshd_config"
BACKUP="/etc/ssh/sshd_config.backup.$(date +%Y%m%d%H%M%S)"
echo "=== SSH 安全加固 ==="
# 备份原始配置
cp "$SSHD_CONFIG" "$BACKUP"
echo "✅ 已备份到 $BACKUP"
# 加固配置
cat >> "$SSHD_CONFIG" << 'EOF'
# ===== 安全加固 (ssh-hardening.sh) =====
Protocol 2
MaxAuthTries 3
LoginGraceTime 60
PermitEmptyPasswords no
X11Forwarding no
AllowTcpForwarding no
PermitTunnel no
GatewayPorts no
PermitUserEnvironment no
UseDNS no
GSSAPIAuthentication no
ClientAliveInterval 300
ClientAliveCountMax 2
MaxStartups 10:30:60
PrintMotd no
PrintLastLog yes
EOF
echo "✅ 已应用加固配置"
# 检查语法
if sshd -t; then
echo "✅ 配置语法正确"
else
echo "❌ 配置语法错误,正在恢复..."
cp "$BACKUP" "$SSHD_CONFIG"
exit 1
fi
# 重新加载
systemctl reload sshd
echo "✅ SSH 已重新加载"
echo ""
echo "⚠️ 注意:请确认密钥认证可用后再禁用密码认证"
echo " 运行以下命令禁用密码认证:"
echo " sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' $SSHD_CONFIG"
echo " sudo systemctl reload sshd"
扩展阅读
下一章: 第11章 跳板机与堡垒机 → 学习 ProxyJump、ProxyCommand 和堡垒机架构。