How to connect to PostgreSQL from Phoenix Web App via SSL? How to connect to PostgreSQL from Phoenix Web App via SSL? postgresql postgresql

How to connect to PostgreSQL from Phoenix Web App via SSL?


Background

I was experiencing the same problem connecting Phoenix/Ecto/Postgrex to Azure Database for PostgreSQL server. Even after setting ssl: true in my Repo configuration, I was still not able to connect to the database with Postgrex even though connecting using psql "postgresql://...?sslmode=require" -U ... on the same machine succeeded. The error returned with 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    ...

After digging through the source code, I discovered that the failing call was actually the ssl.connect/3 call from the Erlang ssl module:

# deps/postgrex/lib/postgrex/protocol.ex:535defp 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)  endend

Doing some snooping with Wireshark, I was able to see that when connecting successfully with psql, I could see packets with TLSV1.2 as the protocol, but when postgrex was connecting with ssl: true I was seeing packets with SSL as the protocol before failing to connect.

Looking at the Ecto.Adapters.Postgres options docs, you'll see there's an ssl_opts configuration option which ends up getting passed to :ssl.connect/3 in which you can set versions to override the TLS version(s) used to connect.

Solution

I was able to connect to the database by adding the following to my Repo configuration:

ssl_opts: [  versions: [:"tlsv1.2"]]

My full configuration ended up looking like this:

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"]  ]

I'm not really sure why the TLS version needs to be set explicitly, perhaps someone with more expertise in this area can shed some light on this.


You might also need to add your ip (or ip range) to the postgres firewall in azure. It's right under the SSL settings.


Erlang is usually built with OpenSSL, and does require it for several libraries. You haven't posted the error you get with ssl: true, but if your erlang was built without OpenSSL it might be the cause. From the build/install guide:

OpenSSL -- The opensource toolkit for Secure Socket Layer and Transport Layer Security. Required for building the application crypto. Further, ssl and ssh require a working crypto application and will also be skipped if OpenSSL is missing. The public_key application is available without crypto, but the functionality will be very limited.

What output do you get if you run :ssl.versions() in an iex shell?