从逻辑上讲。防火墙可以大体分为主机防火墙和网络防火墙。
主机防火墙:针对于单个主机进行防护。
网络防火墙:往往处于网络入口或边缘,针对于网络入口进行防护,服务于防火墙背后的本地局域网。
网络防火和主机防火墙并不中突,可以理解为,网络防火墙主外(集体),主机防火墙主内(个人)。
从物理上讲,防火墙可以分为硬件防火墙和软件防火墙。
硬件防火墙:在硬件级别实现部分防火墙功能,另一部分功能基于软件实现,性能高,成本高。如:思科ASA 华为防火墙 天融信防火墙 等。
软件防火墙:应用软件处理逻辑运行于通用硬件平台之上的防火墙,性能低,成本低。如:iptables firewall(centos7独有的)等。
在linux中,netfilter才是真正实现防火墙功能的组件。它存在于内核中,主要采用连线跟踪(Connection Tracking)、包过滤(Packet Filtering)、地址转换、包处理(Packet Mangling)4种关键技术。
由于netfilter处于内核空间,我们无法直接对它来进行操控。所以iptables只是一款工具,利用它来进行编写规则,并将规则直接输送到内核中。它存在于用户空间
。
对于两个进程间的数据传输来说,有请求报文和响应报文,这也就决定了无论数据怎么传输,它会都有流入和流出这两个操作。
为了保护我们服务器的安全,需要对进入的报文进行一些检查 ,只有符合了我们规则的才可以放行。linux定义了5条链,每一条链是一个“关卡”。每一个"关卡"上有管理员定义的规则,只有按规则通过了种种"关卡",才能去到自己想去的目的地。
链是规则的容器,我们可以在链中写很多的规则。而表就是链的容器,每一个表中包含着若干个链。netfilter中定义了4个表,如下:
INPUT,FORWARD,OUTPUT
PREROUTING,OUTPUT,POSTROUTING,INPUT
PREROUTING,OUTPUT,POSTROUTING,INPUT,FORWARD
PREROUTING,OUTPUT
raw>mangle>nat>filter
由上而下依次检查。统一语法规则: iptables [-t table] COMMAND chain [-m matchname [per-match-options]] -j targetname [per-target-options]
-t table:用来指明是哪个表。有filter,raw,mangle,nat,默认为filter。
COMMAND参数:
-L:列出指定链上的所有规则,默认是filter
-n:以数字格式显示地址和端口号
-v:显示详细信息
-x:显示计数器结果的精确值
--line-numbers:显示规则的序号
[root@localhost ~]# iptables -vnxL --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 41 2796 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
2 0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
3 1 108 INPUT_direct all -- * * 0.0.0.0/0 0.0.0.0/0
4 1 108 INPUT_ZONES_SOURCE all -- * * 0.0.0.0/0 0.0.0.0/0
5 1 108 INPUT_ZONES all -- * * 0.0.0.0/0 0.0.0.0/0
6 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
7 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
#注意:简写的时候,L需要写在小写字母的最后面。
COMMAND | 解释 |
---|---|
-N | 创建一条新的自定义链 |
-X | 删除自定义的规则链,只有符合无规则,引用次数为0,用户自定义的三个条件才能删除 |
-P | 设置默认策略,主要用来定义链上是白名单还是黑名单 |
-E | 重命名自定义链名称 |
示例:
[root@localhost ~]# iptables -t filter -N in_web_rules
[root@localhost ~]# iptables -vnL
Chain in_web_rules (0 references) #此为引用次数
pkts bytes target prot opt in out source destination
#在filter上创建一个新的自定义链。
[root@localhost ~]# iptables -t filter -X in_web_rules
#删除自定义链
[root@localhost ~]# iptables -t filter -P FORWARD DROP
[root@localhost ~]# iptables -vnL
Chain FORWARD (policy DROP 0 packets, 0 bytes) #此处的policy即为默认策略
pkts bytes target prot opt in out source destination
注解:
ACCEPT:通过,如果将默认策略都设置成通过,则要将链规则当黑名单来写
DROP:丢弃, 如果将默认策略都设置成丢弃,则要将链规则当白名单来写
REJECT:拒绝, 如果将默认策略都设置成拒绝,则要将链规则当白名单来写
(但是在实验过程中发现使用REJECT是bad policy,REJECT是动作处理,不是默认策略)[root@bogon ~]# iptables -I OUTPUT 2 -s 192.168.1.128 -j REJECT
[root@bogon ~]# iptables -A INPUT -d 192.168.1.128 -j REJECT
COMMAND | 解释 |
---|---|
-A | 追加规则 |
-I | 插入,需要指明插入位置,不写默认插入第一条 |
-D | 删除规则 |
-R | 替换指定链上的指定规则 |
-F | 清空指定链上的所有规则 |
-Z | 清空所有的计数器 |
示例:
[root@localhost ~]# iptables -t filter -A INPUT
[root@localhost ~]# iptables -vnL
Chain INPUT (policy ACCEPT 33 packets, 3424 bytes)
pkts bytes target prot opt in out source destination
33 3424 all -- * * 0.0.0.0/0 0.0.0.0/0
#添加规则在INPUT上
[root@localhost ~]# iptables -t filter -I INPUT 2
[root@localhost ~]# iptables -vnL
Chain INPUT (policy ACCEPT 6 packets, 428 bytes)
pkts bytes target prot opt in out source destination
268 46002 all -- * * 0.0.0.0/0 0.0.0.0/0
6 428 all -- * * 0.0.0.0/0 0.0.0.0/0
#在第一个下面插入一个规则
[root@localhost ~]# iptables -Z
[root@localhost ~]# iptables -vnL
Chain INPUT (policy ACCEPT 6 packets, 428 bytes)
pkts bytes target prot opt in out source destination
6 428 all -- * * 0.0.0.0/0 0.0.0.0/0
#将计数清零0之后重新计数
[root@localhost ~]# iptables -F
[root@localhost ~]# iptables -vnL
Chain INPUT (policy ACCEPT 7 packets, 1740 bytes)
pkts bytes target prot opt in out source destination
#清空所有规则
匹配条件分为基本匹配条件和扩展匹配条件
其中基本匹配条件由iptables/netfilter直接提供。无需提供模块。
基本匹配条件 | 解释 |
---|---|
[!]-s | 检查报文中源IP是否符合此处指定的地址或范围 |
[!] -d | 检查报文中目标IP是否符合此处指定的地址或范围 |
-p | 指明协议 |
-i | 数据报文流入的接口 |
-o | 数据报文流出的接口 |
-j targetname [per-target-options]
ACCEPT
DROP
REJECT
练习:
1、开放本机的所有tcp服务给所有主机;
首先根据题目是包过滤功能,所以写在filter上,给所有主机开放,就需要所有主机可以进来本机(这时本机是目标地址)
。并且本机响应给所有主机(这时主机是源地址)
。
[root@localhost ~]# iptables -t filter -p tcp -A INPUT -d 192.168.199.161 -j ACCEPT
[root@localhost ~]# iptables -t filter -A OUTPUT -p tcp -s 192.168.199.161 -j ACCEPT
2、开放本机的所有udp服务给192.168.199.0/16网络中的主机,但不包含192.168.199.116;
[root@localhost ~]# iptables -t filter -I INPUT 2 -p udp -d 192.168.199.0/16 -j ACCEPT
[root@localhost ~]# iptables -t filter -I OUTPUT 2 -p udp -s 192.168.199.0/16 -j ACCEPT
#开放udp服务给所有主机
[root@localhost ~]# iptables -t filter -p udp -I INPUT 3 -s 192.168.199.116 -d 192.168.199.0/16 -j REJECT
#拒绝192.168.199.116,直接在INPUT上写就好,因为拒绝就无须在响应它。
3、默认策略为DROP;
[root@localhost ~]# iptables -t filter -P INPUT DROP
#使用这种方式之后,那么你写的规则一般都是白名单。
扩展匹配条件分为隐式扩展和显式扩展
不需要手动加载模块,但凡使用了-p指明了协议,就已经表明了要扩展的模块
隐式扩展 | 解释 |
TCP协议 | |
[!]--source-port,--sport port[:port] | 匹配报文的源端口号,也可以是端口范围 |
[!]--destination-port,--dport port[:port] | 匹配报文的目标端口,可以是端口范围 |
UDP协议 | |
[!] --source-port, --sport port[:port] | 匹配报文的源端口;可以是端口范围; |
[!]--destination-port,--dport port[:port] | 匹配报文的目标端口,可以是端口范围 |
ICMP协议 | |
[!] --icmp-type {type[/code]|typename} | 常用的有echo-request(8,ping别人)和echo-reply(0,别人的响应) |
示例:
(1)让主机116的SSH服务不能使用
[root@bogon ~]# iptables -t filter -A INPUT -p tcp --dport 22 -j REJECT
[root@bogon ~]#
Socket error Event: 32 Error: 10053.
Connection closing...Socket close.
#显示连接失败了,只能去服务器上取消规则,所以你要在做默认策略为DROP时,一定要先让ssh服务通行。
(2)让主机161和116上可以进行ping操作。我之前默认的策略是DROP,所以需要设置
[root@localhost ~]# iptables -t filter -I OUTPUT 3 -p icmp --icmp-type 8 -s 192.168.199.161 -d 192.168.199.116 -j ACCEPT
[root@localhost ~]# iptables -t filter -I INPUT 3 -p icmp --icmp-type 0 -d 192.168.199.161 -s 192.168.199.116 -j ACCEPT
#此处只为161向116上ping主机有响应,但是116向161ping没有响应,需要在加一条内容。
[root@localhost ~]# iptables -t filter -I INPUT 4 -p icmp -d 192.168.199.161 --icmp-type 8 -j ACCEPT
需要使用-m指明要调用的扩展模块的扩展机制,帮助文档man iptables-extensions
(1)multiport :以连续或者离散的方式定义多端口匹配条件,最多15个。其中离散用逗号表示,连续端用分号表示。只能和-p tcp 或者-p udp连着使用
选项 | 解释 |
---|---|
[!] --source-ports,-sport port [,port] [,port:port] | 指定多个源端口 |
[!]–destination-ports,-dport port [,port] [,port:port] | 指定多个目标端口 |
示例:
开放本机的httpd、ssh、vsftpd、mariadb服务给所有主机
[root@bogon ~]# iptables -A INPUT -p tcp -d 192.168.1.128 -m multiport --dport 22,80,3306,21 -j ACCEPT
[root@bogon ~]# iptables -I OUTPUT 1 -p tcp -s 192.168.1.128 -m multiport --sport 22,80,3306,21 -j ACCEPT
(2)iprange:指明多个IP地址匹配条件
选项 | 解释 |
---|---|
[!]–src-range from [-to] | 源地址从哪儿到哪儿 |
[!]–dst-range from [-to] | 目标地址从哪儿到哪儿 |
示例:
让192.168.199.213的主机无法连接161上的数据库,使用多地址的方式
[root@localhost ~]# iptables -I INPUT 2 -p tcp -d 192.168.199.161 -m multiport --dport 80,21,3306 -m iprange --src-range 192.168.199.160-192.168.199.190 -j ACCEPT
[root@localhost ~]# iptables -I OUTPUT 2 -p tcp -s 192.168.199.161 -m multiport --sport 80,21,3306 -m iprange --dst-range 192.168.199.160-192.168.199.190 -j ACCEPT
(3)time:主要用于指定时间段的匹配。
选项 | 解释 |
---|---|
–timestart hh:mm[:ss] | 起始时间 |
–timestop hh:mm[:ss] | 结束时间 |
[!]–weekdays day [,day…] | 周几,可以使用数字0-6来表示 |
[!]–monthdays day [,day…] | 表示每月的几号 |
–datestart YYYY[-MM[-DD[Thh[:mm[:ss]]]]] | 开始于某年某月某日某分某秒 |
–datestop YYYY[-MM[-DD[Thh[:mm[:ss]]]]] | 停止于某年某月某日某分某秒(上述两种不经常使用) |
–kerneltz | 使用内核配置的时区而非默认的UTC; |
示例
网页开放访问的时间为周五到日的下午13:00-17:00。
[root@localhost ~]# iptables -R INPUT 2 -p tcp -d 192.168.199.161 -m multiport --dport 80,3306 -m time --timestart 13:00:00 --timestop 17:00:00 --weekdays 5,6,7 --kerneltz -j ACCEPT
[root@localhost ~]# iptables -R OUTPUT 2 -s 192.168.199.161 -p tcp -m multiport --sport 80,3306 -m time --timestart 13:00:00 --timestop 17:00:00 --weekdays 5,6,7 --kerneltz -j ACCEPT
[root@bogon ~]# curl http:/192.168.199.161
^C
[root@bogon ~]# date
2019年 06月 16日 星期日 12:10:30 CST
#由于未到13点,所以无法访问,这是另一台主机
(4)string:字符串匹配
大家知道数据在计算机中是以流式存在的,而且机器只懂01这两个简单的数字。所以机器识别字符的时候仍然是以01二进制为基础的。比如 ‘moxxer fxxker’ 这两个单词,他两相对应的二进制数字是0011,0110。在机器中这两个单词被分析成了00110110,如果机器想要去匹配fxxk这个单词,就会拿0110这个二进制数字去匹配00110110里面,看是否含有0110。
选项 | 解释 |
---|---|
–algo{bm|kmp} | 使用哪一种匹配算法,两种都可以选用 |
–string pattern | 匹配检查的字符串 |
–hex-string-pattern | 匹配16进制法则的给定模式 |
–from offset | 从哪个字符开始检测 |
–to offset | 设置要扫描的偏移量 |
示例:
网页如果含有fxxk字样,禁止访问
[root@localhost ~]# iptables -I OUTPUT 2 -m string --algo bm --string "fxxk" -j REJECT
#只需要在出报文处定义规则就可以。无需再INPUT写。
(5)connlimit:限制客户端发送并发请求数量
选项 | 解释 |
---|---|
–connlimit-upto n | 允许连接数量,小于等于连接数 |
–connlimit-above n | 拒绝连接数量,大于设定的连接数 |
选择:基于你的默认策略,如果默认策略是拒绝的,就要使用小于等于,因为你做的是白名单,反之,就是大于。在做的时候只给请求的一方做限制即可,响应的一方不用。
示例:
只能允许连接三次mariadb服务
[root@localhost ~]# iptables -R INPUT 3 -d 192.168.199.161 -p tcp --dport 3306 -m connlimit --connlimit-upto 3 -j ACCEPT
(6)limit:限速,对发送报文的速率限制。使用的令牌桶算法
选项 | 解释 |
---|---|
–limit n[/second/minute/hour/day] | 设定定义速率 |
–limit-brust number | 定义能收集多少个令牌 |
示例:
ping操作每分钟响应20个报文,相当于每3秒一个报文
[root@localhost ~]# iptables -R INPUT 2 -d 192.168.199.161 -p icmp --icmp-type 8 -m limit --limit 20/minute -j ACCEPT
[root@localhost ~]# iptables -R OUTPUT 2 -s 192.168.199.161 -p icmp --icmp-type 0 -m limit --limit 20/minute -j ACCEPT
(7)state:连接追踪机制
选项 | 解释 |
---|---|
[!]–state state | 表明连接的状态 |
客户端在访问服务器时,服务器对它是否为老客户没有记忆,服务器一直认为来访问我的都是新朋友。我们需要让它有记忆,有能力知道谁是老朋友,谁是新朋友。因此在服务器内存中专门有一个文件来记录来访客户,当文件中记录的客户再次来访问时,我们知道是老朋友,所以直接敞开大门让老朋友进来。但是服务器的记忆时间是有限的。比如存放tcp的记忆2个小时,2个小时后,它再来访问我们还是认为它是新朋友。
注意:服务器内存是有限的,如果能接收100个,但是一下来了1000,那么就有900会被超时连接拒绝。所以对于那些繁忙的服务器要不关闭连接追踪机制,要不调高最大的追踪值。
连接状态 | 解释 |
---|---|
NEW | 新连接的请求,只有第一次进行连接的才能叫NEW |
ESTABLISHED | 已建立的连接 |
INVALID | 无法识别的连接,比如第一次握手发送syn=1,ack=1,明显这个是错误的,无法知道其表达的意思 |
RELATED | 与ESTABLISHED相关联的连接, |
UNTRACKED | 未追踪的连接 |
相关联的文件:
/proc/net/nf_conntrack
/proc/sys/net/nf_conntrack_max
/proc/sys/net/netfilter/*timeout*
对于服务器来说,它很少来访问外部服务,如果有木马在你的服务器内,通过查找可以端口以实现外部来控制你的主机。没有这个机制之前,我们的响应报文也就将木马给带出去了。但是有了这个机制,我们在响应报文部分就可以专门放行ESTABLISHED的状态。将NEW的通通拒绝。这样就可以大大的提高安全的性能。
示例:
[root@bogon ~]# iptables -R INPUT 1 -p tcp -d 192.168.1.128 -m multiport --dport 22,80,3306,23 -m state --state NEW -j ACCEPT
#开放服务端口22,80,3306,23给所有主机,仅为NEW状态的。
[root@bogon ~]# iptables -A OUTPUT -s 192.168.1.128 -m state --state ESTABLISHED,RELATED -j ACCEPT
#并且只要进入的报文是ESTABLISHED和RELATED状态,通通放行
[root@bogon ~]# iptables -A OUTPUT -s 192.168.1.128 -m state --state ESTABLISHED,RELATED -j ACCEPT
#同上,只要是ESTABLISHED和RELATED的状态,通通响应
(8)-j:处理动作
语法格式: -j targetname [per-target-options]
简单的target:ACCEPT,DROP
扩展target:LOG
选项 | 解释 |
---|---|
--log-level | 指定log的级别 |
--log-prefix | 用来加一些前缀 |
默认的日志保存在/var/log/messages
示例:
[root@bogon ~]# iptables -I INPUT 2 -p tcp -d 192.168.1.128 --dport 80 -j LOG
#将访问80端口的外部主机记录于日志中,可在/var/log/messages中查看
自定义链做target
主调用者在调用防火墙规则时,自上而下匹配,碰见自定义链的规则时,将跳转到自定义链的规则,自定义链的规则如果匹配,调用就结束了。如果不匹配,那么将返回到跳转处接着向下匹配。
注意:自定义链写好规则之后,需要主链调用才能生效。
示例:
[root@bogon ~]# iptables -N in_ping_rules
#创建一条自定义链
[root@bogon ~]# iptables -A in_ping_rules -d 192.168.1.128 -p icmp --icmp-type 8 -j ACCEPT
#为自定义链上写规则,接收所有主机的ping操作
[root@bogon ~]# iptables -I INPUT 1 -p icmp -d 192.168.1.128 -j in_ping_rules
#定义INPUT链上如果icmp访问的话调用in_ping_rules上的规则
如果要删除自定义链,首先要清空自定义链的规则,然后在删除主链上调用自定义链的规则,最后删除自定义链
[root@bogon ~]# iptables -F in_ping_rules
[root@bogon ~]# iptables -D INPUT 1
[root@bogon ~]# iptables -X in_ping_rules
(9)保存防火墙规则
由于规则都是存在于内核中的,所以重启之后全部消失不见。centos6和7都提供了保存规则的方式。
iptables-save > /etc/sysconfig/iptables_2019_6_7_v1
iptables-restore < /etc/sysconfig/iptables_2019_6_7_v1
-n:不清除原规则,添加新的规则
保存文件存放于/etc/sysconfig/iptables文件,覆盖保存
默认重载/etc/sysconfig/iptables文件
按照上图为centos1主机配置两张网卡,其中192.168.1.105为桥接,172网段为本地连接。实现内网主机1与外网主机2相互通信。其中172.6.1.10指向的网关为172.6.1.6。外网主机的路由条目需要手动添加一条到172段的网址需要到192.168.105的网关上找、
对于centos1来说,IP地址是存在于内核空间中的,并非网卡,即使centos1的网卡看似不处于同一网络段中,内核也是知道自己有192和172两张网卡。因为路由表中存在了内网1和外网2的条目。所以内网1和外网1各自向自己ping操作的时候,内核可以识别出来。
当内网1对centos1主机进行ping操作时,centos1接收到请求报文,发现目标地址属于自己的ens36的网卡,于是直接发送给了ens36,并原路在返回一个ping回显响应。同样,外网主机2也是同样的操作。
但是当内网1去ping外网主机2时,却发现ping不通。这是因为centos1主机发现内网1访问的是我另一个网卡的同一段的网络主机。需要我通过另一个网卡转发给外网主机2。但是centos1主机想要完整转发,需要开启主机的内核转发机制。
[root@bogon ~]# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
然后进行内网1ping外网主机2的操作就可以ping通了。如果不通,看看是否centos1的主机开着呢。如果不添加防火墙规则,现在就可以使用外网主机上的任何服务。
对于NAT来说,它最要的功能就是地址转换,通过地址转换达到隐藏客户机或者服务器的目的。大家知道,互联网中存在各种不安全的因素,如果直接服务器或者客户机暴露于互联网中,结果可想而知。而有了NAT功能,任何有威胁的东西想要侵害服务器或者客户机的话,都需要先经过NAT服务的防火墙才行。所以大大加强了安全性。
假如说好几个客户机访问web服务,那web服务是怎么知道谁是谁的呢?在nat服务上拥有一个连接追踪表,来保存那些前来访问的IP地址,用于将请求的web服务原路返回给客户机。
此处由于换了网络,所以ip地址也跟着改变了。根据下图解释SNAT
SNAT:源地址转换
根据上图,内网访问外网时,源ip进入centos1中,发现需要经由192.168.199.180这张网卡送出,所以将内网IP转换成了192.168.199.180来访问外网,外网也以为是192.168.199.180来访问。所以外网响应时,将结果返回给了192.168.199.180。但是centos1根据路由表的连接追踪表发现161返回的结果是172网段的,所以将结果返回给了内网主机。这样就达到了隐藏客户主机的目的。
SNAT的防火规则写在nat表上的POSTROUTING上,这是因为请求报文进来之后,需要先经过路由选择,这样才能知道报文的去向。如果PREROUTING上的话,报文都没有进进来,所以无法进行转发功能。
示例:
--to-source:将源地址改为某个ip地址。
[root@bogon ~]# iptables -t nat -R POSTROUTING 1 -s 172.6.1.0/24 -j SNAT --to-source 192.168.199.180
#将172.6.1.0网段的地址改为192.168.199.180
与SNAT相反,是目标地址转换。规则写在NAT表上的PREROUTING。
依然以上图为例,外网访问内网主机时,需要经过centos1,外网首先向ens33网卡发送请求报文,ens33发现请求的报文是内网主机的。所以由ens36转发给内网主机。同样内网响应时,先发送给ens36,在由内核转给ens33发送,响应给外网。外网同样以为是ens33这个地址响应的。但是其实真正服务的是内网主机。这样就达到了隐藏内部主机的目的。因为外网以为访问的都是centos1。
示例:
--to-destination:将目标地址该为某个地址
[root@bogon ~]# iptables -t nat -A PREROUTING -d 192.168.199.180 -p tcp --dport 80 -j DNAT --to-destination 172.6.1.8
#将访问192.168.199.180的web服务目标地址转为172.6.1.8
[root@bogon ~]# iptables -A FORWARD -s 192.168.199.161 -d 172.6.1.8 -p tcp --dport 80 -j REJECT
#拒绝161的主机连接内网主机的web服务。
总结:简单来说SNAT是用来保护内部主机访问外部网络。可以理解为主动请求。DNAT是保护外边网络访问的内部主机。被请求的一方。