Iptables
基本介绍
netfilter是位于Linux内核空间的防火墙安全框架, 主要功能有网络地址转化NAT、数据包内容修改、数据包过滤
基本概念
链
iptables有prerouting, forward, postrouting, input, output五大链
分别对应着路由前, 转发, 路由后, 进入本机, 从本机出
规则
指定匹配条件, 符合匹配条件则执行规则对应的动作
匹配条件有源地址Source IP, 目标地址Destination IP, 源端口Source Port, 目标端口Destination Port等
| 动作 | 作用 |
|---|---|
| ACCEPT | 允许数据包通过 |
| DROP | 直接丢弃数据包, 不给任何回应信息 |
| REJECT | 拒绝数据包通过 |
| SNAT | 源地址转换, 解决内网上网问题 |
| MASQUEREAD | SNAT的特殊形式, 适用于动态的、临时会变的ip上 |
| DNAT | 目标地址转换 |
| REDIRECT | 在本机做端口映射 |
| LOG | 在/var/log/messages文件中记录日志信息, 然后将数据包传递给下一条规则 |
表
每个链上均有不同规则, 将规则按照功能进行集合就形成了表
iptables默认有filter, nat, mangle, raw四个表
表的优先级为raw > mangle > nat > filter
| 表 | 作用 |
|---|---|
| filter | 负责过滤功能, 防火墙 |
| nat | 网络地址转换功能 |
| mangle | 拆解修改封装报文功能 |
| raw | 关闭nat表上启用的连接追踪机制 |
由于不同链的分工不同, 部分表仅能使用在部分链上(下表CentOS 7?)
| 链 | 表 |
|---|---|
| prerouting | raw, mangle, nat |
| input | mangle, filter |
| forward | mangle, filter |
| output | mangle, nat, mangle |
| postrouting | nat, mangle |
| 表 | 链 |
|---|---|
| raw | prerouting, output |
| mangle | prerouting, input, forward,output, postrouting |
| nat | prerouting, output, postrouting |
| filter | input, forward, output |
基本操作
查
iptables -t TABLE_NAME -nvL CHAIN_NAME --line
# -t 指定table, 若未指定默认使用filter
# -n 不对ip地址进行反解
# -v 查看详细信息
# -x 显示详细计数值
# -L 列出所有规则
# --line 显示序号
## policy 默认策略| pkts | bytes | target | prot | opt | in | out | source | destination |
|---|---|---|---|---|---|---|---|---|
| 匹配报文个数 | 匹配报文大小总和 | 规则对应动作 | 规则对应协议 | 规则对应选项 | 数据包流入接口 | 数据包流出接口 | 规则源地址 | 规则目标地址 |
删
# 清空TABLE_NAME在CHAIN_NAME的所有规则
iptables -t TABLE_NAME -F CHAIN_NAME
# 删除指定num条规则
iptables -t TABLE_NAME -D CHAIN_NAME num
iptables -t TABLE_NAME -D CHAIN_NAME -s IP -j TARGET
# 删除TABLE_NAME在CHAIN_NAME中符合所有条件的规则
# -s 指定源IP
# -j 指定动作增
iptables -t TABLE_NAME -I/A CHAIN_NAME -s IP -j TARGET
# 向TABLE_NAME在CHAIN_NAME中添加一条规则
# -I [num] 将规则插在最前/第num条
# -A 将规则插在最后条
# -s 指定源IP
# -j 指定动作改
iptables -t TABLE_NAME -R CHAIN_NAME num -s IP -j TARGET
# 修改TABLE_NAME在CHAIN_NAME中第num条规则, 注意, 若要修改规则必须将所有参数加上, 否则未指定参数全会被改为默认值
# 修改默认策略
iptables -t TABLE_NAME -P CHAIN_NAME TARGET例1:
root@VM-4-6-ubuntu:~# iptables -t filter -nvL INPUT --line Chain INPUT (policy ACCEPT 3800 packets, 327K bytes) num pkts bytes target prot opt in out source destination 1 400K 66M YJ-FIREWALL-INPUT all -- * * 0.0.0.0/0 0.0.0.0/0 默认策略为ACCEPT, 已接受3800个包, 共计327Kb INPUT里边有一条filter表的规则, 源地址和目的地址均为所有IP地址, 所有协议均可, 数据流入和流出所有接口都可以, 若匹配上述条件则执行YJ-FIREWALL-INPUT动作例2:
iptables -t filter -I INPUT -s 192.168.1.146 -j DROP 在INPUT链中filter表里插入新规则, 对于所有192.168.1.146发来的报文一律执行DROP
保存及恢复配置
iptables-save > filepath
iptables-restore < filepath匹配条件
基础匹配条件
# source 源地址
-s 192.168.134.154 # 指定单个IP
-s 192.168.134.154,192.168.134.155 # 指定多个IP
-s 192.168.134.0/24 # 指定整个网段
! -s 192.168.134.154 # 对匹配条件取反
# destination 目的地址
-d 192.168.134.154 # 指定单个IP
-d 192.168.134.154,192.168.134.155 # 指定多个IP
-d 192.168.134.0/24 # 指定整个网段
! -d 192.168.134.154 # 对匹配条件取反
# prot 报文协议
-p tcp,udp,udplite,icmp,icmpv6,esp,ah,sctp,mh # 指定报文协议(CentOS 7)
# in 网卡接口
-i eth0
# out
-o wlan0
拓展匹配条件
# tcp/udp模块 + multiport模块
## dport destination-port 目标端口 要求指定使用协议
-p tcp -m tcp --dport 22 # -m 指定拓展模块名称 dport和sport均由tcp拓展模块实现
-p tcp -m tcp --dport 22:25 # 连续端口
-p tcp -m multiport --dport 22,25,80 # 离散端口
-p tcp -m multiport --dport 22,25:80 # multiport模块仅能给予tcp,udp使用
## sport source-port 源端口 要求指定使用协议
-p tcp -m tcp --sport 22 # 与上同
# iprange模块 指定一段连续IP地址范围
## src-range 源地址所在范围
-m iprange --src-range 192.168.1.1-192.168.1.5
## dst-range 目的地址所在范围
-m iprange --dst-range 192.168.1.1-192.168.1.5
# string模块 字符串匹配
-m string --algo bm --string "abc" # 使用bm算法匹配 bm/kmp
# time模块 根据时间段匹配报文
-m time --timestart 09:00:00 --timestop 18:00:00 # 早上9点到下午6点
-m time --weekdays 6,7 # 周六周日
-m time ! --weekdays 6 # 可取反
-m time --monthdays 26,27 # 每月26, 27号
-m time ! --monthdays 26 # 可取反
-m time --datestart 2024-09-10 --datestop 2025-09-09 # 起止日期
# connlimit模块 设置并发限制
-m connlimit --commlimit-above 2 # 连接数大于2时匹配
-m connlimit --commlimit-above 2 --connlimit-mask 24 # C类网段连接数大于2时匹配
# limit模块 限制报文速度
-m limit --limit 10/minute # 每分钟最多10个包
-m limit --limit 10/minute --limit-burst num # 每分钟最多10个包, 空闲时最多有num个包通过
## minute second hour day
# tcp-flags拓展匹配条件 对tcp头的标志位进行匹配
-m tcp --tcp-flags SYN,ACK,FIN,RST,URG,PSH SYN # 对SYN,ACK,FIN,RST,URG,PSH标志位进行匹配, 其中SYN必须为1, 其他部分必须为0
-m tcp --tcp-flags ALL SYN # 与上述等价
-m tcp --syn # 与上述等价
# icmp模块 可用sport和dport
-p icmp -m icmp --icmp-type 8/0 # 匹配type为8, code为0的报文, 即ping请求报文
-p icmp -m icmp --icmp-type "echo-request" # 与上述相同
# state模块 实现连接追踪
## 所有报文分为5种状态
## NEW 连接中第一个包
## ESTABLISHED NEW后的包
## RELATED 与其他报文有关系的包
## INVALID 无法识别/无状态
## UNTRACKED 未被追踪
-m state --state RELATED,ESTABLISHED # 对ESTABLISHED和RELATED包进行匹配例:
iptables -I INPUT -s 124.220.94.103 -p tcp -m tcp --dport 22 -j REJECT 对124.220.94.103向本机22口发来的tcp报文进行拒绝, 即拒绝ssh iptables -I INPUT -m iprange --src-range 192.168.134.154-192.168.134.255 -j DROP 对192.168.134.154到192.168.134.255范围内ip发来的请求一律进行拒绝 iptables -I INPUT -m string --algo bm --string "abc" -j REJECT 对明文含有abc的报文进行拒绝 iptables -I OUTPUT -p tcp -m tcp --dport 80 -m time --weekdays 6 -j REJECT 对每周六向本机80端口发来的报文进行拒绝 iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 5 -j REJECT iptables -I INPUT -p tcp --dport 22 -m connlimit ! --connlimit-above 5 -j ACCEPT 每个IP地址最多同时占5个22口的连接(需考虑默认策略) iptables -I INPUT -p icmp -m limit --limit 10/minute -j ACCEPT 期望每分钟最多10个包通过, 但注意令牌桶算法以及默认策略 iptables -I INPUT -p tcp -m tcp --dport 22 --tcp-flags ALL SYN -j REJECT 对tcp报文中的标志位进行匹配, 若SYN位为1, 其他位为0, 则执行拒绝 iptables -I INPUT -p icmp -m icmp --icmp-type 8/0 -j REJECT 拒绝所有type8 code0的icmp报文, 即拒绝别人的ping请求报文 iptables -A INPUT -j REJECT && \ iptables -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 仅通过RELATED包RELATED包, 其他包全部拒绝
黑白名单
- 黑名单, 默认策略为ACCEPT, 对特定报文进行拦截
- 白名单, 默认策略为DROP, 允许特定报文通过
自定义链
iptables -t filter -N CHAIN_NAME # 创建自定义链
iptables -t filter -I INPUT -j CHAIN_NAME # 引用自定义链
iptables -t filter -E CHAIN_NAME NEW_CHAIN_NAME # 修改自定义链名字
iptables -X NEW_CHAIN_NAME # 删除自定义chain, 要求没有被引用且其中无规则例.
iptables -N IN_WEB # 创建名为IN_WEB的自定义链 iptables -I IN_WEB -s 192.168.134.154 -j REJECT # 对192.168.134.154发来的报文一律拒绝 iptables -I INPUT -p tcp --dport 80 -j IN_WEB # 对于发往80端口的tcp报文一律转交IN_WEB
拓展动作
# REJECT
## --reject-with选项 设置提示信息
### 可用参数:
#### icmp-net-unreachable
#### icmp-host-unreachable
#### icmp-port-unreachable 默认
#### icmp-proto-unreachable
#### icmp-net-prohibited
#### icmp-host-pro-hibited
#### icmp-admin-prohibited
-j REJECT --reject-with icmp-host-pro-hibited # 设置提示信息为icmp-host-pro-hibited
# LOG 对符合条件的报文相关信息记录到日志中, 继续交给下面报文处理
# 日志位置在/etc/syslog.conf, 配置kern.warning /var/log/iptables.log
## --log-level选项, 指定日志级别, 有emerg, alert, crit, error, warning, notice, info, debug
## --log-prefix选项, 给相关信息添加标签, 不超过29个字符
-j LOG --log-prefix "test"
# NAT 网络地址转换
# Source Network Address Translation + Destinationnetwork address translation
# SNAT解决内网上网问题, DNAT解决内网穿透问题
-j SNAT --to-source 121.248.201.8 # 将符合条件的报文源地址改为121.248.201.8
-j DNAT --to-destination 192.168.134.154:80 # 将符合条件的报文目的地改为192.168.134.154:80
# MASQUERADE 动态地址转换, 与SNAT功能类似但不用指明明确IP
# REDIRECT 本机端口映射
-j REDIRECT --to-ports 8080 # 将报文重定向到本机8080端口例.
iptables -t nat -A POSTROUTING -s 192.168.6.0/24 -j SNAT --to-source 121.248.201.8 将192.168.6.0/24网段发来的所有报文源地址改为121.248.201.8, iptables会自动维护NAT表, 将响应报文的目标地址转换回来 iptables -t nat -A POSTROUTING -d 121.248.201.8 -p tcp --dport 3389 -j DNAT --to-destination 192.168.6.1:3389 对于发往本机121.248.201.8的3389端口的请求, 将其一律转交给192.168.6.1:3389, 注意SNAT双向通讯 iptables -t nat -IPOSTROUTING -s 192.168.6.0/24 -o eth0 -j MASQUERADE 对于192.168.6.0/24网段发来的报文一律改为eth0的地址转发
详细样例
wlan分析
# 暂时开启网络转发功能
echo 1 > /proc/sys/net/ipv4/ip_forward
sysctl -w net.ipv4.ip_forward=1
# 永久开启网络转发功能
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.conf
iptables -t nat -A POSTROUTING -o !wlan0 -s 192.168.6.0/24 -j MASQUERADE
# 对于源地址为192.168.6.0/24网段, 目标网卡不是wlan0的报文进行动态SNATdocker网络分析
iptables -t nat -nvLPREROUTING
- Chain PREROUTING (policy ACCEPT 963 packets, 29576 bytes)
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 28M | 1065M | DOCKER | all | -- | * | * | 0.0.0.0/0 | 0.0.0.0/0 | ADDRTYPE match dst-type LOCAL |
OUTPUT
- Chain OUTPUT (policy ACCEPT 1021 packets, 65958 bytes)
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | DOCKER | all | -- | * | * | 0.0.0.0/0 | !127.0.0.0/8 | ADDRTYPE match dst-type LOCAL |
POSTROUTING
- Chain POSTROUTING (policy ACCEPT 1212 packets, 77070 bytes)
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 20813 | 1335K | MASQUERADE | all | -- | * | !docker0 | 172.17.0.0/16 | 0.0.0.0/0 | |
| 0 | 0 | MASQUERADE | tcp | -- | * | * | 172.17.0.2 | 172.17.0.2 | tcp dpt:10100 |
| 0 | 0 | MASQUERADE | tcp | -- | * | * | 172.17.0.3 | 172.17.0.3 | tcp dpt:443 |
DOCKER
- Chain DOCKER (2 references)
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | RETURN | all | -- | docker0 | * | 0.0.0.0/0 | 0.0.0.0/0 | |
| 3023 | 158K | DNAT | tcp | -- | !docker0 | * | 0.0.0.0/0 | 0.0.0.0/0 | tcp dpt:10100 to:172.17.0.2:10100 |
| 11602 | 599K | DNAT | tcp | -- | !docker0 | * | 0.0.0.0/0 | 0.0.0.0/0 | tcp dpt:443 to:172.17.0.3:443 |
分析:
首先在PREROUTING, 若数据包的目标地址类型属于本机系统的本地网络地址, 则跳转到DOCKER
在DOCKER中判断, 若是从
docker0网卡流入的, 则直接返回若报文不是从
docker0网卡流入, 且是发给本机的10100端口, 则进行DNAT, 将其ip地址转为172.17.0.2(相当于容器启动时-p 10100:10100)若报文不是从
docker0网卡流入, 且是发给本机的443端口, 则进行DNAT, 将其ip地址转为172.17.0.3(相当于容器启动时-p 443:443)在OUTPUT中, 若数据包的目标地址不是127.0.0.1, 且目标地址类型属于本机系统的本地网络地址, 则跳转DOCKER
在DOCKER中被无条件捕获并返回?为啥要这么来一下
在POSTROUTING中, 若报文ip是172.17.0.0/16网段, 流向的网卡不是
docker0, 则进行动态网络地址转换若报文源ip和目的ip均是172.17.0.2, 且目的端口是10100, 则进行动态网络地址转换
若报文源ip和目的ip均是172.17.0.3, 且目的端口是443, 则进行动态网络地址转换
iptables -t filter -nvLFORWARD
- Chain FORWARD (policy DROP 0 packets, 0 bytes)
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 81258 | 18M | DOCKER-USER | all | -- | * | * | 0.0.0.0/0 | 0.0.0.0/0 | |
| 81258 | 18M | DOCKER-ISOLATION-STAGE-1 | all | -- | * | * | 0.0.0.0/0 | 0.0.0.0/0 | |
| 103M | 27G | ACCEPT | all | -- | * | docker0 | 0.0.0.0/0 | 0.0.0.0/0 | ctstate RELATED,ESTABLISHED |
| 2297K | 135M | DOCKER | all | -- | * | docker0 | 0.0.0.0/0 | 0.0.0.0/0 | |
| 115M | 31G | ACCEPT | all | -- | docker0 | !docker0 | 0.0.0.0/0 | 0.0.0.0/0 | |
| 0 | 0 | ACCEPT | all | -- | docker0 | docker0 | 0.0.0.0/0 | 0.0.0.0/0 |
DOCKER
- Chain DOCKER (2 references)
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 3023 | 158K | ACCEPT | tcp | -- | !docker0 | docker0 | 0.0.0.0/0 | 172.17.0.2 | tcp dpt:10100 |
| 11594 | 598K | ACCEPT | tcp | -- | !docker0 | docker0 | 0.0.0.0/0 | 172.17.0.3 | tcp dpt:443 |
DOCKER-ISOLATION-STAGE-1
- Chain DOCKER-ISOLATION-STAGE-1 (1 references)
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 115M | 31G | DOCKER-ISOLATION-STAGE-2 | all | -- | docker0 | !docker0 | 0.0.0.0/0 | 0.0.0.0/0 | |
| 246M | 61G | RETURN | all | -- | * | * | 0.0.0.0/0 | 0.0.0.0/0 |
DOCKER-ISOLATION-STAGE-2
- Chain DOCKER-ISOLATION-STAGE-2 (2 references)
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | DROP | all | -- | * | docker0 | 0.0.0.0/0 | 0.0.0.0/0 | |
| 126M | 32G | RETURN | all | -- | * | * | 0.0.0.0/0 | 0.0.0.0/0 |
DOCKER-USER
| pkts | bytes | target | prot | opt | in | out | source | destination | |
|---|---|---|---|---|---|---|---|---|---|
| 247M | 61G | RETURN | all | -- | * | * | 0.0.0.0/0 | 0.0.0.0/0 |
分析:
首先报文进入FORWARD, 被FORWARD 1无条件捕获跳转DOCKER-USER(猜测可能是docker留给用户修改的)
随后从DOCKER-USER返回, 被FORWARD 2无条件捕获跳转DOCKER-ISOLATION-STAGE-1
若报文是由
docker0网卡流入, 但目的地不是docker0网卡, 则被DOCKER-ISOLATION-STAGE-1 1捕获, 前往DOCKER-ISOLATION-STAGE-2, 否则返回若报文目的地是
docker0网卡, 则直接丢弃(与上文矛盾, 所以没有任何匹配), 否则返回回到FORWARD, 所有发给
docker0网卡的RELATED和ESTABLISHED类型报文全部接受继续FORWARD, 所有发给
docker0网卡的报文捕获跳转DOCKER如果该报文是由非
docker0网卡发往docker0网卡, 目的地为172.17.0.2, 目标端口是10100, 则进行接受如果该报文是由非
docker0网卡发往docker0网卡, 目的地为172.17.0.3, 目标端口是443, 则进行接受返回FORWARD, 若报文是从
docker0网卡发往非docker0网卡, 则直接接收若报文是从
docker0网卡发往docker0网卡, 则直接接收
有点云里雾里)

