fix(mobilizon): trust the internal CA on the server-side OIDC calls

Erlang's ssl stack does not read SSL_CERT_FILE, and hackney ships certifi,
so the Keycloak strategy only trusted public roots. Every server-side token
and userinfo call against the stack's own Keycloak failed certificate
verification.

The CA plumbing already mounts the anchor and exports SSL_CERT_FILE into
every compose service, so the config reads that path and hands it to the
strategy as cacertfile. Ueberauth merges the whole keyword list into
OAuth2.Client.new/1, where request_opts is a struct field that oauth2 2.1.0
forwards to hackney unchanged.

The key is ssl_options, not ssl: Mobilizon configures
Tesla.Adapter.Hackney, whose call passes opts straight into
:hackney.request/5, and hackney reads maps:get(ssl_options, Opts, []). An
ssl key would have been dropped without a word. Only the httpc adapter
spells it ssl.

Overriding ssl_options replaces hackney's default trust store, so
customize_hostname_check restores the https hostname match that
ssl_verify_fun would otherwise have provided.

With no SSL_CERT_FILE the value stays [] and nothing changes.

Verified by rendering the template with Ansible's Jinja settings and
evaluating it under Config.Reader on elixir:1.17-alpine: the empty branch
yields [], the populated branch yields the ssl_options list with
:public_key.pkix_verify_hostname_match_fun/1 resolved to a real function.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-08-12 02:37:13 +02:00
1 parent e6fb78af49
commit 6b941c1e51
1 file changed
+8 -1
@@ -269,6 +269,8 @@ config :mobilizon, :auth,
{:keycloak, "{{ OIDC.BUTTON_TEXT }}"}
]
ca_trust = System.get_env("SSL_CERT_FILE")
config :ueberauth, Ueberauth.Strategy.Keycloak.OAuth,
client_id: "{{ OIDC.CLIENT.ID }}",
client_secret: "{{ OIDC.CLIENT.SECRET }}",
@@ -276,5 +278,10 @@ config :ueberauth, Ueberauth.Strategy.Keycloak.OAuth,
authorize_url: "{{ OIDC.CLIENT.AUTHORIZE_URL }}",
token_url: "{{ OIDC.CLIENT.TOKEN_URL }}",
userinfo_url: "{{ OIDC.CLIENT.USER_INFO_URL }}",
token_method: :post
token_method: :post,
request_opts:
if(ca_trust in [nil, ""],
do: [],
else: [ssl_options: [verify: :verify_peer, cacertfile: ca_trust, customize_hostname_check: [match_fun: :public_key.pkix_verify_hostname_match_fun(:https)]]]
)
{% endif %}