ARTICLE DETAIL

资讯详情

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

Linkerd2 模糊测试(Fuzzing)指南:基于 OSS-Fuzz 的持续安全测试与 Go Fuzzer 实战

Linkerd2 模糊测试(Fuzzing)指南:基于 OSS-Fuzz 的持续安全测试与 Go Fuzzer 实战 服务网格云原生可观测性【免费下载链接】linkerd2Ultralight, security-first service mesh for Kubernetes. Main repo for Linkerd 2.x.项目地址https://gitcode.com/gh_mirrors/li/linkerd2点击查看免费下载本指南以 Linkerd2 仓库中 test/fuzzing/README.md 为骨架系统讲解 Linkerd 如何借助 Google OSS-Fuzz 构建持续模糊测试体系从脚本化配置的组织方式、本地运行方法到test/fuzzing/fuzzers.go与散布在各核心包中的 fuzzer 入口的实现细节。读完本文你将理解 Linkerd2 的 fuzz 目标如何被声明、compile_go_fuzzer如何工作、如何在本机复现 OSS-Fuzz 的构建与运行流程并能据此为其他 Go 项目搭建同类模糊测试。一、概述Linkerd 的模糊测试体系如何组织Linkerd2 的模糊测试采用脚本化配置 OSS-Fuzz 驱动的架构项目自身只维护 fuzzer 的实现代码与运行所需的脚本说明而实际的 Docker 构建与持续执行由 Google 的 google/oss-fuzz 项目托管。这一点在 test/fuzzing/README.md 中写得非常明确——OSS-Fuzz 会持续对 Linkerd 项目执行模糊测试并负责调度、回归与崩溃报告。具体来说Linkerd 的 fuzzing 配置位于 OSS-Fuzz 仓库的 linkerd2 项目目录中该目录负责编写并维护Dockerfile提供 fuzzer 运行所需的构建环境编写build.sh逐一调用 Linkerd2 项目中每个 fuzzer 的入口函数完成编译。因此整个体系分成两层仓库侧本仓库提供 Go 源码形式的 fuzzer 入口*_fuzzer.go以及test/fuzzing/目录下的说明与工具函数OSS-Fuzz 侧外部提供持续集成、Docker 镜像构建与崩溃管理基础设施。本仓库内所有 fuzzer 入口文件的分布情况可以通过find_files类工具一览见下文各小节核心集中在以下位置位置作用test/fuzzing/fuzzers.go对pkg/util端口解析、pkg/healthcheck健康检查的 fuzz 入口pkg/healthcheck/healthcheck_fuzzer.go对FetchCurrentConfiguration的 fuzz 入口pkg/inject/inject_fuzzer.go对 Pod 注入/反注入inject/uninject链路的 fuzz 入口pkg/profiles/profiles_fuzzer.go对 ServiceProfile 校验与 proto 渲染的 fuzz 入口pkg/identity/identity_fuzzer.go对 Identity 服务Certify方法的 fuzz 入口controller/api/destination/destination_fuzzer.go对 destination API 服务 Get/GetProfile 等方法的 fuzz 入口二、在本地运行 Linkerd fuzzer2.1 前置条件克隆 oss-fuzz 仓库README 指出本地运行 fuzzers 的说明在 oss-fuzz 官方文档中。核心前提是先在本地克隆 google/oss-fuzz 仓库因为build.sh、Dockerfile等基础设施都由该仓库提供git clone https://github.com/google/oss-fuzz随后按照 oss-fuzz 文档中Testing locally一节的步骤执行构建与模糊测试命令。由于 Linkerd 的 fuzzing 配置Dockerfile 与 build.sh位于 oss-fuzz 仓库的projects/linkerd2目录本地运行时需要把本仓库的代码作为构建上下文接入该目录。2.2 关键脚本文件的作用README 明确列出了两个核心文件及其职责Dockerfile为运行 fuzzer 提供必要的环境最关键的是基于oss-fuzz-base基础镜像——该镜像中预置了compile_go_fuzzer等函数供本目录的build.sh调用build.sh负责为 linkerd2 项目中的每个 fuzzer 调用对应的编译函数把 Go 源码编译成可执行的 fuzz 二进制。从源码结构看compile_go_fuzzer的作用是把形如FuzzXxx的入口函数编译为 OSS-Fuzz 约定的 fuzz 目标通常基于 libFuzzer 引擎因此每个*_fuzzer.go文件中的函数签名都必须符合func FuzzXxx(data []byte) int的约定——这一点在下面所有 fuzzer 实现中均可印证。三、仓库内的 fuzzer 入口实现详解Linkerd2 遵循 Go 标准约定将 fuzz 入口命名为FuzzName(data []byte) int并放置在*_fuzzer.go文件中。函数接收任意字节流data返回1表示正常处理完成0表示输入无效被丢弃。大多数 fuzzer 使用 AdaLogics/go-fuzz-headers 库把原始字节流转换为结构化的 Go 对象。3.1 端口解析test/fuzzing/fuzzers.go该文件位于 test/fuzzing/fuzzers.go是 README 直接关联的脚本目录中的核心实现包含三个 fuzzerFuzzParsePorts—— 直接对util.ParsePorts进行原始字节模糊测试// FuzzParsePorts fuzzes the ParsePorts function. func FuzzParsePorts(data []byte) int { _ util.ParsePorts(string(data)) return 1 }其底层被测试函数位于 pkg/util/parsing.go。ParsePorts会把逗号分隔的端口字符串解析为端口集合支持8080、8000-9000这类范围表示法逐项展开为map[uint32]struct{}遇到非法范围时记录Invalid port range告警并跳过。该 fuzzer 的价值在于用随机字符串冲击字符串分割、范围解析与边界循环逻辑。FuzzParseContainerOpaquePorts—— 使用fuzz.NewConsumer把输入字节流拆解为容器数量、一组corev1.Container结构体以及一个 override 字符串func FuzzParseContainerOpaquePorts(data []byte) int { f : fuzz.NewConsumer(data) qtyOfContainers, err : f.GetInt() if err ! nil { return 0 } qtyOfContainers % 20 containers : make([]corev1.Container, 0) for i : 0; i qtyOfContainers; i { newContainer : corev1.Container{} err f.GenerateStruct(newContainer) if err ! nil { return 0 } containers append(containers, newContainer) } override, err : f.GetString() if err ! nil { return 0 } _ util.ParseContainerOpaquePorts(override, util.GetNamedPorts(containers)) return 1 }这里体现了两个设计细节容器数量被% 20取模限制在 019 个避免生成天文数字的结构体拖垮性能它同时驱动了两个被测函数ParseContainerOpaquePorts 负责把 opaque ports 注解解析为端口范围列表并把命名端口named port换算为实际端口号GetNamedPorts 负责从容器列表中提取Name - ContainerPort映射。组合测试覆盖了注解覆盖override、命名端口查找、范围解析三者的交互。FuzzHealthCheck—— 模糊测试健康检查器的构造逻辑func FuzzHealthCheck(data []byte) int { f : fuzz.NewConsumer(data) options : healthcheck.Options{} err : f.GenerateStruct(options) if err ! nil { return 0 } _ healthcheck.NewHealthChecker([]healthcheck.CategoryID{healthcheck.KubernetesAPIChecks}, options) return 1 }它用随机数据填充healthcheck.Options再调用 NewHealthChecker 构造健康检查器仅启用KubernetesAPIChecks分类用于验证健康检查配置结构在极端输入下的健壮性。3.2 健康检查配置抓取pkg/healthcheck/healthcheck_fuzzer.gohealthcheck_fuzzer.go 中的FuzzFetchCurrentConfiguration把原始字节当作YAML/API 对象数据喂给k8s.NewFakeAPI构造出 fake clientset 后再调用 FetchCurrentConfigurationfunc FuzzFetchCurrentConfiguration(data []byte) int { clientset, err : k8s.NewFakeAPI(string(data)) if err ! nil { return 0 } _, _, _ FetchCurrentConfiguration(context.Background(), clientset, linkerd) return 1 }该测试链路的意图是用随机的 Kubernetes 资源定义去冲击从控制平面命名空间抓取当前配置ConfigMap 与 charts.Values的解析逻辑从而发现反序列化或字段映射缺陷。3.3 Pod 注入链路pkg/inject/inject_fuzzer.goinject_fuzzer.go 是整个注入链路最完整的端到端 fuzzer覆盖了注入、反注入与 opaque ports patch 生成func FuzzInject(data []byte) int { f : fuzz.NewConsumer(data) yamlBytes, err : f.GetBytes() if err ! nil { return 0 } v : l5dcharts.Values{} err f.GenerateStruct(v) if err ! nil { return 0 } conf : NewResourceConfig(v, OriginUnknown, ) _, _ conf.ParseMetaAndYAML(yamlBytes) injectProxy, err : f.GetBool() if err ! nil { return 0 } _, _ conf.GetPodPatch(injectProxy, GetOverriddenValues) _, _ conf.CreateOpaquePortsPatch() report : Report{} err f.GenerateStruct(report) if err nil { _, _ conf.Uninject(report) } return 1 }它依次驱动了pkg/inject中资源配置的核心流程随机 YAML 解析ParseMetaAndYAML、注入补丁生成GetPodPatch、opaque ports 补丁生成CreateOpaquePortsPatch、以及基于随机报告的反注入Uninject。由于注入是 Linkerd 将 sidecar 代理注入 Pod 的核心机制这条 fuzzer 对保证任何用户清单都不会让注入器崩溃至关重要。3.4 ServiceProfile 校验与渲染pkg/profiles/profiles_fuzzer.goprofiles_fuzzer.go 包含两个 fuzzerFuzzProfilesValidate直接把随机字节交给profiles.Validate冲击 ServiceProfile 的 OpenAPI 校验逻辑FuzzRenderProto从字节流中拆出 proto 内容、namespace、name、clusterDomain 四个字段写入临时文件后调用RenderProto覆盖proto 描述 - DestinationProfile 渲染的完整路径protofile, err : os.Create(protofile) if err ! nil { return 0 } defer protofile.Close() defer os.Remove(protofile.Name()) _, err protofile.Write(protodata) if err ! nil { return 0 } _, err RenderProto(protofile.Name(), namespace, name, clusterDomain) if err ! nil { return 0 }注意实现中使用了os.Create写临时文件再删除的惯例fuzzer 本身也承担了随机 proto 定义 随机标识符组合下的容错验证。3.5 Identity 证书签发pkg/identity/identity_fuzzer.goidentity_fuzzer.go 的FuzzServiceCertify用随机数据填充pb.CertifyRequest再构造带 fake validator 与 fake issuer 的 Identity 服务并调用Certifyfunc FuzzServiceCertify(data []byte) int { f : fuzz.NewConsumer(data) req : pb.CertifyRequest{} err : f.GenerateStruct(req) if err ! nil { return 0 } svc : NewService(fakeValidator{successful-result, nil}, nil, nil, nil, , , ) svc.updateIssuer(fakeIssuer{tls.Crt{}, nil}) _, _ svc.Certify(context.Background(), req) return 1 }mTLS 身份签发是 Linkerd 安全模型的核心此处用假的 validator/issuer 隔离真实加密组件专门检验Certify对畸形请求如缺失身份、异常证书序列化字段的处理。3.6 Destination APIcontroller/api/destination/destination_fuzzer.godestination_fuzzer.go 针对控制平面最繁忙的 destination 服务包含四个 fuzzerFuzzAdd随机生成watcher.AddressSet调用 endpoint translator 的Add与Remove验证地址集合增删的稳定性FuzzGet随机生成三个GetDestination请求并依次调用server.Get配合带缓冲的 mock stream覆盖流式订阅接口FuzzGetProfile随机GetDestination请求驱动server.GetProfile的 profile 订阅流FuzzProfileTranslatorUpdate随机sp.ServiceProfile驱动 profile translator 的Update方法覆盖 ServiceProfile 变更传播逻辑。该文件还在init()中调用testing.Init()说明 fuzzer 内部复用了testing.T与测试辅助函数如makeServer、makeEndpointTranslator因此它同时是对测试基础设施的一种压力测试。四、OSS-Fuzz 文件设置与本地构建流程结合 README 与仓库源码完整的本地复现流程如下克隆 oss-fuzz 并进入项目目录git clone https://github.com/google/oss-fuzz进入projects/linkerd2确认环境oss-fuzz 的本地文档要求 Docker 环境因为所有构建都在oss-fuzz-base基础镜像中完成构建 fuzzer 镜像按照 oss-fuzz 文档执行build_image与build_fuzzers脚本build.sh会调用compile_go_fuzzer编译 test/fuzzing/fuzzers.go 等文件中的全部FuzzXxx入口运行 fuzzer使用run_fuzzer脚本运行指定目标例如对应FuzzParsePorts的 fuzz 二进制OSS-Fuzz 基础设施会自动收集崩溃样本、生成回归测试语料corpus并归档。要点Linkerd2 仓库本身不需要 Dockerfile 与 build.sh——这两者由 oss-fuzz 仓库托管本仓库只需保证 fuzzer 函数可被compile_go_fuzzer正确识别并编译。这是脚本化设置scripting setup一词的准确含义仓库侧提供脚本化的入口OSS-Fuzz 侧提供容器化执行。五、为 Linkerd2 新增一个 fuzzer 的实践要点从上述实现可以总结出在 Linkerd2 中新增 fuzz 目标的通用模式可直接对照现有文件命名与位置新建*_fuzzer.go导出FuzzName(data []byte) int函数输入结构化优先使用fuzz.NewConsumer(data)配合GenerateStruct/GetBytes/GetString/GetInt从原始字节构造结构体与标量对纯字符串解析函数如ParsePorts可以直接string(data)失败快速返回任何一次解析失败都应return 0避免在畸形输入上继续执行限制规模对循环生成的结构体数量做取模限制如% 20防止 fuzzer 自身成为性能瓶颈覆盖核心流程优先选择用户可控输入 - 解析/校验/渲染的边界函数如端口注解解析、YAML 注入、proto 渲染、证书请求、流式 API 订阅——这些正是本仓库六个 fuzzer 文件覆盖的六类高危路径回归依赖OSS-Fuzz 会为每个崩溃自动生成回归测试用例因此 fuzzer 入口应当保持稳定签名避免频繁破坏既有语料。六、小结Linkerd2 的模糊测试体系是仓库内实现 OSS-Fuzz 持续执行的标准 Go 开源实践。仓库侧通过 test/fuzzing/fuzzers.go 以及散落在 pkg/healthcheck、pkg/inject、pkg/profiles、pkg/identity、controller/api/destination 的六个 fuzzer 文件覆盖了端口解析、健康检查、Pod 注入、ServiceProfile、mTLS 身份签发与 destination 流式 API 等核心解析与安全路径OSS-Fuzz 侧则负责 Docker 构建、持续模糊与崩溃管理。对本仓库读者而言理解compile_go_fuzzerbuild.sh的协作方式后即可参照 test/fuzzing/README.md 的指引在本地克隆 oss-fuzz 并复现整套流程将同一模式推广到其他 Go 服务。赞分享服务网格云原生可观测性【免费下载链接】linkerd2Ultralight, security-first service mesh for Kubernetes. Main repo for Linkerd 2.x.项目地址https://gitcode.com/gh_mirrors/li/linkerd2点击查看免费下载相关推荐Skia 模糊测试Fuzzing完全指南从 fuzz 复现到 OSS-Fuzz 持续集成Skia 模糊测试Fuzzing完全指南从 fuzz 复现到 OSS Fuzz 持续集成 导读 本文以 fuzz/README.md https://li图形学Crossplane 模糊测试实战指南编写 Go Fuzz 测试用例并接入 OSS-Fuzz 与 CIFuzz 持续模糊测试Crossplane 模糊测试实战指南编写 Go Fuzz 测试用例并接入 OSS Fuzz 与 CIFuzz 持续模糊测试 本指南围绕 Crossplane云原生后端GitPython 模糊测试实战指南基于 OSS-Fuzz 与 Atheris 的 fuzzing 目录全解析GitPython 模糊测试实战指南基于 OSS Fuzz 与 Atheris 的 fuzzing 目录全解析 本文围绕 GitPython 仓库中 fuzz版本控制开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表