Certified - ACME for the modern Elixir stack
View SourcePhoenix + 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
- Docs docs docs, and examples
- Storage adapters for the EC key and certs (File system and S3)
- HTTP challenges (https://github.com/mtrudel/bandit?tab=readme-ov-file#using-bandit-with-plug-applications)
- Don't require EAB tokens
- Full testing against LetsEncrypt and ZeroSSL
- Support earlier Elixir versions? (switch to Jason or at least allow it to be used)