背景
我在将 Phoenix/Ecto/Postgrex 连接到 Azure Database for PostgreSQL 服务器时遇到了同样的问题。即使设置后ssl: true
在我的 Repo 配置中,即使使用连接,我仍然无法使用 Postgrex 连接到数据库psql "postgresql://...?sslmode=require" -U ...
在同一台机器上成功了。返回的错误为ssl: true
was:
[error] Postgrex.Protocol (#PID<0.1853.0>) failed to connect: **(DBConnection.ConnectionError) ssl connect: closed
** (DBConnection.ConnectionError) connection not available because of disconnection
(db_connection) lib/db_connection.ex:926: DBConnection.checkout/2
...
在深入研究源代码后,我发现失败的调用实际上是ssl.connect/3
电话来自Erlang ssl 模块 http://erlang.org/doc/man/ssl.html:
# deps/postgrex/lib/postgrex/protocol.ex:535
defp ssl_connect(%{sock: {:gen_tcp, sock}, timeout: timeout} = s, status) do
case :ssl.connect(sock, status.opts[:ssl_opts] || [], timeout) do
{:ok, ssl_sock} ->
startup(%{s | sock: {:ssl, ssl_sock}}, status)
{:error, reason} ->
disconnect(s, :ssl, "connect", reason)
end
end
使用 Wireshark 进行一些窥探,我能够在成功连接时看到psql
,我可以看到数据包TLSV1.2
作为协议,但是当 postgrex 连接时ssl: true
我看到数据包SSL
作为连接失败之前的协议。
看着Ecto.Adapters.Postgres 选项文档 https://hexdocs.pm/ecto/Ecto.Adapters.Postgres.html#module-options,你会看到有一个ssl_opts
配置选项最终被传递给:ssl.connect/3
您可以在其中设置versions
覆盖用于连接的 TLS 版本。
Solution
我可以通过将以下内容添加到我的 Repo 配置来连接到数据库:
ssl_opts: [
versions: [:"tlsv1.2"]
]
我的完整配置最终如下所示:
config :myapp, Myapp.Repo,
adapter: Ecto.Adapters.Postgres,
username: "myapp@dev-db",
password: "...",
database: "myapp_dev",
port: 5432,
hostname: "dev-db.postgres.database.azure.com",
pool_size: 10,
ssl: true,
ssl_opts: [
versions: [:"tlsv1.2"]
]
我不太确定为什么需要明确设置 TLS 版本,也许在这方面具有更多专业知识的人可以对此有所了解。