ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Envoy Upstream RBAC 过滤器完全指南:在主机选择阶段实施上游访问控制与 SSRF 防护

2026/9/13 19:19:13 拓冰建站 浏览量
Envoy Upstream RBAC 过滤器完全指南:在主机选择阶段实施上游访问控制与 SSRF 防护 Envoy Upstream RBAC 过滤器完全指南在主机选择阶段实施上游访问控制与 SSRF 防护【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本文以 Envoy 仓库中的 Upstream RBAC Filter 官方文档 为骨架结合source/extensions/filters/http/upstream_rbac/目录下的实现源码、API proto 定义以及单元/集成测试系统讲解这一上游 HTTP 过滤器的工作原理、配置方法、统计语义与最佳实践。读完本文你将掌握如何在 Envoy 中于「上游连接建立之前」对选中的上游主机执行基于角色的访问控制RBAC尤其是利用upstream_ip_port匹配器对所有集群类型实施默认拒绝的 SSRF 防护并正确挂载过滤器链、理解其统计作用域与路由级覆盖机制。一、什么是 Upstream RBAC 过滤器Upstream RBAC 过滤器扩展名envoy.filters.http.upstream_rbac是 Envoy 的一个上游 HTTP 过滤器它针对已被选中的上游主机selected upstream host执行授权检查且检查发生在上游连接发起之前。它的最大特点是配置方式与下游 RBAC 过滤器 完全一致——直接复用envoy.extensions.filters.http.rbac.v3.RBAC这一配置消息type URL 为type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC详见 rbac.proto但运行时机不同普通下游 RBAC 在请求解码阶段decodeHeaders评估策略而 Upstream RBAC 从**主机选择回调onHostSelected**中评估策略。关键行为差异体现在评估发生在「主机已选定、连接尚未建立」之间因此upstream_ip_port匹配器可以直接拿到主机选择阶段解析出的上游地址当请求被拒绝时不会建立任何上游连接直接返回403 (Forbidden)本地应答local reply。从源码看这一过滤器是下游 RBAC 过滤器的薄特化upstream_rbac_filter.h 中UpstreamRoleBasedAccessControlFilter继承自RBACFilter::RoleBasedAccessControlFilter并实现Http::UpstreamCallbacks其decodeHeaders直接透传返回Continue注释明确说明 RBAC 检查在onHostSelected()中执行而非解码阶段真正的授权逻辑集中在onHostSelected()回调里。二、核心使用场景基于上游 IP 的 SSRF 防护该过滤器的主要使用场景是通过默认拒绝策略保护上游 IP 地址防止 SSRF服务端请求伪造攻击。为什么需要一个新的上游过滤器而不是沿用下游 RBAC关键在于upstream_ip_port匹配器的数据来源下游 RBAC 过滤器 只能在上游 IP 已被前序 HTTP 过滤器写入过滤器状态filter state后才能匹配上游 IP——例如动态转发代理dynamic forward proxy过滤器开启save_upstream_address时。Upstream RBAC 在主机选择时刻评估直接从主机选择结果中获取已解析的上游地址去除了这一前置依赖。因此上游 IP 匹配对所有集群类型均有效包括动态转发代理sub_cluster_config该场景下上游地址 filter state 通常永远不会被填充静态集群staticEDS 集群严格 DNSstrict DNS集群。由于过滤器持有下游连接与请求头下游 RBAC 过滤器可用的全部匹配器请求头、源/目的 IP 与端口、SSL 属性、动态元数据、CEL 属性等都可以与上游 IP 匹配自由组合实现基于请求特征 上游地址的精细化授权。三、upstream_ip_port 匹配器配置详解upstream_ip_port匹配器的配置消息定义在 upstream_ip_port_matcher.proto字段类型说明upstream_ipconfig.core.v3.CidrRange用于匹配上游 IP 的 CIDR 网段IPv4 与 IPv6 均可匹配upstream_port_rangetype.v3.Int64Range用于匹配上游端口的整数区间proto 注释明确了两个约束两个字段虽均为可选但至少必须提供一个若只提供其中一个另一个视为通配wildcard匹配该匹配器要求过滤器链中的某个过滤器在执行前已将上游地址保存到过滤器状态中键为envoy.stream.upstream_address参见 upstream_address.hproxy_filter.cc 是填充该 FilterState 的示例。Upstream RBAC 过滤器在主机选择回调中会主动把本次选中的主机地址写入该 filter state见下文源码剖析因此它不依赖其他过滤器预先填充这正是其能覆盖所有集群类型的原因。四、过滤器链的挂载位置重要提醒官方文档特别给出attention级提醒该过滤器必须以「上游」HTTP 过滤器身份配置。集群上没有顶层的upstream_http_filters字段上游 HTTP 过滤器链只存在于两处集群级per cluster位于集群的typed_extension_protocol_options中envoy.extensions.upstreams.http.v3.HttpProtocolOptions条目下的http_filters列表里该字段定义于 http_protocol_options.proto路由器级on the router对所有集群生效通过Router.upstream_http_filters配置定义于 router.proto。两条链都必须以envoy.filters.http.upstream_codec终结过滤器terminal filter收尾。此外该过滤器在下游 HTTP 过滤器链中不产生任何效果因此仅注册为上游过滤器——这一点在 config.cc 中通过REGISTER_FACTORY(..., Server::Configuration::UpstreamHttpFilterConfigFactory)得到印证。五、完整配置示例拒绝私有网段上游以下Cluster配置会拒绝任何选中上游 IP 落在私有 CIDR 网段内的请求。上游过滤器链配置在集群级路径为typed_extension_protocol_options→envoy.extensions.upstreams.http.v3.HttpProtocolOptions→http_filters即承载上游协议配置的同一个HttpProtocolOptions条目并以upstream_codec终结过滤器结尾clusters: - name: egress_cluster connect_timeout: 5s type: STRICT_DNS load_assignment: {} # ... cluster endpoints ... typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: type: type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions # Upstream protocol selection (required by HttpProtocolOptions). explicit_http_config: http_protocol_options: {} # Upstream HTTP filter chain. Must end with envoy.filters.http.upstream_codec. http_filters: - name: envoy.filters.http.upstream_rbac typed_config: type: type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC rules: action: DENY policies: deny-private-ips: permissions: - matcher: name: envoy.rbac.matchers.upstream_ip_port typed_config: type: type.googleapis.com/envoy.extensions.rbac.matchers.upstream_ip_port.v3.UpstreamIpPortMatcher upstream_ip: address_prefix: 10.0.0.0 prefix_len: 8 principals: - any: true - name: envoy.filters.http.upstream_codec typed_config: type: type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec要点解读action: DENY配合principals: any: true形成「凡上游 IP 命中10.0.0.0/8即拒绝」的默认拒绝式策略若想放行全部、仅拦特定网段这正是 SSRF 防护的典型写法rules支持ALLOW/DENY/LOG动作且rules与matcher匹配树二选一两者同时配置时rules被忽略matcher为空但已配置时同样会拒绝所有请求语义见 rbac.proto若改为路由器级挂载则将上述两个过滤器放入HttpConnectionManager的http_filters后追加的router过滤器配置的upstream_http_filters列表中其余结构不变。六、统计指标命名规则与作用域语义Upstream RBAC 过滤器输出与下游 RBAC 过滤器 相同的统计项计数器命名形如scope.rbac.[rules_stat_prefix.]{allowed,denied,shadow_allowed,shadow_denied}其中scope前缀取决于过滤器挂载的位置而非过滤器自身挂载位置统计作用域计数示例路由器级Router.upstream_http_filters继承路由器HTTP 连接管理器 / 监听器的作用域监听器未设置 stats 前缀时为根作用域rbac.rules_stat_prefix.allowed集群级HttpProtocolOptions.http_filters该集群的作用域cluster.name.rbac.*两种挂载方式的行为差异值得注意路由器级每个 Envoy 只产生一组聚合计数与请求时选中的上游集群无关集群级计数进入各自集群作用域。此外可选字段rules_stat_prefix/shadow_rules_stat_prefix会在rbac.之后再追加一段例如rules_stat_prefix: private得到rbac.private.allowed该前缀与上述作用域前缀是正交的。动态转发代理下的基数警告官方文档特别提示当配合动态转发代理的sub_cluster_config使用时请求时选中的集群是按主机动态创建的子集群形如DFPCluster:host:port。若在该模式下将过滤器挂在集群级则每个被解析的上游主机都会产生一组计数器序列——即无界基数unbounded cardinality拒绝计数会被打散到大量序列中。因此动态转发代理场景应优先将过滤器挂在路由器级让计数器保持为单个根作用域聚合。源码层面统计前缀的解析逻辑位于 config.cc通过运行时开关envoy.reloadable_features.upstream_http_filters_correct_stats_prefix控制是否使用extra_context.stats_prefix携带父级命名空间如路由器的http.stat_prefix.或集群的cluster.name.关闭时回退到空前缀。七、动态元数据Upstream RBAC 过滤器输出与下游 RBAC 过滤器相同的动态元数据dynamic metadata但命名空间键为envoy.filters.http.upstream_rbac该键在 upstream_rbac_filter.h 中以常量DynamicMetadataKey定义并在onHostSelected()中通过setDynamicMetadata写入评估结果shadow 引擎或 enforced 引擎被评估时。下游过滤器如 access log、其他基于动态元数据的过滤器可据此感知上游 RBAC 的判定结果。八、路由级覆盖配置RBACPerRoute与下游 RBAC 过滤器 相同Upstream RBAC 的策略引擎也可以按虚拟主机 / 路由 / 加权集群被覆盖或禁用将 RBACPerRoute 挂到路由的typed_per_filter_config上键为该过滤器配置的名字即可。覆盖解析基于请求的下游路由进行因此同一条路由可以同时携带不同的下游与上游 RBAC 覆盖互不干扰。RBACPerRoute中rbac字段缺省时该路由上的 RBAC 策略将被禁用。九、源码剖析onHostSelected 处的授权调用链upstream_rbac_filter.cc 完整呈现了授权时机的实现细节注册回调setDecoderFilterCallbacks中获取upstreamCallbacks()并注册自身使onHostSelected()能在主机选定后被调用地址守卫onHostSelected()首先取host-address()若选中主机没有已解析地址例如逻辑主机 logical host直接告警跳过评估避免解引用空地址写入 filter statesetUpstreamAddress()将本次选中的地址写入StreamInfo::UpstreamAddress::key()envoy.stream.upstream_address生命周期为LifeSpan::Request。注释强调上游地址 filter state 按请求作用域、在重试间共享且可能被动态转发代理过滤器预先填充因此必须总是覆写本次尝试的主机地址绝不能信任陈旧的stale或外部写入的地址做 SSRF 拒绝决策——这正是防止重试时绕过拒绝的关键设计连接守卫代理请求必然持有下游连接但仍以OptRef判空保护避免解引用空连接取请求头由于onHostSelected()发生在下游路由器的decodeHeaders()期间、早于本上游过滤器链自身decodeHeaders()因此从下游stream info 中读取请求头为空时回退到静态空头映射复用评估逻辑调用下游 RBAC 过滤器基类RoleBasedAccessControlFilter的evaluateShadowEngine()与evaluateEnforcedEngine()定义于 rbac_filter.h后者在拒绝时发送403本地应答中止上游连接并返回StopIteration。十、测试验证行为与数据双重印证仓库中的测试完整覆盖了上述行为可作为理解与验证该过滤器的直接依据upstream_rbac_filter_test.cc单元测试验证了DENY规则命中选中主机时发送403本地应答并写入上游地址 filter state、非命中主机放行、ALLOW规则命中放行、请求头与上游 IP 的 AND 组合匹配、重试时忽略陈旧 filter state 并重新评估回归测试、动态元数据写入envoy.filters.http.upstream_rbac键、空引擎配置时全放行、路由级覆盖优先于全局配置、无地址主机/无下游连接/无请求头三种边界跳过路径、decodeHeaders透传以及路由器/集群两种上下文下的统计前缀http.ingress.rbac.allowed、cluster.cluster_0.rbac.allowed且不重复加前缀upstream_rbac_integration_test.cc集成测试在真实双栈IPv4/IPv6环境验证命中拒绝规则时响应码为403且cluster.cluster_0.upstream_cx_total保持为 0证明连接确实未建立、未命中时正常返回200、集群级统计为cluster.cluster_0.rbac.denied且不存在双重前缀。十一、最佳实践总结综合文档与源码使用 Upstream RBAC 过滤器时建议遵循以下原则SSRF 防护首选默认拒绝以DENY动作 upstream_ip_port匹配器拒绝 RFC1918 私有网段10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、链路本地与回环地址并将principals置为any: true形成兜底动态转发代理场景挂路由器级避免DFPCluster:host:port带来的统计基数爆炸同时保证单一聚合计数集群级挂载时善用集群作用域统计cluster.name.rbac.*便于按集群粒度观测拦截量适合规则按集群差异化配置的场景务必以envoy.filters.http.upstream_codec收尾且该过滤器不能出现在下游过滤器链中结合其他匹配器做精细化授权请求头、源/目的 IP 与端口、SSL 属性、动态元数据、CEL 表达式等均可与上游 IP 组合实现请求特征 上游地址的多维策略利用 shadow 规则先行演练通过shadow_rules/shadow_matcher与shadow_rules_stat_prefix在真实流量上观察命中情况shadow_allowed/shadow_denied确认无误后再切换为强制执行降低误伤风险。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考