TLS for Phoenix

HTTP/2 support already available in Phoenix thanks to Cowboy2. Depending on how your load balancing was setup you may now be in a situation where you now need to termiate TLS directly on Cowboy.

When I looked into it there didn’t seem to be a lot of information around it so I decided to assemble the config and details that I used here.

There’s two things that I wanted to do when I set this up:

  1. only use secure tls versions and protocols.
  2. keep the keys encrypted.

My suggestions here are based on the following:

One challenge that we have is the DH params group file. I’d prefer to generate this on the server but it takes too long to be practical. I have excluded it for the time being as it makes not sense to generate it but then store it in a git repos.

However, your deployment process may be different so if you can take advatage of it, generate it as follows:

openssl dhparam -out dh-params.pem 2048

Beyond that we are encrypting our keyfile and then only provide the password to the production ENV. The resulting config looks as follows:

config :phoenix, YourApp.Endpoint,
  https: [
    port: 443,
    cacertfile: "/path/to/certs/cacert.pem",
    certfile: "/path/to/certs/cert.pem",
    keyfile: "/path/to/certs/key.pem",
    password: "keyfile-password-so-read-this-from-the-env",
    versions: [:"tlsv1.2"],
    dhfile: "/path/to/certs/dh-params.pem",
    ciphers: [
      "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES128-SHA",
      "ECDHE-ECDSA-AES256-SHA", "ECDHE-ECDSA-AES128-SHA256", "ECDHE-ECDSA-AES256-SHA384",
      "ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-RSA-AES128-SHA",
      "ECDHE-RSA-AES256-SHA", "ECDHE-RSA-AES128-SHA256", "ECDHE-RSA-AES256-SHA384",
      "DHE-RSA-AES128-GCM-SHA256", "DHE-RSA-AES256-GCM-SHA384", "DHE-RSA-AES128-SHA",
      "DHE-RSA-AES256-SHA", "DHE-RSA-AES128-SHA256", "DHE-RSA-AES256-SHA256"
    ],
    secure_renegotiate: true,
    reuse_sessions: true,
    honor_cipher_order: true,
    max_connections: :infinity
  ]

In my case, we use distillery and read much of our configuration from the ENV. And we are not generating the DHPARAMS So our file looks more like this:

config :phoenix, YourApp.Endpoint,
  https: [
    port: "${PHX_TLS_PORT}",
    cacertfile: "${CA_FILE_PATH}",
    certfile: "${CERTFILE_PATH}",
    keyfile: "${KEYFILE_PATH}",
    password: "${KEYFILE_PASSWORD}",
    versions: [:"tlsv1.2"],
    ciphers: [
      "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES128-SHA",
      "ECDHE-ECDSA-AES256-SHA", "ECDHE-ECDSA-AES128-SHA256", "ECDHE-ECDSA-AES256-SHA384",
      "ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-RSA-AES128-SHA",
      "ECDHE-RSA-AES256-SHA", "ECDHE-RSA-AES128-SHA256", "ECDHE-RSA-AES256-SHA384",
      "DHE-RSA-AES128-GCM-SHA256", "DHE-RSA-AES256-GCM-SHA384", "DHE-RSA-AES128-SHA",
      "DHE-RSA-AES256-SHA", "DHE-RSA-AES128-SHA256", "DHE-RSA-AES256-SHA256"
    ],
    secure_renegotiate: true,
    reuse_sessions: true,
    honor_cipher_order: true,
    max_connections: :infinity
  ]

Note that we are setting files paths dynamically which allows us to switch between certificate sets between staging and production ENVs.


Mark Madsen

When I started writing Apps and APIs, phones had buttons!


Recent Stories
08 Sep 2017

AWS Network Load Balancers

11 Jun 2016

Tips for SFO and WWDC

11 Jun 2016

Packing for WWDC

Recent Tweets