Envoy前端代理401状态下如何仅对单个域名而非全域名执行重定向
问题解答
1. SNI获取失败原因及正确实现
你当前使用的v1.16.2版本Envoy不支持requestedServerName() API,这个接口是v1.17.0版本才正式加入的,所以调用返回空属于版本兼容问题。如果要使用SNI匹配方案,需要先将Envoy镜像升级到envoyproxy/envoy:v1.17.0及以上版本。
另外你原有代码存在变量作用域错误:SNI是envoy_on_request函数内的局部变量,envoy_on_response函数无法直接访问,需要将值存入动态元数据跨阶段传递,正确写法如下:
inline_code: | function envoy_on_request(request_handle) -- 将SNI存入动态元数据,供response阶段读取 local sni = request_handle:streamInfo():requestedServerName() request_handle:streamInfo():dynamicMetadata():set("envoy.lua", "request_sni", sni) end function envoy_on_response(response_handle) local sni = response_handle:streamInfo():dynamicMetadata():get("envoy.lua")["request_sni"] local match_domain = "app.domain.tld" if response_handle:headers():get(":status") == "401" and sni == match_domain then response_handle:headers():replace(":status", "301") response_handle:headers():replace("location", "https://login.domain.tld") end end
2. 无需升级的替代方案(推荐)
如果不想升级Envoy版本,完全不需要依赖SNI匹配,直接读取HTTP请求的:authority伪头(即Host头)即可拿到访问域名,兼容所有支持Lua过滤器的Envoy版本,代码更简洁:
inline_code: | function envoy_on_response(response_handle) local host = response_handle:headers():get(":authority") local match_domain = "app.domain.tld" -- 如需匹配带端口的域名,可修改判断条件为host == "app.domain.tld:3000" if response_handle:headers():get(":status") == "401" and host == match_domain then response_handle:headers():replace(":status", "301") response_handle:headers():replace("location", "https://login.domain.tld") end end
该方案不需要新增任何额外过滤器,完全基于现有Lua过滤器即可实现,也避免了非TLS场景下SNI为空的问题。如果后续需要扩展规则,比如排除所有api开头的子域名,直接新增not string.find(host, "^api%-.*%.domain%.tld")判断条件即可。
内容的提问来源于stack exchange,提问作者niolm
相关产品推荐
相关产品推荐

