CertMagex
Automatic SSL certs from Let's Encrypt for your Phoenix applications. This is based on the ZeroSSL library which is used for the ACME handshake. Plugging into the sni_fun and the name is inspired by similar functionality of the golang certmagic library.
This is used in the real world for example on https://tcpbin.net.
Installation
For Cowboy add to your prod.exs:
config <your_app>, <your_endpoint>,
https: [port: 443, sni_fun: &CertMagex.sni_fun/1],
# ATTENTION: Ensure you comment http: out and port 80 is free!
...
For Bandit add to your prod.exs:
config <your_app>, <your_endpoint>,
https: [port: 443, thousand_island_options: [transport_options: [sni_fun: &CertMagex.sni_fun/1]]],
# ATTENTION: Ensure you comment http: out and port 80 is free!
...
And add this to your deps:
def deps do
[
{:certmagex, "~> 1.0"}
]
end
You're done!
Optional Configuration values
The following configuration values are optional and can be set in your config.exs file.
user_email: The email to use for the ACME handshake. Let's encrypt might send informational emails to this address.provider: The provider to use for the ACME handshake. Can be:letsencryptor:zerossl. Defaults to:letsencrypt.account_key: The account key to use for the ACME handshake. Required only for:zerosslprovider.addr: The address to bind to for the ACME handshake. Defaults to0.0.0.0on IPv4 and::on IPv6.storage_module: The module to use for storage. Defaults toCertMagex.Storage.Acmev2Adapter. Changing the module allows storing retrieved certificates in a different storage location.storage_backend: The low-level key/value store for all CertMagex persisted data. Defaults toCertMagex.Storage.Dets(on-disk DetsPlus store). Configure withconfig :certmagex, :storage_backend, MyApp.CertStorage. Unlikestorage_module— the ACME adapter Zerossl calls for account keys and EAB credentials — the backend provides genericinsert,lookup, anddeletefor arbitrary Erlang-term keys and values (e.g.{:acmev2, key},{:cache, domain}).renewal_threshold: The threshold for certificate renewal. Defaults to renewing certificates if they have86_400seconds (1 day) of validity left.sni_allowed_hosts: If set to a non-empty list of hostnames, only those names (compared case-insensitively) will trigger certificate handling; any other TLS SNI value yields no certificate and does not run ACME. Useful to avoid spurious Let’s Encrypt requests from internet scanners. If unset or[], every SNI is considered (the previous default).
Custom storage backend
A backend implements the CertMagex.Storage.Backend behaviour:
defmodule MyApp.CertStorage do
@behaviour CertMagex.Storage.Backend
@impl true
def child, do: :ignore
@impl true
def insert(_key, _value), do: :ok
@impl true
def delete(_key), do: :ok
@impl true
def lookup(_key), do: nil
end
Return a supervisor child spec from child/0 when the backend needs its own process (as CertMagex.Storage.Dets does with DetsPlus); return :ignore if it does not.
IP address certificates
When using Let's Encrypt (provider: :letsencrypt or :letsencrypt_test), certmagex can issue certificates for IP addresses (IPv4 and IPv6). IP certificates are short-lived (~6 days) and require reliable automated renewal. They are not supported with the :zerossl provider. If the SNI callback receives an IP and the provider does not support IP certs, no certificate is generated (and a warning is logged).
Example config.exs
config :certmagex,
provider: :zerossl,
account_key: System.get_env("ZEROSSL_ACCOUNT_KEY"),
addr: "0.0.0.0",
user_email: "your@email.com",
storage_module: CertMagex.Storage.Acmev2Adapter
Notes
Generated certificates are by default stored in $HOME/.local/share/certmagex but the XDG_DATA_HOME variable is respected.
This wouldn't be possible without the Acmev2 module from zerossl https://hex.pm/packages/zerossl