服务器防火墙怎么配置

结论: 服务器防火墙的正确配置是"双层防御 + 默认拒绝":云安全组做外层粗粒度过滤,系统内用 ufw(Ubuntu/Debian)、firewalld(CentOS/RHEL 系)或 iptables 做细粒度控制,策略一律设为默认拒绝入站、按需放行。同时必须给 SSH 加限速或接入 fail2ban,并在启用前先放行 22 端口防止把自己锁在门外。

引言:防火墙不是"开了就行"

很多服务器的防火墙状态是"装了但等于没装"——要么默认策略是 ACCEPT,几百条放行规则里躺着 3306、6379;要么 ufw 和 firewalld 同时运行,规则互相覆盖;还有一类更隐蔽:开了 ufw,但 Docker 映射的端口照样能被公网访问。

配置防火墙的目标不是"阻止所有流量",而是最小化暴露面:只开放业务必需的端口,只对必要的来源 IP 开放,对管理类端口(SSH、数据库)做限速与源限制。本文给出可直接落地的配置模板。示例系统为 Ubuntu 24.04 LTS 与 Rocky Linux 9(截至 2026 年均为主流 LTS 版本)。

防火墙方案怎么选?

先给结论:先选系统自带的默认工具,不要为了"统一"强行混用。

工具底层适用系统优点缺点
ufwiptables/nftablesUbuntu、Debian语法最简,适合单机复杂规则表达能力弱
firewalldnftables(默认)RHEL、CentOS、Rocky、AlmaLinux支持 zone 与动态重载,无需断连概念偏多
iptablesnetfilter通用控制最精细,脚本化方便规则顺序敏感,易出错
nftablesnftables新发行版语法统一,性能好学习成本较高
云安全组虚拟化层所有云服务器独立于系统,系统被攻破仍生效粒度较粗,无连接状态跟踪

关键原则:只启用其中一种系统防火墙工具。 同时跑 ufw 和 firewalld 会导致规则集互相覆盖,排查起来极其痛苦。检查与清理:

bash
sudo ufw status                      # Status: inactive 才是关闭
sudo systemctl is-active firewalld
sudo iptables -L -n | head -20       # 看现有规则是否被某个工具接管

第一步:先配云安全组(第一道防线)

结论:安全组在虚拟化层过滤,即使系统被攻破、系统防火墙被清空,它依然生效,是最可靠的一道防线。

配置要点:

  • 入站默认拒绝,逐条添加放行规则,而不是"默认全放再补黑名单"。
  • SSH(22)的授权对象填运维出口 IP 段,不要 0.0.0.0/0。
  • Web 端口(80/443)可对全网开放。
  • 数据库、缓存、注册中心端口不要出现在安全组里,走内网或 SSH 隧道。
  • 出方向一般默认全放,但高安全场景应限制出站,防止被控主机外连 C2 服务器。

第二步:ufw 实战(Ubuntu / Debian)

结论:ufw 的策略是"默认拒绝 + 显式放行",加上自带的 limit 语法可以一行搞定 SSH 防暴力破解。

bash
sudo apt install -y ufw

# 1) 默认策略
sudo ufw default deny incoming
sudo ufw default allow outgoing

# 2) 务必先放行 SSH —— 改过端口就填改后的端口
sudo ufw limit 22/tcp comment 'SSH rate limit'   # limit 会在 30 秒内超过 6 次连接时拒绝
# 或者限定来源 IP
sudo ufw allow from 203.0.113.5 to any port 22 proto tcp

# 3) Web 服务
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'

# 4) 需要的话放行自定义业务端口
sudo ufw allow 8080/tcp comment 'app'

# 5) 允许内网网段访问数据库(注意只限内网网卡)
sudo ufw allow in on eth1 from 10.0.0.0/8 to any port 3306 proto tcp

# 6) 启用
sudo ufw enable
sudo ufw status verbose

日常管理命令:

bash
sudo ufw status numbered          # 带编号列出,方便删除
sudo ufw delete 4                 # 按编号删除规则
sudo ufw insert 1 allow from 203.0.113.0/24 to any port 22 proto tcp   # 插到最前
sudo ufw reload
sudo ufw app list                 # 查看预定义的应用配置(OpenSSH、Nginx Full 等)

ufw 的规则文件在 /etc/ufw/user.rules 与 /etc/ufw/user6.rules,备份时直接复制这两个文件即可。

第三步:firewalld 实战(RHEL 系)

结论:firewalld 的核心是 zone(区域):网卡属于哪个 zone 就应用哪套规则。默认网卡多在 public zone,最安全的做法是把管理类服务单独放到受信任来源里。

bash
sudo systemctl enable --now firewalld
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --list-all

# 把默认 zone 设为 public,并只放行必要服务
sudo firewall-cmd --set-default-zone=public
sudo firewall-cmd --permanent --remove-service=dhcpv6-client
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-port=8080/tcp

# 端口转发:把 80 的访问转到本机 8080(需开启 masquerade 才能跨机)
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

用 rich rule 做精细化控制,这是 firewalld 最强大的部分:

bash
# 只允许指定 IP 访问 SSH
sudo firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4" source address="203.0.113.5/32"
  service name="ssh" log prefix="ssh-accept" level="info" accept'

# 对 SSH 做速率限制:每分钟超过 4 次新连接就拒绝
sudo firewall-cmd --permanent --add-rich-rule='
  rule family="ipv4" service name="ssh"
  limit value="4/m" accept'

# 直接封禁某个 IP 段
sudo firewall-cmd --permanent --add-rich-rule='
  rule family="ipv4" source address="198.51.100.0/24" drop'

sudo firewall-cmd --reload

第四步:iptables 实战(通用且最可控)

结论:iptables 的规则按顺序匹配、命中即停,因此放行规则必须写在 DROP 之前。下面是一份可直接使用的生产级脚本,含状态跟踪、SSH 限速、ICMP 限速与丢弃日志。

bash
sudo tee /usr/local/sbin/fw-init.sh <<'EOF'
#!/bin/bash
# 基础防火墙初始化:默认拒绝入站,按需放行
iptables -F
iptables -X
iptables -P INPUT   DROP
iptables -P FORWARD DROP
iptables -P OUTPUT  ACCEPT

# 1) 回环与已建立的连接必须放通
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

# 2) SSH 限速:60 秒内新连接超过 5 次即丢弃(防暴力破解)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --set --name SSHPOOL
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --update --seconds 60 --hitcount 5 --rttl --name SSHPOOL -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# 3) Web 服务
iptables -A INPUT -p tcp --dport 80  -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# 4) 允许被 ping,但限速防 ICMP Flood
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/s -j ACCEPT

# 5) SYN Flood 缓解
iptables -A INPUT -p tcp --syn -m limit --limit 20/s --limit-burst 30 -j ACCEPT

# 6) 丢弃前记录日志(务必限速,否则日志会刷爆磁盘)
iptables -A INPUT -m limit --limit 5/min -j LOG \
  --log-prefix "IPT-DROP: " --log-level 4

iptables -A INPUT -j DROP
EOF

sudo chmod +x /usr/local/sbin/fw-init.sh
sudo /usr/local/sbin/fw-init.sh
sudo iptables -L -n -v --line-numbers

执行前先在云控制台开好 VNC 通道。如果规则有误导致 SSH 断开,可用 VNC 登录后执行 iptables -P INPUT ACCEPT 恢复。

持久化:

bash
# Debian / Ubuntu
sudo apt install -y iptables-persistent
sudo netfilter-persistent save        # 写入 /etc/iptables/rules.v4 与 rules.v6

# 通用手工保存与恢复
sudo iptables-save  | sudo tee /etc/iptables/rules.v4
sudo iptables-restore < /etc/iptables/rules.v4

第五步:用 fail2ban 自动封禁扫描源

结论:防火墙的规则是静态的,fail2ban 是动态的——它通过扫描日志识别失败登录,超过阈值就临时把来源 IP 加入防火墙黑名单,是防暴力破解(Brute Force)的标配。

bash
sudo apt install -y fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

编辑 /etc/fail2ban/jail.local:

ini
[DEFAULT]
bantime  = 3600            # 封禁 1 小时
findtime = 600             # 统计窗口 10 分钟
maxretry = 3               # 失败 3 次即封
ignoreip = 127.0.0.1/8 203.0.113.5     # 白名单,务必加上自己的出口 IP
backend  = systemd

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3
bantime  = 86400           # SSH 封 24 小时

[nginx-http-auth]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log
bash
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd                  # 查看被封 IP 数
sudo fail2ban-client set sshd unbanip 198.51.100.7   # 误封时解封
sudo tail -f /var/log/fail2ban.log