> ## Content Index
> Fetch the complete content index at: https://www.process-one.net/llms.txt
> Use this file to discover other available public pages before exploring further.

# Security Advisory: eMudhra Intermediate CA Compromise and ejabberd
- URL: https://www.process-one.net/blog/security-advisory-emudhra-intermediate-ca-compromise-and-ejabberd/
- Published: 2026-10-23T15:25:26.000Z
- Updated: 2026-10-23T15:25:26.000Z
- Description: How to check and protect your ejabberd server
- Author: Jérôme Sautret
- Tags: ejabberd, News

The recent [eMudhra Intermediate CA Compromise](https://emudhra.we-have-a-problem.com/?ref=process-one.net) is a security threat for ejabberd if you use s2s, modules that make outgoing HTTPS requests, or TLS connections to MQTT brokers (`mod_mqtt_bridge`).

To check if your ejabberd is vulnerable, you can use the following code snippet (works on ejabberd 19.08 and later).

Open the debug console with:

```shell
ejabberdctl debug

```

Paste the following code into the debug console (the indented version of this code is available at the end of this post):

```erlang
(fun() -> Host = "dv-g3b-emudhra.we-have-a-problem.com", Fail = fun(Err) -> {"unable to connect to test server", Err} end, case gen_tcp:connect(Host, 443, [binary, {active, false}]) of {ok, Sock} -> case fast_tls:tcp_to_tls(Sock, [connect, {cafile, ejabberd_option:ca_file()}]) of {ok, TSock} -> _ = fast_tls:send(TSock, list_to_binary("HEAD / HTTP/1.0\r\nHost: " ++ Host ++ "\r\n\r\n")), case fast_tls:recv(TSock, 0, 10000) of {ok, _} -> case fast_tls:get_peer_certificate(TSock) of {ok, Cert} -> FT = case fast_tls:get_cert_verify_string(fast_tls:get_verify_result(TSock), Cert) of <<"ok">> -> "vulnerable"; _ -> "ok" end, HT = case list_to_integer(erlang:system_info(otp_release)) >= 26 of true -> case httpc:request("https://" ++ Host) of {ok, _} -> "vulnerable"; _ -> "ok" end; false -> "not verified (OTP < 26)" end, "fast_tls status: " ++ FT ++ ", ssl status: " ++ HT; E4 -> Fail(E4) end; E3 -> Fail(E3) end; E2 -> Fail(E2) end; E1 -> Fail(E1) end end)().

```

Exit the console with Ctrl-C twice.

It returns `"fast_tls status: ok, ssl status: ok"` if you are not vulnerable. If either status is vulnerable, please read on.

- If ssl status is not verified (OTP < 26): on Erlang/OTP < 26, outgoing TLS connections made with OTP's ssl (HTTP requests, MQTT bridge) don't verify server certificates at all; upgrading to OTP 26+ is recommended.
- If it returns {"unable to connect to test server", ...}, the check couldn't run (no outbound access, DNS, firewall…); this is not a "not vulnerable" result.

If you are running ejabberd with the official Docker image, you can update to 26.09.1 to fix the problem. It just removes the problematic certificates from the OS; it still runs ejabberd 26.09\. Pull `ghcr.io/processone/ejabberd:26.09.1` and recreate your container.

To fix it manually without restarting ejabberd, you can follow this procedure (requires Erlang/OTP 25+; on older versions, restart ejabberd).

First, update the CA bundle on your OS by upgrading the `ca-certificates` package:

```shell
# Debian / Ubuntu
apt update && apt install --only-upgrade ca-certificates
# RHEL / Rocky / Alma / Fedora
dnf update ca-certificates
# Alpine
apk upgrade ca-certificates

```

If your distribution has not shipped an updated package yet, you can distrust the certificates manually: see [update-ca-certificates(8)](https://manpages.debian.org/stable/ca-certificates/update-ca-certificates.8.en.html?ref=process-one.net) on Debian/Ubuntu, or [Using shared system certificates](https://docs.redhat.com/en/documentation/red%5Fhat%5Fenterprise%5Flinux/9/html/securing%5Fnetworks/using-shared-system-certificates%5Fsecuring-networks?ref=process-one.net) on RHEL-based systems.

Once the OS bundle is updated, paste the following into the debug console to reload it:

```erlang
public_key:cacerts_clear(), public_key:cacerts_load(), ejabberd_admin:reload_config().

```

Exit the console with Ctrl-C twice.

Finally, re-run the check above to verify that you are not vulnerable anymore.

If you are still vulnerable, your ejabberd may be using CA certs from a specific location. You can use the following functions in the debug console to check where to update your certs (ca\_file shows the CA file actually in use; if s2s\_cafile is undefined, s2s uses ca\_file).

```erlang
ejabberd_option:ca_file().
ejabberd_option:s2s_cafile().

```

If you are using some s2s CA cert per vhost, pass it as a parameter, for example:

```erlang
ejabberd_option:s2s_cafile(<<"example.com">>).

```

Here is the check above, indented for readability:

```erlang
(fun() ->
    Host = "dv-g3b-emudhra.we-have-a-problem.com",
    Fail = fun(Err) -> {"unable to connect to test server", Err} end,
    case gen_tcp:connect(Host, 443, [binary, {active, false}]) of
        {ok, Sock} ->
            case fast_tls:tcp_to_tls(Sock, [connect, {cafile, ejabberd_option:ca_file()}]) of
                {ok, TSock} ->
                    _ = fast_tls:send(TSock, list_to_binary("HEAD / HTTP/1.0\r\nHost: " ++ Host ++ "\r\n\r\n")),
                    case fast_tls:recv(TSock, 0, 10000) of
                        {ok, _} ->
                            case fast_tls:get_peer_certificate(TSock) of
                                {ok, Cert} ->
                                    FT = case fast_tls:get_cert_verify_string(fast_tls:get_verify_result(TSock), Cert) of
                                        <<"ok">> -> "vulnerable";
                                        _ -> "ok"
                                    end,
                                    HT = case list_to_integer(erlang:system_info(otp_release)) >= 26 of
                                        true ->
                                            case httpc:request("https://" ++ Host) of
                                                {ok, _} -> "vulnerable";
                                                _ -> "ok"
                                            end;
                                        false ->
                                            "not verified (OTP < 26)"
                                    end,
                                    "fast_tls status: " ++ FT ++ ", ssl status: " ++ HT;
                                E4 -> Fail(E4)
                            end;
                        E3 -> Fail(E3)
                    end;
                E2 -> Fail(E2)
            end;
        E1 -> Fail(E1)
    end
end)().

```