Appearance
通用操作
此文档后面如果发现新的用法会不断地进行更新
前面我们已经学习了如何创建 表、链,本章我们重点研究规则如何创建
首先,我们清空表,创建一个空表inet test
进行测试
1. 数据过滤
meta
输入接口索引iif <input interface INDEX>
输入接口名称iifname <input interface NAME>
输出接口索引 oif <output interface INDEX>
输出接口名称oifname <output interface NAME>
(oif 和 iif 接受字符串参数并转换为接口索引)
(oifname 和 iifname 更动态,但由于字符串匹配,速度较慢),官方的意思是索引的方式更快
通过ip a
命令可以查看网卡的名称与索引,如下分别是1 lo
2 enss33
shell
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:03:19:e8 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.26/24 brd 192.168.1.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet6 2408:8244:8600:61:20c:29ff:fe03:19e8/64 scope global dynamic noprefixroute
valid_lft 232088sec preferred_lft 145688sec
inet6 fe80::20c:29ff:fe03:19e8/64 scope link noprefixroute
valid_lft forever preferred_lft forever
[root@localhost ~]#
我们可以针对所有网卡ens33
的输入做策略
shell
[root@localhost ~]# nft add rule inet test chain1 meta iif 2 accept
[root@localhost ~]# nft add rule inet test chain1 meta iifname ens33 accept
[root@localhost ~]# nft -a -n list chain inet test chain1
table inet test {
chain chain1 { # handle 1
type filter hook input priority 0; policy accept;
tcp dport 822 accept # handle 4
icmp type 0 accept # handle 7
meta l4proto 1 drop # handle 6
ip saddr 192.168.1.20 tcp dport 80 drop # handle 14
ip protocol 6 accept # handle 15
ip protocol 17 accept # handle 16
ip protocol 51 accept # handle 17
ip protocol 6 accept # handle 18
ip saddr 192.168.1.25 accept # handle 19
ip daddr 192.168.1.26 accept # handle 20
iif "ens33" accept # handle 21
iifname "ens33" accept # handle 22
}
}
[root@localhost ~]#
ip
协议protocol <protocol>
源地址daddr <destination address>
目的地址saddr <source address>
shell
[root@localhost ~]# nft add rule inet test chain1 ip protocol tcp accept
[root@localhost ~]# nft add rule inet test chain1 ip saddr 192.168.1.25 accept
[root@localhost ~]# nft add rule inet test chain1 ip daddr 192.168.1.26 accept
[root@localhost ~]# nft -a -n list table inet test
table inet test { # handle 1
ct timeout customs { # handle 5
protocol tcp
l3proto ip
policy = { established : 200, close : 20 }
}
chain chain1 { # handle 1
type filter hook input priority 0; policy accept;
tcp dport 822 accept # handle 4
icmp type 0 accept # handle 7
meta l4proto 1 drop # handle 6
ip saddr 192.168.1.20 tcp dport 80 drop # handle 14
ip protocol 6 accept # handle 15
ip protocol 17 accept # handle 16
ip protocol 51 accept # handle 17
ip protocol 6 accept # handle 18
ip saddr 192.168.1.25 accept # handle 19
ip daddr 192.168.1.26 accept # handle 20
}
chain chain2 { # handle 2
type filter hook input priority 0; policy accept;
}
chain chain3 { # handle 3
}
}
[root@localhost ~]#
TCP/UDP
目的端口 dport <destination port>
源端口 sport <source port>
shell
[root@localhost ~]# nft add rule inet test chain1 tcp dport 443 accept
[root@localhost ~]# nft add rule inet test chain1 tcp sport 443 accept
[root@localhost ~]# nft -a -n list chain inet test chain1
table inet test {
chain chain1 { # handle 1
type filter hook input priority 0; policy accept;
tcp dport 822 accept # handle 4
icmp type 0 accept # handle 7
meta l4proto 1 drop # handle 6
ip saddr 192.168.1.20 tcp dport 80 drop # handle 14
ip protocol 6 accept # handle 15
ip protocol 17 accept # handle 16
ip protocol 51 accept # handle 17
ip protocol 6 accept # handle 18
ip saddr 192.168.1.25 accept # handle 19
ip daddr 192.168.1.26 accept # handle 20
iif "ens33" accept # handle 21
iifname "ens33" accept # handle 22
tcp dport 443 accept # handle 23
tcp sport 443 accept # handle 24
}
}
[root@localhost ~]#
icmp
常用的 icmp type就两个,其他的做了解就行
type | 值 | 说明 |
---|---|---|
echo-reply | 0 | 回显应答 |
destination-unreachable | 3 | 目的地不可达 |
source-quench | 4 | 当接收方无法处理所有传入的数据包时,源站抑制报文会发送到发送方,以通知其减慢数据传输速度 |
redirect | 5 | 用于通知发送方,其正在使用的路由不再是最佳路由,建议使用另一条路由 |
echo-request | 8 | 回显请求 |
time-exceeded | 11 | 超时 |
常用报文类型
类型TYPE | 代码CODE | 用途|描述 Description | 查询类Query | 差错类Error |
---|---|---|---|---|
0 | 0 | Echo Reply——回显应答(Ping应答) | x | |
3 | 0 | Network Unreachable——网络不可达 | x | |
3 | 2 | Protocol Unreachable——协议不可达 | x | |
3 | 1 | Host Unreachable——主机不可达 | x | |
3 | 3 | Port Unreachable——端口不可达 | x | |
3 | 4 | Fragmentation needed but no frag. bit set——需要进行分片但设置不分片比特 | x | |
3 | 13 | Communication administratively prohibited by filtering——由于过滤,通信被强制禁止 | x | |
5 | 1 | Redirect for host——对主机重定向 | ||
8 | 0 | Echo request——回显请求(Ping请求) | x | |
11 | 0 | TTL equals 0 during transit——传输期间生存时间为0 | x | |
12 | 0 | IP header bad (catchall error)——坏的IP首部(包括各种差错) | x |
禁用所有icmp
nft add rule inet <table> <chain> meta l4proto icmp drop
禁止别人ping自己
nft add rule inet test chain1 handle 4 icmp type 8 drop
禁止并回复 with icmp code
nft add rule inet test chain1 handle 4 icmp type 8 reject with icmp 3
2. NAT
2.1 透明代理
nat与其他的策略不同之处在于,一般用于类似路由器,或者防火墙设备,需要对数据包进行转发,并且转发的过程中需要对源地址或者目的地址进行转换
首先我们需要在Linux中开启数据转发的功能
shell
# 编辑sysctl配置文件
sudo vi /etc/sysctl.conf
# 添加以下行来启用IP转发
net.ipv4.ip_forward = 1
# 应用配置
sudo sysctl -p
实际测试中 可能需要关闭 SELINUX
shell
[root@client ~]# sudo sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config #
[root@client ~]# cat /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=enforcing #
SELINUX=disabled #
# SELINUXTYPE= can take one of three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
[root@client ~]#
网络拓扑结构为,配置完此功能以后,网络已经可以通过中间设备正常通信
NAT
首先创建好表与链
shell
[root@router ~]# nft add table ip test
[root@router ~]# nft add chain test test_dnat '{type nat hook prerouting priority 0 ; }'
[root@router ~]# nft add chain test test_snat '{type nat hook postrouting priority 0 ; }'
[root@router ~]# nft list table test
table ip test {
chain test_dnat {
type nat hook prerouting priority filter; policy accept;
}
chain test_snat {
type nat hook postrouting priority filter; policy accept;
}
}
[root@router ~]#
2.2 SNAT
首先我们的中间机器是能访问互联网的,但是我们的 192.168.132.3
这台机器是不能上网,如果需要访问互联网,就需要对地址做转换
nft add rule nat postrouting ip saddr 192.168.132.0/24 oif ens33 snat to 192.168.177.31
在这里指定输出的接口,是为了精准的做源地址转换,而不会影响回包地址
shell
[root@mimt ~]# nft add table test
[root@mimt ~]# nft add chain test chain_snat '{type nat hook postrouting priority 100;}'
[root@mimt ~]# nft add rule test chain_snat ip saddr 192.168.132.0/24 oif ens33 snat to 192.168.177.31
此时在 192.168.132.3
这台机器上已经可以上公网了
shell
[root@client ~]# ping 114.114.114.114
PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.
64 bytes from 114.114.114.114: icmp_seq=1 ttl=127 time=24.9 ms
64 bytes from 114.114.114.114: icmp_seq=2 ttl=127 time=25.2 ms
64 bytes from 114.114.114.114: icmp_seq=3 ttl=127 time=25.3 ms
可以指定源 NAT 池,在这里就不演示了
nft add rule inet nat postrouting snat ip to 10.0.0.2/31
nft add rule inet nat postrouting snat ip to 10.0.0.4-10.0.0.127
使用传输协议源端口映射:
nft add rule inet nat postrouting ip protocol tcp snat ip to 10.0.0.1-10.0.0.100:3000-4000
2.3 DNAT
现在举一个场景的例子,在公网上,我们需要访问内网的某个服务,怎么办呢,此时就需要将内网的服务ip+端口映射到公网出口的ip+端口上面
shell
[root@mimt ~]# nft add chain test chain_dnat '{type nat hook prerouting priority 100;}'
[root@mimt ~]# nft add rule test chain_dnat iif 2 tcp dport 123 dnat to 192.168.132.3:80
[root@mimt ~]# nft -a list table test
table ip test { # handle 3
chain chain_snat { # handle 1
type nat hook postrouting priority srcnat; policy accept;
ip saddr 192.168.132.0/24 oif "ens33" snat to 192.168.177.31 # handle 4
}
chain chain_dnat { # handle 5
type nat hook prerouting priority 100; policy accept;
iif "ens33" tcp dport 123 dnat to 192.168.132.3:80 # handle 8
}
}
[root@mimt ~]#
此时我们在192.168.177.3
服务器访问 http://192.168.177.31:123
即可访问到 192.168.132.3
的服务
通过现象,我们可以发现,SNAT时,目标服务器获取的是转换后的地址,而DNAT时,目标服务器可以获取真实的ip地址
思考一个问题,如果在同一个内网里访问转后的地址能访问到吗?
通过实际测试,我们发现同一个内网访问转换后的地址无法访问,这是为什么呢
其实是DNAT不会修改源IP,这样就导致服务器在回包的时候,会在内网直接回复回去,但是客户端的发起访问目的ip就不一致了,客户端会丢弃数据包
因此,如果需要内网也能使用公网IP访问的话,我们还需要做源地址转换
shell
[root@mimt ~]# nft add rule test chain_snat ip daddr 192.168.132.3 tcp dport 80 oif 3 snat to 192.168.177.31
[root@mimt ~]# nft -a list table test
table ip test { # handle 3
chain chain_snat { # handle 1
type nat hook postrouting priority srcnat; policy accept;
ip saddr 192.168.132.0/24 oif "ens33" snat to 192.168.177.31 # handle 4
ip daddr 192.168.132.3 tcp dport 80 oif "ens34" snat to 192.168.177.31 # handle 12
}
chain chain_dnat { # handle 5
type nat hook prerouting priority 100; policy accept;
tcp dport 123 dnat to 192.168.132.3:80 # handle 8
}
}
[root@mimt ~]#
但是缺点是什么呢,就是内网服务器记录的都是公网的地址为源IP了,因此不建议内网直接访问公网IP
3. CT(连接跟踪)
3.1 state
一般用于表示TCP连接状态
ct state { new, established, related, untracked }
例如,以下禁止新的ssh连接,原有的ssh则不会受影响
shell
[root@mimt ~]# nft add rule test chain1 tc state new drop
[root@mimt ~]# nft -a -n list ruleset
table ip test { # handle 1
chain chain1 { # handle 1
type filter hook input priority 100; policy accept;
ct state 0x8 drop # handle 4
}
}
[root@mimt ~]#
3.2 限流(limit rate)
limit rate 400/minute
limit rate 400/hour # 无over表示匹配低于后面结果
limit rate over 40/day # over表示匹配高于后面结果
limit rate over 400/week
limit rate over 1023/second burst 10 packets
limit rate 1025 kbytes/second
limit rate 1023000 mbytes/second
limit rate 1025 bytes/second burst 512 bytes # burst表示可以突破最多限制速度,在over中不生效
limit rate 1025 kbytes/second burst 1023 kbytes
limit rate 1025 mbytes/second burst 1025 kbytes
limit rate 1025000 mbytes/second burst 1023 mbytes
我们在服务器上下载可以发现,不限速的情况下,我们可以满速度下载
shell
[root@mimt ~]# wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
--2024-08-17 00:03:45-- https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
Resolving go.dev (go.dev)... 216.239.38.21, 216.239.36.21, 216.239.32.21, ...
Connecting to go.dev (go.dev)|216.239.38.21|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz [following]
--2024-08-17 00:03:45-- https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz
Resolving dl.google.com (dl.google.com)... 114.250.67.33
Connecting to dl.google.com (dl.google.com)|114.250.67.33|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 73590011 (70M) [application/x-gzip]
Saving to: ‘go1.23.0.linux-amd64.tar.gz’
go1.23.0.linux-amd64.tar.gz 100%[======================================================================================================================>] 70.18M 45.0MB/s in 1.6s
2024-08-17 00:03:47 (45.0 MB/s) - ‘go1.23.0.linux-amd64.tar.gz’ saved [73590011/73590011]
[root@mimt ~]#
当我们需要进行下载限速时,则可以在input链进行限制,可以做如下策略,不过此一般用于限制从服务器下载的限流,保证服务器的带宽
shell
[root@mimt ~]# nft add rule test chain1 limit rate over 1 mbytes/second drop
[root@mimt ~]#
[root@mimt ~]# nft -a -n list ruleset
table ip test { # handle 2
chain chain1 { # handle 1
type filter hook input priority 100; policy accept;
limit rate over 1 mbytes/second drop # handle 2
}
}
[root@mimt ~]# wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
--2024-08-17 00:23:37-- https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
Resolving go.dev (go.dev)... 216.239.36.21, 216.239.38.21, 216.239.34.21, ...
Connecting to go.dev (go.dev)|216.239.36.21|:443... failed: Connection refused.
Connecting to go.dev (go.dev)|216.239.38.21|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz [following]
--2024-08-17 00:23:58-- https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz
Resolving dl.google.com (dl.google.com)... 114.250.63.33
Connecting to dl.google.com (dl.google.com)|114.250.63.33|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 73590011 (70M) [application/x-gzip]
Saving to: ‘go1.23.0.linux-amd64.tar.gz.5’
go1.23.0.linux-amd64.tar.gz.5 100%[======================================================================================================================>] 70.18M 1016KB/s in 72s
2024-08-17 00:25:10 (1005 KB/s) - ‘go1.23.0.linux-amd64.tar.gz.5’ saved [73590011/73590011]
[root@mimt ~]#
外发限速
shell
[root@mimt ~]# nft add table test
[root@mimt ~]# nft add chain test chain1 '{type filter hook output priority 100;}'
[root@mimt ~]# nft add rule test chain1 limit rate over 1 mbytes/second drop
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 1
chain chain1 { # handle 1
type filter hook output priority 100; policy accept;
limit rate over 1 mbytes/second drop # handle 4
}
}
[root@mimt ~]#
限速前后对比
cmd
C:\Users\more>curl -o d:\go.tar.gz http://192.168.132.31/download/go.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 70.1M 100 70.1M 0 0 131M 0 --:--:-- --:--:-- --:--:-- 131M
C:\Users\more>curl -o d:\go.tar.gz http://192.168.132.31/download/go.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 70.1M 100 70.1M 0 0 1036k 0 0:01:09 0:01:09 --:--:-- 1073k
C:\Users\more>
3.3 计数器(Counters)
我们可以统计指定想要去统计的流量,比如网卡,服务,端口等等
匿名计数器
比如我想统计所有访问80端口的流量
shell
[root@mimt ~]# nft add table test
[root@mimt ~]# nft add chain test chain_output '{type filter hook output priority 100;}'
[root@mimt ~]# nft add chain test chain_input '{type filter hook input priority 100;}'
[root@mimt ~]# nft add rule test chain_input tcp dport 80 counter
[root@mimt ~]# nft add rule test chain_output tcp sport 80 counter
[root@mimt ~]#
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 2
chain chain_output { # handle 1
type filter hook output priority 100; policy accept;
tcp sport 80 counter packets 0 bytes 0 # handle 6
}
chain chain_input { # handle 2
type filter hook input priority 100; policy accept;
tcp dport 80 counter packets 0 bytes 0 # handle 5
}
}
[root@mimt ~]#
# 下载访问完成后统计的流量
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 2
chain chain_output { # handle 1
type filter hook output priority 100; policy accept;
tcp sport 80 counter packets 2320 bytes 73690747 # handle 6
}
chain chain_input { # handle 2
type filter hook input priority 100; policy accept;
tcp dport 80 counter packets 25289 bytes 1033737 # handle 5
}
}
[root@mimt ~]#
命名计数器
我们也可以单独声明一个计数器,以便后期进行同一维护,适用于有多个需要计数场景,这里为了方便,就不分开进行流量统计了
shell
[root@mimt ~]# nft add counter test nginx '{comment "NGINX counter";}'
[root@mimt ~]# nft add rule test chain_input tcp dport 80 counter name nginx
[root@mimt ~]# nft add rule test chain_output tcp sport 80 counter name nginx
[root@mimt ~]#
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 2
counter nginx { # handle 7
comment "NGINX counter"
packets 0 bytes 0
}
chain chain_output { # handle 1
type filter hook output priority 100; policy accept;
tcp sport 80 counter name "nginx" # handle 11
}
chain chain_input { # handle 2
type filter hook input priority 100; policy accept;
tcp dport 80 counter name "nginx" # handle 10
}
}
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 2
counter nginx { # handle 7
comment "NGINX counter"
packets 7620 bytes 73895192
}
chain chain_output { # handle 1
type filter hook output priority 100; policy accept;
tcp sport 80 counter name "nginx" # handle 11
}
chain chain_input { # handle 2
type filter hook input priority 100; policy accept;
tcp dport 80 counter name "nginx" # handle 10
}
}
[root@mimt ~]#