Certified - ACME for the modern Elixir stack

View Source

Phoenix + Bandit + ACME, built for distributed deployments.

Certified is built for distributed Phoenix apps that are using Bandit and Thousand Island. This stack allows Certified to hook into the :ssl.handshake function call and inject updated certificates and their private keys. This approach also supports multiple certificates and keys to be passed along.

A singular CertificateUpdater per cluster, orchestrated by ProcessHub, is in charge of requesting the certificates (if they aren't available on another node), and renewing them when they have only 25% of their validity left.

And with the power of Phoenix.PubSub, your nodes can sync their certificates without the need to have shared storage. This also works smoothly when new nodes come online as they announce their presence and ask for the certificates and keys from the running CertificateUpdater.

Important

This integration has been developed for use with Anchor.dev, which takes care of ACME challenges for you. Future releases will support other ACME providers and support HTTP challenges.

Installation

def deps do
  [
    {:certified, "~> 0.1.0"}
  ]
end

Configure

In your config, usually config/runtime.exs, change your Endpoint to use the included Certified.SSLTransport transport, as well as a self-signed key and cert to use on boot.

These settings are added to the thousand_island_options of your Endpoint config:

config :my_app, MyApp.Endpoint,
  url: [host: "myhost.com"],
  https: [
    port: 4040,
    otp_app: :my_app,
    thousand_island_options: [
      # How we hook into the `:ssl.handshake` flow
      transport_module: Certified.SSLTransport,
      transport_options: [
        # Some self signed certs to use on application startup
        certs_keys: [%{certfile: "cert.pem", keyfile: "key.pem"}]
      ]
    ]
  ]

And then all you need to do is to configure Certified with your ACME details.

config :certified,
  domains: System.get_env("ACME_DOMAINS"),
  acme: [
    directory_url: System.get_env("ACME_DIRECTORY_URL"),
    eab: [
      kid: System.get_env("ACME_EAB_KID"),
      hmac_key: System.get_env("ACME_EAB_HMAC_KEY")
    ]
  ]

Upcoming improvements