# 防火墙

# iptables

  • RHEL 7 之前默认使用 iptables 工具来管理防火墙。
  • iptables 会将用户配置的规则传给内核的 netfilter 网络过滤器处理。
    • netfilter 会从上到下读取用户配置的规则,一旦找到匹配的规则就立即执行,没有找到则执行默认规则。
  • iptables 默认定义了四个规则表:
    • raw
    • mangle
    • nat :用于端口转发、NAT 地址转换。
    • filter :用于定义流量出入规则,过滤数据包。
      • 每个规则表包含多个规则链。
      • 网络传输的数据包会按 raw > mangle > nat > filter 的优先级依次被四个规则表处理。
  • 每类规则称为一个规则链。常见的几类规则:
    • INPUT :处理接收的数据包。
    • OUTPUT :处理发出的数据包。
    • FORWARD :处理路由转发的数据包。
    • PREROUTING :在选择路由之前处理数据包,比如修改目标 IP 。
    • POSTROUTING :在选择路由之后处理数据包,比如修改源 IP 。
  • 常见的几种动作(target):
    • ACCEPT
      • :允许数据包通过。
    • REJECT
      • :拒绝数据包通过,并回复一条拒绝信息给发送方。
      • 不能用作默认动作。
    • DROP
      • :丢弃数据包,并且不作出回复。这可能导致发送方误以为网络不通。
    • LOG
      • :记录日志。
      • 日志规则应该放在其它规则之前。因为执行日志规则之后还会执行后续规则,但执行其它动作之后就会结束规则链。
    • DNAT
      • :修改数据包的目标 IP、PORT 。
      • 只能用于 nat 表的 OUTPUT 或 PREROUTING 链。
    • SNAT
      • :修改数据包的源 IP、PORT 。
      • 只能用于 nat 表的 INPUT 或 POSTROUTING 链。
    • REDIRECT :将数据包重定向到本机的某个端口。
      • 与 DNAT 动作类似,会修改数据包的目标地址,但还会记录旧的目标地址,便于实现代理转发。
      • 另外,mangle 表的 TPROXY 动作可将数据包重定向到本机某个 Socket ,且不修改数据包的目标地址。

# 安装

yum install iptables-services
systemctl start iptables
systemctl enable iptables

# 命令

$ iptables
          -L [chain]          # 显示所有规则,或只显示某个规则链
            -n                # 将服务名显示成具体的端口号
            -v                # 显示详细的信息
            --line-numbers    # 显示行号
          -I <chain> [n]      # 编辑某个规则链,插入一条新规则到第 n 行,默认是第一行
          -A <chain>          # 编辑某个规则链,添加一条新规则到最后一行
          -D <chain> <n>      # 删除规则链的第 n 行
          -F [chain]          # 清空某个规则链,不指定 chain 则清空所有规则链
          -P <chain> <target> # 设置某个规则链的默认动作

          -t filter           # 指定表,默认指定 filter 表
          -p <protocal>       # 指定协议
          -s <ip>             # 指定源地址
          -d <ip>             # 指定目的地址
          --sport <n>         # 指定源端口
          --dport <n>         # 指定目的端口
          -j <target>         # 指定动作
          -i eth0             # input ,指定接收流量的网口
          -o eth0             # ouput ,指定发出流量的网口
  • 例:给 filter 表添加规则

    iptables -D INPUT                                                 # 删除 INPUT 规则链
    iptables -P INPUT DROP                                            # 设置 INPUT 规则链的默认动作为 DROP
    iptables -I INPUT -p icmp -j REJECT                               # 在 INPUT 规则链的开头插入一条新规则,拒绝接收 icmp 数据包
    iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT  # 允许接收本机发起的连接的响应
    iptables -A INPUT -p tcp --dport 80 -j ACCEPT                     # 允许所有 IP 地址发送 tcp 数据包到 80 端口
    iptables -I INPUT -p tcp -s 192.168.10.0/24 --dport 22 -j ACCEPT  # 允许指定 IP 地址网段发送 tcp 数据包到 22 端口
    
    • 可以重复添加同样的规则。
    • 指定多个端口的语法:
      --dport 80:90                     # 指定一组连续的端口
      --match multiport --dport 80,90   # 指定多个端口,用逗号分隔
      
  • 例:查看详细的 INPUT 规则

    [root@CentOS ~]# iptables -nvL  INPUT  --line-numbers
    Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
    num   pkts bytes target     prot opt in     out     source               destination
    1     2819  446K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80
    2    50557  383M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    3        0     0 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0
    4       53  4960 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate INVALID
    5      484 26644 REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-host-prohibited
    

    各字段含义:

    pkts          # 该规则已匹配的数据包数
    bytes         # 该规则已匹配的数据包总大小
    target        # 该规则采取的动作
    prot          # 该规则针对的协议
    opt           # 该规则的选项
    in            # 该规则针对从哪个网口接收的数据包
    out           # 该规则针对向哪个网口发出的数据包
    source        # 该规则针对哪个源地址的数据包
    destination   # 该规则针对哪个目的地址的数据包
    
  • 例:给 nat 表添加规则

    iptables -t nat -A OUTPUT -p tcp -d 192.168.1.0/24 --dport 80 -j REDIRECT --to-ports 80             # 采用 REDIRECT 动作,将发向指定目标 IP、PORT 的流量,重定向到本机的 80 端口
    iptables -t nat -A OUTPUT -p tcp -d 192.168.1.0/24 --dport 80 -j DNAT --to-destination 10.0.0.1:80  # 采用 DNAT 动作
    iptables -t nat -A POSTROUTING -p tcp -s 10.0.0.1 -j SNAT --to-source 1.1.1.1                       # 采用 SNAT 动作
    
  • 例:记录日志

    iptables -t nat -I OUTPUT -d 192.168.21.0/24 -j LOG --log-level info --log-prefix '<iptables log>'
    tail -f /var/log/messages | grep '<iptables log>'
    
  • 执行 service iptables save 会将 iptables 的当前配置保存到 /etc/sysconfig/iptables 文件中,每次系统重启时会自动读取。

    • 可以手动导入、导出 iptables 的配置:
      iptables-save > iptables.conf       # 导出到一个文件
      iptables-restore < iptables.conf    # 从文件中导入
      

# firewalld

  • RHEL 7 开始默认使用 firewalld 工具来管理防火墙。
    • iptables 和 firewalld 两个防火墙可以同时起作用。但同时使用容易搞混规则,建议将一个防火墙关闭或默认允许所有流量,只使用另一个防火墙。
    • 重启 firewalld 时会自动重置 iptables 规则。
    • firewalld 从 0.6.0 版本开始,将默认后端从 iptables 改为 nftables 。
  • firewalld 引入了区域(zone)的设定,用户可以在各个 zone 中分别配置防火墙,然后通过切换 zone 来快速切换防火墙。常用的 zone 如下:
    • trusted :允许接收所有数据包。
    • home 、internal :只允许接收 ssh mdns samba-client dhcpv6-client 服务的数据包。
    • work 、public :只允许接收 ssh、dhcpv6-client 服务的数据包。
    • external 、dmz :只允许接收 ssh 服务的数据包。
    • block 、drop :不接收外部发来的数据包。
    • 所有的 zone 默认都允许发出数据包、接收回复本机的数据包。
  • firewalld 各个区域的配置文件以 XML 文件的形式保存在 /etc/firewalld/zones/ 目录下。

# 安装

yum install firewalld
systemctl start firewalld
systemctl enable firewalld

# 命令

$ firewall-cmd
              --get-zones                 # 显示所有区域的名字
              --get-active-zones          # 显示当前激活的区域
              --get-default-zone          # 显示默认区域的名称
              --set-default-zone=public   # 设置默认区域

              --get-zone-of-interface=eth0            # 显示指定网口采用的区域
              --change-interface=eth0 --zone=public   # 修改指定网口采用的区域

              [--zone=public]                         # 选择指定区域进行配置,不声明则是选择默认区域
                            --list-all                # 显示区域的信息
                            --add-service=http        # 允许接收 http 服务(即 TCP 80 端口)的数据包
                            --remove-service=http     # 拒绝接收
                            --add-port=80/tcp         # 允许接收发送到 TCP 80 端口的数据包
                            --add-port=80-82/tcp      # 开放一组端口
                            --remove-port=80/tcp      # 拒绝接收

              --config_something... --permanent       # 将配置写入配置文件(从而会永久保存,但是需要重启或 reload 才会生效)
              --runtime-to-permanent                  # 将运行时的所有配置写入配置文件
              --reload                                # 重新加载 firewall 的配置文件(这会覆盖运行时的配置)
              --complete-reload                       # 完全重新加载(这会丢失所有运行时的配置)

              --panic-on                              # 开启 panic 模式,拒绝接收所有数据包
              --panic-off                             # 关闭 panic 模式
              --query-panic                           # 显示是否开启了 panic 模式
  • 使用 --add-service 的效果与 --add-port 一样,只是标明了开放的端口是被哪个服务使用。

  • 指定 IP 地址时,可以加上子网掩码,比如"10.0.0.1/32"。

  • 例:

    [root@CentOS ~]# firewall-cmd --list-all
    public (active)
      target: default                                   # 处理数据包的默认动作(比如 ACCEPT、DROP)
      icmp-block-inversion: no
      interfaces: eth0                                  # 应用的网口
      sources:
      services: ssh dhcpv6-client                       # 允许接收哪些服务的数据包
      ports: 3000/tcp
      protocols:
      masquerade: yes
      forward-ports: port=80:proto=tcp:toaddr=10.0.0.2  # 端口转发的规则
      source-ports:
      icmp-blocks:
      rich rules:
    
  • 通过 rich-rule 可以配置更丰富的防火墙规则,如下:

    firewall-cmd [--zone=public]
                                --add-rich-rule='rule family=ipv4 port port=22 protocol=tcp accept'
                                --add-rich-rule='rule family=ipv4 source address=10.0.0.2 port port=80 protocol=tcp accept'
                                --remove-rich-rule='...'
    

# 端口转发

  • 防火墙能够将本机网口某个端口收到的数据包转发到另一个端口,甚至是其它网口的端口。
  • 防火墙的端口转发在内核态工作,比 Socket 监听端口的优先级更高。

命令:

firewall-cmd [--zone=public]
                            --add-forward-port=port=80:proto=tcp:toport=8080                # 将本机 TCP 80 端口收到的数据包转发到 8080 端口
                            --add-forward-port=port=80:proto=tcp:toaddr=10.0.0.2            # 将 TCP 80 端口收到的数据包转发到另一个主机的相同端口
                            --add-forward-port=port=80:proto=tcp:toport=80:toaddr=10.0.0.2  # 将 TCP 80 端口收到的数据包转发到另一个主机的 80 端口
                            --remove-forward-port=...                                       # 取消端口转发

                            --add-masquerade     # 开启伪装
                            --remove-masquerade  # 取消伪装

                            --add-rich-rule="rule family=ipv4 source address=10.0.0.3 forward-port port=80 protocol=tcp to-port=80 to-addr=10.0.0.2"  # 允许接收数据包,并进行端口转发
  • 端口转发时不需要本机有进程监听该端口。
  • 只能转发外部发给本机的数据包,不能转发本机内部发出的数据包。不如 iptables 的 NAT 功能。
  • 跨网口转发数据包时,需要以下条件:
    • 启用内核参数:
      echo 1 > /proc/sys/net/ipv4/ip_forward
      
    • 开启 masquerade 伪装功能,在转发数据包时保留源 IP ,否则会使用本机 IP 作为源 IP 。该功能与 SNAT 相似。

# 相关概念

  • 有状态防火墙(stateful firewall)
    • :能记录数据包的上下文信息,据此过滤数据包。
    • 其它类型的防火墙称为无状态,只能根据源地址、目标地址等信息过滤数据包。
    • 例:
      • Linux 的 iptables、firewalld 都属于有状态防火墙。
      • TCP 握手成功之后,就会将该 Socket 添加到状态表中。即使有状态防火墙关闭相应的端口,也不会中断该 Socket 连接,可以继续通信。
      • UDP 是无连接的,因此有状态防火墙关闭端口之后,就不能继续通信。