Skip to content

懒猫微服开发篇(八):反向代理与自签名 SSL 的优雅共存之道

在分布式系统中,HTTPS 是默认的通信规范。但当你在内部环境中部署服务时,特别是很多以容器方式运行的组件,但是很多时候 它们往往只默认开启了 HTTPS 接口,却附带了一个 自签名证书(Self-signed certificate)

这意味着,只要一层反向代理(reverse proxy)去转发 HTTPS 请求,TLS 校验就会报错。 这不是配置错误,而是 SSL 的“安全特性”在起作用。

本文就带大家看看如何在懒猫微服上架过程中如何优雅地解决这一问题。

问题背景:HTTPS 转发失败

假设我们要在懒猫微服上架一个 App,用来转发请求到后端的 Easysearch 搜索引擎。

项目的 manifest(lzc.yaml)配置如下:

yaml
lzc-sdk-version: "0.1"
name: proxy
package: cloud.lazycat.app.proxy
version: 0.0.1

application:
  subdomain: proxy
  routes:
    - /=https://easysearch:9200/

这段配置的意思很简单: 当访问 proxy.lzcapp.xxx 时,请将请求转发到容器内的 https://easysearch:9200/

然而在 Dozzle 日志中,你会看到熟悉的报错信息:

2025/10/08 10:20:15 http: proxy error: tls: failed to verify certificate: x509: certificate is valid for infini.cloud, *.infini.cloud, not easysearch
2025/10/08 10:20:17 http: proxy error: tls: failed to verify certificate: x509: certificate is valid for infini.cloud, *.infini.cloud, not easysearch

image-20251008102358716

报错很直白:

证书仅对 infini.cloud*.infini.cloud 有效,而不是 easysearch

这正是 证书域名不匹配(SNI mismatch) 的典型例子。

原因分析:为什么 HTTPS 会报证书错

在 HTTPS 连接建立过程中,客户端(此处是反向代理)会验证服务器返回的证书:

  • 证书是否过期;
  • 是否由受信任的 CA 签发;
  • 证书上的主机名是否与请求的主机名匹配。

而我们的容器内部通常使用服务名(例如 easysearch)访问,不是证书上写的正式域名(infini.cloud)。 这就造成了验证失败。

在浏览器中,如果你访问一个自签名 HTTPS,会看到类似提示:“连接不安全”。 在反向代理中,它不会弹窗,而是直接抛出异常中断。

Nginx 的特性:默认不验证上游证书

在以前的实验中,我用过 Nginx 来转发自签名 HTTPS 的 Easysearch,发现并没有报错。 这是因为——Nginx 默认不会验证上游 HTTPS 证书。

在官方文档 ngx_http_proxy_module 中明确写道:

Syntax: proxy_ssl_verify on | off; > Default: proxy_ssl_verify off; > Context: http, server, location

This directive sets whether to verify the SSL certificate of the proxied server.

也就是说:

  • 默认情况下,proxy_ssl_verifyoff
  • Nginx 不会校验后端证书;
  • 只有你显式开启 proxy_ssl_verify on; 时,它才会去核对证书域名、签发方、有效期等信息。

这点在内部网络部署中非常有用 —— 因为大部分内网服务使用的都是自签名或临时证书。

典型案例:Docker Compose + Nginx 反代 Easysearch

要更直观理解,我们先构造一个简单的实验场景。 下面是一个 Docker Compose 配置,启动一个 Easysearch 容器和一个 Nginx 容器:

bash
version: "3.8"

services:
  easysearch:
    image: infinilabs/easysearch:1.15.3
    container_name: es-single
    ports:
      - "9200:9200"   # 本地直连(可选)

  nginx:
    image: nginx:latest
    container_name: nginx-proxy
    depends_on:
      - easysearch
    ports:
      - "80:80"       # 对外访问端口
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

Nginx 配置如下:

nginx
events {}

http {
    server {
        listen 80;
        server_name localhost;

        location / {
            proxy_pass https://easysearch:9200;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

这个配置里没有加任何 SSL 校验相关指令。 Nginx 在请求 https://easysearch:9200 时会直接忽略自签名问题,代理层不会抛错。

懒猫微服中的 OpenResty

懒猫微服默认 app.proxy 是 OpenResty(Nginx 增强版) 。 它天然具备和 Nginx 一样的 SSL 默认策略 —— 即忽略内网自签名的证书验证。

外挂的配置文件很麻烦,我们可以使用 setup_script 来执行 shell 命令来动态写入。效果和前面是一样的。

yaml
lzc-sdk-version: "0.1"
name: proxy
package: cloud.lazycat.app.proxy
version: 0.0.1

application:
  subdomain: proxy
  routes:
    - /=http://app-proxy:80/

services:
  app-proxy:
    image: registry.lazycat.cloud/app-proxy:v0.1.0
    setup_script: |
      cat <<'EOF' > /etc/nginx/conf.d/default.conf
      server {
        listen 80;
        server_name _;

        location / {
          proxy_pass https://easysearch:9200/;
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
      }
      EOF

  easysearch:
    image: registry.lazycat.cloud/u04123229/infinilabs/easysearch:5dc6296f5370aacc

这段配置中,setup_script 会在容器启动时执行,会动态写入 Nginx 配置文件。 容器之间的通信使用内网域名,不校验证书。这样代理转发就能成功,日志也不会再出现 x509 错误。

再优化:使用官方环境变量

如果你不想写配置文件,懒猫官方提供的 app-proxy 镜像还支持 UPSTREAM 环境变量 来简化操作:

yaml
lzc-sdk-version: "0.1"
name: proxy
package: cloud.lazycat.app.proxy
version: 0.0.1

application:
  subdomain: proxy
  routes:
    - /=http://app-proxy:80/

services:
  app-proxy:
    image: registry.lazycat.cloud/app-proxy:v0.1.0
    environment:
      - UPSTREAM="https://easysearch:9200"

  easysearch:
    image: registry.lazycat.cloud/u04123229/infinilabs/easysearch:5dc6296f5370aacc

这种方式更轻量,也便于在应用市场中复用。 UPSTREAM 会自动注入到镜像配置中,无需手动挂载文件。

进阶用法:新版本的 upstreams 支持 SSL 开关

懒猫微服新版本的 SDK 进一步增强了反向代理的灵活性。 你可以直接在 application 部分的 upstreams 中声明多个后端服务,并显式配置 SSL 行为:

yaml
lzc-sdk-version: "0.1"
name: proxy
package: cloud.lazycat.app.proxy
version: 0.0.1

application:
  subdomain: proxy

  upstreams:
    - location: /
      backend: https://easysearch:9200
      disable_backend_ssl_verify: true # 关键参数:禁用 SSL 校验

services:
  easysearch:
    image: registry.lazycat.cloud/u04123229/infinilabs/easysearch:5dc6296f5370aacc

disable_backend_ssl_verify: true 设置后,平台在代理时会自动跳过证书校验步骤。 这相当于在 Nginx 中使用了:

nginx
proxy_ssl_verify off;

部署后日志如下:

PATH:"/" is served by {/ false  https://easysearch:9200 false  true false  false [] false false []}
✨ Internal health check successful.
Standing by until other services are healthy.  ⌛

image-20251008103902543

代理成功生效。

结语

通过本文的两种方法,我们可以:

  • 安全地代理 HTTPS 服务;
  • 解决自签名证书报错;
  • 保持部署流程一致、代码简洁;
  • 同时兼顾上架懒猫商店的兼容性。

未来如果要接入多后端服务的多路复用机制也能轻松应对。

反向代理不仅是“流量的中转站”,更是“安全与兼容的缓冲层”。 理解 SSL 校验机制后,才能写出既安全又高可用的微服务架构。

❤️喜欢