Héctor Sansores

April 19, 2024

TLS middlebox error in Elixir

When I was developing an interface between a system using REST and a system using SOAP, suddenly I could not connect anymore to the SOAP test server. 

I got the following cryptic error message:

{:error,
 %Mint.TransportError{
   reason: {:tls_alert,
    {:unexpected_message,
     ~c"TLS client: In state hello_middlebox_assert at ssl_gen_statem.erl:807 generated CLIENT ALERT: Fatal - Unexpected Message\n {unexpected_msg,\n     {internal,{encrypted_extensions,\#{alpn => {alpn,<<2,104,50>>}}}}}"}}
 }}

I was terrified with the idea that the production server started to reject my connections.

Searching for "TLS client: In state hello_middlebox_assert at ssl_gen_statem.erl" I found multiple references about issues related to Erlang, so my first approach was to try distinct Erlang and Elixir combinations.

I was using Req, which uses Finch, which in turn uses Mint, so I isolated the problem using the following mint_test.exs elixir script.

Mix.install(
  [
    :castore,
    :mint
  ],
  force: true,
  verbose: true
)

soap_server_url = "Here goes the SOAP server URL"

Mint.HTTP.connect(:https, soap_server_url, 443) |> dbg()

I ran it using Erlang 26.0.2 and Elixir 1.15.5-otp-24 and could reproduce the error easily.

Using asdf I installed multiple Erlang and Elixir versions until I found that the error disappeared when using 
Erlang 24.3.2 and Elixir 1.14.5-otp-24.

Even though that solved the problem, I was not happy with the idea of being stuck with older versions of my development stack.

When running the above script I noticed that there was another message.

[warning] Description: ~c"Failed to assert middlebox server message"
Reason: [missing: {:change_cipher_spec, 1}]

That lead me to think that perhaps the problem could be solved passing some options to Mint.HTTP.connect.

After trying a lot of options I finally reached to the following code:

Mix.install(
  [
    :castore,
    :mint
  ],
  force: true,
  verbose: true
)

soap_server_url = "Here goes the SOAP server URL without https://"

opts = [
  transport_opts: [
    middlebox_comp_mode: false,
  ]
]


Mint.HTTP.connect(:https, soap_server_url, 443, opts)
|> dbg()

This code worked fine with every Erlang and Elixir combination that I tried.

The next step was to pass the options from Req to Mint which as it appears in the following script.

Mix.install(
  [
    :req
  ],
  force: true,
  verbose: true
)

soap_server_url = "https://" <> "Here goes the SOAP server URL"

opts = [
  transport_opts: [
    middlebox_comp_mode: false
  ]
]

req = Req.new(url: soap_server_url, connect_options: opts)
Req.get(req)
|> dbg()


That finally solved my problem.