Envoy Basic Auth 过滤器实战指南:配置、源码原理与每路由鉴权
2026/9/13 18:10:21 网站建设 项目流程

Envoy Basic Auth 过滤器实战指南:配置、源码原理与每路由鉴权

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

Basic Auth 是 Envoy 内置的 HTTP 过滤器,用于解析并校验 RFC 7617 为核心骨架,结合 Envoy 仓库内过滤器源码(basic_auth_filter.cc)、配置工厂(config.cc)、API 定义(basic_auth.proto)与单元测试(filter_test.cc),完整讲解过滤器配置、htpasswd 用户文件格式、认证判定流程、每路由级鉴权覆盖与统计指标,帮助你直接在 Envoy 中落地一套可复制的 HTTP Basic Auth 鉴权方案。

过滤器是什么

Basic Auth 过滤器是一个 HTTP解码路径过滤器(decoder filter),它拦截进入的请求头(decodeHeaders阶段),从 HTTPAuthorization头中提取用户名和密码,与过滤器配置中预置的用户名-密码列表逐一比对:

  • 用户名和密码有效:请求被放行,继续传递到过滤链中的下一个过滤器;
  • 用户名和密码无效或缺失:请求被拒绝,返回401 Unauthorized响应,并附带符合 RFC 7617 的WWW-Authenticate挑战头。

从源码结构看,过滤器的核心实现位于source/extensions/filters/http/basic_auth/,通过BasicAuthFilter::decodeHeaders完成全部判定逻辑,过滤器注册名称为envoy.filters.http.basic_auth(见 well_known_names.h),对应扩展类型 URL 为type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth,API 版本为 v3(见 basic_auth.proto 中的[#extension: envoy.filters.http.basic_auth]标记)。

过滤器配置

type URL 与 users 字段

过滤器通过 HTTP 过滤链中的typed_config完成配置,type URL 固定为:

type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth

核心字段users是一个"用户名-密码对"列表,用于校验请求Authorization头中的用户凭据。它的取值需要符合htpasswd工具的输出格式(Apache HTTP Server 的密码文件格式),users字段类型为config.core.v3.DataSource,既可以内联提供,也可以指向磁盘上的文件(详见 basic_auth.proto 第 37 行,且该字段被标记为sensitive,即敏感字段)。

最小可运行配置示例

以下配置来自 basic_auth_filter.rst,可直接放入http_filters过滤链(通常放在router过滤器之前):

http_filters: - name: envoy.filters.http.basic_auth typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth users: inline_string: |- user1:{SHA}hashed_user1_password user2:{SHA}hashed_user2_password

注意inline_string中使用|-块标量保留换行格式:文件每行一条用户记录,格式为用户名:{SHA}哈希值目前过滤器仅支持{SHA}格式(即 Base64 编码的 SHA-1 摘要),文档明确指出其他格式可能在未来加入。

更完整的字段级配置

users外,BasicAuth消息还提供了四个可选字段(均定义于 basic_auth.proto 第 33~74 行):

字段类型默认行为说明
usersDataSource(敏感)必填htpasswd 格式的用户名-密码对,内联或引用文件
forward_username_headerstring不转发认证成功后,将用户名以该请求头名注入到转发给后端的请求中;留空则不转发
authentication_headerstringAuthorization指定从哪个请求头读取 Basic 凭据;留空则读取Authorization
allow_missingboolfalsetrue时,缺失凭据(无Authorization头,或头不是Basicscheme)的请求被放行;但已携带 Basic 凭据的请求仍会严格校验
emit_dynamic_metadataboolfalse认证成功后,向动态元数据(dynamic metadata)写入 key 为username的字段,命名空间为过滤链中配置的过滤器名称

其中forward_username_headerauthentication_header在 proto 中都带HTTP_HEADER_NAME校验规则(非严格模式),确保填写的必须是合法 HTTP 头名。一个同时使用这些字段的示例:

http_filters: - name: envoy.filters.http.basic_auth typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth users: filename: /etc/envoy/htpasswd.users forward_username_header: x-auth-user authentication_header: authorization allow_missing: false emit_dynamic_metadata: true

需要说明的是:users使用filename时,文件内容由Config::DataSource::read在过滤器工厂创建时读取(见 config.cc 第 70 行),因此修改密码文件后需要触发配置热更新(如 xDS 推送或重启)才能生效;而 per-route 的users则是在路由匹配时按需读取(同文件第 88 行传入了true)。

htpasswd 用户文件格式:源码级解析规则

过滤器对用户列表的解析实现在readHtpasswd函数中(config.cc 第 16~63 行)。了解这些规则有助于你写出合法、不会导致配置加载失败的 htpasswd 文件:

  1. 逐行解析,使用:作为用户名与密码哈希的分隔符;
  2. 跳过空行以及#开头的注释行(源码注释中保留了一个 TODO,未来可能考虑修剪行首行尾空格);
  3. 格式非法(行内没有:)会直接返回错误,错误信息为basic auth: invalid htpasswd format, username:password is expected
  4. 用户名或哈希为空同样报错;
  5. 重复用户名会报duplicate users错误——用户表中不允许同名用户;
  6. 哈希必须带{SHA}前缀,否则报错unsupported htpasswd format: please use {SHA}
  7. {SHA}之后的 Base64 字符串长度必须为 28 个字符(Base64 编码的 SHA-1 摘要恰好为 28 字节文本),长度不符直接报错。

因此一个合法的 htpasswd 文件形如:

# 注释行会被忽略 user1:{SHA}tESsBmE/yNY3lb6a0L6vVQEZNqw= user2:{SHA}EJ9LPFDXsN9ynSmbxvjp75Bmlx8=

上面两个哈希值正是单元测试中使用的样例(filter_test.cc 第 22~23 行,分别对应明文密码test1test2),可作为自测基准。

{SHA}哈希的生成方式与过滤器内部校验逻辑一一对应:computeSHA1(basic_auth_filter.cc 第 25~33 行)对明文密码计算 SHA-1 摘要,再对 20 字节的二进制摘要做 Base64 编码。你可以用任意支持 SHA-1 + Base64 的工具(例如htpasswd -bn user pass,或openssl dgst -sha1 -binary | base64)生成同格式的哈希。

常量时间比较

值得注意的实现细节是:密码比对使用了 OpenSSL 的CRYPTO_memcmp(basic_auth_filter.cc 第 124 行),且在比较前先校验长度是否一致(第 121~122 行)。这是一种常量时间比较(constant-time comparison)做法,可以降低基于响应时间差异的时序侧信道攻击风险;先比长度也能避免对长度不匹配的字符串进行无谓的哈希比较。

认证判定流程:源码调用链剖析

过滤器在请求头阶段(decodeHeaders)完成全部认证逻辑(basic_auth_filter.cc 第 48~110 行),完整流程如下:

  1. 解析每路由覆盖配置:通过Http::Utility::resolveMostSpecificPerFilterConfig获取当前路由的FilterConfigPerRoute;若存在,则用路由级用户表替换全局用户表(第 49~54 行);
  2. 定位认证头:若配置了authentication_header,从该头读取凭据;否则读取标准Authorization头(第 56~61 行);
  3. 凭据缺失处理Authorization头不存在时——若allow_missingtrue直接放行,否则以no_credential_for_basic_auth拒绝(第 63~69 行);
  4. Scheme 校验:凭据必须以Basic前缀开头(区分大小写)。非 Basic scheme(如Bearer)时,allow_missing=true放行,否则以invalid_scheme_for_basic_auth拒绝(第 73~79 行);
  5. Base64 解码:去掉Basic前缀后,对剩余 token 做无填充 Base64 解码(第 81~84 行);
  6. 切分用户名/密码:解码后的字符串格式为username:password,以第一个:切分;找不到冒号则以invalid_format_for_basic_auth拒绝(第 86~94 行);
  7. 凭据校验validateUser在用户表中查找用户名,对密码计算{SHA}哈希并与存储值做常量时间比较;失败以invalid_credential_for_basic_auth拒绝(第 96~99 行,实现见第 112~125 行);
  8. 成功后处理:若配置了forward_username_header,把用户名写入该请求头后转发给上游;若开启emit_dynamic_metadata,将username写入 stream 的动态元数据(命名空间为过滤链中的过滤器名称);allowed计数器 +1 并放行(第 101~109 行)。

401 响应与 WWW-Authenticate 挑战头

当认证失败时,onDenied(第 135~150 行)会:

  • 使denied计数器 +1;
  • 调用sendLocalReply返回401 Unauthorized,响应体为对应的失败原因文本;
  • 构造WWW-Authenticate: Basic realm="<原始URI>"响应头,其中 URI 由请求头重建(最多截取 256 字符,MaximumUriLength常量),让浏览器等客户端可以弹窗重新提示输入凭据;
  • 返回StopIteration终止后续过滤器处理。

单元测试对此有明确断言(filter_test.cc 第 113~126 行):对http://host/的请求,失败响应的WWW-Authenticate头值为Basic realm="http://host/",同时携带details字段(invalid_credential_for_basic_auth等),这些 details 会被记录到访问日志,方便排查认证失败原因。

每路由(Per-Route)配置

Basic Auth 过滤器支持在路由、虚拟主机或加权集群级别覆盖认证配置,实现"同一条过滤链、不同路径不同鉴权策略"。其类型 URL 为:

type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuthPerRoute

BasicAuthPerRoute消息仅有一个必填字段users(类型 DataSource,标记为敏感字段,见 basic_auth.proto 第 78~82 行),即只覆盖用户表,不覆盖其他字段。从源码看,路由级配置通过BasicAuthFilterFactory::createRouteSpecificFilterConfigTyped构建为FilterConfigPerRoute(config.cc 第 84~93 行),并在每次请求的decodeHeaders开头被解析(basic_auth_filter.cc 第 49~54 行)。

官方示例:定制用户 + 关闭鉴权

以下示例来自 basic_auth_filter.rst 的 Per-Route Configuration 章节,演示两种典型场景:为/admin路径定制专属用户,以及对/static前缀路径关闭认证:

route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: { path: "/admin" } route: { cluster: some_service } typed_per_filter_config: envoy.filters.http.basic_auth: "@type": type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuthPerRoute users: inline_string: |- admin:{SHA}hashed_admin_password - match: { prefix: "/static" } route: { cluster: some_service } typed_per_filter_config: envoy.filters.http.basic_auth: "@type": type.googleapis.com/envoy.config.route.v3.FilterConfig disabled: true - match: { prefix: "/" } route: { cluster: some_service }

解读:

  • /admin路由:通过BasicAuthPerRoute覆盖该路径的用户表,只有admin用户能访问;
  • /static前缀路由:使用type.googleapis.com/envoy.config.route.v3.FilterConfigdisabled: true直接禁用该过滤器,静态资源无需认证;
  • 其余/前缀路由:不配置typed_per_filter_config,回落到过滤链级别的全局用户表。

路由级配置遵循 Envoy 标准的"最具体配置优先"(most specific per filter config)解析语义,因此你可以按route -> virtual host -> weighted cluster的粒度层层覆盖用户表。全局过滤链级的users是兜底,只有未被路由配置覆盖的请求才使用它。

与 JWT 等其他认证方法组合(OR 语义)

allow_missingemit_dynamic_metadata两个字段的组合,是 Basic Auth 过滤器与其他认证过滤器(如 JWT)叠加、实现"任一认证通过即可放行"(OR 语义)的关键,API 文档对此有明确设计说明(见 basic_auth.proto 第 52~73 行注释):

  • allow_missingtrue时,缺失凭据或非 Basic scheme 的请求不会被拒绝,而是放行到链中后续的认证过滤器(例如 JWT 过滤器);
  • 携带了 Basic 凭据的请求仍会严格校验,不会因为allow_missing而被绕过;
  • 为了防止"所有认证过滤器都因缺失凭据而放行、导致请求完全未认证"的漏洞,需要配合emit_dynamic_metadata: true——认证成功时过滤器把username写入动态元数据;
  • 再在链尾配置一个 RBAC 过滤器(rbac_filter.rst),根据动态元数据中是否存在username来放行请求,从而保证至少有一种认证方式成功。

动态元数据的命名空间等于过滤链中该过滤器的名称(默认即envoy.filters.http.basic_auth),写入的 key 为username(见 basic_auth_filter.cc 第 127~133 行,DynamicMetadataUsernameKey常量)。单元测试验证了这一点:emit_dynamic_metadata开启时,认证成功后元数据中包含username: "user1",而默认关闭时不会写入任何元数据(filter_test.cc 第 62~98 行)。

统计指标

Basic Auth 过滤器在http.<stat_prefix>.basic_auth.命名空间下输出两项计数器统计(见 basic_auth_filter.rst 的 Statistics 章节,stat_prefix来自过滤链的通用配置前缀):

名称类型描述
allowedCounter通过认证、被放行的请求总数
deniedCounter被拒绝的请求总数

这两项计数器的定义位于 basic_auth_filter.h 第 19~28 行的ALL_BASIC_AUTH_STATS宏,在FilterConfig构造时以stats_prefix + "basic_auth."为前缀注册(basic_auth_filter.cc 第 44 行)。递增时机分别在认证成功放行前(第 108 行)与onDenied中(第 137 行)。你可以通过这两个指标观察认证拒绝率,作为告警与容量规划的输入。

测试用例验证

仓库的单元测试与集成测试覆盖了过滤器的主要行为,可作为你验证自身配置行为的参照:

  • filter_test.cc(465 行):覆盖正确凭据放行、错误用户拒绝、错误密码拒绝、x-username转发头注入、动态元数据写入、401 响应头与 details 断言等;
  • config_test.cc:覆盖 htpasswd 解析的合法性校验(重复用户、非法格式、{SHA}前缀、哈希长度等);
  • basic_auth_integration_test.cc:端到端验证过滤器在真实 HTTP 链路中的表现。

相关文件速查

  • 官方文档:basic_auth_filter.rst
  • API 定义:basic_auth.proto
  • 过滤器实现:basic_auth_filter.cc、basic_auth_filter.h
  • 配置工厂与 htpasswd 解析:config.cc
  • 单元/集成测试:filter_test.cc、config_test.cc、basic_auth_integration_test.cc

结合官方配置示例与上述源码实现,你可以在 Envoy 中快速启用全局 Basic Auth 鉴权、按路由定制用户甚至局部禁用认证,并通过统计指标与访问日志中的 details 字段持续观测认证效果。

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询