Python Consul Connect 的 mTLS 配置

1次阅读

consul connect 的 mtls 默认开启但仅作用于控制平面,服务间加密通信必须在服务注册中显式配置 connect 块;python 应用应通过 localhost:21000 经 envoy sidecar 透明代理调用,而非直连或手动处理 tls。

Python Consul Connect 的 mTLS 配置

Consul Connect 的 mTLS 默认就开启,但服务注册时没配 connect 块就等于白搭

Consul 1.9+ 默认启用全局 mTLS,但这个“启用”只管控制平面(agent 之间通信),不影响服务间流量。真正让两个服务走 mTLS 加密通信,得在服务注册里显式声明 connect 配置。否则即使 Consul agent 开着 TLS,curl http://service:8080 还是明文直连——你看到的健康检查通过、服务注册成功,跟实际连接是否加密完全无关。

常见错误现象:consul catalog services 能查到服务,consul connect envoy -service=myapp 启得起来,但下游调用报 503 no healthy upstream 或 Envoy 日志里反复出现 upstream connect Error or disconnect/reset before headers

  • 必须在服务注册 json/YAML 里加 connect 字段,哪怕只是空对象{"connect": {"sidecar_service": {}}}
  • 如果用了 sidecar 模式,sidecar_service 下还要配 proxy,否则 Envoy 不知道监听哪个端口、上游是谁
  • 不配 connect,Consul 根本不会为该服务生成证书、下发配置,mTLS 流量路径从第一步就断了

Python 应用怎么接上 Consul Connect 的 mTLS?别碰证书文件,用 localhost:21000 走本地 proxy 就行

Python 应用本身不需要加载 consul-agent-ca.pem 或配置 TLS 上下文。Connect 的设计哲学是“透明代理”:Envoy sidecar 在本地监听 127.0.0.1:21000(默认 upstream 端口),所有出向请求发给它,由它完成证书签发、mTLS 握手、服务发现和负载均衡。你的 Python 代码照常发 HTTP 请求,目标地址改成 http://localhost:21000 即可。

使用场景:django/flask/fastapi 服务作为上游调用另一个 Connect 服务;或 Python 脚本做定时任务,需要安全调用内部 API。

立即学习Python免费学习笔记(深入)”;

  • 不要用 requests.get("https://svc-name.service.consul") —— DNS 解析会绕过 Envoy,且没证书信任链
  • 正确写法:requests.get("http://localhost:21000", headers={"Host": "svc-name.service.consul"}),靠 Host 头触发 Envoy 路由
  • 如果下游服务监听 HTTPS(比如用 https://backend:443),Envoy 默认会降级成 HTTP 转发;如需保持上游 HTTPS,得在 proxy.config 里设 protocol = "https"
  • Python 的 urllib3 默认复用连接,而 Envoy 的 HTTP/2 连接管理可能引发 ConnectionResetError,建议加 requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10)

consul connect envoy 启动失败?重点盯 upstream 配置里的服务名和命名空间

启动命令返回 failed to generate bootstrap config: unknown service 或日志里反复刷 no endpoints found for service,八成是 upstream 配置写错了。Consul Connect 对服务名大小写、命名空间、分区极其敏感,且不报语法错,只静默失败。

参数差异:service 字段必须和 consul catalog services 输出的名称**完全一致**(包括破折号、下划线);Namespace 默认是 default,但如果你启用了 Namespaces,漏写或写错就会找不到服务。

  • 查真实服务名:consul catalog services -ns=my-ns,注意输出里带命名空间前缀的完整名
  • upstream 示例必须长这样:{"destination_type": "service", "destination_name": "api-gateway", "local_bind_port": 8080, "datacenter": "dc1", "namespace": "my-ns"}
  • 如果目标服务在另一个分区(Partition),还得加 "partition": "test",缺了就查不到实例
  • Envoy 启动后,用 curl http://localhost:19000/config_dumpcluster 列表里有没有对应 upstream,没有就是配置没生效

Python 日志里出现 ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED]?说明你误走了 HTTPS 直连

这个错误不是证书过期或 CA 不对,而是 Python 客户端试图直接跟远端服务做 TLS 握手,但对方(比如 Envoy sidecar)根本没开 HTTPS 监听,或者你连的是纯 HTTP 的 upstream 端口。Consul Connect 的 mTLS 是双向的、发生在 Envoy 层,应用层走 HTTP + localhost proxy 才是正路。

性能影响:强行在 Python 里做 TLS(比如用 certifi 和自签名 CA)不仅多此一举,还会让连接无法复用、增加握手延迟,而且绕过 Envoy 后,熔断、重试、指标采集全失效。

  • 删掉所有类似 verify="/var/lib/consul/tls/ca.pem" 的 requests 参数
  • 确认你调用的目标 URL 是 http://localhost:21000,不是 https://...http://svc-name.service.consul
  • 如果必须用 HTTPS(比如对接外部系统),应该在 Consul 的 proxy.upstreams 里配 protocol = "https",让 Envoy 去处理 TLS,Python 仍走 HTTP
  • Envoy 默认只监听 localhost,如果 Python 在容器里跑,确保 network_mode: "host" 或用 host.docker.internal(Mac/Win)替代 localhost

最易被忽略的点:Consul 的 mTLS 是“服务网格层”的事,不是“应用 TLS 配置”的事。一旦开始手动管理证书、改 Python 的 SSL 上下文,方向就反了。

text=ZqhQzanResources