命令格式:iptables [选项] [参数]
常用选项:-L
: list的缩写,list我们通常翻译成列表,意思是列出每条链上的规则,因为多条规则就是一个列表,所以用-L
来表示。-L
后面还可以跟上5条链(POSTROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING)的其中一条链名,注意链名必须全大写,如查看“INPUT链”上的规则:
1 | iptables -L INPUT |
1 | ExplainChain INPUT (policy ACCEPT) |
不指定的话就是默认查看所有链上的规则列表:
1 | iptables -L |
1 | ExplainChain INPUT (policy ACCEPT) |
iptables -N July_filter
,然后在INPUT链上添加一条规则,让它跳转到刚刚的新链-A INPUT -p tcp -j July_filter
,再用iptables -L
查看,可以看到target此时已经是真正的“target(July_filter)”而不再是动作了:1 | ExplainChain INPUT (policy ACCEPT) |
-t
:前面-L
不是列出所有链的规则列表吗?为什么没有PREROUTING和POSTROUTING链呢?因为有默认参数-t
,t是table的缩写,意思是指定显示哪张“表”中的规则(前面说过iptables有四种表),iptables -L
其实就相当于iptables -t filter -L
,即相当于你查看的是“filter”表中的规则。而根据前面的讲解,filter表只可用于INPUT、FORWARD、OUTPUT三条链中,这就是为什么iptables -L
不显示PREROUTING链和POSTROUTING链的原因。
-n
: numeric的缩写,numeric意思是数字的,数值的,意思是指定源和目标地址、端口什么的都以数字/数值的方式显示,否则默认会以域名/主机名/程序名等显示,该选项一般与-L
合用,因为单独的-n
是没有用的(没有-L
列表都不显示,所以用-n
就没有意义了)。
1 | ExplainChain INPUT (policy ACCEPT) |
-v
: 基本上有点Linux常识的童鞋就应该知道,-v
在Linux命令里,一般都是指“verbose”,这个词的意思是是“冗余的,啰嗦的”,即输出更加详细的信息,在iptables这里也是这个意思,一般可以跟-L
连用:
1 | iptables -v -L |
1 | ExplainChain INPUT (policy ACCEPT 0 packets, 0 bytes) |
可以看到多了四列:
当然还可以跟前面的-n
合用:
1 | iptables -n -v -L |
这样source和destination中用域名或者字符串表示的方式就换成ip了:
1 | ExplainChain INPUT (policy ACCEPT 0 packets, 0 bytes) |
稍微了解Linux命令的童鞋应该都知道,在很多情况下,Linux的选项是可以合并的,比如前面的iptables -n -v -L
其实是可以合并成iptables -nvL
的,并且参数顺序一般情况下是无关紧要的,比如iptables -vnL
也是一样的。但是-L
一定要写在最后,原因是-L
是要接收参数的选项(虽然可以不传参数),而-v
和-n
是不需要接收参数的,假如你写成iptables -Lvn
,那就表示是用-n
来接收参数了,这肯定是不行的。
-x
: 加了-v
后,Policy那里变成了“(policy ACCEPT 0 packets, 0 bytes)”,即多了过滤的数据包数量和字节数,其中的字节数,如果数据大了之后,会自动转换单位,比如够KB不够MB,它会显示“xxxk”,够了MB它显示“MB”,但单位转换之后,就不完全精确了,因为它没有小数,如果还是想要看以“字母”即“bytes”为单位查看的话,加个-x
就行了,“x”来自于“exact”,意思是“精确的;准确的”,不取首字母应该是太多选项首字母是e了。
--line-numbers
: 如果你想列表有序号,可以加上该选项:
1 | iptables -nvL --line-numbers |
结果中多了一列“num”,就是序号:
1 | ExplainChain INPUT (policy ACCEPT 0 packets, 0 bytes) |
其实,--line-numbers
并不用写全,不然也太长了,其实写成--line
就行了,甚至你不写最后一个e,即写成--lin
都行。
合并使用各选项的命令示例:
1 | iptables --line -t filter -nvxL INPUT |
我们可以向某条链中的某个表的最前面添加记录(我们叫“插入”,会用到-I
选项,I表示Input),也可以向某条链中的某个表的最后面添加记录(我们叫“追加”,会用到-A
选项,A表示Append),熟悉vi/vim
的童鞋会对这个“I”和“A”感觉到熟悉,因为在vi/vim
的命令模式下,按I
是在光标所行的行首插入,按A
是在光标所在行的行尾插入,跟这个在表头跟表尾插入非常像。
之所以有向前添加和向后添加,是因为如果前面规则的是丢弃或拒绝,那么后面的规则是不会起作用的;而如果前面的是接受后面的是丢弃或拒绝,则接受之后后面的丢弃或拒绝也是不会生效的。
向INPUT链的filter表中添加一条规则:
1 | iptables -t filter -I INPUT -s 10.37.129.2 -j DROP |
-t
: 是指定插入到哪个表中,不写的话默认为“filter”表;-I
: 指定插入到哪条链中,并且会在该链指定表(在这里是filter表)中的最前面插入(I:Input),如果用-A
则是在最后插入(A:Append)。-s
: 匹配源ip,s: source,源。-j
: jump,跳转的意思,后面可指定跳转的target(目标),比如自定义的链,当然更多的是跳转到“action(动作)”中,比如ACCEPT、DROP、REJECT等等。-I INPUT
)的“filter”表(-t filter
)的最前面(-I
)添加一条记录,这条记录会匹配源地址为“10.39.129.2”的请求(-s 10.39.129.2
),并把该请求丢弃掉(-j DROP
)。1、根据编号删除:
前面说过,查询iptables规则列表时,添加--line-numbers
简写成--line
即可显示记录编号,我们现在就可以根据这个编号来删除了:
1 | iptables -t filter -D INPUT 2 |
-t filter
指定操作的表为filter表,-D
表示delete,后面跟的两个参数,第一个是链名,第二个是要删除的规则的编号。
2、根据条件删除:
1 | iptables -t filter -D INPUT -s 10.37.129.2 -j DROP |
删除INPUT链中的filter表中源地址为“10.37.129.2”并且动作为“DROP”的规则。
3、清空:-F
: flush的缩写,flush是“冲洗、冲掉”的意思,在这里是清空的意思,iptables -t filter -F INPUT
代表清空“INPUT”链中“filter”表中的所有规则,如果不指定链不指定表,即直接用iptables -F
,则清空所有链中所有表的规则。
事实上用“替换”来描述会更好一点,因为所谓的修改其实就是把整个规则替换成新的规则:
1 | iptables -t filter -R INPUT 1 -s 10.37.129.3 -j ACCEPT |
其中的-R
就是replace,即替换的意思,整句命令意思是从INPUT链中的filter表中替换编号为1的规则,编号1后面的-s 10.37.129.3 -j ACCEPT
就是要替换成的新规则。
修改策略(policy):
1 | iptables -P FORWARD DROP |
-P
: policy,即策略。整个意思是把FORWARD链的默认规则设置为DROP,需要注意的是,策略对整个链起作用,不会在同一条链中对两个不同的表起作用,虽然man iptables
中有iptables [-t table] -P chain target
这个说明,但我认为这是错的,不信你可以执行以下两条命令试试,不管先执行哪行,后面执行的总会替换前面执行的(即使它们指定的表不一样),这就说明指定表跟不指定表是没区别的,因为默认规则是作用于整条链的,无法单独对表作用:
1 | iptables -t raw -P OUTPUT ACCEPT |
注:这个结论还有待证明,我目前认为是这样。
对于CentOS6:
1 | service iptables save |
保存时它会输出:
1 | iptables: Saving firewall rules to /etc/sysconfig/iptables:[ OK ] |
是的,它是保存到/etc/sysconfig/iptables
文件中的。
另一种方式保存方式:iptables-save
命令能把需要保存规则数据输出到控制台,由前面可知,保存iptables规则其实是保存到/etc/sysconfig/iptables
文件中,所以我们只要把iptables-save
输出的内容重定向输入到/etc/sysconfig/iptables
文件中即可:
1 | iptables-save > /etc/sysconfig/iptables |
同理,还有另一个命令用于重载配置(即以下操作相当于service iptables reload
):
1 | iptables-restore < /etc/sysconfig/iptables |
对于CentOS7:
前面说过CentOS7需要自己安装iptables-services
:
1 | yum install -y iptables-services |
安装后,就可以跟CentOS6一样保存了:
1 | service iptables save |
但要注意的是,这个save必须用service的方式,不能用systemctl,也就是不能这样:
1 | systemctl save iptables |
在CentOS7中start/status/stop/restart/reload都可以换成systemctl的方式,但唯独save不能换成systemctl的方式。
另外,iptables-save
和iptables-restore
也跟CentOS6一样可以使用。
-d
:destination,用于匹配报文的目标地址,可以同时指定多个ip(逗号隔开,逗号两侧都不允许有空格),也可指定ip段:
1 | iptables -t filter -I OUTPUT -d 192.168.1.111,192.168.1.118 -j DROP |
-p
:用于匹配报文的协议类型,可以匹配的协议类型tcp、udp、udplite、icmp、esp、ah、sctp等(centos7中还支持icmpv6、mh):
1 | iptables -t filter -I INPUT -p tcp -s 192.168.1.146 -j ACCEPT |
-i
:用于匹配报文是从哪个网卡接口流入本机的,由于匹配条件只是用于匹配报文流入的网卡,所以在OUTPUT链与POSTROUTING链中不能使用此选项:
1 | iptables -t filter -I INPUT -p icmp -i eth0 -j DROP |
-o
:用于匹配报文将要从哪个网卡接口流出本机,于匹配条件只是用于匹配报文流出的网卡,所以在INPUT链与PREROUTING链中不能使用此选项。
1 | iptables -t filter -I OUTPUT -p icmp -o eth0 -j DROP |
-p tcp -m tcp --sport
用于匹配tcp协议报文的源端口,可以使用冒号指定一个连续的端口范围(-p
:protocol,-m
:module,--sport
: source port);-p tcp -m tcp --dport
用于匹配tcp协议报文的目标端口,可以使用冒号指定一个连续的端口范围(--dport
:destination port):
1 | Explain: |
此外,tcp扩展模块还有–tcp-flags选项,它可以根据TCP头部的“标识位”来匹配,具体直接点进去看吧。
-p tcp -m multiport --sports
用于匹配报文的源端口,可以指定离散的多个端口号,端口之间用”逗号”隔开;-p udp -m multiport --dports
用于匹配报文的目标端口,可以指定离散的多个端口号,端口之间用”逗号”隔开:
1 | Explain#示例如下 |
使用iprange扩展模块可以指定”一段连续的IP地址范围”,用于匹配报文的源地址或者目标地址。iprange扩展模块中有两个扩展匹配条件可以使用:
– --src-range
(匹配源地址范围)
– --dst-range
(匹配目标地址范围)
1 | iptables -t filter -I INPUT -m iprange --src-range 192.168.1.127-192.168.1.146 -j DROP |
假设我们访问的是“http://192.168.1.146/index.html”,当“index.html”中包括“XXOO”字符时,就会被以下规则匹配上:
1 | iptables -t filter -I INPUT -m string --algo bm --string "XXOO" -j REJECT |
-m string
:表示使用string模块--algo bm
:表示使用bm算法来匹配index.html中的字符串,“algo”是“algorithm”的缩写,另外还有一种算法叫“kmp”,所以--algo
可以指定两种值,bm或kmp,貌似是bm算法速度比较快。
我们可以通过time扩展模块,根据时间段区匹配报文,如果报文到达的时间在指定的时间范围以内,则符合匹配条件。
我想要自我约束,每天早上9点到下午6点不能看网页:
1 | iptables -t filter -I INPUT -p tcp --dport 80 -m time --timestart 09:00:00 --timestop 18:00:00 -j REJECT |
周六日不能看网页:
1 | iptables -t filter -I INPUT -p tcp --dport 80 -m time --weekdays 6,7 -j REJECT |
--weekdays
可用1-7表示一周的7天,还能用星期的缩写来指定匹配:Mon、Tue、Wed、Thu、Fri、Sat、Sun。
匹配每月22,23号:
1 | iptables -t filter -I INPUT -p tcp --dport 80 -m time --monthdays 22,23 -j REJECT |
多个条件是“相与”的关系,比如以下规则指定匹配每周五,并且这个周五还必须是在22,23,24,25,26,27,28中的一天(其实就相当于设置每月的第四个星期五):
1 | iptables -t filter -I INPUT -p tcp --dport 80 -m time --weekdays 5 --monthdays 22,23,24,25,26,27,28 -j REJECT |
另外还有daystart和daystop:
1 | iptables -t filter -I INPUT -p tcp --dport 80 -m time --daystart 2019-07-20 --daystop 2019-07-25 -j REJECT |
--monthdays
和--weekdays
可以用感叹号!
取反,其他的不行。
使用connlimit扩展模块,可以限制每个IP地址同时链接到server端的链接数量,注意:我们不用指定IP,其默认就是针对”每个客户端IP”,即对单IP的并发连接数限制。
限制22端口(ssh默认端口)连接数量上限不能超过2个;
1 | iptables -t filter -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECT |
在CentOS6中可对--connlimit-above
取反:
1 | iptables -t filter -I INPUT -p tcp --dport 22 -m connlimit ! --connlimit-above 2 -j REJECT |
表示连接数量只要不超过两个就允许连接,至于超过两个并不一定不允许连接,这得看默认策略是ACCEPT还是DROP或REJECT,又或者有其它规则对它进行限制。
在CentOS7中有一个叫--connlimit-upto
的选项,它的作用跟! --connlimit-above
一样,不过这种用法还是比较少用的。
配合--connlimit-mask
来限制网段:
1 | iptables -t filter -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 2 --connlimit-mask 24 -j REJECT |
网址由32位二进制组成,最大可写成:255.255.255.255,而mask就是掩码(网络知识,请自行了解),24表示24个1,即255.255.255.0。
limit模块是限速用的,用于限制“单位时间内流入的数据包的数量”。
每6位秒放行一下ping包(因为1分钟是60秒,所以1分钟10个包,就相当于每6秒1个包):
1 | iptables -t filter -I INPUT -p icmp -m limit --limit 10/minite -j ACCEPT |
--limit
后面的单位除了minite,还可以是second、hour、day
--limit-burst
: burst是爆发、迸发的意思,在这里是指最多允许一次性有几个包通过,要理解burst,先看以下的“令牌桶算法”。
令牌桶算法:
有一个木桶,木桶里面放了5块令牌,而且这个木桶最多也只能放下5块令牌,所有报文如果想要出关入关,都必须要持有木桶中的令牌才行,这个木桶有一个神奇的功能,就是每隔6秒钟会生成一块新的令牌,如果此时,木桶中的令牌不足5块,那么新生成的令牌就存放在木桶中,如果木桶中已经存在5块令牌,新生成的令牌就无处安放了,只能溢出木桶(令牌被丢弃),如果此时有5个报文想要入关,那么这5个报文就去木桶里找令牌,正好一人一个,于是他们5个手持令牌,快乐的入关了,此时木桶空了,再有报文想要入关,已经没有对应的令牌可以使用了,但是,过了6秒钟,新的令牌生成了,此刻,正好来了一个报文想要入关,于是,这个报文拿起这个令牌,就入关了,在这个报文之后,如果很长一段时间内没有新的报文想要入关,木桶中的令牌又会慢慢的积攒了起来,直到达到5个令牌,并且一直保持着5个令牌,直到有人需要使用这些令牌,这就是令牌桶算法的大致逻辑。
看完了“令牌桶算法”,其实--limit
就相当于指定“多长时间生成一个新令牌”,而--limit-burst
则用于指定木桶中最多存放多少块令牌。
udp扩展模块中能用的匹配条件比较少,只有两个,就是--sport
与--dport
,即匹配报文的源端口与目标端口。
放行samba服务的137和138端口:
1 | iptables -t filter -I INPUT -p udp -m udp --dport 137 -j ACCEPT |
当使用扩展匹配条件时,如果未指定扩展模块,iptables会默认调用与-p
对应的协议名称相同的模块,所以,当使用-p udp
时,可以省略-m udp
:
1 | iptables -t filter -I INPUT -p udp --dport 137 -j ACCEPT |
udp扩展中的--sport
与--dport
与tcp一样,同样支持指定一个连续的端口范围:
1 | iptables -t filter -I INPUT -p udp --dport 137:157 -j ACCEPT |
--dport 137:157
表示匹配137-157之间的所有端口。
另外与tcp一样,udp也能使用--multiport
指定多个不连续的端口。
ping是使用icmp协议的,假设要禁止所有icmp协议的报文进入本机(根据前面所说,我们可以省略用-m icmp
来指定使用icmp模块,因为不指定它会默认使用-p
指定的协议对应的模块):
1 | iptables -t filter -I INPUT -p icmp -j REJECT |
上述命令能产生两个效果:
– 1、别人ping本机时,无法ping通,因为数据报文无法进入;
– 2、本机ping别人时,虽然数据包可以出去,但别人的响应包也是icmp协议,无法进来(即“有去无回”)。
所以这样设置会导致,不止别人ping不通本机,本机也ping不通别人。
很明显上边的规则不是我们想要的,我们想要的一般都是允许本机ping别人,不允许别人ping本机:
1 | iptables -t filter -I INPUT -p icmp --icmp-type 8/0 -j REJECT |
--icmp-type 8/0
用于匹配报文type为8,code为0时才会被匹配到,至于会是type和code,这是icmp协议的知识,可以参考这里iptables详解(7):iptables扩展之udp扩展与icmp扩展。
其实上边的命令还可以省略code(即把“8/0”写成“8”即可,省略掉“/0”,原因是type=8的报文中只有code=0一种,所以我们不写默认就是code=0,不会有其它值):
1 | iptables -t filter -I INPUT -p icmp --icmp-type 8 -j REJECT |
除了能用type/code来匹配icmp报文,还可以使用icmp的描述名称来匹配:
1 | iptables -t filter -I INPUT -p icmp --icmp-type "echo-request" -j REJECT |
--icmp-type "echo-request"
的效果与icmp --icmp-type 8/0
或icmp --icmp-type 8
的效果完全一样(你可能发现了,icmp协议的描述“echo-request”其实是“echo request”,只不过我们用于作为匹配条件时,要把空格换成横杠)。
在TCP/IP协议簇中,UDP和ICMP是没有所谓的连接的,但是对于state模块来说,tcp报文、udp报文、icmp报文都是有连接状态的,我们可以这样认为,对于state模块而言,只要两台机器在”你来我往”的通信,就算建立起了连接。
而”连接”中的报文可以分为5种状态,报文状态可以为NEW、ESTABLISHED、RELATED、INVALID、UNTRACKED。具体请查看:iptables详解(8):iptables扩展模块之state扩展
放行RELATED和ESTABLISHED状态的报文:
1 | iptables -t filter -I INPUT -m state --state RELATED, ESTABLISHED -j ACCEPT |
报文在经过iptables的链时,会匹配链中的规则,遇到匹配的规则时,就执行对应的动作,如果链中的规则都无法匹配到当前报文,则使用链的默认策略(默认动作),链的默认策略通常设置为ACCEPT或者DROP。
那么,当链的默认策略设置为ACCEPT时,如果对应的链中没有配置任何规则,就表示接受所有的报文,如果对应的链中存在规则,但是这些规则没有匹配到报文,报文还是会被接受。
同理,当链的默认策略设置为DROP时,如果对应的链中没有配置任何规则,就表示拒绝所有报文,如果对应的链中存在规则,但是这些规则没有匹配到报文,报文还是会被拒绝。
所以,当链的默认策略设置为ACCEPT时,按照道理来说,我们在链中配置规则时,对应的动作应该设置为DROP或者REJECT,为什么呢?
因为默认策略已经为ACCEPT了,如果我们在设置规则时,对应动作仍然为ACCEPT,那么所有报文都会被放行了,因为不管报文是否被规则匹配到都会被ACCEPT,所以就失去了访问控制的意义。
所以,当链的默认策略为ACCEPT时,链中的规则对应的动作应该为DROP或者REJECT,表示只有匹配到规则的报文才会被拒绝,没有被规则匹配到的报文都会被默认接受,这就是”黑名单”机制。
同理,当链的默认策略为DROP时,链中的规则对应的动作应该为ACCEPT,表示只有匹配到规则的报文才会被放行,没有被规则匹配到的报文都会被默认拒绝,这就是”白名单”机制。
如果使用白名单机制,我们就要把所有人都当做坏人,只放行好人。
如果使用黑名单机制,我们就要把所有人都当成好人,只拒绝坏人。
白名单机制更加安全一些,黑名单机制更加灵活一些。
如果服务器是做网关功能,一般设置为黑名单模式;如果是做主机防火墙功能,那么设置为白名单模式。
在最前面的时候就说过,iptables有五个“关卡”,即五条“链”,这些都是默认的,但其实我们可以创建自己的自定义链。
还记得前面介绍iptables -L
时的那个“target”列吗?为什么target列都是一些“动作”呢?这样的话为什么不把target写成“action”呢?其实就是因为target不一定是“动作”,它还可以是“自定义链”,当指定target为自定义链时,如果匹配上了,那么就会跳转到指定的自定义链中。
你可能会问,前面一直用默认链不也都能实现想要实现的功能吗?为什么还要自定义呢?
原因是当默认链中的规则非常多时,不方便我们管理。
想象一下,如果INPUT链中存放了200条规则,这200条规则有针对httpd服务的,有针对sshd服务的,有针对私网IP的,有针对公网IP的,假如,我们突然想要修改针对httpd服务的相关规则,难道我们还要从头看一遍这200条规则,找出哪些规则是针对httpd的吗?这显然不合理。
所以,iptables中,可以自定义链,通过自定义链即可解决上述问题。
假设,我们自定义一条链,链名叫IN_WEB,我们可以将所有针对80端口的入站规则都写入到这条自定义链中,当以后想要修改针对web服务的入站规则时,就直接修改IN_WEB链中的规则就好了,即使默认链中有再多的规则,我们也不会害怕了,因为我们知道,所有针对80端口的入站规则都存放在IN_WEB链中,同理,我们可以将针对sshd的出站规则放入到OUT_SSH自定义链中,将针对Nginx的入站规则放入到IN_NGINX自定义链中,这样,我们就能想改哪里改哪里,再也不同担心找不到规则在哪里了。
但是要注意的是,自定义链并不能直接使用,而是需要被默认链引用才能够使用,即默认生效的还是默认的五条链,而自定义链必须在某条默认链的某个规则里设置target为自定义链,然后才会被引用。
创建一条名叫“IN_WEB”的自定义链(-N
:new):
1 | iptables -N IN_WEB |
1 | Chain IN_WEB (0 references) |
我们可以看到有“0 references”这个字样,reference是“引用”的意思,“0 references”表示引用计数为0,“引用”就是前面说的“自定义链必须在某条默认链的某个规则里设置target为自定义链,然后才会被引用”。
创建一条引用“IN_WEB”链的规则(所谓的引用就是用-j
跳转到该规则里):
1 | iptables -I INPUT -p tcp --dport 80 -j IN_WEB |
此时我们再用iptables -L
查看,可以看到“引用计数”已经是1了:
1 | Chain IN_WEB (1 references) |
修改自定义链名称(把名为IN_WEB的自定义链的名称改为WEB,-E
是edit的意思):
1 | iptables -E IN_WEB WEB |
能修改肯定也能删除,但删除是有条件的:
– 1、自定义链没有被任何默认链引用,即自定义链的引用计数为0;
– 2、自定义链中没有任何规则,即自定义链为空。
删除名为IN_WEB的自定义链:
1 | iptables -X IN_WEB |
ufw是Uncomplicated Firewall,翻译为“不复杂的防火墙”,是Ubuntu系统上配置iptables防火墙的工具,现在为ubuntu和debian的默认防火墙,ufw的底层还是iptables,它只不过帮我们简化了操作命令。如果我们只是简单的开放端口,禁止端口,可以使用ufw更简单,你可以完全不学习iptables的原理。
1 | Explain# 开启防火墙,同时会开启开机自启动 |