adding certbot to a phoenix project
the phoenix framework is the de-facto web server for the elixir ecosystem. it’s production ready and doesn’t need any sort of ingress like nginx in front of it. there’s a section in the docs about how to set up ssl which does a great job, but you need to get the ssl certs first. which, most of the time, means you need certbot!
since you’ve skipped setting up nginx and apache, certbot can’t auto-configure itself. you’ll need to use the --webroot option instead, which means you’ll need to configure phoenix to serve the challenges that certbot creates.
I found and tried SiteEncrypt but it errored in some GenServer and I wasn’t up to the task of digging into the stack trace. it turns out it’s only a couple of lines of code anyways!
my first instinct was to use Plug.Static, since all I needed to do was serve the files in the .well-known/ directory that certbot creates. this fell flat because I wanted to configure the WELL_KNOW_PATH at runtime, but plug Plug.Static seems to all happen at compile time. so have to add the route myself!
-
add a route for certbot
--- a/lib/qian_bei_web/router.ex +++ b/lib/qian_bei_web/router.ex + + # Certbot + scope "/", QianBeiWeb do + pipe_through :browser + + get "/.well-known/*path", CertbotController, :challenge + end -
add the the
CerbotController--- /dev/null +++ b/lib/qian_bei_web/controllers/certbot_controller.ex defmodule QianBeiWeb.CertbotController do @moduledoc "serves files at /.well-known/ for `certbot` challenges" use QianBeiWeb, :controller @spec challenge(Plug.Conn.t(), map()) :: Plug.Conn.t() def challenge(conn, %{"path" => path}) do with well_known_dir when not is_nil(well_known_dir) <- Application.fetch_env!(:qian_bei, :certbot)[:well_known_dir], path = Enum.join(path, "/"), file = Path.join(well_known_dir, path), true <- File.exists?(file) do conn |> Plug.Conn.send_file(:ok, file) else _ -> conn |> put_view(QianBeiWeb.ErrorView) |> render(:"404") |> halt() end end end - deploy your phoenix webapp, making sure to set
config :qian_bei, :certbot, well_known_dir: System.fetch_env!("WEBROOT_PATH")(same as below) - get the certificates
sudo certbot -d "${DOMAIN:qianbei.com}" -v certonly --webroot --webroot-path="${WEBROOT_PATH:/var/www/qianbei}" -
configure your
Endpointto use the new certs--- a/config/runtime.exs +++ b/config/runtime.exs + config :qian_bei, QianBeiWeb.Endpoint, + https: [ + certfile: System.fetch_env!("WEB_SSL_CERT_PATH") || "/etc/letsencrypt/live/qianbei.com/fullchain.pem", + keyfile: System.fetch_env!("WEB_SSL_KEY_PATH") || "/etc/letsencrypt/live/qianbei.com/privkey.pem" + ] - recompile/redoply/restart the server (note the server will have to be run under the
rootaccount to access/et/letsencrypt) - visit your website and make sure it’s working!
- set up autorenew
sudo certbot renew --dry-run(this will add the autorenew to/etc/crontabor/etc/cron.*/*orsystemctl list-timers)