【问题标题】:How to connect to PostgreSQL from Phoenix Web App via SSL?如何通过 SSL 从 Phoenix Web App 连接到 PostgreSQL?
【发布时间】:2017-10-15 11:36:01
【问题描述】:

当尝试使用托管 3rd 方“数据库即服务”的 PostgreSQL 数据库运行 Elixir (Phoenix) Web 应用程序时 (Azure Database for PostgreSQL)。

我们尝试使用mix phoenix.server 启动应用程序,我们看到以下错误:

[info] Running Pxblog.Endpoint with Cowboy using http://localhost:4000
[error] GenServer #PID<0.247.0> terminating
** (FunctionClauseError) no function clause matching in Postgrex.Messages.decode_fields/1
    (postgrex) lib/postgrex/messages.ex:339: Postgrex.Messages.decode_fields("")
    (postgrex) lib/postgrex/messages.ex:344: Postgrex.Messages.decode_fields/1
    (postgrex) lib/postgrex/messages.ex:344: Postgrex.Messages.decode_fields/1
    (postgrex) lib/postgrex/messages.ex:131: Postgrex.Messages.parse/3
    (postgrex) lib/postgrex/protocol.ex:1842: Postgrex.Protocol.msg_decode/1
    (postgrex) lib/postgrex/protocol.ex:1816: Postgrex.Protocol.msg_recv/3
    (postgrex) lib/postgrex/protocol.ex:560: Postgrex.Protocol.auth_recv/3
    (postgrex) lib/postgrex/protocol.ex:475: Postgrex.Protocol.handshake/2
    (db_connection) lib/db_connection/connection.ex:134: DBConnection.Connection.connect/2
    (connection) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol

洞察:在 Azure DB 上启用了“强制 SSL”...

通过调查,我们意识到错误的原因是 Azure PostgreSQL 服务将 Enforce SSL Connection 设置为 Enableddefault):

我们认为对Enabled“实施 SSL”对于安全性而言良好,但我们无法让它与 Phoenix 一起使用 ...

临时)解决方案:禁用“强制 SSL”

所以,我们现在(暂时禁用 SSL:

但我们更喜欢针对这个问题的“永久”解决方案。

首选解决方案:连接 PostgreSQL 时使用 SSL

如果有人能澄清(或指出我们如何从 Phoenix/Ecto 通过 SSL 连接到 PostgreSQL
我们将非常感激! :-)

应用程序(凤凰)服务器是否需要配置 SSL 证书才能从应用程序服务器连接到数据库服务器...?
例如:http://www.phoenixframework.org/docs/configuration-for-ssl?

Microsoft 有以下帮助指南: https://docs.microsoft.com/en-us/azure/postgresql/concepts-ssl-connection-security 似乎表明我们需要应用服务器上的 OpenSSL ... 任何人都可以确认吗?

【问题讨论】:

  • 你试过在repo配置中设置ssl: true吗?
  • @michalmuskala 很遗憾,只是在prod.secrets.exsRepo 部分添加ssl: true 并不能解决问题。我们试过了:git.io/v9Ne2 :-( 我们怀疑可能需要 SSL 证书才能使其工作。
  • @nelsonic ssl: true 有什么错误?

标签: postgresql azure erlang elixir phoenix-framework


【解决方案1】:

背景

我在将 Phoenix/Ecto/Postgrex 连接到 Azure Database for PostgreSQL 服务器时遇到了同样的问题。即使在我的 Repo 配置中设置了 ssl: true 之后,即使在同一台机器上使用 psql "postgresql://...?sslmode=require" -U ... 连接成功,我仍然无法使用 Postgrex 连接到数据库。 ssl: true 返回的错误是:

[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
    ...

在挖掘源代码后,我发现失败的调用实际上是来自the Erlang ssl modulessl.connect/3调用:

# 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 连接时,我看到带有 @ 的数据包987654332@作为连接失败前的协议。

查看Ecto.Adapters.Postgres options docs,您会看到ssl_opts 配置选项最终会传递给:ssl.connect/3,您可以在其中设置versions 以覆盖用于连接的TLS 版本。

解决方案

我可以通过将以下内容添加到我的 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 版本,也许在这方面有更多专业知识的人可以对此有所了解。

【讨论】:

    【解决方案2】:

    您可能还需要将您的 ip(或 ip 范围)添加到 azure 中的 postgres 防火墙。它就在 SSL 设置下。

    【讨论】:

      【解决方案3】:

      Erlang 通常是使用 OpenSSL 构建的,并且确实需要它用于多个库。您尚未发布使用ssl: true 得到的错误,但如果您的 erlang 是在没有 OpenSSL 的情况下构建的,则可能是原因。来自build/install guide

      OpenSSL -- 用于安全套接字层的开源工具包和 传输层安全。构建应用程序所必需的 加密货币。此外,ssl 和 ssh 需要一个有效的加密应用程序和 如果缺少 OpenSSL,也会跳过。 public_key 应用程序 无需加密即可使用,但功能将非常 有限。

      如果你在iex shell 中运行:ssl.versions(),你会得到什么输出?

      【讨论】:

      • iex(1)&gt; :ssl.versions() [ssl_app: '8.1.1', supported: [:"tlsv1.2", :"tlsv1.1", :tlsv1], available: [:"tlsv1.2", :"tlsv1.1", :tlsv1, :sslv3]]
      • @nelsonic 根据该输出,您的 erlang 是使用 openssl 构建的,所以问题不在于那个。如果您发布使用ssl: true 得到的错误,将会非常有帮助
      【解决方案4】:

      这是我使用 ssl 和证书的数据库 init() 连接代码。检查您的ssl_opts 设置,也许。

      def init() do 
          case Postgrex.start_link(
              hostname: App.Endpoint.config(:dbhost), 
              username: App.Endpoint.config(:username), 
              database: App.Endpoint.config(:dbname), 
              port: App.Endpoint.config(:dbport),
              ssl: true,
              ssl_opts: [
                  keyfile: "priv/cert.key",
                  certfile: "priv/cert.crt"
              ]) do 
              {:ok, postgrex} ->
                  postgrex 
              _ ->
                  :error
          end
      end
      

      【讨论】:

        【解决方案5】:

        要添加到上述答案,这是我在使用自签名证书时的ssl_opts 块,并且在您的 pg_hba.conf 中将auth-options 设置为clientcert=verify_full。我的连接使用的是 TLSv1.3。

          ssl_opts: [
             verify: :verify_peer,
             versions: [:"tlsv1.3"],
             ciphers: :ssl.cipher_suites(:all, {3,4}),
             cacertfile: Path.expand("priv/certs/ca-cert.pem"),
             certfile: Path.expand("priv/certs/client-cert.pem"),
             keyfile: Path.expand("priv/certs/client-key.pem")
            ],
        

        【讨论】:

          猜你喜欢
          • 2016-09-20
          • 1970-01-01
          • 1970-01-01
          • 2020-11-04
          • 2019-10-25
          • 1970-01-01
          • 2019-02-19
          • 2013-08-23
          • 2017-03-26
          相关资源
          最近更新 更多