服务器就得要使用防火墙来处理未公开的服务!同时,办公室或企业内部,要分不同资安条件,也需要用到它!
Linux 的防火墙机制直接集成在 Linux 内核当中,称为 Netfilter 的防火墙机制!达成这个防火墙机制的服务非常多! 为了简化设置,不同的 distributions 提供了很多不同的管理服务~但是基本上万变不离其宗。所以,鸟哥将以 iptables 为基础开始介绍, 然后才会谈到简单的 firewalld 以及较新的 nftables 这两个 Red Hat 建议的防火墙管理机制。事实上,大部分的 distributions 都还是保留着 iptables 的管理服务模式,所以,鸟哥认为,先了解一下这东西,应该没有什么坏处!
『数据安全』应该有所谓的实体数据安全与网络安全,实体数据安全例如存放的磁盘硬件系统、文件系统等等所管理,为了让数据保存较不容易损毁/遗失, 适当的磁盘数组与备份机制就显得很重要。数据的网络安全则应该是指谁有权限来读取这份数据,在本机上,我们主要通过 rwx 权限设计与帐号/群组的规范, 来限制读写的权限。而在本机之外的访问权限,则主要是通过网络服务器服务来提供相关功能,并通过服务本身的访问规范,或通过防火墙机制, 来限制不同的访问权限。防火墙就是通过订定一些有顺序的规则,并管制进入到我们网域内的主机 (或者可以说是网域) 数据封包的一种机制!更广义的来说,只要能够分析与过滤进出我们管理之网域的封包数据,就可以称为防火墙。
防火墙又可以分为硬件防火墙与本机的软件防火墙。硬件防火墙是由厂商设计好的主机硬件, 这部硬件防火墙内的操作系统主要以提供封包数据的过滤机制为主,并将其他不必要的功能拿掉。因为单纯作为防火墙功能而已, 因此封包过滤的效率较佳。软件防火墙本身就是在保护系统网络安全的一套软件(或称为机制),例如 Netfilter 与 nftables 都可以称为软件防火墙。
仔细分析第一章的图 1.3.1 可以发现,封包进入本机时会通过:防火墙、服务器软件进程、文件系统与权限、 SELinux 流程等。所以基本上,如果你的系统 (1)已经关闭不需要而且危险的服务; (2)已经将整个系统的所有软件都保持在最新的状态; (3)权限设置妥当且定时进行备份工作; (4)已经教育用户具有良好的网络、系统操作习惯。 那么你的系统实际上已经颇为安全了!要不要架设防火墙?那就见仁见智啰!
不过,毕竟网络世界是很复杂的,而 Linux 主机也不是一个简单的东西,说不定哪一天你在进行某个软件的测试时, 主机突然间就启动了一个网络服务,如果你没有管制该服务的使用范围,那么该服务很可能就会对所有 Internet 开放, 那就麻烦了!因为该服务可能可以允许任何人登录你的系统,那不是挺危险?
所以啰,防火墙能作什么呢?防火墙最大的功能就是帮助你『限制某些服务的访问来源』!举例来说: (1)你可以限制文件传输服务 (FTP) 只在子网域内的主机才能够使用,而不对整个 Internet 开放; (2)你可以限制整部 Linux 主机仅可以接受客户端的 WWW 要求,其他的服务都关闭; (3)你还可以限制整部主机仅能主动对外连接。反过来说,若有用户端对我们主机发送主动连接的封包状态 (TCP 封包的 SYN flag) 就予以抵挡等等。这些就是最主要的防火墙功能了!
所以鸟哥认为,防火墙最重要的任务就是在规划出:
当然啦,咱们 Linux 的防火墙软件还可以进行更细部深入的 NAT (Network Address Translation) 的设置,并进行更弹性的 IP 封包伪装功能,不过,对於单一主机的防火墙来说,最简单的任务还是上面那三项就是了!所以,你需不需要防火墙呢? 理论上,当然需要!而且你必须要知道『你的系统哪些数据与服务需要保护』,针对需要受保护的服务来设置防火墙的规则吧!
Linux 上面的防火墙,除了可以进行上面的三项功能之外,也能作为路由器!就是刚刚谈到的 NAT 技术!无论是针对本机的防护, 还是局域网路的防护,或者是路由的切换,这些防火墙的类型,大概可以分为封包过滤式以及代理服务器等类型。
封包过滤式防火墙: 所谓的封包过滤,亦即是分析进入主机的网络封包,将封包的表头数据捉出来进行分析,以决定该连接为放行或抵挡的机制。 由于这种方式可以直接分析封包表头数据,所以包括硬件地址(MAC), 软件地址 (IP), TCP, UDP, ICMP 等封包的信息都可以进行过滤分析的功能, 因此用途非常的广泛。与网络基础相呼应的话,大概可以理解防火墙主要分析的是 OSI 七层协定的 2, 3, 4 层。至于目前常见的封包过滤机制, 大致有 Netfilter 与 nftable 两种~Red Hat 官方建议使用新的 nftable 机制就是了。
代理服务器: 代理服务器基本上并不是防火墙~只是,代理服务器的网络逻辑位置通常就是在防火墙的角色上,所以, 很容易被当成防火墙这样~所谓的代理服务器 (proxy) 就是一个服务,用户端需要连网时,就调用 proxy,请 proxy 代为处理! 当 proxy 处理完毕之后,会将结果回传给用户端~看看连网情况,跟封包过滤式非常类似!所以,许多朋友才将 proxy 也视为一种防火墙。
强者我师父网中人曾经说过一个很有趣的比喻,怎么说明封包过滤式防火墙与 proxy 服务器的差异。现在,假设以你要去 google 查数据好了, 那假设你得要通过巷口检查站 (路由,亦即封包过滤防火墙或 proxy 服务器),才能够去到 google 中心。这两个服务的情况分别会是这样:
目前一般都用封包过滤式防火墙啦~比较少使用 proxy 服务器了!
封包过滤式防火墙主要可以针对 OSI 的 layer 2, 3, 4 进行管理~但是防火墙并不是万能的!否则我们就不需要额外安装防木马、杀毒软件啊! 基本上,防火墙还是有些做不到的地方:
因此,在讲防火墙之前,鸟哥在前面的章节才会先说明,我们一定要先关闭不必要的网络服务、升级全系统的软件、做好权限方面的控管、 进行良好的备份规则。当然啦,企业内部人员的教育训练应该是最重要的!大家要有良好的操作习惯喔!
Linux 内核的防火墙机制持续在变更当中,当然也是因为网络技术的进步与改变,内核防火墙机制也不得不做因应之故。 比较重大的变化是这样的:
目前 Linux 内核已经是 6.x (2023) 版了,基本上,应该是使用较新的 nftables 才对~不过,因为 iptables 实在使用的太广泛, 所以,即使到目前的版本中,两者都还是同步提供啦!看你想用哪个就用哪个。但是,当使用 iptables 机制时,因为最早设计 iptables 时,还没有 IPv6 的版本,早期只有针对 IPv4 的 iptables 防火墙功能。后来网络陆陆续续更新机制,搞到一个 iptables 需要加载一大堆模块! 连操作指令都需要有 iptables, ip6tables, ebtables, arptables 等等,代码变得很复杂。因此,在 3.13 内核版本之后,加入 nftables, 这个指令可以支持不同的网络类型,降低防火墙代码的重复性,改善错误回报的消息功能,更好的运行性能等,因此目前建议使用 nftables 这个防火墙机制。
事实上,目前的 Linux 内核防火墙,都是使用 Netfilter 这个团队所开发出来的项目,这个团队的项目主要就是 iptables 与 nftables 啦! 如前所述,iptables 与 nftables 使用的模块并不相同,语法也不同~由于软件会冲突,因此,只能选择其中一个来运作喔!另外, 为了方便管理员架设自己的防火墙规则,所以 RHEL 也提供一个相对简单的 firewalld 服务,这个服务内有区分数个领域 (zone), 不同领域有相对的缺省功能,只要将网络卡放入相对的领域内,该网卡就拥有该领域的防火墙设置了!相当的简易!也就是说, 目前 RHEL 9.x 衍生的 distributions 当中,提供可以设置的防火墙软件,主要有这几个:
也就是说,从 3.13 Linux 内核版本之后,建议都使用 nftables,使用的是 nft 管理工具~不过,为了向下兼容,我们还是会从 iptables 开始介绍!等到大家了解 iptables 了,再转来使用 firewalld 以及 nftables 啰!另外,nftables 适用于所有的系统上, 如果仅只是单机系统,则建议直接使用 firewalld 即可喔!
那么 Linux 的防火墙,是怎么判断来自网络的封包到底是能不能进入我本机或者是某些环境的呢?基本上:『 封包过滤的防火墙就是根据封包的分析数据 "比对" 你预先定义的规则内容,若封包数据与规则内容相同则进行动作, 否则就继续下一条规则的比对!』重点在那个『比对与分析顺序』上。以底下的过滤流程图标来解释:
当封包通过防火墙的分析 (图标上方中央 IP Filter) 后,最终结果可能是 (1) ACCEPT(接受) 或 (2) DROP(丢弃), 若动作为接受时,封包就顺利进入到本机,开始进行服务的运作。如果是 DROP 的话,用户端的连接就会被丢弃而无法使用本机服务了。 如图所示,假定有 10 条规则,这 10 条规则依序创建完毕之后,封包的数据会先比对第 1 条,若符合设置值,就会运行第一个动作 (Action 1), 每个动作都可能是 ACCEPT/DROP,当开始动作之后,就不会继续比对此规则后面的其他规则!若不符合第 1 条规则,则开始第 2 条规则比对。 以此类推,直到 10 条规则都比对完成,若还是无法运行动作,就会以缺省的 Policy 政策动作来运作。缺省的 Policy 政策同样可以设置为接受或丢弃!
现在,假定你的防火墙底下依序有这几条规则,且缺省政策为接受 (ACCEPT) (底下的规则都是乱设置的,不是正确的设置方式! 目的是让我们理解规则顺序的重要性而已!)
假设:设置上面防火墙规则的系统称为 server,且局域网路内有部系统称为 client,且互联网上有部系统为 interclient。 那么来想一想,假设有底下发起连接的方式,先不要看答案,自己思考一下,该连接有没有可能成功或失败? 而成功或失败的可能性来自于那一条防火墙规则!
说明如下:
说明如下:
说明如下:
看了原本设置的 7 条规则,你可能会以为你的服务器对所有来源放行 WWW 服务,但是由于更优先的第 6 条规则的缘故, 第 7 条规则将永远不会被使用!因为第 6 条拒绝全部连接,一定是符合所有的封包状态!所以啰,规则顺序就显的非常重要啊! 至于规则与政策的设置上,一般我们建议使用比较严格的方式来处理,亦即是『关闭所有连接,开放特定服务』的设置机制较佳。 所以,规则的动作大部份都是接受,最后不符合所有规则的封包,就用缺省政策去丢弃它!
为了实现局域网路的搭建,在我们的 Master 骨干系统上,需要事先处理好全部需要的网络参数才好! 如第六章、图 6.2-1 所示,我们会具有四个网络界面,三个有线及一个无线。 目前预计的网络界面参数设计如下 (WAN 需要跟你的实际网络环境搭配,至于 LAN, DMZ, AP 则可以使用鸟哥的建议设置即可):
习惯上,作为局域网路的管理端,大概使用的都是整个 IP 地址段的最后一个可用 IP 地址,因此,上述除了 WAN 的网卡之外, 其他的网段大部分都是最后一个 IP 地址的设置~另外,我们一直忘记设置各系统的主机名称了... 在这里补充处理~让 Master 的主机名称成为: master.vbird 好了!
# 1. 设置 Master 骨干系统的主机名称 [root@localhost ~]# hostnamectl hostname master.vbird [root@localhost ~]# hostnamectl Static hostname: master.vbird Icon name: computer-vm Chassis: vm 🖴 ..... [root@localhost ~]# vim /etc/hosts .... 192.168.201.249 cloud.vbird cloud 192.168.201.245 master.vbird master 192.168.10.254 masterap.vbird masterap 192.168.20.254 masterlan.vbird masterlan 192.168.30.254 masterdmz.vbird masterdmz 192.168.20.1 client001.vbird client001 192.168.30.1 server001.vbird server001
接下来,让我们来设置好对外的网络、对内的两个局域网路,以及无线 AP 架设吧!
# 1. 观察网络状态,删除无用的网络设置: [root@master ~]# nmcli connection show NAME UUID TYPE DEVICE enp1s0 eddac43d-2bb6-4575-81b9-30477ef394e3 ethernet enp1s0 lo c57a4996-72fd-4af2-991d-f791d1bd204f loopback lo Wired connection 1 5fb71d7d-b9c7-3262-9e34-8ed1d24ef77d ethernet -- Wired connection 2 ccbb41fc-de77-3cf6-9b5a-84bbac325865 ethernet -- # 上面的 Wired connection XX 就是系统自己设置的!建议先拿掉自己重建! [root@master ~]# nmcli connection delete Wired\ connection\ 1 [root@master ~]# nmcli connection delete Wired\ connection\ 2 # 2. 先创建好 WAN 的环境: [root@master ~]# nmcli connection modify enp1s0 ipv4.method manual \ > ipv4.addresses 192.168.201.245/24 ipv4.gateway 192.168.201.254 \ > ipv4.dns 120.114.100.1,168.95.1.1 [root@master ~]# nmcli connection up enp1s0 [root@master ~]# ip addr show enp1s0 | grep 'inet ' inet 192.168.201.245/24 brd 192.168.201.255 scope global noprefixroute enp1s0 [root@master ~]# ip route show default via 192.168.201.254 dev enp1s0 proto static metric 100 192.168.201.0/24 dev enp1s0 proto kernel scope link src 192.168.201.245 metric 100 [root@master ~]# grep nameserver /etc/resolv.conf nameserver 120.114.100.1 nameserver 168.95.1.1 # 3. 开始处理 switch LAN 接的网络: [root@master ~]# nmcli connection add con-name enp2s0 type ethernet ifname enp2s0 \ > ipv4.method manual ipv4.addresses 192.168.20.254/24 [root@master ~]# nmcli connection up enp2s0 [root@master ~]# ip addr show enp2s0 | grep 'inet ' inet 192.168.20.254/24 brd 192.168.20.255 scope global noprefixroute enp2s0 # 4. 开始处理 switch DMZ 接的网络: [root@master ~]# nmcli connection add con-name enp3s0 type ethernet ifname enp3s0 \ > ipv4.method manual ipv4.addresses 192.168.30.254/24 [root@master ~]# nmcli connection up enp3s0 [root@master ~]# ip addr show enp3s0 | grep 'inet ' inet 192.168.30.254/24 brd 192.168.30.255 scope global noprefixroute enp3s0 # 5.1. 先确认无线 USB 网络卡在虚拟机当中,是否支持 AP 模式: [root@master ~]# nmcli device DEVICE TYPE STATE CONNECTION enp1s0 ethernet connected enp1s0 enp2s0 ethernet connected enp2s0 enp3s0 ethernet connected epn3s0 lo loopback connected (externally) lo wlp7s0u1 wifi unmanaged -- [root@master ~]# nmcli -f WIFI-PROPERTIES.AP device show wlp7s0u1 WIFI-PROPERTIES.AP: yes # 确定是有支持 AP 模式的! # 5.2. 安装 Wifi 模式,并且重新启动: [root@master ~]# yum install NetworkManager-wifi [root@master ~]# systemctl restart NetworkManager # 5.3. 创建网卡、连接名称与 SSID 还有密码数据: [root@master ~]# nmcli device wifi hotspot ifname wlp7s0u1 con-name masterap ssid masterap \ > password "mylanIsveryGood" [root@master ~]# nmcli connection modify masterap ipv4.addresses 192.168.10.254/24 [root@master ~]# nmcli connection up masterap # 6. 最后检测所的网卡对应的 IP 是否正确: [root@master ~]# ip addr show | egrep '^[0-9]|inet '| sed 's/<.*$//g' | sed 's/scop.*$//g' 1: lo: inet 127.0.0.1/8 2: enp1s0: inet 192.168.201.245/24 brd 192.168.201.255 3: enp2s0: inet 192.168.20.254/24 brd 192.168.20.255 4: enp3s0: inet 192.168.30.254/24 brd 192.168.30.255 5: wlp7s0u1: inet 192.168.10.254/24 brd 192.168.10.255 [root@master ~]# ip route show default via 192.168.201.254 dev enp1s0 proto static metric 100 192.168.10.0/24 dev wlp7s0u1 proto kernel scope link src 192.168.10.254 metric 600 192.168.20.0/24 dev enp2s0 proto kernel scope link src 192.168.20.254 metric 103 192.168.30.0/24 dev enp3s0 proto kernel scope link src 192.168.30.254 metric 104 192.168.201.0/24 dev enp1s0 proto kernel scope link src 192.168.201.245 metric 100
在这样的情况下,Master 骨干系统的网络参数应该就设置妥当了!当然,目前仅 master 骨干系统本身可以自行对外连接, 内部的网络连接通通是不可行的!因为我们还没有设置好 IP 地址的伪装之故。同时,通过 AP 来的连接, 则是因为『找不到 IP 地址』的缘故而无法上网~接下来,我们就要一个一个处理设置了!
一般来说,作为路由器 (router, gateway) 功能的服务器,才需要启动内核的 IP 转递功能,否则不需要启动这功能! IP 转递的目的,在于让封包流向不同的网络界面。所以,底下的功能只需要在第六章、图 6.2-1 当中的 Master 骨干系统上面设置即可,其他单一任务的服务器,不需要启动此功能!
启动 IP forward 功能很简单,只需要使用 sysctl 的设置即可!建议直接多加一个设置档于 /etc/sysctl.d/ 内就好。 最简单的方法如下:
[root@master ~]# vim /etc/sysctl.d/server.conf net.ipv4.ip_forward = 1 [root@master ~]# sysctl -p /etc/sysctl.d/server.conf [root@master ~]# cat /proc/sys/net/ipv4/ip_forward 1 # 最后出现的消息为 1,就是启动了 IP 转递的功能了!
/etc/sysctl.d/*.conf 设置档,在内核开机之后,会主动去唤醒设置的内核资源,所以写入到这个设置档之后,所有的设置值, 在下次开机都会持续存在喔!
如前所述,RHEL 9.x 的衍生产品中,防火墙的软件至少提供了三种,而且你不能同时启动任何两种以上的防火墙机制, 否则,你的防火墙不是变个更严密,而是会神仙打架...相当困惑~因此,我们得要先将防火墙全部关闭后,仅启动我们需要防火墙软件才行。 第一个我们要先来讨论的是 iptables 这个旧旧但是比较好用来理解防火墙设置的机制~我们可以通过 iptables 来达成单机防火墙, 也可以用它来处理后端主机的 IP 分享器功能以及外部连接到内部服务器的方式~至于我们一定要了解的,就是所谓的 table (表格) 以及 chain (链) 的相关概念!尤其是后面的 nat 表格以及 POSTROUTING 链!相当重要!
其实目前 RHEL 9.x 的衍生产品中,已经没有早期实际的 iptables 模块了,目前使用的其实是 nftables 向下兼容的支持模块! 所以,我们要安装的软件,其实也不是以前的 iptables-services 了!而是 iptables-nft-services 才对!让我们来查找一下吧! 看看目前的系统支持的软件名称与说明!
# 1. 先查找软件名称关键字,然后再查找该软件的说明! [root@master ~]# yum search iptable*serv* iptables-nft-services.noarch : Services for nft-variants of iptables, ebtables and arptables [root@master ~]# yum info iptables-nft-services.noarch Last metadata expiration check: 0:20:21 ago on Thu Nov 2 09:43:35 2023. Available Packages Name : iptables-nft-services Version : 1.8.8 Release : 6.el9_1 Architecture : noarch Size : 22 k Source : iptables-1.8.8-6.el9_1.src.rpm Repository : appstream Summary : Services for nft-variants of iptables, ebtables and arptables URL : https://www.netfilter.org/projects/iptables License : GPLv2 and Artistic 2.0 and ISC Description : Services for nft-variants of iptables, ebtables and arptables : This package provides the services iptables, ip6tables, arptables and ebtables : for use with iptables-nft which provides nft-variants of these tools. # 2. 安装起来吧! [root@master ~]# yum install iptables-nft-services [root@master ~]# rpm -ql iptables-nft-services | egrep 'iptables($|[.-])' /etc/sysconfig/iptables # 主要的规则纪录档 /etc/sysconfig/iptables-config # 加载模块的纪录档 /usr/lib/systemd/system/iptables.service # 服务启动的 systemd 设置档 /usr/libexec/initscripts/legacy-actions/iptables /usr/libexec/iptables /usr/libexec/iptables/iptables.init # 3. 先关闭 firewalld, nftables 之后,启动 iptables [root@master ~]# systemctl stop nftables [root@master ~]# systemctl disable nftables [root@master ~]# systemctl stop firewalld [root@master ~]# systemctl disable firewalld [root@master ~]# systemctl start iptables [root@master ~]# systemctl enable iptables # 很重要!一定要注意顺序!不要的防火墙软件要先关闭!最后才开需要的软件!流程很重要! [root@master ~]# iptables-save # Generated by iptables-save v1.8.8 (nf_tables) on Thu Nov 2 10:29:26 2023 *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [40:4176] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT # Completed on Thu Nov 2 10:29:26 2023 # 最后 iptables-save 有看到输出类似上面这样的数据,就是成功启动 iptables 了!
再次强调,防火墙软件的启动/关闭流程中,可以的话,将目前的所有看到的防火墙软件先通通关掉 (甚至连原本要开的,也关掉!), 然后再仅针对需要的软件项目去打开即可!否则会有冲突!规则会乱掉...要注意!要注意!而当你使用 iptables-save 这个观察的指令时, 看到如上的显示,那就是顺利启动 iptables 服务啦!接下来,就让我们开始来谈谈什么是 filter 与 nat 表格, 然后再来开始处理怎么操作 iptables 这个指令。
刚刚图 7.1.3 的规则内容仅只是某个 chain (链) 而已!缺省的情况下,iptables 至少就有三个表格, 包括管理本机进出的 filter 、管理后端主机 (防火墙内部的其他电脑) 的 nat 、管理特殊旗标使用的 mangle 等。 在本章,我们只会介绍 filter 与 nat,这两个表格与其中链的用途分别是这样的:
所有表格内的链都有点关系~也就是说,网络封包想要进入我们的服务器,不是只有经过一群规则,则是得要经过好几群规则! 每个链里面都有自己的一群规则的意思。简化过后剩下两个表格 (filter, nat) 之后,各个链的关系有点像底下这样:
你可以看到上图有路径方向,依据封包进入的行进,主要分为三个方向来查看:
在缺省的情况底下,除了 filter 的 INPUT/FORWARD 链有规则之外,其他链规则都是空的!而且缺省政策都是接受的! 这是针对单机环境底下的防火墙设置~所以,底下我们先来聊一聊,如果以 Linux 服务器本身提供服务的环境下, 单机的防火墙规则应该是如何设置呢?
防火墙规则的制定要很注意,最好能够在本机上面通过 tty1~tty6 来测试会比较好!这是因为...很多时候,我们会将自己的连接切断... 然后就再也无法跟 Linux 主机连接了...还好,我们现在使用的是虚拟机,所以,只要 KVM 母机器没有乱动,随意更动虚拟机的防火墙, 看起来算是还挺保险的~反正出状况时,就通过 VNC 协定去登录虚拟机的终端机即可~
刚刚接触 iptables 时,鸟哥比较喜欢使用 iptables -L -n 来查看,不过后来发现,其实使用 iptables-save 更简便!因此后来都直接用 iptables-save 了无过, iptables -L -n 真的很容易观察~所以,还是先来看看怎么用它吧!
# 1. 基本语法与说明 [root@master ~]# iptables [-t tables] [-L] [-nv] 选项与参数: -t :后面接 table ,例如 nat 或 filter ,若省略此项目,则使用缺省的 filter -L :列出目前的 table 的规则 -n :不进行 IP 与 HOSTNAME 的反查,显示消息的速度会快很多! -v :列出更多的信息,包括通过该规则的封包总比特数、相关的网络接口等 # 2. 列出目前 filter 表格三条链的规则 [root@master ~]# iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT) target prot opt source destination REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT) target prot opt source destination
上面的输出,以 Chain 开头的项目,就是该链的缺省政策,所以我们可以看到缺省的三条链政策都是 ACCEPT 喔! 从下往上看, OUTPUT 链里面没有规则, FORWARD 链有一条规则,该规则为全部拒绝。INPUT 链底下有 5 条规则, 在说明规则之前,先来看看输出的信息里面有什么:
根据上面的说明,那么 INPUT 链里面的 5 条规则功能分别是:
上面的显示中,除了第 3 条,其他的说明我们应该是看得懂的!第 3 跟第 5 条规则,似乎刚刚好相反~所以,如果只看第 3 条, 好像所有的规则都被放行了!那怎么得了!但其实实际设置并不是这样的!这就是直接使用 iptables -L 观察时的困扰! 所以,建议你还是使用底下的 iptables-save 来观察比较妥当:
# 直接列出规则来观察 [root@master ~]# iptables-save # Generated by iptables-save v1.8.8 (nf_tables) on Thu Nov 2 11:54:11 2023 *filter <==星号 (*) 开头的是表格名称,这里是 filter :INPUT ACCEPT [0:0] <==冒号 (:) 开头的是链的名称,三条内置的链 :FORWARD ACCEPT [0:0] <==链后面接的就是缺省政策,这里都是 ACCEPT :OUTPUT ACCEPT [129:15208] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT <==针对 INPUT 的规则 -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited <==针对 FORWARD 的规则 COMMIT # Completed on Thu Nov 2 11:54:11 2023
上表当中,出现 # 的都不用看,那仅是告知 iptables-save 的运行与输出时间而已,至于 * 与 : 与 -A 开头的就得要注意了:
关于 5 条规则的详细信息,之后的小节谈到的综合规则中,就会有所解释~请耐心继续往下瞧瞧~
你要创建新的规则之前,最好将以前的规则全部删除后,依序创建一条一条的规则顺序,毕竟规则之间是有顺序差别的!这点很重要! 清除链与规则需要一条一条来运行,无法一口气运行完毕。底下为缺省的语法,同时请清除所有缺省链的规则,并且观察看看是否正确清除了?
# 1. 基本的语法 [root@master ~]# iptables -[F|X|Z] (清除规则|自订链|统计信息) [root@master ~]# iptables -P [INPUT|OUTPUT|FORWARD] [ACCEPT|DROP] (是大写的 P) # 2. 实际清除规则后,查看规则 [root@master ~]# iptables -F [root@master ~]# iptables -X [root@master ~]# iptables -Z [root@master ~]# iptables-save *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT # 可以看到,没有任何的 -A 出现! # 3. 让 INPUT/OUTPUT 政策为 ACCEPT,但是 FORWARD 为 DROP [root@master ~]# iptables -P INPUT ACCEPT [root@master ~]# iptables -P OUTPUT ACCEPT [root@master ~]# iptables -P FORWARD DROP [root@master ~]# iptables-save *filter :INPUT ACCEPT [112:8104] :FORWARD DROP [0:0] :OUTPUT ACCEPT [43:4152] COMMIT
我们这边仅是做个范例示意,并不是真的要将 FORWARD 设置为 DROP 喔!未来我们会再修改~
一般防火墙大多针对『要连接到本服务器』来的封包做分析,所以,大部分都是针对 INPUT 这条链的设置啦! 那分析的封包有几个地方要考虑:
复合式的语法有点像底下这样,没写到的代表『通通接受』的意思:
[root@master ~]# iptables [-[ADI] INPUT] [-i lo|eth0|ens3] [-s IP/Netmask] [-d IP/Netmask] \ > [-p tcp|udp [--sport ports] [--dport ports]] [-j ACCEPT|REJECT|DROP]
上面的 -A INPUT 代表接续当前的规则之后添加此规则, -D INPUT 代表删除后面写的这条规则,-I INPUT 代表将此规则插入成为第一条, 既有的规则往后顺延的意思。现在,让我们回想一下原本规则当中的第 2~5 条的内容,分别解释如下:
# 复原原有的第 2 条规则 [root@master ~]# iptables -A INPUT -p icmp -j ACCEPT # 不论什么界面、不论什么网域、只要是 ICMP 封包协定,此封包就放行 # 复原原有的第 3 条规则 [root@master ~]# iptables -A INPUT -i lo -j ACCEPT # 任何封包格式类型,只要通过 lo 这个设备,该封包就予以放行,亦即 lo 为『信任设备』 # 复原原有的第 4 条规则 [root@master ~]# iptables -A INPUT -m state --state NEW -p tcp --dport 22 -j ACCEPT # 不论什么界面,无论什么网域,只要是主动连接 (NEW) 且使用 port 22/tcp 封包,就予以放行 # 这种方式也是最常用来设置服务器服务的规则! # 复原原有的第 5 条规则 [root@master ~]# iptables -A INPUT -j REJECT # 任何封包通通拒绝 [root@master ~]# iptables-save :INPUT ACCEPT [469:54016] :FORWARD DROP [0:0] :OUTPUT ACCEPT [271:27400] -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-port-unreachable COMMIT
如果你是在外部系统连接到这部虚拟机练习防火墙的话,那么上面第 5 条规则复原后,你的连接应该会立刻停止了... 而且可能无法继续连接到这部主机上~这是因为缺乏 ESTABLISHED,RELATED 那条规则的关系!鸟哥也是因为这样被暂停住... 如果是这样的话,那妳可能得要将第 5 条复原的规则删除才行!请从本机登录,然后删除的方式为
# 无论你是否还可以连接,若无法使用终端机了,就从本机端登录后,运行下列指令删除规则: [root@master ~]# iptables -D INPUT -j REJECT
现在就剩下 3 条规则才对~再来,想想看,现在使用上面的复合式指令,测试一下完成底下的防火墙规范:
# 1. 信任设备,不论任何来源,连接到此设备的封包,都会被放行! [root@master ~]# iptables -A INPUT -i enp2s0 -j ACCEPT # 2. 信任网域,只要来自这个网域,任何封包都可被放行 [root@master ~]# iptables -A INPUT -s 192.168.20.0/24 -j ACCEPT # 3./4. 限制服务的使用,只信任部份来源 (因为 ssh 太重要!只放行内部来源使用较佳) [root@master ~]# iptables -A INPUT -i enp3s0 -s 192.168.30.0/24 -p tcp --dport 22 -j ACCEPT [root@master ~]# iptables -A INPUT -i enp1s0 -s 192.168.201.0/24 -p tcp --dport 22 -j ACCEPT # 5. 放行根本机网页服务器有关的服务 [root@master ~]# iptables -A INPUT -p tcp --dport 80 -j ACCEPT [root@master ~]# iptables -A INPUT -p tcp --dport 443 -j ACCEPT # 6. 因为 DNS 同时具有 UDP 与 TCP 的连接功能,所以要放行两条规则 [root@master ~]# iptables -A INPUT -p udp --dport 53 -j ACCEPT [root@master ~]# iptables -A INPUT -p tcp --dport 53 -j ACCEPT # 7. 拒绝不信任的用户!这里是拒绝喔!并不是丢弃! [root@master ~]# iptables -A INPUT -s 10.30.30.0/24 -j REJECT # 8. 放行连接到本机的 port 5901~5910,连续端口口可以这样写: [root@master ~]# iptables -A INPUT -p tcp --dport 5901:5910 -j ACCEPT
为什么说上面的第 7 条可能放错地方呢?看一下整体的流程,我们可以发现,原本想要拒绝 10.30.30.0/24 的所有连接,但是因为 port 80, 443, 53 等服务写在前面,所以,基本上该网域还是可以使用我们服务器的某些服务!因此,基本上,要拒绝的项目, 应该要挪到放行的规则之前!否则就无法顺利被拒绝~
在目前的规则下,假设最后一条规则是全部拒绝时,那如果你在本机端连接到 google 去,但是没有判断从 google 回来的封包,你的这条连接就会被拒绝...这是因为我们的 INPUT 规则,并没有针对 google 的来源封包做判断处理的缘故。 那怎么办?没关系,这就是原本规则的第 1 条内容,通过封包的『状态 (state)』来进行过滤!封包状态过滤基本语法像底下这样:
[root@master ~]# iptables -A INPUT [-m state] [--state 状态] [--mac-source MAC_ADDR] ... 选项与参数: -m :一些 iptables 的插件模块,主要常见的有: state :状态模块 mac :网络卡硬件地址 (hardware address) --state :一些封包的状态,主要有: INVALID :无效的封包,例如数据破损的封包状态 ESTABLISHED:已经连接成功的连接状态; NEW :想要新创建连接的封包状态; RELATED :这个最常用!表示这个封包是与我们主机发送出去的封包有关 --mac-source :就是网卡卡号设置!
试着完成底下规则看看:
# 1. 这次是『插入』规则,而且应用的是状态模块 [root@master ~]# iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 2. 让局域网路内的某个设备,变成为信任来源 [root@master ~]# iptables -A INPUT -i enp2s0 -m mac --mac-source aa:bb:cc:dd:ee:ff -j ACCEPT # 3. 将全部连接都拒绝的规则加回来 [root@master ~]# iptables -A INPUT -j REJECT # 4. 修改缺省政策 [root@master ~]# iptables -P INPUT DROP [root@master ~]# iptables -P FORWARD ACCEPT [root@master ~]# iptables-save *filter :INPUT DROP [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [4579:315039] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT <==确实回到第 1 条 -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -i enp2s0 -j ACCEPT -A INPUT -s 192.168.20.0/24 -j ACCEPT -A INPUT -s 192.168.30.0/24 -i enp3s0 -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -s 192.168.201.0/24 -i enp1s0 -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT -A INPUT -p udp -m udp --dport 53 -j ACCEPT -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT -A INPUT -s 10.30.30.0/24 -j REJECT --reject-with icmp-port-unreachable <==应该要放到 port 80 前 -A INPUT -p tcp -m tcp --dport 5901:5910 -j ACCEPT -A INPUT -i enp2s0 -m mac --mac-source aa:bb:cc:dd:ee:ff -j ACCEPT -A INPUT -j REJECT --reject-with icmp-port-unreachable
基本上,直接手动输入的 iptables 指令,其动作只写入到目前的环境中,并没有写入设置档,因此下次重新开机后, 刚刚努力创建的规则,就通通消失了...那么数据保存文件在哪里?就是在 /etc/sysconfig/iptables 这个文件中! 这个文件其实就是 iptables-save 的输出数据耶!所以,要将目前的规则顺序保存下来,我们可以这样做:
# 保存目前的防火墙规则成为默认值 [root@master ~]# iptables-save > /etc/sysconfig/iptables
我们知道顺序很重要,例如上面练习的环节中,我们就有特别说明, 10.30.30.0/24 那一条相关规则应该放错地方...那怎办?最简单的方法,使用 -D INPUT 删除后, 再用 -I INPUT 插入到某条规则之后...真不直观~因此,创建一个简单的防火墙脚本档,每次修改完之后,直接运行一次脚本就好了! 简单方便又直观~现在,使用 vim 或 nano 打开你的防火墙规则档,创建如下的规则看看:
[root@master ~]# vim ~/iptables.sh #!/bin/bash # part 1: 清除规则并设置缺省政策 iptables -F iptables -X iptables -Z iptables -P INPUT DROP iptables -P OUTPUT ACCEPT iptables -P FORWARD ACCEPT # part 2; 基础的三条防火墙规则 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -p icmp -j ACCEPT # part 3; 拒绝黑名单、开放白名单的设置 iptables -A INPUT -s 10.30.30.0/24 -j REJECT iptables -A INPUT -i enp1s0 -s 192.168.201.254 -j ACCEPT iptables -A INPUT -i enp2s0 -s 192.168.10.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT iptables -A INPUT -i enp2s0 -s 192.168.20.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT iptables -A INPUT -i enp3s0 -s 192.168.30.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT # part 4: 一般通用放行的互联网服务 iptables -A INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT iptables -A INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT iptables -A INPUT -m state --state NEW -p udp --dport 53 -j ACCEPT iptables -A INPUT -m state --state NEW -p tcp --dport 53 -j ACCEPT # part 5: 不要忘记保存规则 iptables-save > /etc/sysconfig/iptables [root@master ~]# sh ~/iptables.sh
未来如果有新的服务设置,那只要在第四部份增加相关的设置规则之后,重新运行这个脚本即可!轻松愉快!
NAT 的全名是 Network Address Translation,字面上的意思是『网络地址的转换』。由字面上的意思我们来想一想, TCP/IP 的网络封包不是有 IP 地址吗?那 IP 地址不是有来源与目的吗?我们的 iptables 指令就能够修改 IP 封包的表头数据, 连目标或来源的 IP 地址都可以修改呢!甚至连 TCP 封包表头的 port number 也能修改!
再回想一下 nat 这个 iptables 的表格有什么链?以及可以进行的动作大概是什么?
参考第六章、图 6.2-1 的图标内容,我们会知道由 switch LAN 用户端要连接到互联网上面, 那么封包应该是会通过 Master 骨干系统进行如下的流程:
NAT 服务器功能主要就是实行上面步骤 1, 4,主要修改的链为 nat 表格的 PREROUTING, POSTROUTING。那个本机 FORWARD 链则需要打开啊! 至于 nat 的链主要的目的:POSTROUTING 在修改来源 IP/port ,PREROUTING 则在修改目标 IP/port。由于修改的 IP 不一样,所以就称为来源 NAT (Source NAT, SNAT) 及目标 NAT (Destination NAT, DNAT)。
当你想要成为 IP 分享器时,那么所有从内部 LAN 要出去到 Internet 的封包,都要将『出去的那个 IP 改为 server 的对外 IP』, 这改的就是『来源』地址,因此称为 SNAT!图标如下:
如上图所示,在用户端 192.168.20.1 这部主机要连接到 http://vbird.cn 去时,他的封包表头会如何变化?
此时 linux.vbird.org 看到这个封包并将要求处理完毕之后,就会将回应封包数据回传到 public IP 上面,linux.vbird.org 并不会知道这个 IP 地址的来源其实是在你的局域网路内~再来想一想,这个从 linux.vbird.org 回传回来的回应封包又是如何回到用户端电脑上? 这里要先注意喔,刚刚传出去的封包状态会有数据纪录在 iptables 的内存中,所以,回应封包是这么跑的:
经过上面这两个流程后,所有内部 LAN 的主机都可以通过这部 Master 服务器连接出去,外界看到这些封包表头信息,都只有 Master 的对外 public IP 而已。所以,如果内部 LAN 主机没有连上不明网站的话,那么内部主机其实是具有一定程度的安全性的啦! 因为 Internet 上的其他人没有办法主动攻击你 LAN 内的 PC 嘛!所以我们才会说,NAT 最简单的功能就是类似 IP 分享器啦!那也是 SNAT 的一种。
那如何达成 SNAT 的功能呢?很简单!通过 SNAT 的『伪装』功能即可!那如果你有好几个 public IP 地址怎办?可以指定某一个嘛? 也是可以的~就说可以改变你的『来源 IP 地址或端口口』功能~基本语法有点像这样:
[root@master ~]# iptables -t nat -A POSTROUTING [-s LAN_IP] [-o 对外界面] -j MASQUERADE [root@master ~]# iptables -t nat -A POSTROUTING [-o 对外界面] -j SNAT [--to 对外的某个IP地址]
第 1 条规则,是针对内部网络的网域 (LAN_IP) ,当该封包要从『对外界面』那张网卡传出时,才伪装 (MASQUERADE) 成为该网卡的 IP 地址之意。第 2 条规则,则是让任何通过『对外界面』的封包,其来源 IP 地址改为固定的『某个IP地址』之意。 因为每个人家里的 public IP 一定不相同,所以使用上面第 1 条会是比较正确的作法。
在前一章我们有事先做了 switch LAN 连接的用户端,现在,请在终端机上面登录系统 (因为我们还没有设置网络), 然后将 client_raw 主机的网络环境设置成为底下这样:
# 1. 设置主机名称: [root@localhost ~]# hostnamectl hostname client001.vbird # 2. 设置正确的网络参数: [root@client001 ~]# nmcli connection modify enp1s0 ipv4.method manual ipv4.addresses 192.168.20.1/24 \ > ipv4.gateway 192.168.20.254 ipv4.dns 120.114.100.1,168.95.1.1 [root@client001 ~]# nmcli connection up enp1s0 [root@client001 ~]# ip addr show enp1s0 2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 52:54:00:00:20:01 brd ff:ff:ff:ff:ff:ff inet 192.168.20.1/24 brd 192.168.20.255 scope global noprefixroute enp1s0 .... [root@client001 ~]# ip route show default via 192.168.20.254 dev enp1s0 proto static metric 100 192.168.20.0/24 dev enp1s0 proto kernel scope link src 192.168.20.1 metric 100 [root@client001 ~]# ping -c 3 192.168.20.254 PING 192.168.20.254 (192.168.20.254) 56(84) bytes of data. 64 bytes from 192.168.20.254: icmp_seq=1 ttl=64 time=0.295 ms 64 bytes from 192.168.20.254: icmp_seq=2 ttl=64 time=0.324 ms 64 bytes from 192.168.20.254: icmp_seq=3 ttl=64 time=0.303 ms --- 192.168.20.254 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2049ms rtt min/avg/max/mdev = 0.295/0.307/0.324/0.012 ms [root@client001 ~]# ping -c 3 -w 3 168.95.1.1 PING 168.95.1.1 (168.95.1.1) 56(84) bytes of data. --- 168.95.1.1 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2043ms # 3. 取得 /etc/hosts 的正确设置 [root@client001 ~]# scp -p 192.168.20.254:/etc/hosts /etc [root@client001 ~]# ping -c 3 masterlan PING masterlan.vbird (192.168.20.254) 56(84) bytes of data. 64 bytes from masterlan.vbird (192.168.20.254): icmp_seq=1 ttl=64 time=0.287 ms ...
简单的发现到,目前这部 client001 系统可以连接到 gateway 上面,但是却无法连接到 internet 上!就是因为我们的 Master 系统尚未处理 SNAT 啦!现在,你加入这条规则在 Master 系统上面看看:
# 1. 在 Master 骨干系统上面修改防火墙,加入 SNAT 机制: [root@master ~]# iptables -t nat -A POSTROUTING -s 192.168.20.0/24 -o enp1s0 -j MASQUERADE # 2. 在 client001 上面,直接 ping 互联网 IP 地址 [root@client001 ~]# ping -c 3 -w 3 168.95.1.1 PING 168.95.1.1 (168.95.1.1) 56(84) bytes of data. 64 bytes from 168.95.1.1: icmp_seq=1 ttl=52 time=4.53 ms ...
这样就做好 SNAT 的 IP 分享器功能!简单方便又快速!很容易理解,对吧!
如果你的系统因为某些缘故,所以需要将本机的 port 2121 用来作为 port 22 的连接端口口,那么需要将 sshd 这个服务启动到非正规的 port 2121 嘛?似乎不太需要~我们可以通过重导向 (redirection) 的方式来处理即可! 其实想法很简单,就是在 PREROUTING 的链里面,让 port 2121 『转向』到 port 22 去而已。亦即是,不论你连接 port 2121 还是 port 22,最终都是连接到 port 22 !而能不能从 port 2121 连接成功,还需要看 port 22 的设置值才行! 更简单的说, REDIRECT 有点像是『符号链接档』的概念,port 22 是实际文件,而 port 2121 则是符号链接 (捷径), 这样想,有没有比较简单呢?
做个简单的测试,先看看我们的 sshd 这个服务启动在哪个端口口?
[root@master ~]# netstat -tlnp
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 665/sshd: /usr/sbin
tcp6 0 0 :::22 :::* LISTEN 665/sshd: /usr/sbin
我们看到仅有 port 22 有放行而已~现在,让我们使用底下的语法来将 port 2121 导向 port 22 看看:
[root@master ~]# iptables -t nat -A PREROUTING -p tcp --dport 2121 -j REDIRECT --to 22
现在,请『从外网连接到 Master 的 port 2121』测试看看能不能连接到 sshd 这个服务呢?非常简单! 测试过后你就知道重导向的意义啦!
管理系统的操作者,通常会希望『系统越简单越好』,所以会希望在一部系统上面只打开一个服务~但是,我们没有这么多的 public IP 可以使用。所以,这个时候就得要通过所谓的端口口对应 (port mapping) 来进行后端主机的服务对应!因为会修改目的地端的网络参数, 所以就称为目标 NAT (destination NAT, DNAT)。简单的说,DNAT 主要是用在内部主机想要架设可以让 internet 访问的服务器。 以下图为例来说明好了:
假设我的 192.168.30.1 启动了 Web 服务器,这个服务的端口口放行在 port 80,那么互联网来源 (IP: aa.bb.cc.dd) 要如何连接到我内部的这部系统? 这当然还是要通过 Master 系统的外部 public IP 来对应端口口的!整个流程分析如下:
同样的,当 192.168.30.1 处理好 aa.bb.cc.dd 这个用户端需要的数据后,将回应封包回传到 Master 主机的 192.168.30.254, 再通过路由将封包转到 public IP 那个界面上,最终再将来源 IP 地址改成 public IP 之后,就可以送回去给 aa.bb.cc.dd 了! 这样也就完成了内部服务器的连接!
至于达成 DNAT 的指令其实也很简单!只要记得封包转递是在 PREROUTING 的链之前处理即可:
[root@master ~]# iptables -t nat -A PREROUTING [-i 外部界面] [-p tcp --dport 分析的端口口] \ > -j DNAT [--to 后端IP:port]
前一章我们有使用了 server_raw 系统附挂在 switch DMZ 的桥接 (serverbr0) 上面,现在请登录该系统,然后将 server_raw 主机的环境设置成为这样:
# 1. 设置主机名称 [root@localhost ~]# hostnamectl hostname server001.vbird # 2. 设置正确的网络参数: [root@server001 ~]# nmcli connection modify enp1s0 ipv4.method manual ipv4.addresses 192.168.30.1/24 \ > ipv4.gateway 192.168.30.254 ipv4.dns 120.114.100.1,168.95.1.1 [root@server001 ~]# nmcli connection up enp1s0 [root@server001 ~]# ip addr show enp1s0 2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 52:54:00:00:30:01 brd ff:ff:ff:ff:ff:ff inet 192.168.30.1/24 brd 192.168.30.255 scope global noprefixroute enp1s0 [root@server001 ~]# ip route show default via 192.168.30.254 dev enp1s0 proto static metric 100 192.168.30.0/24 dev enp1s0 proto kernel scope link src 192.168.30.1 metric 100 [root@server001 ~]# ping -c 3 192.168.30.254 PING 192.168.30.254 (192.168.30.254) 56(84) bytes of data. 64 bytes from 192.168.30.254: icmp_seq=1 ttl=64 time=0.299 ms ... [root@server001 ~]# ping -c 3 -w 3 168.95.1.1 PING 168.95.1.1 (168.95.1.1) 56(84) bytes of data. --- 168.95.1.1 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2048ms # 3. 取得 /etc/hosts 的正确设置 [root@server001 ~]# scp -p 192.168.30.254:/etc/hosts /etc [root@server001 ~]# ping -c 3 masterdmz PING masterdmz.vbird (192.168.30.254) 56(84) bytes of data. 64 bytes from masterdmz.vbird (192.168.30.254): icmp_seq=1 ttl=64 time=0.232 ms ...
基本上,跟之前 client 的设置几乎一模一样!而且 server_raw 系统也是没有对外网络的喔!现在,让我们来激活 SNAT 与 DNAT, 让外部连接可以连到内部 server 上!底下的作法是在 Master 系统上喔!
# 1. 修改 iptables.sh 文件,增加 nat 相关的机制 [root@master ~]# vim ~/iptables.sh .... # part 2.1: 清除 nat 表格的相关数据,并设置缺省政策 iptables -t nat -F iptables -t nat -X iptables -t nat -Z iptables -t nat -P PREROUTING ACCEPT iptables -t nat -P POSTROUTING ACCEPT iptables -t nat -P OUTPUT ACCEPT # part 2.2: 针对 Switch LAN 的 SNAT 设置: iptables -t nat -A POSTROUTING -s 192.168.20.0/24 -o enp1s0 -j MASQUERADE # part 2.3: 针对 Switch DMZ 的 SNAT/DNAT 设置 iptables -t nat -A POSTROUTING -s 192.168.30.0/24 -o enp1s0 -j MASQUERADE iptables -t nat -A PREROUTING -i enp1s0 -p tcp --dport 80 -j DNAT --to 192.168.30.1:80 # part 2.4: 针对本机的 redirection 设置 iptables -t nat -A PREROUTING -p tcp --dport 2121 -j REDIRECT --to 22 # part 2.5: 累加保存规则 iptables-save -t nat >> /etc/sysconfig/iptables [root@master ~]# sh ~/iptables.sh [root@master ~]# iptables-save -t nat *nat :PREROUTING ACCEPT [5:926] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] -A PREROUTING -i enp1s0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.30.1:80 -A PREROUTING -p tcp -m tcp --dport 2121 -j REDIRECT --to-ports 22 -A POSTROUTING -s 192.168.20.0/24 -o enp1s0 -j MASQUERADE -A POSTROUTING -s 192.168.30.0/24 -o enp1s0 -j MASQUERADE
这样就设置妥当了!接下来,请前往 server_raw 安装 WWW 服务器,并且启动该服务 (无须开机启动,目前测试而已), 然后从外部连接到 Master 的 public ip 地址处,应该就可以看到该数据了!
# 1. 先在 server_raw 处,进行底下的实做:(目的:启动 WWW 并且修改首页) [root@server001 ~]# yum install -y httpd [root@server001 ~]# systemctl start httpd [root@server001 ~]# firewall-cmd --add-service=http [root@server001 ~]# echo "I am server001" > /var/www/html/index.html # 2. 再到外部系统上,直接连接到 Master 的网页测试看看即可: [user@outside ~]$ curl http://192.168.201.245 I am server001 <==你会看到首页数据显示的其实是 server_raw 那部主机的网页数据喔!
通过 iptables 的 POSTROUTING 链,我们可以进行 MASQUERADE/SNAT 等任务,通过 PREROUTING 链则可以达成 REDIRECT/DNAT 等任务! 其他用途,就等着各位去开发啦!
大家要有个先入为主的观念:『我提供给互联网访问的系统,很可能会被攻击攻破,而由于这部系统被攻破,同一区段的局域网路, 就很有可能同时被攻击』这样的概念!因此,我们提供的 server_raw 系统,就是那个很有可能被攻击攻破的系统啦! 那,这样的系统,就应该要放置在管制比较严格的区域吧!也就是说,这个区域应该要跟我员工系统放置的区网切开才行吧! 所以啰,将这样对外公开服务的系统,放置到非军事区 (Demilitarized Zone, DMZ) 就是个好主意!
简单的说,所谓的 DMZ 就是只能让互联网或内网来访问的子网络地址,这个子网络地址不可以自己连网!毕竟这部主机『理论上』确实不该自己连网才对! 所以,我们可以从 WAN 主动连到 switch DMZ,可以从 switch LAN 主动连到 switch DMZ,switch DMZ 往外的回应封包 (ESTABLISHED, RELATED) 也可以放行! 但是主要来自 switch DMZ 的主动封包,就应该要拒绝这样!这就是主要非军事区 (DMZ) 的基础想法。
那要怎么达成 DMZ 的想法呢?用哪个 table 哪个 chain?其实,只要修改 FORWARD 这条链就可以了!看看上面 SNAT 与 DNAT, 我们缺省的情况下,都没有管制 FORWARD 对吧!那如果加以管制呢?就可以达成 DMZ 的作法了!只是实做上还得要留意的是, 丢进 DMZ 的 server 应该要先做好网络服务的安装与其他软件的整理,否则由于无法直接主动对外连接,所以恐怕连自我更新都会失败! 那就糗了!所以需要特别注意啊!
设计上面要注意的是,因为 FORWARD 链是在 filter 表格内,因此你得要在 part 5 (保存) 之前,将 FORWARD 规则写好! 所以,鸟哥将这些 DMZ 的信息纪录成为 part 4.5 的部份:
[root@master ~]# vim ~/iptables.sh # part 4.5.1: DMZ 设置 (针对 internet) iptables -A FORWARD -i enp3s0 -o enp1s0 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp1s0 -p udp --dport 53 -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp1s0 -p tcp --dport 53 -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp1s0 -p tcp --dport 443 -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp1s0 -j REJECT # part 4.5.2: DMZ 设置 (针对 intranet) iptables -A FORWARD -i enp3s0 -o enp2s0 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp2s0 -j REJECT # part 5: 不要忘记保存规则 iptables-save > /etc/sysconfig/iptables [root@master ~]# sh ~/iptables.sh
如何测试呢?其实测试挺简单的!大致上我们就来进行底下的动作即可:
请自行测试喔~这样基本上就将 iptables 的大部分主要功能学全啰!真是不容易啊~结果...iptables 被放弃了!哈哈哈! 所以,准备来学其他的防火墙软件吧~
firewalld 是一个防火墙软件,目前这个软件使用的后端其实还是 nftables 的机制,只是 firewalld 具有领域 (zone) 的概念, 每个领域都有其特定的功能,管理员只要选择适当的领域,就可以直接搭配缺省的防火墙规则。举例来说,当使用 external 这个领域时, 只要通过这个领域的网卡送出去的封包,就直接具有伪装的功能了!你不用手动去额外做设置!相当方便!鸟哥认为,这个 firewalld 对於单机环境的 Linux 服务器来说,实在是太方便了!但是,要拿 firewalld 来当防火墙的学习软件,则有点太过简单, 同时,对于 DMZ 或不同的领域转换间的状况,还得要额外使用 policy 方式来处理~那就有点画蛇添足~所以啦,用在单一服务 server 上, 或在单一用户端环境,可以使用 firewalld 就好!简单方便又轻松~如果是放在类似我们的 Master 骨干系统,这个...其实反而有点伤脑筋! 要考虑的点实在太多了....所以,依据环境来选择处理吧!
我们底下还是会用到 firewalld 来达成 Master 骨干服务器的所有防火墙任务,当然,某些任务处理后,会跟我们原本想像的情境结果不相同~ 无论如何,我们还是可以利用这个工具来聊聊防火墙的架设~
基本上,缺省的 RHEL 9.x 衍生品原本缺省的防火墙机制,就是 firewalld 啊!只是我们为了要介绍比较详细的防火墙观念, 所以前一小节使用了 iptables 的服务就是了。现在,就让我们将 nftables, iptables 关闭,然后激活 firewalld 防火墙, 再以 firewall-cmd 这个指令列管理程序来进行查阅吧!不过,由于 iptables 是 nftables 的仿真模块所支持, 如果只是关闭 iptables 似乎没有办法完整的移除其相关模块!建议修改过不同机制后,可能需要 reboot 比较好!
# 1. 仅激活 firewalld 服务 [root@master ~]# systemctl disable --now nftables [root@master ~]# systemctl disable --now iptables [root@master ~]# systemctl enable --now firewalld [root@master ~]# reboot # 加上 --now 可以取代 stop/start 功能!可以达到立即关闭或启动服务 # 另外,由于 iptables 的规则与 firewalld 会冲突,建议变更成为 firewalld 后,可以尝试重新开机! # 2. 检查目前的 firewalld 服务列表 [root@master ~]# firewall-cmd --list-all public (active) <==激活的 zone 为 public target: default <==缺省的政策为 default (相当于 REJECT) icmp-block-inversion: no <==是否拒绝 icmp 封包 interfaces: enp1s0 enp2s0 enp3s0 <==目前放在此 zone 的网络界面 sources: <==针对的来源地址 services: cockpit dhcpv6-client ssh <==提供放行的服务列表 ports: <==提供放行的端口口列表 protocols: <==提供放行的协定列表 forward: yes <==是否支持 forwarder 功能 masquerade: no <==是否支持 NAT 伪装功能 forward-ports: <==转递的端口口 source-ports: <==来源端口口 icmp-blocks: <==拒绝连接的 icmp 类型 rich rules: <==更多详细的自动规则 # 如果没有启动 firewalld 的话,这边就会显示错误了!
firewalld 使用了 firewall-cmd 这个指令来进行管理~在输出的结果中,会有激活的领域 (zone) 名称,那个 target 就是我们在前面章节谈到的『缺省政策』的意思。firewalld 的缺省政策主要有『ACCEPT / DROP / REJECT / default』。 至于 default 的功能,跟 REJECT 相似就是了!同样是缺省拒绝的意思~
services 有点类似公认的服务启动的端口口,就是你要架设什么服务,就得要放行什么服务的端口口,那通过这个 services 来设置即可。 如果有非正规的端口口,例如我们想要放行 VNC 的 5900~5910 这几个连续端口口,就需要用到 ports 那个设计!基本上, firewalld 针对单机环境下,就这几个最重要!其他都可以暂时忽略的!
如前所述,firewalld 预先定义好许多规则,放在不同的领域 (zone) 内,只要调用该领域,你就可以直接套用缺省的防火墙规则! 轻松又方便!那么如何知道目前有多少的领域在 firewalld 里面呢?使用 --get-zones 来查看即可:
[root@master ~]# firewall-cmd --get-zones
block dmz drop external home internal nm-shared public trusted work
要先说明的是,这些领域只对设置的本机有关,例如 block(阻挡/拒绝) 不是自我 block,而是加入这个 block 领域的网卡, 会针对进入本机 (input) 的封包做拒绝 (reject) 的动作之意!所以思考的时候,要想到『这是针对那一张网卡做的设计』喔! 好了,那么上面这些领域的意思到底是什么呢?大概说明如下:
从前一章节的内容,我们也会大概知道 dmz(非军事区)、external(外部连接)、trusted(信任领域) 的意思~以我们前一小节的连接来看, enp1s0 (public ip) 网卡应该要丢进去 external 领域, enp2s0(switch LAN) 网卡应该要丢进去 trusted 或 internal 领域, 而 enp3s0 (switch DMZ) 网卡则应该丢进去 dmz 领域的概念!好了,那么举 internal 为例,这个领域里面的防火墙规则是怎么回事呢? 我们可以使用 --info-zone 来查看:
[root@master ~]# firewall-cmd --info-zone=internal
internal
target: default
....
services: cockpit dhcpv6-client mdns samba-client ssh
....
forward: yes
masquerade: no
....
上面的意思是,缺省的政策 (target) 是 default (类似 REJECT),而放行的服务有 cockpit, dhcpv6-client, mdns, samba-client 与 ssh 等。 至于转递 (forword) 是放行的,不过并不处理伪装问题,因为伪装通常是对外 IP 地址 (public ip) 才会进行的。好像看得懂对吧! 问题是,我们怎么知道 ssh 或 mdns 或 cockpit 这种服务的端口口是 TCP 或 UDP 以及其端口口号码呢?
就跟许多已知服务 (known service) 一样,firewalld 为了管理员设置方便,有指定许多的服务了!放行该服务就可以放行某些特定的端口口。 要查找到底有哪些内置服务?可以这样做:
[root@master ~]# firewall-cmd --get-services
RH-Satellite-6 .... ssh ...zabbix-server zerotier
数据真的是非常非常多!如同上面的说明,那么类似 ssh 以及 cockpit 到底是放行什么鬼呢?我们可以这样看:
[root@master ~]# firewall-cmd --info-service=ssh ssh ports: 22/tcp <==缺省放行的端口口号码/协定 protocols: <==针对的协定 source-ports: <==来源端口口 modules: <==使用的模块 destination: <==目的地地址 includes: <==其他包含项目 helpers: <==其他协助模块 [root@master ~]# firewall-cmd --info-service=cockpit cockpit ports: 9090/tcp ...
很简单看到 ssh 主要是放行 port 22/tcp 这个端口口而已!同理, cockpit 服务则是放行 port 9090/tcp。有没有多重端口口的设置呢? 例如我们 iptables 章节谈到的 DNS 似乎就得要有 udp 与 tcp!好啊!那就来瞧一瞧 dns 服务好了:
[root@master ~]# firewall-cmd --info-service=dns
dns
ports: 53/tcp 53/udp
...
果然是多重端口口的放行在 dns 服务上!这样我们未来设置服务,就非常方便!不用去管端口口了!用缺省的服务搭配标准端口口即可。 另外,我们可能会用到的还有一个 ftp 服务!这个服务挺有趣!我们先来看一看好了:
[root@master ~]# firewall-cmd --info-service=ftp
ftp
ports: 21/tcp
....
helpers: ftp
除了 ftp 缺省的端口口为 21/tcp 之外,竟然还有个协助模块 (helpers) 的功能!同样也是 ftp 这个名称!那这个协助功能在干麻? 我们来瞧一瞧:
[root@master ~]# firewall-cmd --info-helper=ftp
ftp
family:
module: nf_conntrack_ftp
ports: 21/tcp
重点在这个协助模块使用了 nf_conntrack_ftp 这个防火墙插件模块的功能~FTP 服务是个很复杂的东西,连接成功之后会激活两条信道, 一条是指令信道,一条是数据信道,这个 nf_conntrack_ftp 则是在监听指令信道,并告知防火墙应该要主动放行哪一条数据信道的意思! 有这个模块,架设 FTP 服务会方便许多!这就是 firewalld 的 helper 功能!
上述这些 zone 与 servcie 还有其他种种的数据,其实都放置到底下的两个目录:
大部分的数据都以 xml 样式存在,有兴趣的朋友可以自行查阅看看喔
实际管理 firewalld 防火墙,可以使用 firewall-cmd 来处理!从上面的说明来看,我们要管理的, 可能有 (1)缺省领域的查看与切换功能、(2)黑/白名单的创建、(3)服务的增加与删除、(4)非标准端口口的放行。大致上就这样! 就让我们来练习一下 firewall-cmd 吧!
firewalld 的默认值是在 /usr/lib/firewalld/ 目录下,目前的实际设置档则是在 /etc/firewalld/ 目录内~但是, 内存还有 firewalld 的运作状态!在当下的环境中,我们可以分别查找/写入纪录档,或者是直接运行!一般来说, 操作 firewall-cmd 有两种常见的模式,说明如下:
以鸟哥的建议来说,使用第一种方式可能比较好,这是因为我们可能会进行很多测试,测试成功之后,那就成功了~结果就是忘记写入设置~ 最终 reboot 后,规则就消失不见了...另外,如果后面还想要学习规则政策 (policy) 的建置,该方式仅能支持写入设置档后加载的模式, 所以,为了统一处理流程,还是使用先写入设置档后立刻加载的方式比较好喔!
取得目前活动中的领域,以及缺省的领域,可以这样处理:
# 1. 取得目前的缺省领域 [root@master ~]# firewall-cmd --get-default-zone public # 2. 取得活动中的领域 [root@master ~]# firewall-cmd --get-active-zones public interfaces: enp1s0 enp2s0 enp3s0
如上所示,目前活动中的领域为 public,我们目前的三张网卡,通通包含在这个领域当中。现在,就依据之前的设计, 让我们将不同的网卡放置到不同的领域去~基本的想法是这样:
# 1. 开始切换不同的领域搭配的网卡 [root@master ~]# firewall-cmd --permanent --change-interface=enp1s0 --zone=external The interface is under control of NetworkManager, setting zone to 'external'. success # 由于 firewalld 的防火墙领域,针对网卡来说,还是跟 NetworkManager 有关!所以这里会给说明! # 不用担心!结果还是成功的! [root@master ~]# firewall-cmd --permanent --change-interface=enp2s0 --zone=internal [root@master ~]# firewall-cmd --permanent --change-interface=enp3s0 --zone=dmz [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --get-active-zones dmz interfaces: enp3s0 external interfaces: enp1s0 internal interfaces: enp2s0 # 最终三张网卡就放置到三个不同的领域去! # 2. 设置缺省领域成为 external [root@master ~]# firewall-cmd --set-default-zone=external # 3. 检查 NetworkManager 的设置档,跟 zone 有关的设置: [root@master ~]# nmcli connection show enp1s0 | grep zone connection.zone: external [root@master ~]# nmcli connection show enp2s0 | grep zone connection.zone: internal [root@master ~]# nmcli connection show enp3s0 | grep zone connection.zone: dmz [root@master ~]# nmcli connection up enp1s0 [root@master ~]# nmcli connection up enp2s0 [root@master ~]# nmcli connection up enp3s0 # 可以看到使用 firewall-cmd 设置网卡对应 zone 时,NetworkManager 会同步写入! # 为了准确性,我们还是重新加载一次各个网卡比较妥当!
由于我们使用的网络管理机制其实是 NetworkManager 这个服务,这个服务内容有个 zone 的设置,那就是跟 firewalld 连动的设置值! 这个设置值必须要写入系统去~所以,改变了领域对应的网卡之后,一定要写入设置值才行! 在目前的情况下,internal 以及 dmz 领域所在的系统,基本上还是不能连网的~等等我们谈到最简单 SNAT 时再来处理。 接下来我们先了解一下怎么设置黑/白名单,通过 zone 的预先设置方式来处置!
每个 zone 里面都有个名为 sources 的设置项目,我们也知道有两个 zone 很有趣,分别是信任 (trusted) 与拒绝 (block) 领域! 那么,能不能简单的将信任的来源与可恶的来源分别放置到这两个 zone 去?赞啊!这就是简易的黑/白名单制作方式了! 处理的方式挺简单:
# 将 192.168.201.254, 10.30.30.0/24 分别加入白/黑名单 [root@master ~]# firewall-cmd --permanent --add-source=192.168.201.254 --zone=trusted [root@master ~]# firewall-cmd --permanent--add-source=10.30.30.0/24 --zone=block [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --get-active-zones block sources: 10.30.30.0/24 <==黑名单 dmz interfaces: enp3s0 external interfaces: enp1s0 internal interfaces: enp2s0 trusted sources: 192.168.201.254 <==白名单
鸟哥觉得白名单的制作挺棒的!这是因为我们可能会从外部登录 master 骨干系统来设计一些有的没有的,此时, 我们外部的工作机 (范例中的 192.168.201.254) 就是一个可以加入白名单的来源!如此可以避免不小心的错误设置, 结果把自己挡死了的问题...如果设错 source 的话,可以通过 --remove-source 来删除规则!
内部网域 (internal) 所在的主机,应该可以使用我们骨干服务器比较多的服务才合理!假设我们的 Master 骨干系统有 NTP 服务器 (时间服务器), 并且打算提供给这个网域来使用,这时可以通过这样的方式来处理:
# 1. 增加提供服务的端口口给 internal 的系统 [root@master ~]# firewall-cmd --permanent --zone=internal --add-service=ntp [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --zone=internal --list-services cockpit dhcpv6-client mdns ntp samba-client ssh # 2. 假设 http, https, dns 服务也提供吧! [root@master ~]# firewall-cmd --permanent --zone=internal --add-service={http,https,dns} [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --zone=internal --list-services cockpit dhcpv6-client dns http https mdns ntp samba-client ssh
那如果要移除服务呢?举例来说,将 dmz 的 ssh 服务移除,应该是个好主意!
# 列出 dmz 领域支持的服务,然后移除该服务! [root@master ~]# firewall-cmd --zone=dmz --list-services ssh <==还是提供了 DMZ 端的 ssh 连接!准备取消! [root@master ~]# firewall-cmd --permanent --zone=dmz --remove-service=ssh [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --zone=dmz --list-services
你会发现最终指令输出会是一片空白!因为已经有提供连接登录的服务啰!对于 DMZ 来说,还是这样比较妥当!
某些时刻,我们某些端口口可能得要放行在非正规的端口口上!举例来说,许多内部的服务器,可能都会通过 8081, 3000 等端口口来提供某些特定的服务~那么,firewalld 如何处理这个东西呢?很简单,通过 port/tcp 或 port/udp 来规范即可! 例如上面两个端口口同时加入,可以这样做:
# 1. 加入独立的端口口号码 [root@master ~]# firewall-cmd --permanent --zone=internal --add-port={3000/tcp,8081/tcp} # 2. 加入连续的端口口号码 [root@master ~]# firewall-cmd --permanent --zone=internal --add-port=5901-5910/tcp [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --zone=internal --list-ports 3000/tcp 5901-5910/tcp 8081/tcp
一般来说,针对单机服务器而言,了解上述的功能就很棒了!其他不用再学习啦!现在,让我们来想想:
你需要登录 client001 以及 server001 做底下的动作喔:
# 1. 在 client001 上面进行如下的动作: [root@client001 ~]# firewall-cmd --set-default-zone=work [root@client001 ~]# firewall-cmd --permanent --add-source=192.168.20.254 --zone=trusted [root@client001 ~]# firewall-cmd --reload [root@client001 ~]# firewall-cmd --get-active-zones trusted sources: 192.168.20.254 work interfaces: enp1s0 [root@client001 ~]# firewall-cmd --list-all work (active) target: default icmp-block-inversion: no interfaces: enp1s0 sources: services: cockpit dhcpv6-client ssh ... # 要注意 (1)白名单的 IP 地址是否正确? (2)services 的服务是否正确!这样就搞定了! # 2. 在 server001 上面进行如下的动作: [root@server001 ~]# firewall-cmd --permanent --add-service={http,https,ftp} [root@server001 ~]# firewall-cmd --reload [root@server001 ~]# firewall-cmd --list-all public (active) target: default icmp-block-inversion: no interfaces: enp1s0 sources: services: cockpit dhcpv6-client ftp http https ssh ...
很简单就处理完毕!不用在那边撰写 iptables 脚本!所以说,对於单机且服务相对单纯的系统来说,使用 firewalld 就对了!
要达成真的比较好的 IP 伪装、DNAT、DMZ 等功能,最好是要学习一下 rich rules 搭配 policy 的流程, 但是如果只是在自己家里非常小的网络环境上,那就不需要这么麻烦~有个超简单的 SNAT 可以来进行伪装! 只是无法达成 DMZ 的控制就是了。
在目前的情况下,基本上,client001 与 server001 在 internal 或 dmz 的领域中,都是无法对外的! 要讲到对外,最简单就是将需要伪装的 IP 地址加入 external 领域中就搞定了!也就是说, 让 internal 内的 192.168.20.0/24 以及 dmz 内的 192.168.30.0/24 加入 external 领域,成为该领域的 source 即可!
# 1. 将两段子网络加入伪装列表中 [root@master ~]# firewall-cmd --permanent --add-source={192.168.20.0/24,192.168.30.0/24} --zone=external [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --get-active-zones block sources: 10.30.30.0/24 dmz interfaces: enp3s0 external interfaces: enp1s0 sources: 192.168.20.0/24 192.168.30.0/24 internal interfaces: enp2s0 trusted sources: 192.168.201.254
现在,你的 server001 与 client001 两部系统,应该都可以顺利对外连接了!但是,dmz 领域也同样可以连接到 internal 领域去, 彼此都没有限制了喔!所以就几乎没有管制就是了!
还记得我们在 DNAT 的章节中,特别强调在 PREROUTING 有 DNAT 与 REDIRECT 的功能吧?那么这个 firewalld 如何达到封包的转递呢? 基本语法有点像底下这样:
firewall-cmd --add-forward-port=port=端口口号码:proto={tcp|udp}:toport={端口口号码}:toaddr={IP地址}
好像很简单对吧!基本上,用在单机服务器或用户端,这个方式是没问题的!但如果是用在比较复杂的 master 骨干系统上呢? 来测试看看。假设我们让 Master 本机的 port 2121 可以导向 port 22 时,应该是这样处理:
[root@master ~]# firewall-cmd --permanent --add-forward-port=port=2121:proto=tcp:toport=22 [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --list-forward-ports port=2121:proto=tcp:toport=22:toaddr=
这样就搞定了!你到 external 的外网部份尝试『 ssh -p 2121 192.168.201.245 』时,就会连接到 port 22 了! 也算是非常简单!那么如果需要往后端的 DMZ 子网络的系统转递呢?举例来说,从 external 领域来的封包, 遇到 port 80 就转递到 192.168.30.1:80 时,那可以这样做:
[root@master ~]# firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toaddr=192.168.30.1 [root@master ~]# firewall-cmd --reload
好像没什么问题啊!很快就做好设置!轻松愉快!
其实上面的设置会出状况喔~我们来测试一下你就知道问题在哪里了:
# 1. 尝试从 client001 连接到 masterlan 上面 [root@client001 ~]# ssh -p 2121 masterlan # 这时,好像很正常啊!没啥问题!真的嘛?那么来测试一下底下的状况: # 2. 尝试从 client001 连接到 10.0.0.1(这台主机并不存在) 上面 [root@client001 ~]# ssh -p 2121 10.0.0.1 The authenticity of host '[10.0.0.1]:2121 ([10.0.0.1]:2121)' can't be established. ED25519 key fingerprint is SHA256:Va9Z79fb2Kv16vT9QP46iRksvxDayLYKcMY64n3txv8. This host key is known by the following other names/addresses: ~/.ssh/known_hosts:3: [masterlan]:2121 Are you sure you want to continue connecting (yes/no/[fingerprint])? ^C
见鬼了!怎么会连接到一个不存在的 IP 地址时,竟然出现也是连接到 master 骨干系统上面的信息? 只有 port 2121 才这样嘛?来来~来测试看看 port 80 会变怎样?
# 1. 正常情况下,连接到 masterlan 的 port 80 看网页,应该是被导向 server001 [root@client001 ~]# curl http://masterlan/ I am server001 # 2. 那如果连接到鸟站的 port 80 呢? [root@client001 ~]# curl http://vbird.cn I am server001 # 同样的,结果通通导向 server001,怎么会这样?
这个问题是这样发生的,因为上面的端口口转递,只有分析到封包的『目标端口口』而已,并没有分析其他特别的项目, 包括绑定来源 IP 或来源界面,所以当你从 lan 要外出时,却也被该规则分析到符合项目,那就造成这个特别的困扰了! 怎么办呢?基本上,最简单的方法,就是使用特别的不会被用到的端口口来做 port mapping!例如当使用 port 8080 时, 就传到 192.168.30.1:80 上!如此一来,就『比较』可以避免这个困扰。
# 1. 删除刚刚创建的 REDIRECT/DNAT 功能 [root@master ~]# firewall-cmd --permanent --remove-forward-port=port=2121:proto=tcp:toport=22 [root@master ~]# firewall-cmd --permanent --remove-forward-port=port=80:proto=tcp:toport=:toaddr=192.168.30.1 # 2. 让 port 8080 可以连接到 192.168.30.1:80 上 [root@master ~]# firewall-cmd --permanent --add-forward-port=port=8080:proto=tcp:toport=80:toaddr=192.168.30.1 [root@master ~]# firewall-cmd --reload # 3. 在不同的系统上 (看底下指令前的主机名称) 测试 [root@cloud ~]# curl http://192.168.201.245 curl: (7) Failed to connect to 192.168.201.245 port 80: No route to host [root@cloud ~]# curl http://192.168.201.245:8080 I am server001 # 在外部系统上面测试,看起来整体效果是正确的!只有 port 8080 才会生效! [root@client001 ~]# curl http://masterlan curl: (7) Failed to connect to masterlan port 80: No route to host [root@client001 ~]# curl http://masterlan:8080 I am server001 [root@client001 ~]# curl http://192.168.30.1 I am server001 # 内部网段传向 master 的 8080 端口口是成功导向,而主动前往 DMZ 也是没问题! [root@client001 ~]# curl http://vbird.cn # 也确实可以看到鸟站的数据喔!
得要使用非正规端口口来分析,确实很有点困扰!那如何使用复合式的规则呢?可能就得要用到 rich rule 了! 毕竟是非正规~所以,还是将这个转递功能先取消:
[root@master ~]# firewall-cmd --permanent --remove-forward-port=port=8080:proto=tcp:toport=80:toaddr=192.168.30.1 [root@master ~]# firewall-cmd --reload
虽然 firewalld 还挺简单的,但是如果要处理复杂的任务时,恐怕就得要额外设置了!例如要规范某个网域才能使用 ssh 时, 就不是简单的加入来源地址而已~你可能需要使用 rich rule 的项目才行!详细的用法可以参考『 man firewalld.richlanguage 』的说明, 该说明就只要看 example 的部份即可!相信大家都能看得懂才对!
现在,假设我们要让 Master 系统的对外 (enp1s0) 只有 192.168.201.0/24 这个网域可以使用 ssh 服务, 并且将对外的 ssh 服务关闭!这时应该要这样处理:
[root@master ~]# firewall-cmd --permanent --remove-service=ssh [root@master ~]# firewall-cmd --permanent --add-rich-rule='\ > rule family="ipv4" source address="192.168.201.0/24" service name="ssh" accept' [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --list-all external (active) target: default icmp-block-inversion: no interfaces: enp1s0 sources: 192.168.20.0/24 192.168.30.0/24 services: ... rich rules: rule family="ipv4" source address="192.168.201.0/24" service name="ssh" accept
我们可以看到服务的部份已经没有提供 ssh 的连接,但是在 rich rule 的地方,则有提供单一网域打开的 ssh 功能! 这样是管理比较好的方式!不要让 ssh 对全世界放行~也就是说,在外网的部份,ssh 是有针对性的~不是谁都可以连接过来的意思。
我们知道 DMZ 的主要功能其实是需要通过 external 来的封包进行处理~也就是说,从 external 来的封包,通过某些服务, 就可以转到 dmz 内。使用简易的 --add-forward-port 如果怪怪的,那使用 rich rules 可能不失为一个简单有效的方法! 我们可以分析,当封包的『目标地址』是 external 所在的 IP 地址 (就是对外 IP),且服务为 port 80 时,就予以转递~ 处理的方式会有点像这样:
[root@master ~]# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" destination address="192.168.201.245" \ > forward-port port="80" protocol="tcp" to-port="80" to-addr="192.168.30.1"' [root@master ~]# firewall-cmd --reload
那么我们需要的本机 port 2121 转到 port 22 应该也能通过这个方法来处理嘛?简单的说,就是要连接到 192.168.201.245 这个我们 master 主要对外的 IP 地址上的 port 2121 时,才转向本机的 port 22,那处理方法会变怎样呢?
[root@master ~]# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" destination address="192.168.201.245" \ > forward-port port="2121" protocol="tcp" to-port="22" ' [root@master ~]# firewall-cmd --reload
使用上相当的方便!如果还有更多的需求,倒是可以参考 man firewalld.richlanguage 的内容说明!
到目前为止,我们设计的 firewalld 防火墙,大部分都还是针对单一领域来设计,也可以使用 rich rules 进行 forward port 等功能。 那如果需要使用类似 iptables 可以使用 -i 与 -o 声明进/出界面的方式,而不是直接绑死 IP 地址的方式来处理时,该如何是好? 这个时候,可能就得要通过 policy 这个特殊的功能来实做了!
一般 policy 的语法会有点像这样:
# 1. 一定要有的部份: # firewall-cmd --permanent --new-policy=政策名称 # firewall-cmd --permanent --policy=政策名称 --add-ingress-zone=来源领域 # firewall-cmd --permanent --policy=政策名称 --add-egress-zone=目标领域 # firewall-cmd --permanent --policy=政策名称 --set-target=[ACCEPT|DROP|REJECT|CONTINUE] # 2. 具有特殊规则的部份 # firewall-cmd --permanent --policy=政策名称 --add-service=放行服务 # firewall-cmd --permanent --policy=政策名称 --add-rich-rule=放行的特殊规则 # firewall-cmd --permanent --policy=政策名称 --add-rich-rule=放行的特殊规则 # firewall-cmd --permanent --policy=政策名称 --set-priority=[-32768~32768] # firewall-cmd --reload
上面的许多设置参数,你应该都看得懂才对~ingress-zone 为规范的封包规则之来源领域,同理, egress-zone 则是封包规则路由之后发送出去的领域。除了正常的 external, public... 等缺省的领域之外, 还有两个特别的名词要注意:
另外,set-target 的部份,基本上分为 4 种状态:
除此之外,那个 set-priority 是什么鬼?我们可能会有许多的 policy 存在,那么哪一个 policy 会优先被运行呢? 不是考虑 policy 加载的顺序,而是通过 priority 这个设置值!这个设置值越小就越优先!大概是这样!
使用 policy 对 internal 传向 external 的封包进行伪装的功能,使用 policy 还算简单!不过,如果想要制定的更严格, 例如只有来自 internal 且 IP 地址在 192.168.20.0/24 才能进行伪装时,那就可以使用底下的方式啰!
# 1. 先移除原本加在 external 的两个内部 source IP 地址~ [root@master ~]# firewall-cmd --get-active-zones .... external interfaces: enp1s0 sources: 192.168.20.0/24 192.168.30.0/24 .... [root@master ~]# firewall-cmd --permanent --remove-source={192.168.20.0/24,192.168.30.0/24} [root@master ~]# firewall-cmd --reload # 2. 创建名为 intranet 的新政策,针对 internal 来源与任何目标领域,同时缺省流向为 REJECT [root@master ~]# firewall-cmd --permanent --new-policy=intranet [root@master ~]# firewall-cmd --permanent --policy=intranet --add-ingress-zone=internal [root@master ~]# firewall-cmd --permanent --policy=intranet --add-egress-zone=ANY [root@master ~]# firewall-cmd --permanent --policy=intranet --set-target=REJECT [root@master ~]# firewall-cmd --reload # 3. 放行 192.168.20.0/24 的使用权!且查阅 intranet 的政策内容 [root@master ~]# firewall-cmd --permanent --policy=intranet --add-rich-rule='rule family="ipv4" \ > source address="192.168.20.0/24" accept' [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --info-policy=intranet intranet (active) priority: -1 target: REJECT ingress-zones: internal egress-zones: ANY services: ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: rule family="ipv4" source address="192.168.20.0/24" accept
这个时候,你的 internal 领域内的任何主机 (相当于你的 LAN),就被放行了!要去哪边都可以! 从 external 出去时,就伪装 IP 地址外出~从 dmz 出去时,就保持原有的 IP 地址~看起来就相当简单方便啊~
因为 DMZ 未来要放置 public server,所以看起来不应该提供太多的功能才对!因此,我们实做一个 dmz2wan 的政策, 这个政策里面,只让来自 dmz 的封包可以外出 external,且仅有 dns, http, https 可以主动对外,其他连接通通拒绝! 作法似乎也不怎么难:
# 1. 创建名为 dmz2wan 的政策,且来源领域/目标领域分别为 dmz/external,缺省流向为 REJECT [root@master ~]# firewall-cmd --permanent --new-policy=dmz2wan [root@master ~]# firewall-cmd --permanent --policy=dmz2wan --add-ingress-zone=dmz [root@master ~]# firewall-cmd --permanent --policy=dmz2wan --add-egress-zone=external [root@master ~]# firewall-cmd --permanent --policy=dmz2wan --set-target=REJECT # 2. 仅放行 dns, http, https 的服务: [root@master ~]# firewall-cmd --permanent --policy=dmz2wan --add-service={dns,http,https} [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --info-policy=dmz2wan dmz2wan (active) priority: -1 target: REJECT ingress-zones: dmz egress-zones: external services: dns http https ...
接下来你可以前往 192.168.30.1 这部 server001 测试一下,就能知道我们的设置是否成功!包括这样:
所以说,在 master 骨干服务器上面实做 firewalld 时,得要熟悉 policy 的制定方式才好!而要制定 policy,对于网络与 iptables 要有一定程度的了解会比较好!加油加油!
如前所述,nftables 也是 netfilter 的机制之一,开发的目的是因为 iptables 的代码太过杂乱,为了统一格式与代码, 所以使用了 nftables 来进行管理~nftables 同样使用了表格与链的方式来进行规则的设置,同样保留了 nat, prerouting, postrouting 等特殊关键字,同样将防火墙规则写入到链 (chain) 里面去。比较不同的是,nftables 的表格与链的名称,都可以随便你设置! 所以弹性很高!而跟 iptables 的启动脚本都要去读取 /etc/sysconfig/iptables 不同,你可以在 /etc/sysconfig/nftables.conf 去指定你要启动的脚本名称~甚至可以在其他脚本内调用额外的脚本~方便管理员进行不同的环境设置。
总之,目前的 Linux 系统,内核已经转向 nftables 了,不论你喜欢不喜欢,都得要碰一碰 nftables 啦!闪不掉, 就好好学习吧!呵呵~
跟前面一样,基本上,防火墙软件,你就只能启动其中一个!我们这边要启动的是 nftables,开始来处理一下这个新的防火墙机制啰。 由于 firewalld 的底层其实用的就是 nftables,所以,从 firewalld 切换到 nftables,就不需要重新开机啦!来测试看看:
# 1. 三种防火墙软件,仅打开 nftables 服务: [root@master ~]# systemctl disable --now iptables [root@master ~]# systemctl disable --now firewalld [root@master ~]# systemctl enable --now nftables # 2. 激活缺省的防火墙机制:先修改设置档 /etc/sysconfig/nftables.conf [root@master ~]# vim /etc/sysconfig/nftables.conf include "/etc/nftables/main.nft" # 大概在第 4 行,将注解 # 拿掉即可! [root@master ~]# systemctl restart nftables [root@master ~]# nft list chains table inet nftables_svc { chain allow { } chain INPUT { type filter hook input priority 20; policy accept; } }
基本上,在 nftables 的环境下,所有的表格、链、规则等等,都需要管理员自己手动设置!虽然系统有缺省给予一个规则, 但是却缺省没有激活...所以,我们先来设计一下,让系统可以加载最阳春的防火墙设置啰!缺省仅有针对本机的设置~ 最终使用 nftables 的管理工具,亦即是 nft 这个指令来观察一下目前的链的数量,与链的缺省政策!目前到这里应该就 OK 啦! 准备让我们来看看上述的表格、链、规则又是啥鬼~
跟 iptables 缺省就有定义好需要的表格不同,nftables 所有的数据都要管理员自己指定~该如何制定表格?链? 让我们从观察缺省的数据去学习吧!
让我们来观察一下在缺省的 RHEL 9.x 衍生品当中,缺省的防火墙表格是怎么回事?
# 输出缺省的 nftables 的表格名称与格式 [root@master ~]# nft list tables table inet nftables_svc table [IP地址格式] [表格名称]
输出的格式『table IP地址格式 表格名称』,所以,缺省的防火墙表格中,仅有一个名为 nftables_svc 的表格, 该表格支持的 IP 地址格式为 inet 格式的意思~那个 nftables_svc 的名称是可变的,不一定要用该名称! 然后, inet 又是符合什么样的 IP 地址格式?底下是常见的 IP 地址格式:
根据上面的说明,我们可以简单的通过 nft add 来创建表格~假设我们要针对 ipv4 的地址进行封包分析, 假设表格名称为 mytable,那么创建这个表格的方法会是这样:
[root@master ~]# nft add table ip mytable [root@master ~]# nft list tables table inet nftables_svc table ip mytable
这样就简单的创建好一个自订的表格了!
跟 iptables 一样,表格内就是链啦!列出链的名称与缺省政策如下:
[root@master ~]# nft list chains table inet nftables_svc { chain allow { } chain INPUT { type filter hook input priority 20; policy accept; } } table ip mytable { }
我们可以看到两个表格,mytable 刚刚创建,内容是空的!至于 nftables_svc 则有两个链,分别是 allow 跟 INPUT, allow 的内容很少,而 INPUT 则应该是针对连接到本机的封包所作的链的样子!在 INPUT 底下那一行, 则是针对 INPUT 的规则描述!因为链的名称是可以自订的,所以我们还是得要规范这条链到底是针对输入/输出/路由等做的规范! 同时给予缺省的政策数据。上面的链的规范基本语法如下:
type [链的类型] hook [主要针对的封包流向] priority [优先序数值]; policy [缺省政策];
每一种类型 (type) 可以使用的封包流向 (hook, 钩连) 并不相同!根据 Red Hat 手册的说明,每种链的类型可适用的 IP 地址格式与封包流向方式, 简单的说明如下:
类型(type) | 适用的IP地址格式 | 可用的封包流向(hook) | 应用说明 |
---|---|---|---|
filter | 适用所有格式 | 适用所有流向 | 主要的标准链格式 |
nat | ip, ip6, inet | prerouting, postrouting, input, output | 适用于进行 NAT 的功能! |
route | ip, ip6 | output | 当封包的表头经过修改,经由路由过后,可通过这个链来进行后续分析 |
在链的类型以及封包流向 (type 与 hook) 之后,设置的是优先序的数值~在 nftables 当中,因为没有缺省的链, 所以每个链就没有直接的关系,不像 iptables 是由缺省的 prerouting-->forward-->postrouting 这样。 既然每个链都没有缺省的关系,那么如何知道那一个链要先运作?这就得要通过这个 priority 的参数来处理! 而为了方便管理员设置,事实上,还是有一些可以参考的文本与优先序相关性的默认值,列表如下:
优先序文本 | 代表的数值 | 适用的 IP 格式 | 封包流向功能 |
---|---|---|---|
raw | -300 | ip, ip6, inet | all |
mangle | -150 | ip, ip6, inet | all |
dstnat | -100 | ip, ip6, inet | prerouting |
-300 | bridge | prerouting | |
filter | 0 | ip, ip6, inet, arp, netdev | all |
-100 | bridge | all | |
security | 50 | ip, ip6, inet | all |
srcnat | 100 | ip, ip6, inet | postrouting |
300 | bridge | postrouting | |
out | 100 | bridge | output |
事实上,你也可以发现,dstnat (dnat) 比较优先,然后是 filter,最终才是 srcnat (snat),跟以前学的 iptables 也没有差太多啦! 哈哈!所以我们才要先了解 iptables 啊!最后的政策 (policy) 就是缺省的封包动作!主要大概就两个:
所以缺省的 INPUT 链,基本规范说的是:『针对所有 IP 地址的类型,针对封包流向为输入的方式,优先序数值为 20,缺省政策为接受』。 因为这条基本规范,我们也才能确认 INPUT 链就是针对流进本机的封包所制定的规则。
因为链是在表格内置立的,所以,要创建链的时候,当然要说明是在那一个表格内!所以,基础的语法会有点像这样:
[root@master ~]# nft add chain [IP格式] [表格名称] [链的名称] { \ > type [链的类型] hook [封包流向] priority [优先序] \; policy [缺省政策] \; }
因为是指令模式,在指令模式内,分号 (;) 是有特别意义的,因此在这里使用跳脱符号去处理他!你也可以使用单引号来将所有的指令放在一起, 这样也不用加上跳脱符号!现在,让我们创建一个新的链,这个链想要管理输入的封包,那么你应该需要的数据有:
所以整个添加链的指令就会变成这样:
[root@master ~]# nft 'add chain ip mytable myinput { type filter hook input priority filter; policy accept; }' [root@master ~]# nft list chains [root@master ~]# nft list chain ip mytable myinput table ip mytable { chain myinput { type filter hook input priority filter; policy accept; } }
查看链的缺省流向设置有两种方式,一种直接使用『 nft list chains 』列出所有的链即可,一种则是列出某个表格的链名称, 如上所示,最后一个指令主要是列出 mytable 内的 myinput 数据而已!检查上面会比较单纯!
上一小节谈到 table 以及 chain 之后,现在要来讨论的就是规则 (rule) 了!要学先要偷,所以,我们先来偷偷瞧一瞧缺省的规则, 了解缺省的规则之后,再来模仿与创建我们自己的规则~然后学习一下如何添加、移除规则~并且开始学习创建运行脚本的方式来建置整体规则啰!
我们先来瞧瞧全部 nftables 的规则,可以使用 list ruleset 指令来处理:
[root@master ~]# nft list ruleset
table inet nftables_svc {
set allowed_protocols {
type inet_proto
elements = { icmp, ipv6-icmp }
}
set allowed_interfaces {
type ifname
elements = { "lo" }
}
set allowed_tcp_dports {
type inet_service
elements = { 22, 9090 }
}
chain allow {
ct state established,related accept
meta l4proto @allowed_protocols accept
iifname @allowed_interfaces accept
tcp dport @allowed_tcp_dports accept
}
chain INPUT {
type filter hook input priority 20; policy accept;
jump allow
reject
}
}
table ip mytable {
chain myinput {
type filter hook input priority filter; policy accept;
}
}
我们可以看到两个 chain,分别是 allow 以及 INPUT 两个~因为 allow 并没有 type 或 hook 的相关说明, 因此缺省情况下,仅有 INPUT 链会被运行~分析 INPUT 链,我们只有看到 jump allow 以及 reject 而已! jump allow 意思是跳向 allow 链的意思~所以,会主动去分析 allow 那个链的内容!该内容有 4 条规则!
其实,缺省的设置值写的非常有结构,但是初学者很难看得懂~所以,我们还是拿当初的 iptables 三条最阳春的防火墙规则来说明! 还记得是那三条吗?分别是 (1)放行回应封包 (2)放行 ICMP 封包 (3)放行 lo 设备!所以,我们就通过上述的语法, 然后在我们的 myinput 链里面增加这三条规则测试看看。不过,首先要先知道基本语法!相关语法有点像这样:
基本语法: nft add rule [IP类型] [表格名称] [链名称] [规则项目]
基础范例: nft add rule ip mytable myinput
规则项目与 iptables 互相比较,常见的项目有:
现在就让我们来处理三条基础防火墙规则吧!
# 1. 放行回应封包,所以需要用到 ct state 这个设置: [root@master ~]# nft add rule ip mytable myinput ct state established,related accept # 2. 放行 ICMP 这个检测网络的封包协定,所以要用 meta l4proto 设置: [root@master ~]# nft add rule ip mytable myinput meta l4proto icmp accept # 3. 放行 lo 这个设备成为信任设备,所以需要 iifname 这个设置: [root@master ~]# nft add rule ip mytable myinput iifname lo accept # 4. 列出刚刚设置的规则 [root@master ~]# nft list ruleset [root@master ~]# nft list ruleset [ip|ip6|inet...] [root@master ~]# nft list ruleset ip table ip mytable { chain myinput { type filter hook input priority filter; policy accept; ct state established,related accept meta l4proto icmp accept iifname "lo" accept } }
这样就搞定基础防火墙设置~现在,继续来放行其他的设置值!包括白名单/黑名单/特定网域放行的服务设计, 同时针对特定网卡的处理!来测试看看:
# 1. 放行 192.168.201.254 成为白名单: [root@master ~]# nft add rule ip mytable myinput ip saddr 192.168.201.254 accept # 2. 拒绝 10.30.30.0/24 整体网域成为黑名单: [root@master ~]# nft add rule ip mytable myinput ip saddr 10.30.30.0/24 reject # 3. 只有来自 enp1s0 的 192.168.201.0/24 要连接到 port 22 才放行 [root@master ~]# nft add rule ip mytable myinput iifname enp1s0 ip saddr 192.168.201.0/24 tcp dport 22 accept # 4. 来自 enp2s0 要连接到 port 22 就放行 [root@master ~]# nft add rule ip mytable myinput iifname enp2s0 tcp dport 22 accept # 5. 连接到本机的 port 80, 443 都予以放行 [root@master ~]# nft add rule ip mytable myinput tcp dport { 80, 443 } accept # 6. 全部拒绝 [root@master ~]# nft add rule ip mytable myinput reject # 7. 列表观察 [root@master ~]# nft list ruleset ip table ip mytable { chain myinput { type filter hook input priority filter; policy accept; ct state established,related accept meta l4proto icmp accept iifname "lo" accept ip saddr 192.168.201.254 accept ip saddr 10.30.30.0/24 reject iifname "enp1s0" ip saddr 192.168.201.0/24 tcp dport 22 accept iifname "enp2s0" tcp dport 22 accept tcp dport { 80, 443 } accept reject } }
基本上,跟 iptables 一样,nft 也提供 add/insert/delete/replace 的规则处理方式~原则上,建议全部使用 add 来依序添加即可, 不过,如果有特殊需求,就可以通过 nft -a list.. 的方式,列出额外的参数后,就可以在某个位置 (handle) 增加规则了! 现在,让我们来查看一下想要处理的『把手』点:
# 列出目前 ip 类型的规则的 handle 点 [root@master ~]# nft -a list ruleset ip table ip mytable { # handle 60 chain myinput { # handle 1 type filter hook input priority filter; policy accept; ct state established,related accept # handle 2 meta l4proto icmp accept # handle 3 iifname "lo" accept # handle 4 ip saddr 192.168.201.254 accept # handle 5 ip saddr 10.30.30.0/24 reject # handle 7 iifname "enp1s0" ip saddr 192.168.201.0/24 tcp dport 22 accept # handle 8 iifname "enp2s0" tcp dport 22 accept # handle 9 tcp dport { 80, 443 } accept # handle 11 reject # handle 12 } }
上面的 handle 是 nftables 会自动记忆的 handle 点~即使你参考鸟哥的设置流程依序创建上述的规则,其 handle 点的数字位置, 应该也不会相同!所以,还是得要参考一下你自己的规则所对照的位置点才行!现在,假设我们要在 10.30.30.0/24 之前, 添加一个 10.30.30.120 的 IP 地址可以使用 port 22,那可以这样处理:
# 使用 nft insert rule [IP类型] [table] [chain] handle [point] 规则: [root@master ~]# nft insert rule ip mytable myinput handle 7 ip saddr 10.30.30.120 tcp dport 22 accept [root@master ~]# nft -a list ruleset ip table ip mytable { # handle 60 chain myinput { # handle 1 type filter hook input priority filter; policy accept; ct state established,related accept # handle 2 meta l4proto icmp accept # handle 3 iifname "lo" accept # handle 4 ip saddr 192.168.201.254 accept # handle 5 ip saddr 10.30.30.120 tcp dport 22 accept # handle 13 ip saddr 10.30.30.0/24 reject # handle 7 iifname "enp1s0" ip saddr 192.168.201.0/24 tcp dport 22 accept # handle 8 iifname "enp2s0" tcp dport 22 accept # handle 9 tcp dport { 80, 443 } accept # handle 11 reject # handle 12 } }
这样就插入一个规则了~如果 IP 地址写错~应该是 10.30.30.210 不是 120 啦!那怎办?可以使用 replace 的方式来处理!
# 使用 nft replace rule [IP类型] [table] [chain] handle [point] 规则: [root@master ~]# nft replace rule ip mytable myinput handle 13 ip saddr 10.30.30.210 tcp dport 22 accept [root@master ~]# nft -a list ruleset ip
那如果该规则是错误的,我们想要删除呢?也很简单~使用 delete 来处理!另外,不用写完整的规则~ 只要找到 handle 点,直接删除即可:
# 使用 nft delete rule [IP类型] [table] [chain] handle [point]: [root@master ~]# nft delete rule ip mytable myinput handle 13 [root@master ~]# nft -a list ruleset ip
从脚本去创建整体的规则,主要有两种方式,一种是将目前的规则倒出来,成为相关的 nftables 设置档,然后在里面修改成需要的模样, 之后就可以从 nft 加载了!另外一种则是跟我们之前创建的 iptables 相同,直接使用指令模式的方式创建好 shell script, 直接运行即可!我们先使用最简单的方式来处理!就是通过 shell script 的方式来处理!另外,我们改用 inet 取代 ip 这种 IP 地址格式, 这是因为 inet 同时涵盖 IPv4 与 IPv6,这样未来要添加不同的 IP 格式比较容易!
为了方便设置,鸟哥这里提供一个简单的脚本,可以让大家在脚本的前面设置好相关的变量之后,后续就可以让脚本直接规划好需要的表格、 链与规则~当然不是尽善尽美,至少可以让大家比较方便一些。使用的方式如下所示果没有相关的界面,直接让该界面为空值,那就可以略过设置。 而在 allow_tcp 与 allow_udp 的部份,那个是针对内网的放行端口口,如果想要放行所有的端口口,直接写下 0-65535 这样也行! 至于白名单与黑名单,如果有多个,用逗号隔开就可以写在一起!相当方便啊~同理,如果你的区网内有多个网段, 将各个网段写入各自的 lan 里面,用逗号隔开,就可以同时支持了!设计查找上面很简便!
# 1. 先创建 nftables.sh 这个脚本流程文件: [root@master ~]# vim ~/nftables.sh #!/bin/bash # Part 0: 用户输入 wanif=enp1s0 wannet=192.168.201.0/24 apif=wlp7s0u1 apnet=192.168.10.0/24 lanif=enp2s0 lannet=192.168.20.0/24 dmzif=enp3s0 dmznet=192.168.30.0/24 white_list="192.168.201.254" black_list="10.30.30.0/24,10.40.40.0/24" allow_tcp="22,53,5901-5910" allow_udp="53,67,123" # Part 1: 针对本机的防火墙设置 # part 1.1: 清除所有规则 nft flush ruleset # part 1.2: 创建本机的表格与与输入的链 nft add table inet mytable nft 'add chain inet mytable myinput { type filter hook input priority filter; policy drop; }' # part 1.2: 基础防火墙规则,针对本机 nft add rule inet mytable myinput ct state established,related accept nft add rule inet mytable myinput meta l4proto icmp accept nft add rule inet mytable myinput iifname lo accept # part 1.3: 白名单/黑名单/特殊来源使用本机危险服务 # part 1.3.1: 针对 WAN 界面 (enp1s0) if [ "${white_list}" != "" ]; then nft add rule inet mytable myinput ip saddr { "${white_list}" } accept fi if [ "${black_list}" != "" ]; then nft add rule inet mytable myinput ip saddr { "${black_list}" } reject fi if [ "${wannet}" != "" ]; then nft add rule inet mytable myinput iifname ${wanif} ip saddr { "${wannet}" } tcp dport 22 accept fi # part 1.3.2: 针对 LAN 界面 (enp2s0),放行 ssh, dns, dhcp, ntp, 5901-5910 等端口口与服务 if [ "${lanif}" != "" ]; then nft add rule inet mytable myinput iifname ${lanif} tcp dport { ${allow_tcp} } accept nft add rule inet mytable myinput iifname ${lanif} udp dport { ${allow_udp} } accept fi # part 1.3.3: 针对 AP 界面 (wlp7s0u1),放行 ssh, dns, dhcp, ntp, 5901-5910 等端口口 if [ "${apif}" != "" ]; then nft add rule inet mytable myinput iifname ${apif} tcp dport { ${allow_tcp} } accept nft add rule inet mytable myinput iifname ${apif} udp dport { ${allow_udp} } accept fi # part final: 保存规则 echo "flush ruleset" > /etc/nftables/mynftables.nft nft -a list ruleset >> /etc/nftables/mynftables.nft # 2. 修改设置档,未来调用的是 /etc/nftables/mynftables.nft 加载档才对! [root@master ~]# vim /etc/sysconfig/nftables.conf #include "/etc/nftables/main.nft" include "/etc/nftables/mynftables.nft" [root@master ~]# systemctl restart nftables [root@master ~]# nft list ruleset table inet mytable { chain myinput { type filter hook input priority filter; policy drop; ct state established,related accept meta l4proto icmp accept iifname "lo" accept ip saddr 192.168.201.254 accept ip saddr { 10.30.30.0/24, 10.40.40.0/24 } reject with icmp port-unreachable iifname "enp1s0" ip saddr 192.168.201.0/24 tcp dport 22 accept iifname "enp2s0" tcp dport { 22, 53, 5901-5910 } accept iifname "enp2s0" udp dport { 53, 67, 123 } accept iifname "wlp7s0u1" tcp dport { 22, 53, 5901-5910 } accept iifname "wlp7s0u1" udp dport { 53, 67, 123 } accept } }
要运行查看一下结果才对!从上面的运行结果,我们也能知道,基本上,我们也可以直接修改 /etc/nftables/mynftables.nft 这个我们自订的规则纪录档~基本设置就跟指令设置差不多~而如果需要加载该设置档的内容,亦即值些修改 /etc/nftables/mynftables.nft 之后,你也可以这样做:
[root@master ~]# nft -f /etc/nftables/mynftables.nft
先来回想一下,我们的 NAT 有 SNAT 与 DNAT,而 SNAT 有『snat, masquerade』,DNAT 有『dnat, redirect』, SNAT 用的是 postrouting 相关封包流向,而 DNAT 则是 prerouting 相关的封包流向~所以,接下来就是需要这些 postrouting / prerouting 等等的链啰!
我们预计想要增加一个名为 mynat 的表格,这个表格适用于所有的 IP 格式 (inet),并且在创建之后,添加两个链,分别是 mysnat 与 mydnat。 这两个链的内部政策设置为:
实在来创建这几样数据:
# 1. 创建 mynat 表格 [root@master ~]# nft add table inet mynat # 2. 创建 mysnat 链 [root@master ~]# nft add chain inet mynat mysnat { type nat hook postrouting priority srcnat \; policy accept \; } # 3. 创建 mydnat 链 [root@master ~]# nft add chain inet mynat mydnat { type nat hook prerouting priority dstnat \; policy accept \; } [root@master ~]# nft list chains .... table inet mynat { chain mysnat { type nat hook postrouting priority srcnat; policy accept; } chain mydnat { type nat hook prerouting priority dstnat; policy accept; } }
回想一下 iptables 的表格与链,在 nat 表格里面有个 POSTROUTING 作为 SNAT 的对吧!目前我们这个 mynat 就是类似 nat 表格, 而 mysnat 就是 POSTROUTING 的意思!表格与链的名称都可以自己取名,不过,内部的类型 (type) 与封包流向,就得要确定为 postrouting! 这个是重要部份!创建好了表格与链之后,再来就是让所有的内部三个接口对 internet 连接时,都可以进行 IP 地址的伪装。
再次回到 7.1.4 小节查看一下,我们的内部网络分为三个区块,这三个区块的网卡与网段对外连接时,都得要通过伪装才行! 伪装的方法很简单,我们可以简易的这样设计即可:
[root@master ~]# nft add rule inet mynat mysnat iifname enp2s0 oifname enp1s0 \ > ip saddr 192.168.20.0/24 masquerade [root@master ~]# nft add rule inet mynat mysnat iifname wlp7s0u1 oifname enp1s0 \ > ip saddr 192.168.10.0/24 masquerade [root@master ~]# nft add rule inet mynat mysnat iifname enp3s0 oifname enp1s0 \ > ip saddr 192.168.30.0/24 masquerade [root@master ~]# nft list chain inet mynat mysnat table inet mynat { chain mysnat { type nat hook postrouting priority srcnat; policy accept; iifname "enp2s0" oifname "enp1s0" ip saddr 192.168.20.0/24 masquerade iifname "wlp7s0u1" oifname "enp1s0" ip saddr 192.168.10.0/24 masquerade iifname "enp3s0" oifname "enp1s0" ip saddr 192.168.30.0/24 masquerade } }
你可以分别前往 server001 以及 client001 测试一下,现在两部系统的网络应该都是通的!也可以互相 ping 的到才对! 这就完成了简单的 SNAT 设置!
假设如同前面小节提到的,或许因为某些缘故,因此我们外网 WAN 界面的 port 22 不能直接使用,得要通过类似 2121 来转递! 这时就得要用到 redirection 了!redirection 方式很简单啊!我们只有转递 WAN 界面而已喔~内部界面没有变化!
[root@master ~]# nft add rule inet mynat mydnat iifname enp1s0 tcp dport 2121 redirect to 22 [root@master ~]# nft list chain inet mynat mydnat table inet mynat { chain mydnat { type nat hook prerouting priority dstnat; policy accept; iifname "enp1s0" tcp dport 2121 redirect to :22 } }
你就可以发现到,只有从外部界面 (enp1s0) 来的连接可以连接到 port 2121 而已!从其他界面来的,就无法连接到 port 2121, 因为没有设置转递的功能!这样就处理好 redirection 啰!
由于我们的 Web 放置在 192.168.30.1 这部位于 DMZ 的服务器上,在 LAN 的内部可以使用 http://192.168.30.1 来查找到这部 Web 页面。 同样的,在外部系统基本上是无法连接到这部 Web 服务器的,我们得要通过 prerouting 的 DNAT 功能!如同在 iptables 的章节, 要进行 DNAT 倒也是很简单~从 WAN 界面来的封包,只要是 port 80 的,就转到后端系统去!那就这样处理:
# 1. 实际运作的指令如下: [root@master ~]# nft add rule inet mynat mydnat iifname enp1s0 tcp dport 80 dnat ip to 192.168.30.1 # 2. 如果切换到不同的端口口,那就得要这样处理 [root@master ~]# nft add rule inet mynat mydnat iifname enp1s0 tcp dport 8080 dnat ip to 192.168.30.1:80 # 3. 如果 IP 地址不是 IPv4 而是 IPv6 的话,得要改成这样: [root@master ~]# nft add rule inet mynat mydnat iifname enp1s0 tcp dport 8081 dnat ip6 to fe80::5054:ff:fe00:3001
这样很快就处理好了外部的 DNAT 连接了!也算是相当的轻松愉快啊!
基本上,DMZ 就是需要做个限制才可以的!所以,我们得要模仿 iptables 当中的 DMZ 章节内容,使用 filter 表格内的 FORWARD 链来管理! 因此,让我们回到 mytable 的表格当中,创建 myforward 链!要注意的是,我们已经有 myinput 链了!所以处理 myforward 链要注意一下, 不用重复创建表格~创建 myforward 这条链也不是太难,这样来处理看看:
[root@master ~]# nft add chain inet mytable myforward { type filter hook forward priority filter \; policy accept \; }
我们当然可以对 LAN 做比较大范围的伪装放行,所以 forward 缺省的政策就先设置为 accept ! 但是对于 DMZ 这个会被 internet 访问的区域而言,应该还是稍微避免这样的情境比较妥当! 我们需要针对两个部份来处理,分别是 (1) DMZ 对 internet 的连接与 (2) DMZ 对 LAN 的连接。
基本上,Internet 还是需要 DMZ 回传的信息,所以,我们会需要回应封包!而且,为了 DMZ 里面 server 的安装与管理, 应该还是要放行 DNS 以及 port 80, 443 的连接,这个部份我们先来处理看看:
[root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp1s0 ct state established,related accept [root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp1s0 udp dport 53 accept [root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp1s0 tcp dport { 53, 80, 443 } accept [root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp1s0 reject
此时 DMZ 对于 Internet 的主动连接就只剩下查找 DNS 主机名称以及网页服务器查找的功能~除非你还要放行 FTP, 否则,这样的情况下,对于 DMZ 的主动连上 Internet 来说,应该是有相当限度的管制了。再来则是管理 DMZ 对于 LAN 的连接了! 这种连接方向中,应该是要放行状态封包,其他通通都关掉才合理!
[root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp2s0 ct state established,related accept [root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp2s0 reject [root@master ~]# nft list chain inet mytable myforward table inet mytable { chain myforward { type filter hook forward priority filter; policy accept; iifname "enp3s0" oifname "enp1s0" ct state established,related accept iifname "enp3s0" oifname "enp1s0" udp dport 53 accept iifname "enp3s0" oifname "enp1s0" tcp dport { 53, 80, 443 } accept iifname "enp3s0" oifname "enp1s0" reject iifname "enp3s0" oifname "enp2s0" ct state established,related accept iifname "enp3s0" oifname "enp2s0" reject } }
这样处理下来,对于整体 LAN 跟 DMZ 的对外连网部份就大致搞定!
某些特殊的情况底下,你的 MTU 可能应该有点改变,例如在 PPPoE 这种拨接的环境中,我们的封包经过 PPPoE 的路由器环境时, 因为表头还得要加上 PPPoE 的信息,因此 MTU 不可能保持 1500,这时我们的 PPPoE 通常会偷偷的帮我们将 MTU 改成 1492 之类的情境。 不过需要注意的是,有时候我们的 router 会以为封包过大,因此会回传 client 端封包过大的问题,而不予以拆解后重新发送。 这时,这个封包的传递,可能就会失败,因此整体连接就会断断续续的~很怪异就是了。这个问题就是所谓的『 TCP maximum segment size (MSS) 的 clamping 』情况。
在过去,用 Linux 内核处理的方法,通常会耗尽很多心力~但是,新的 nftables 机制里面,藏了一个简单的方法,通常在 forward 链里面将 MSS 的最大值设置为系统能通过的最大 MTU 路径!很快就可以搞定这个问题的发生!朋友们可以到后续的参考数据瞧一瞧原始数据, 这里取快速的方式来设置:
[root@master ~]# nft add rule inet mytable myforward tcp flags syn tcp option maxseg size set rt mtu
未来在 forward 链里面,记得都要加上这一段喔!可以排除很多问题!
通过脚本化处理,会比较容易管理 nftables 啰!同样处理一下 /root/nftables.sh 脚本内容:
[root@master ~]# vim ~/nftables.sh .... # part 2: 开始处理 NAT 的部份 # part 2.1: 创建表格与链 nft add table inet mynat nft add chain inet mynat mysnat { type nat hook postrouting priority srcnat \; policy accept \; } nft add chain inet mynat mydnat { type nat hook prerouting priority dstnat \; policy accept \; } # part 2.2: 创建 SNAT 功能 if [ "${apif}" != "" ]; then nft add rule inet mynat mysnat iifname ${apif} oifname ${wanif} ip saddr ${apnet} masquerade fi if [ "${lanif}" != "" ]; then nft add rule inet mynat mysnat iifname ${lanif} oifname ${wanif} ip saddr ${lannet} masquerade fi if [ "${dmzif}" != "" ]; then nft add rule inet mynat mysnat iifname ${dmzif} oifname ${wanif} ip saddr ${dmznet} masquerade fi # part 2.3: 创建 redirection 功能 nft add rule inet mynat mydnat iifname ${wanif} tcp dport 2121 redirect to 22 # part 2.4: 创建 DNAT 功能 nft add rule inet mynat mydnat iifname ${wanif} tcp dport 80 dnat ip to 192.168.30.1 # part 3: 处理 DMZ 功能 nft add chain inet mytable myforward { type filter hook forward priority filter \; policy accept \; } nft add rule inet mytable myforward tcp flags syn tcp option maxseg size set rt mtu nft add rule inet mytable myforward iifname ${dmzif} oifname ${wanif} ct state established,related accept nft add rule inet mytable myforward iifname ${dmzif} oifname ${wanif} udp dport 53 accept nft add rule inet mytable myforward iifname ${dmzif} oifname ${wanif} tcp dport { 53, 80, 443 } accept nft add rule inet mytable myforward iifname ${dmzif} oifname ${wanif} reject nft add rule inet mytable myforward iifname ${dmzif} oifname ${lanif} ct state established,related accept nft add rule inet mytable myforward iifname ${dmzif} oifname ${lanif} reject # part final: 保存规则 [root@master ~]# sh ~/nftables.sh