ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

gVisor iptables 集成测试:从 Docker 配置到 TestCase 框架的完整实战指南

gVisor iptables 集成测试:从 Docker 配置到 TestCase 框架的完整实战指南 gVisor iptables 集成测试从 Docker 配置到 TestCase 框架的完整实战指南【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisorgVisorrunsc在 raw socket / netfilter 场景下需要验证iptables规则能否在沙箱环境中按预期工作。本文基于仓库中的 test/iptables/README.md 展开讲清 iptables 测试套件的前置环境配置IPv6、--net-raw、内核模块、测试框架的TestCase双端协作模型以及单测的构建、运行与扩展方法读完后你可以独立跑通整套 iptables 集成测试并能按规范向套件中新增自己的测试用例。前置环境Docker 与运行时配置iptables 测试并非开箱即用需要在宿主机上做额外配置。README 明确要求两项准备1. 为 Docker 启用 IPv6测试框架会同时以 IPv4 和 IPv6 两种模式运行每个用例见 singleTest 中对false/true两个布尔值的循环。因此需要在/etc/docker/daemon.json中启用 IPv6{ experimental: true, fixed-cidr-v6: 2001:db8:1::/64, ipv6: true, // Runtimes and other Docker config... }修改后必须重启 Docker 才能生效。2. 加载内核模块并启用--net-raw如果不用make目标、手动运行测试需要加载 iptables 所需的内核模块modprobe iptable_filter modprobe ip6table_filter在/etc/docker/daemon.json中为所用运行时启用--net-raw同样需要重启 Docker。配置完成后runsc运行时的配置大致如下runsc: { path: /tmp/iptables/runsc, runtimeArgs: [ --debug-log, /tmp/iptables/logs/runsc.log.%TEST%.%TIMESTAMP%.%COMMAND%, --net-raw ] }, // ...其中--debug-log参数中的%TEST%、%TIMESTAMP%、%COMMAND%是占位符便于按测试、时间戳和命令分别归档日志排障时可按测试名定位对应的 runsc 日志。Make 目标下这些步骤是如何自动化的使用make iptables-tests时Makefile 会自动完成上述准备从源码可以看到完整链路load-iptables构建并加载测试用 Docker 镜像镜像由 images/iptables/Dockerfile 构建基于ubuntu:24.04并安装iptables包——该包同时提供iptables-legacy和iptables-nft默认iptables软链指向后者依次sudo modprobe iptable_filter / ip6table_filter / iptable_nat / ip6table_nat比手动步骤多了 NAT 相关模块因为套件含 NAT 表测试通过install_runtime宏把runsc以--net-raw参数注册为 Docker 运行时并执行//test/iptables:iptables_test再以--net-raw --reproduce-nftables注册runsc-nftables运行时执行//test/iptables:nftables_test这是 nftables_test.sh 承载的 nftables 兼容性 shell 测试。另有一个专门跑iptables-nft客户端的目标iptables-nft-testsMakefile它会加载nfnetlink、nf_tables模块并以--net-raw --TESTONLY-nftables安装运行时后执行//test/iptables:iptables_nft_test。测试结构TestCase 双端协作模型每个 iptables 测试实现TestCase接口由两个并行的“动作”组成一个函数跑在容器内ContainerAction一个函数跑在宿主机本地LocalAction。双方进程会互相告知对端的 IP 地址测试在两边函数都成功时才算通过。从接口定义test/iptables/iptables.go可以看到框架的完整语义type TestCase interface { // Name returns the name of the test. Name() string // ContainerAction runs inside the container. It receives the IP of the // local process. ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error // LocalAction runs locally. It receives the IP of the container. LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error // ContainerSufficient indicates whether ContainerActions return value // alone indicates whether the test succeeded. ContainerSufficient() bool // LocalSufficient indicates whether LocalActions return value alone // indicates whether the test succeeded. LocalSufficient() bool }ContainerAction通常在容器内设置 iptables 规则然后尝试发送/接收数据包LocalAction通常只负责发送/接收数据包验证规则的实际效果ContainerSufficient/LocalSufficient允许单端判定的用例例如“连接被 DROP”时只需本地端观察到超时即可。框架提供三个基类型简化实现baseCase双端都必须成功、localCase本地端成功即可、containerCase容器端成功即可见 iptables.go。关键常量与执行流程框架中有几个关键常量test/iptables/iptables.go常量值作用IPExchangePort2349容器内监听端口用于接收本地进程发来的 IP 地址TerminalStatementFinished!容器端 runner 完成时打印的终止语句测试端以它作为成功信号TestTimeout10s所有测试的超时时间NegativeTimeout2s验证“连接未建立”这类否定用例的等待时间一次完整测试的执行流程在 singleTest / iptablesTest 中体现得最为清楚对IPv4、IPv6两个子测试分别执行一遍nftables 模式下先经skipIfNFTMode判断是否需要跳过如 IPv6 目前不在 nftables 兼容模式下测试REJECT/REDIRECT/Range/multiport/owner match 等目标也暂不支持见 skipIfNFTMode通过dockerutil.MakeContainer创建容器运行镜像为iptables并授予NET_ADMIN能力容器内操作 iptables 表的必要条件同时把本地构建好的 runner 二进制拷贝进容器/runner/runner以-name TESTNAME按需加-ipv6、-nft启动获取容器 IPIPv6 未配置时仅Skip而不失败本地进程向容器的 2349 端口 TCP 连接并发送一个字节sendIP容器内 runner 的 getIP 从连接源地址拿到本地进程 IP——这就是“双方互知 IP”的实现机制并发启动LocalActiongoroutine 和容器端等待 goroutine等待容器日志出现Finished!即视为ContainerAction成功两边各返回一个结果任一失败即测试失败无论成功失败defer 会打印完整容器日志并清理容器。容器内 runner 的职责test/iptables/runner/main.go 是拷贝进容器的入口程序解析-name/-ipv6/-nft三个 flag从iptables.Tests注册表中按名字取出用例监听 2349 端口获取本地进程 IP执行test.ContainerAction(ctx, ip, *ipv6)成功后打印TerminalStatement。其中-nft会设置全局变量iptables.NFT true切换容器内实际调用的 iptables 二进制。legacy 与 nft 二进制的选择规则实际通过exec.Command调用宿标准入的 iptables 可执行文件来下发test/iptables/iptables_util.go默认使用iptables-legacy/ip6tables-legacy当NFT为真时切换到iptables-nft/ip6tables-nft。这解释了为什么测试镜像images/iptables/Dockerfile只需要安装iptables一个包——Ubuntu 24.04 的该包同时提供 legacy 与 nft 两个客户端测试就能在同一镜像中复现两种后端行为。filterTable/natTable等辅助函数则负责在filter、nat两张表上执行任意规则参数。套件覆盖了哪些 iptables 能力从 test/iptables/iptables_test.go 中登记的 90 余个Test*函数可以看出套件系统性覆盖了以下能力域实现分布在 filter_input.go、filter_output.go、nat.go、reject.gofilter 表 INPUT 链按协议/端口 DROPUDP、TCP 源端口、目的端口、全部 DROP、仅放行 UDP、默认 policy ACCEPT/DROP、RETURN 下溢、自定义用户链与 JUMPTestJumpSerialize、TestJumpBasic、TestJumpReturn、TestJumpTwice等、源/目的地址匹配与取反、接口匹配精确、前缀BeginsWith、取反、multiport 多端口匹配与取反、RECVORIGDSTADDR扩展filter 表 OUTPUT 链端口 DROP/取反、owner匹配按 UID/GID 放行、丢弃、取反、owner 为 nil 时默认放行、接口匹配、目的地址匹配NAT 表PREROUTING 的REDIRECTTCP/UDP 端口重定向、!取反、要求协议、loopback 跳过 prerouting、DNAT仅地址、仅端口、SNATPOSTROUTINGTCP/UDP、ORIGINAL_DST扩展、RECVORIGDSTADDRREJECT 目标默认 REJECT、不匹配时行为、tcp-reset对应TestFilterInputRejectTCPReset等四个用例。如何新增一个测试README 给出的扩展流程三步对应到源码即把测试加入iptables包在 test/iptables 下新增文件实现TestCase接口Name返回唯一名字ContainerAction里用filterTable/natTable下发规则并收发数据包LocalAction做对端验证在init函数中通过RegisterTestCase注册可参考 filter_input.go 的init()。注意 RegisterTestCase 对重名会直接panic因此用例名必须全局唯一且该名字就是后续--test_filter使用的名字在 iptables_test.go 中添加对应的TestXxx函数照抄现有样式即可func TestFilterInputDropUDP(t *testing.T) { singleTest(t, FilterInputDropUDP{}) }完成后即可通过 bazel 运行该测试。若新用例涉及 nftables 暂不支持的特性如 REJECT/REDIRECT/Range/multiport/owner match还需同步把用例名加入 skipIfNFTMode 的跳过列表避免 nft 模式下误报失败。运行测试构建、安装与单测过滤构建并安装 runsc每次修改 gVisor 后需要重新执行$ bazel build //runsc sudo cp bazel-out/k8-fastbuild-ST-4c64f0b3d5c7/bin/runsc/runsc_/runsc $(which runsc)注意bazel-out下的配置哈希目录名因机器而异以本机实际输出为准。构建测试容器镜像每次修改test/iptables目录下的测试代码后重新执行$ make load-iptables运行单个测试通过--test_filter指定用例名singleTest会自动派生IPv4/IPv6子测试$ bazel test //test/iptables:iptables_test --test_filterTESTNAMEnftables 兼容模式iptables-nft客户端下运行$ bazel test //test/iptables:iptables_nft_test --test_filterTESTNAME该目标在 test/iptables/BUILD 中与iptables_test的区别仅在于额外传入-iptables-nfttrue参数对应测试主程序中的iptablesNFTflag从而让容器端 runner 走iptables-nft二进制。用 runc 做基线对照同一用例也可以用 runc 运行用于排除“问题出在测试用例本身而非 gVisor”的情况$ bazel test //test/iptables:iptables_test --test_filterTESTNAME --test_envRUNTIMErunc $ bazel test //test/iptables:iptables_nft_test --test_filterTESTNAME --test_envRUNTIMErunc一个值得注意的现成佐证Makefile 中iptables-tests目标里 runc 对照行被注释掉了原因是“权限问题待修复FIXME b/218923513”——即通过make目标跑 runc 基线目前受限于宿主机权限手动按 README 方式加RUNTIMErunc环境变量运行仍是可用手段。排障要点小结IPv6 子测试被 Skip容器拿不到 IPv6 地址时只Skip不失败iptablesTest通常意味着/etc/docker/daemon.json的 IPv6 配置未生效或 Docker 未重启nft 模式大量 Skip属预期行为skipIfNFTMode明确列出了当前 nftables 兼容模式不支持的目标类型排障时应先对照该列表定位失败规则容器端全部日志会被WaitForOutput归入对应子测试上下文测试结束时的 Container logs: 会打印完整容器日志而--debug-log配置的 runsc 日志按%TEST%归档可精确对应用例规则下发报错tableCmd会把 iptables 二进制的完整 stderr 拼进错误信息含具体二进制名与参数可直接据此判断是 legacy/nft 后端差异还是规则语法问题。小结gVisor 的 iptables 测试套件是一个“双进程协作 双地址族 双 iptables 后端”的矩阵化集成测试框架容器内 runner 负责下规则、本地进程负责验证流量效果TestCase接口与注册机制保证了用例可插拔扩展配合--net-raw运行时参数、IPv6 Docker 配置与iptables-legacy/iptables-nft双客户端它能系统性地回归 filter、NAT、REJECT 等 netfilter 能力在沙箱环境下的行为。新增用例只需三步实现接口、init注册、登记Test*函数即可接入现有的 bazel 测试流水线。【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表