Envoy 的 SSL 库构建配置指南:BoringSSL、BoringSSL-FIPS 与 OpenSSL 的选择与验证
【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy
本文基于 Envoy 仓库中的 bazel/SSL.md 编写,系统讲解 Envoy 的 SSL 库构建体系:默认的 BoringSSL、面向合规场景的 BoringSSL-FIPS,以及可选的 OpenSSL 动态加载方案。读完本文,你将掌握三类构建命令、各方案的架构支持与版本字符串特征、从旧版--define boringssl=fips的迁移方法,以及通过envoy --version验证构建结果的标准流程。
一、Envoy 的 SSL 库选型总览
作为云原生边缘/中间/服务代理,Envoy 的 TLS 能力由底层的 SSL 库提供。当前仓库支持三种 SSL 构建形态,全部通过 Bazel 的--config参数切换:
| 构建形态 | 对应 config | 默认状态 | 版本字符串 |
|---|---|---|---|
| 标准 BoringSSL | 无(默认) | 默认 | BoringSSL |
| BoringSSL-FIPS | --config=boringssl-fips | 可选 | BoringSSL-FIPS |
| OpenSSL | --config=openssl | 可选 | OpenSSL |
选择依据很简单:没有特殊合规要求时直接用默认 BoringSSL;需要 FIPS 140 合规且运行在 Linux x86_64/aarch64 上时用 BoringSSL-FIPS;因业务或生态原因必须使用 OpenSSL 时再用--config=openssl。需要特别注意的是,三种形态的版本字符串都由编译期宏注入(详见本文第五部分),因此构建完成后可以随时通过envoy --version快速确认实际链接的是哪个库。
二、默认(非 FIPS)构建:零配置即可使用 BoringSSL
默认构建无需任何额外配置,BoringSSL 是 Envoy 的默认 SSL 实现,直接执行标准构建命令即可:
bazel build //source/exe:envoy-static从仓库的 bazel/BUILD 可以看到默认值的来源:
label_flag( name = "ssl", build_setting_default = "@boringssl//:ssl", ) label_flag( name = "crypto", build_setting_default = "@boringssl//:crypto", )即//bazel:ssl与//bazel:crypto两个标签的默认值分别指向@boringssl//:ssl和@boringssl//:crypto。也就是说,任何不显式覆盖这两个标签的构建,都会默认链接标准 BoringSSL,无需用户干预。
三、FIPS 构建:BoringSSL-FIPS
3.1 支持的 FIPS 构建与架构
Envoy 项目目前仅在 x86_64 上对 BoringSSL FIPS 构建提供官方支持与测试。仓库欢迎其他库或架构的补丁,但维护与兼容性修复的责任由下游项目自行承担。
具体到 BoringSSL-FIPS 形态,bazel/SSL.md 给出的支持范围是:
- 支持的架构:Linux x86_64、aarch64
- 版本字符串:
BoringSSL-FIPS(可在envoy --version输出中看到)
Envoy 遵循 BoringSSL 的 FIPS Update Stream 策略:每当创建 Envoy 稳定发布分支时,所使用的 BoringSSL FIPS 版本都会与该策略保持兼容;除非出现影响 Envoy 的 Bug 或安全漏洞,否则该版本(以及配套的构建工具版本)在发布分支上不会变更。
3.2 构建命令
bazel build --config=boringssl-fips //source/exe:envoy-static--config=boringssl-fips在 .bazelrc 中展开为一系列相互关联的配置:
common:fips-common --@envoy//bazel:fips=True common:fips-common --test_tag_filters=-nofips common:fips-common --build_tag_filters=-nofips common:boringssl-fips --config=fips-common common:boringssl-fips --@envoy//bazel:ssl=@boringssl-fips//:ssl common:boringssl-fips --@envoy//bazel:crypto=@boringssl-fips//:crypto common:boringssl-fips --@quiche//:ssl_lib=@boringssl-fips//:ssl这里有几个关键细节:
--@envoy//bazel:fips=True开启 FIPS 语义标记;- 不仅把
ssl、crypto两个标签切到@boringssl-fips,连 QUICHE(HTTP/3 的 QUIC 栈)的ssl_lib也一并切到 FIPS 库,确保全二进制使用同一套 SSL 实现,避免符号/ABI 冲突; --test_tag_filters=-nofips与--build_tag_filters=-nofips会过滤掉标注为nofips的测试与构建目标——这类目标通常依赖 FIPS 模式不支持的算法或特性,在 FIPS 构建中应被排除。
四、OpenSSL:动态加载的替代方案
4.1 与 BoringSSL 的本质差异
BoringSSL 是 Envoy 官方支持且默认的 SSL 实现,OpenSSL 只是替代方案。两者最根本的差异在于链接方式:
- BoringSSL 系列直接静态链接进 Envoy 二进制;
- OpenSSL 库不会被静态链接进 Envoy。OpenSSL 库(要求3.5 或更高版本,仓库 MODULE.bazel 中声明为
bazel_dep(name = "openssl", version = "3.5.7.envoy"))必须在运行时存在,由 Envoy 通过dlopen()动态加载; - FIPS 模式在 OpenSSL 中是运行时强制开启的(通过 OpenSSL 本身和/或操作系统配置),而不是像 BoringSSL-FIPS 那样在构建期决定。
这一设计在 compat/openssl/README.md 中有更完整的描述:bssl-compat兼容层实现了 BoringSSL API,但底层调用转发到 OpenSSL(source/ossl.c中的转发函数通过dlopen/dlsym调用 OpenSSL)。该库以 C ABI 的静态库形式交付(@bssl-compat//:bssl-compat),并刻意只做data依赖而非link依赖,从而避免客户端把 OpenSSL 库链接进二进制。
4.2 构建命令与注意事项
bazel build --config=openssl //source/exe:envoy-static对应到 .bazelrc:
common:openssl --@envoy//bazel:openssl=True common:openssl --@envoy//bazel:ssl=@envoy//compat/openssl:ssl common:openssl --@envoy//bazel:crypto=@envoy//compat/openssl:crypto common:openssl --@quiche//:ssl_lib=@envoy//compat/openssl:ssl common:openssl --copt=-DENVOY_SSL_OPENSSL common:openssl --host_copt=-DENVOY_SSL_OPENSSL common:openssl --@envoy//bazel:http3=False common:openssl --define=quiche_disable_http3=true common:openssl --test_tag_filters=-nofips,-quiche common:openssl --build_tag_filters=-nofips关键约束与注意事项:
- 支持的架构:Linux x86_64、aarch64、ppc64le;
- 版本字符串:
OpenSSL(可见于envoy --version); - HTTP/3(QUIC)在 OpenSSL 构建中被禁用:config 将
//bazel:http3置为False,并额外--define=quiche_disable_http3=true,把依赖 BoringSSL 专属原语(如SIPHASH_24)的 QUIC 代码排除在 OpenSSL 构建之外。不过 QUICHE 库仍会因非 QUIC 特性(如 HTTP datagram / CONNECT-UDP capsule 支持)被链接进来,因此其ssl_lib也必须指向 OpenSSL 兼容层,防止 BoringSSL 与 OpenSSL 同时链接进同一二进制引发符号/ABI 冲突; - 安全策略范围:OpenSSL 构建目前不在 Envoy 安全策略(SECURITY.md)的覆盖范围内。这意味着用
--config=openssl产出的二进制不受 Envoy 官方安全公告流程保护,生产环境选用前需自行评估风险; - FIPS 语义:在 OpenSSL 形态下启用 FIPS 属于运行时行为,通过 OpenSSL 的 FIPS 模块配置或操作系统层配置完成,与构建期无关。
五、从旧版--define boringssl=fips迁移
历史版本使用--define boringssl=fips选择 FIPS 库,该标志已失效,直接使用会构建失败。仓库通过 bazel/deprecated_features.bzl 中的check_removed_fips_define规则主动检测:一旦检测到ctx.var.get("boringssl") == "fips",就会fail()并输出迁移提示。
迁移对照表如下:
| 旧用法 | 新用法 |
|---|---|
--define boringssl=fips | --config=boringssl-fips |
--define boringssl=fips(在 ppc64le 上) | --config=openssl |
迁移逻辑的要点:旧标志在 ppc64le 上会自动选择一个 FIPS 库;而新方案要求显式选择库。在 ppc64le 上应使用--config=openssl(OpenSSL 的 FIPS 模式在运行时强制开启),而非 BoringSSL-FIPS。
六、SSL 标志完整性:为什么不能直接设置底层标志
Bazel 的 SSL 配置由三个相互依赖的标志共同决定://bazel:ssl、//bazel:crypto和//bazel:fips(此外 OpenSSL 形态还涉及//bazel:openssl)。
这三个标志定义在 bazel/BUILD:
label_flag(name = "ssl", build_setting_default = "@boringssl//:ssl") label_flag(name = "crypto", build_setting_default = "@boringssl//:crypto") bool_flag(name = "fips", build_setting_default = False) bool_flag(name = "openssl", build_setting_default = False)配套的config_setting(bazel/BUILD)用于在源码树中判断当前形态:
config_setting(name = "fips_build", flag_values = {":fips": "True"}) config_setting(name = "using_boringssl", flag_values = {":ssl": "@boringssl//:ssl"}) config_setting(name = "using_boringssl_fips", flag_values = {":ssl": "@boringssl-fips//:ssl"}) config_setting(name = "using_openssl", flag_values = {":openssl": "True"})切勿直接设置这些标志。原因很直接:
- 不一致的组合会产生损坏的构建或错误的版本字符串。例如,选了一个 FIPS 库却保持
--//bazel:fips=False,或ssl与crypto指向不同库,都会破坏构建一致性; --config选项(boringssl-fips/openssl)在 .bazelrc 中一次性把ssl、crypto、fips/openssl乃至quiche的ssl_lib统一设置好,保证三/四者始终一致。
七、版本字符串的生成原理
envoy --version中出现的BoringSSL/BoringSSL-FIPS/OpenSSL并非运行时探测结果,而是编译期宏注入的固定字符串。
在 source/common/version/BUILD 中,version_string_lib通过select依据上文提到的config_setting注入宏:
copts = select({ "//bazel:using_boringssl_fips": ["-DENVOY_SSL_VERSION=\"BoringSSL-FIPS\""], "//bazel:using_openssl": ["-DENVOY_SSL_VERSION=\"OpenSSL\""], "//conditions:default": ["-DENVOY_SSL_VERSION=\"BoringSSL\""], }),而 source/common/version/version_string.cc 中的实现则把它拼进完整的版本字符串:
const std::string& envoySSLVersion() { #ifdef ENVOY_SSL_VERSION static const std::string version = ENVOY_SSL_VERSION; #else static const std::string version = "no-ssl"; #endif return version; } const std::string& envoyVersionString() { CONSTRUCT_ON_FIRST_USE(std::string, fmt::format("{}/{}{}/{}/{}/{}", build_scm_revision, BUILD_VERSION_NUMBER, build_version_suffix, build_scm_status, envoyBuildType(), envoySSLVersion())); }因此完整的版本串形如1.32.0/abc123.Modified/RELEASE/BoringSSL(格式说明见 version_string.h)。这也解释了为什么“错误的 SSL 标志组合会导致错误的版本字符串”:宏在编译期就被select固化,标志设置不一致时版本串与实际链接库就会对不上。
八、验证构建结果
构建完成后,运行:
envoy --version根据输出的最后一段判断实际生效的 SSL 库:
BoringSSL-FIPS— BoringSSL FIPS 构建BoringSSL— 标准(非 FIPS)构建OpenSSL— OpenSSL 构建
九、快速决策清单
| 场景 | 推荐方案 |
|---|---|
| 常规生产环境,无 FIPS 合规要求 | 默认构建(BoringSSL),零配置 |
| 需要 FIPS 140 合规,Linux x86_64/aarch64 | --config=boringssl-fips |
| 必须使用 OpenSSL(如组织强制要求),Linux x86_64/aarch64/ppc64le | --config=openssl(注意 HTTP/3 禁用、不受 Envoy 安全策略覆盖) |
| 迁移旧构建脚本 | 将--define boringssl=fips替换为上表对应--config |
无论选择哪种形态,都建议在构建后用envoy --version复核版本字符串,并确认构建配置与预期一致——这是避免“构建成功但 SSL 库不对”这类隐蔽问题的最直接手段。
【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考