email fuckery

0d14c0d · gmorell · 2026-05-15 02:51

1 files +40 -11

Files changed

modified config/runtime.exs
+40 −11
@@ -167,13 +167,25 @@ if config_env() == :prod do
167 167 end
168 168 end
169 169
170 # CA bundle for TLS verification. `:public_key.cacerts_get/0`
171 # returns `:undefined` on some OTP/OS combos that haven't been pre-
172 # loaded; pointing `ssl` at the file directly via `cacertfile` is
173 # robust across debian-slim, alpine, etc. Override with
174 # `SMTP_CACERTFILE` if your image puts the bundle elsewhere; set
175 # `SMTP_TLS_VERIFY=verify_none` for self-signed / private relays
176 # where you'd rather skip cert validation.
170 + # Eager-load the OS CA bundle into OTP's `public_key` cache. Without
171 + # this, `:public_key.cacerts_get/0` returns `:undefined`, and
172 + # `gen_smtp`'s internal default `tls_options` injects
173 + # `cacerts: :undefined` — which `:ssl` then rejects as incompatible
174 + # with `verify: :verify_peer`, clobbering our explicit `cacertfile`.
175 + case :public_key.cacerts_load() do
176 + :ok ->
177 + :ok
178 +
179 + {:error, reason} ->
180 + require Logger
181 + Logger.warning("public_key:cacerts_load failed: #{inspect(reason)} — TLS verify may fail")
182 + end
183 +
184 + # CA bundle for TLS verification. We pass both `cacertfile` (path)
185 + # and rely on the cacerts_load above for `cacerts_get/0` to work,
186 + # belt-and-braces. Override `SMTP_CACERTFILE` if your image puts the
187 + # bundle elsewhere; set `SMTP_TLS_VERIFY=verify_none` for self-signed
188 + # / private relays where you'd rather skip cert validation.
177 189 smtp_cacertfile =
178 190 System.get_env("SMTP_CACERTFILE", "/etc/ssl/certs/ca-certificates.crt")
179 191
@@ -183,6 +195,18 @@ if config_env() == :prod do
183 195 other -> raise "SMTP_TLS_VERIFY must be verify_peer or verify_none, got: #{inspect(other)}"
184 196 end
185 197
198 + # Resolve the CA store NOW (after `cacerts_load`) rather than letting
199 + # the downstream stack call `cacerts_get/0` lazily. gen_smtp / ssl
200 + # has a habit of evaluating it in a context where it still returns
201 + # `:undefined`, which then collides with our `cacertfile`. Passing
202 + # `cacerts: <list>` explicitly is unambiguous — `:ssl` honors it
203 + # and never re-derives.
204 + smtp_cacerts =
205 + case :public_key.cacerts_get() do
206 + certs when is_list(certs) and certs != [] -> certs
207 + _ -> nil
208 + end
209 +
186 210 mailer_opts =
187 211 if relay = System.get_env("SMTP_RELAY") do
188 212 tls_options =
@@ -191,10 +215,15 @@ if config_env() == :prod do
191 215 server_name_indication: String.to_charlist(relay),
192 216 depth: 99
193 217 ] ++
194 if smtp_verify == :verify_peer do
195 [cacertfile: smtp_cacertfile]
196 else
197 []
218 + cond do
219 + smtp_verify != :verify_peer ->
220 + []
221 +
222 + is_list(smtp_cacerts) ->
223 + [cacerts: smtp_cacerts]
224 +
225 + true ->
226 + [cacertfile: smtp_cacertfile]
198 227 end
199 228
200 229 [

Parents: 4ac908f