无法理解Elixir Phoenix Ecto Postgres Adapter的ssl_opts配置问题
问题描述
我已在Kubernetes上安装Percona Postgres Operator及发行版,运行正常。通过Helm安装生成的SSL证书,能完成官方文档中提到的集群连通性检查(psql可正常连接)。但我的Elixir项目runtime.exs中Postgres配置如下:
config :core, Repo, adapter: Ecto.Adapters.Postgres, username: System.get_env("POSTGRES_USERNAME"), password: System.get_env("POSTGRES_PASSWORD"), database: System.get_env("POSTGRES_DB"), hostname: System.get_env("POSTGRES_HOST"), ssl_opts: [verify: :verify_peer, cacertfile: "./ca.crt",versions: ["tlsv1.3"]], ssl: true, pool_size: 10
执行mix ecto.create时出现错误:
08:47:41.422 [error] Postgrex.Protocol (#PID<0.2436.0>) failed to connect: ** (DBConnection.ConnectionError) ssl connect: TLS client: In state wait_cert at ssl_handshake.erl:2071 generated CLIENT ALERT: Fatal - Bad Certificate
将verify设为:verify_none可正常连接,但这会失去证书验证的安全性。我无法理解为什么同一份证书在psql中可用,但Ecto连接失败,尝试将ca.crt添加到系统受信任证书也无效。
可能的原因及解决方案
1. 证书文件路径问题
Elixir进程运行时的工作目录可能和你放置ca.crt的目录不一致,导致cacertfile: "./ca.crt"找不到正确的证书文件。
- 解决办法:使用绝对路径指定证书位置,比如:
或者通过环境变量传递路径,确保进程能正确读取到证书文件。cacertfile: "/path/to/your/ca.crt"
2. TLS版本兼容性问题
你指定了仅使用TLSv1.3,但Percona Postgres集群可能未启用该版本,或者psql实际使用的是更低版本的TLS完成连接。
- 解决办法:放宽TLS版本限制,同时支持多个版本,比如:
先去掉版本限制测试,确认连接正常后再逐步缩小范围。versions: ["tlsv1.2", "tlsv1.3"]
3. 证书域名验证不匹配
Postgres服务器证书中的CN或SAN字段可能和你配置的hostname不匹配,psql默认可能跳过了严格的域名验证,而Ecto/Postgrex严格执行了验证。
- 解决办法:
- 查看服务器证书的域名信息:
openssl x509 -in ca.crt -text -noout | grep -A 2 "Subject Alternative Name" - 确保
runtime.exs中的hostname和证书中的域名完全一致,或者添加server_name_indication选项指定正确的SNI名称:ssl_opts: [verify: :verify_peer, cacertfile: "/path/to/ca.crt", server_name_indication: "correct-postgres-hostname"]
- 查看服务器证书的域名信息:
4. Erlang SSL库的证书解析问题
Erlang的SSL库对证书格式的要求可能比psql更严格,比如证书是否包含完整的链,或者是否有过期、格式错误等。
- 解决办法:
- 验证证书的有效性:
openssl verify -CAfile ca.crt server.crt - 如果证书链不完整,需要将中间证书合并到
ca.crt中,确保包含完整的信任链。
- 验证证书的有效性:
内容的提问来源于stack exchange,提问作者paulrusu
相关产品推荐
相关产品推荐

