如何通过Cloudflare将客户端证书发送至源站服务器?
解决Cloudflare mTLS客户端证书无法传递到Envoy的问题
核心原因
Cloudflare启用mTLS后,仅完成客户端证书的合法性校验,默认不会将证书的原始内容或相关信息转发至源站服务器,这就是Envoy无法获取证书的直接原因。
可行解决方案
方案1:使用Cloudflare原生请求头传递证书信息
Cloudflare支持通过CF-Client-Cert请求头将客户端证书的DER编码(Base64格式)传递给源站,需完成以下配置:
- 进入子域名
y.xxx.com的SSL/TLS -> Client Certificates页面 - 启用Authenticated Origin Pulls功能,关联你已生成的客户端证书
- 在Envoy中配置解析该请求头,示例Lua过滤器配置片段:
http_filters: - name: envoy.filters.http.lua typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inline_code: | function envoy_on_request(request_handle) local cf_cert = request_handle:headers():get("CF-Client-Cert") if cf_cert then -- 可在此解码Base64并提取证书字段,如序列号、主题等 request_handle:logInfo("Received client cert via CF: " .. cf_cert) end end
方案2:通过Cloudflare Workers自定义转发证书细节
如果原生头无法满足需求,可通过Workers提取证书字段并自定义请求头发送:
- 创建Worker代码如下:
addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const clientCert = request.certificate if (clientCert) { const newHeaders = new Headers(request.headers) // 提取证书核心字段添加到自定义头 newHeaders.set('X-Client-Cert-Serial', clientCert.serialNumber) newHeaders.set('X-Client-Cert-Subject', clientCert.subject) // 转换为PEM格式传递完整证书 const pemCert = `-----BEGIN CERTIFICATE-----\n${btoa(String.fromCharCode(...new Uint8Array(clientCert.raw))).match(/.{1,64}/g).join('\n')}\n-----END CERTIFICATE-----` newHeaders.set('X-Client-Cert-PEM', pemCert) return fetch(request.url, { headers: newHeaders, method: request.method, body: request.body, redirect: 'follow' }) } return fetch(request) } - 将Worker路由到
y.xxx.com,确保所有请求先经过Worker处理再转发到源站 - 在Envoy中配置接收这些自定义请求头,完成证书信息解析
验证步骤
- 发送携带客户端证书的请求到
y.xxx.com - 在Envoy访问日志中添加请求头记录,确认目标头是否存在
- 若使用Worker,可在Cloudflare Worker日志中检查证书信息是否被正确提取
注意事项:
- 需将Cloudflare出站IP范围加入Envoy的允许列表,避免请求被拦截
- 启用Authenticated Origin Pulls时,源站需信任Cloudflare CA证书,否则会拒绝Cloudflare的请求
- 自定义请求头需控制大小,避免超出Cloudflare或Envoy的配置限制导致截断
内容的提问来源于stack exchange,提问作者Amin Alizadeh
相关产品推荐
相关产品推荐

