1419 words
7 minutes
nftables
Install
- 在archlinux上:
sudo pacman -S nftables- 查看有其他防火墙服务是否在运行,如果有先禁用:
sudo systemctl list-units --type=service | grep -E "firewalld|ufw"sudo iptables -L -n -v # 谁有输出就禁用- 启动防火墙:
sudo systemctl enable --now nftables.servicesudo systemctl status nftables.service #查看防火墙状态安装nft的补全脚本
sudo curl -o /etc/bash_completion.d/nft \ https://raw.githubusercontent.com/Zulugitt/bash_completion/main/nft-completion- 重新加载:
source /etc/bash_completion.d/nftUsage
- 查看当前所有规则:
sudo nft list ruleset- 清空当前的规则
sudo nft flush ruleset- 加载配置文件
sudo nft -f /path/to/filename # nftables的配置文件Table
- nftbles没有内置表。每个表只能有一个地址簇。有六个簇。未指定为ip簇。
| nftables family | meaning |
|---|---|
| ip | 处理IPv4数据包 |
| ip6 | 处理IPv6数据包 |
| inet | 同时处理IPv4和IPv6数据包 |
| arp | 处理ARP数据包 |
| bridge | 处理二层桥接数据包 |
| netdev | 处理网络设备ingress(入站)数据包 |
- 创建表
sudo nft add table family_type table_name- 列出表
sudo nft list tables- 列出表中的链和规则
sudo nft list table family_type table_name- 删除表
sudo nft delete table family_type table_name- 刷新表
sudo nft flush table family_type table_nameChains
- 链的用途是包含规则。
- 链有两种类型。分别是基本链(Base chain)和常规链(Regular chain)。区别主要在是否挂接到内核网络堆栈。
- 基本链:入口点,必须有hook,直接处理数据包。
- 常规链:辅助链,没有hook,作为跳转目标,用来组织规则。
Base chain
- 添加基本链:
sudo nft add chain family_type table_name chain_name { type chain_type hook hook_type priority priority_value ; policy policy ;}# type: filter / nat /route# hook: preouting / input / forward /output / postrouting /ingress # ip ip6 family# priority: 名字或整数,可以为负# policy accept / drop- 添加常规链:
sudo nft add chain family_type table_name chain_name- 编辑链:
sudo nft chain family_type table_name chain_name { } # 可以修改 policy 命名集合的元素,计数器,限速器,等状态对象。Ruler
- 规则由匹配条件(Expressions)和动作(Statements)组成。条件和动作从左到右顺序应用。
- 规则中的条件在逻辑上连接在一起(AND运算符)。所有的条件必须全部匹配,才能执行相应的动作。否则,将执行下一条规则。
- 添加规则:
sudo nft add rule family_type table_name chain_name handle handle_value ruler# handle 一个唯一的ID 用来删除、替换、插入的。Expression
- 常见的匹配条件:
| expression | example |
|---|---|
| meta | iif lo oifname "eth0" |
| ip / ip6 | ip saddr 192.168.0.0/16 / ip6 daddr fe80::/10 |
| tcp / udp / sctp | tcp dport 22 / udp sport 53 |
| icmp / icmp6 | icmp type echo-request / icmpv6 type nd-neighbor-solicit |
| ct | ct state established,related / ct state new |
| sets / maps | tcp dport {22,80,443} / ip saddr vmap { 192.168.1.1 : drop,192.168.1.2 : accept } |
- 更多的expression具体信息可以使用
nft describe查询或者man nft。
Statements
- 常见的动作:
| statements | meaning |
|---|---|
| accept | 放行 |
| drop | 丢弃 |
| reject | 拒绝并返回ICMP/ICMPv6错误 |
| queue | 交给用户空间程序处理 |
| return | 返回到调用链 |
| log | 记录日志 |
| counter | 计数 |
| masquerade / snat / dnat | NAT动作 |
| limit / meter | 限速 / 限流 |
| set / add / delete / element | 动态修改集合 |
Sets
- 集合可以是匿名,命名的。它可以用来存放一组元素,然后在规则中引用。
- 匿名集:直接写在规则里,不能修改
- 命名集:单独定义,可动态更新
- example 自动拉黑
table inet ssh_protect { # 定义一个动态黑名单集合 set ssh_blacklist { type ipv4_addr flags dynamic, timeout timeout 1h size 65536 }
chain input { type filter hook input priority 0; policy accept;
ip saddr @ssh_blacklist drop # 如果源 IP 在黑名单中,直接丢弃
tcp dport 22 ct state new \ # 检测 SSH 新连接,超过速率限制则加入黑名单 limit rate over 10/minute burst 5 packets \ add @ssh_blacklist { ip saddr timeout 1h } \ accept }}Maps
- maps是sets的一种扩展形式。 基本语法:
map map_name { type key_type: value_type elements = { key : value, key1 : value1 }
}- example 1 端口重定向
table ip nat { map port_redirect { type inet_service : inet_service elements = { 80 : 8080, 443 : 8443 } }
chain prerouting { type nat hook prerouting priority dstnat; tcp dport @port_redirect dnat to 192.168.1.10 : @port_redirect }}# 访问本机的80 / 443 端口会被转发到 192.168.0.10 的 8080 / 8443 端口上- example 2 服务端口分类
table inet filter { map service_dispatch { type inet_service : verdict elements = { 22 : jump ssh_chain, 23122 : jump ssh_chain, 80 : jump web_chain, 443 : jump web_chain } }
chain input { type filter hook input priority 0; policy drop ; tcp dport vmap @service_dispatch }
chain ssh_chain { tcp dport 22 drop tcp dport 23122 accept }
chain web_chain { tcp dport {80,443} accept }}# 把不同的端口映射到不同的链上- example 3 基于不同IP的策略
table inet filter { map ip_policy { type ipv4_addr : verdict elements = { 192.168.1.100 : accept, 10.0.0.5 : drop } }
chain input { type filter hook input priority 0; policy drop ; ip saddr vmap @ip_policy }}# 不同来源的IP走不同的链Objects
- counter
- quota
- limit
- ct helper
- flowtable
例子
flush ruleset
table inet myfw { # 黑名单集合 set ssh_blacklist { type ipv4_addr flags dynamic, timeout timeout 1h }
# 对象:计数器 + 限速器 counter ssh_counter limit ssh_limit { rate 10/minute burst 5 }
chain input { type filter hook input priority 0; policy drop;
# 基础放行 ct state established,related accept iif lo accept
# 黑名单 ip saddr @ssh_blacklist drop
# SSH 防护:速率限制 + 计数 + 自动拉黑 tcp dport 22 ct state new limit name ssh_limit counter name ssh_counter \ add @ssh_blacklist { ip saddr timeout 1h } drop
# 允许 Web 服务 tcp dport {80,443} accept }}- 流程图
flowchart TD
A[Packet enters input chain] --> B{ct state established,related?}
B -- yes --> Z[ACCEPT established/related]
B -- no --> C{iif lo?}
C -- yes --> Z
C -- no --> D{Source IP in ssh_blacklist?}
D -- yes --> X[DROP blacklist]
D -- no --> E{tcp dport 22 and state new?}
E -- no --> F{tcp dport 80 or 443?}
E -- yes --> G[Count ssh_counter + Limit ssh_limit]
G --> H[Add source IP to ssh_blacklist 1h]
H --> X[DROP SSH over limit]
F -- yes --> Z[ACCEPT Web service]
F -- no --> X[DROP default policy]
如何写一条规则
- 要实现什么功能,确定在哪个表和链里写。
- 写匹配条件。如协议,端口,地址等等。
- 写动作。如 accept,drop,log等。
Finally
更多内容请查看nftables官网
Some information may be outdated