Letsencrypt Elixir/Phoenix 1.3 traffic hosted on AWS ECS
by: Peter Shoukry

People feel more secure when the sites they are visiting uses HTTPS. I started my career working as a security consultant and during that I did lots of penetration testing engagements. People are right! The browser leaks a ton of information if you are using HTTP. HTTPS is now heavily optimized, it’s overhead is minimal and there are even claims it’s overall faster than HTTP. Please check: https://www.troyhunt.com/i-wanna-go-fast-https-massive-speed-advantage/ and https://www.maxcdn.com/blog/ssl-performance-myth/ Switching the Elixir/Phoenix 1.3 apps to use Letsencrypt certificates is pretty straight forward, I use Docker and AWS ECS to deploy my app, so there are a couple of additional steps. ## The full deployment 1. Create a volume in the task to persist the certificates so we don’t need to generate them with each deployment and mount it in the container at /etc/letsencrypt 2. Map the container port running SSL in the phoenix app to to port 443 in the host. 3. Add certbot to the Dockerfile of the image you deploy and expose the relevant ports. I use a Debian image so i will add: ```bash RUN apt-get install -y openssl postgresql-client mysql-client lksctp-tools certbotENV PORT 80 ENV PORT_SSL 443EXPOSE 80 EXPOSE 443 ``` 4. Create file letsencrypt.iniin /etc/letsencrypt with the contents: ```bash rsa-key-size = 4096 email = myemail@mydomain.com authenticator = webroot domains = mydomain.com, www.mydomain.com text = True preferred-challenges = http-01 webroot-path = /usr/src/app/myapp/lib/myapp_web-0.0.1/priv/static ``` 5. Modify Plug.Static in the endpoint to serve .well-known for webroot authentication to work ```elixir plug Plug.Static, at: "/", from: :myapp_web, gzip: false, only: ~w(css fonts images js favicon.ico robots.txt .well-known) ``` 6. Generate the certificates ```bash docker exec -it the_app_container certbot certonly -c /etc/letsencrypt/letsencrypt.ini ``` 7. Enable SSL app in apps/myapp_web/mix.exs ```elixir def application do [mod: {Myapp.Web.Application, []}, extra_applications: [:logger, :runtime_tools, :ssl]] end ``` 8. Configure SSL in your config/prod.exs and endpoint.ex file respectively: ```elixir config :myapp_web, Myapp.Web.Endpoint, load_from_system_env: true, url: [host: "mydomain.com", port: 443], cache_static_manifest: "priv/static/cache_manifest.json", server: true, force_ssl: [hsts: true, host: nil], https: [keyfile: "/etc/letsencrypt/live/mydomain.com/privkey.pem", cacertfile: "/etc/letsencrypt/live/mydomain.com/chain.pem", certfile: "/etc/letsencrypt/live/mydomain.com/cert.pem"] ``` and ```elixir def init(_key, config) do if config[:load_from_system_env] do port = System.get_env("PORT") || raise "expected the PORT environment variable to be set" port_ssl = System.get_env("PORT_SSL") || raise "expected the PORT environment variable to be set" config_https = Keyword.get(config, :https) |> Keyword.put(:port, port_ssl) new_config = Keyword.put(config, :http, [:inet6, port: port]) {:ok, Keyword.put(new_config, :https, config_https)} else {:ok, config} end end ``` Remember to restart your tasks after deploying and you will have a fully working HTTPS only app.