HTTPoison.Error sending a mail with Brevo : {:options, :incompatible, [verify: :verify_peer, cacerts: :undefined]}

Hi everyone,

Recently, our transactional emails won’t launch and return HTTPoison.Error like :
%HTTPoison.Error{reason: {:options, :incompatible, [verify: :verify_peer, cacerts: :undefined]}, id: nil}

I’m using the Brevo API V3. And my guess is that’s a problem with SSL; I currently use [ssl: [{:versions, [:"tlsv1.2"]}]].

Any idea or lead to help me on this case?

Thanks.

UPDATE :
Linked to OTP 26 upgrade. I obtain some progress by changing my request otps like :

  def process_request_options(_options) do
    [
      ssl: [
        verify: :verify_peer,
        cacerts: :public_key.cacerts_get(),
        versions: [:"tlsv1.2"]
      ]
    ]

Now the error is :
%HTTPoison.Error{reason: {:tls_alert, {:handshake_failure, ~c"TLS client: In state certify at ssl_handshake.erl:2140 generated CLIENT ALERT: Fatal - Handshake Failure\n {bad_cert,hostname_check_failed}"}}, id: nil}

Have you upgraded to OTP-26? You can read about the new default feature introduced to OTP-26: Erlang/OTP 26 Highlights - Erlang/OTP

Thanks @D4no0 , it’s surely that I upgraded recently to OTP 26.

Do you have any resources on how this translated into Elixir?

I suppose it’s something to change in Application.ensure_all_started(:ssl)?

I tried :

 config :my_app, MyApp.Mailer,
    [...]
    allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
    ssl: true,
    ssl_opts: [verify: :verify_peer, cacerts: :public_key.cacerts_get()],

Waiting for deploy on staging to see if it works. But does this seem like the right approach to you?

EDIT: No, sadly it didn’t change anything.

We use Finch with Brevo, we recently upgraded to Elixir 1.15.4 and OTP 26.0.2 and it works:

Finch.build(
   :delete,
   "https://api.sendinblue.com/v3/contacts/#{email}",
   [
      {"api-key", api_key()},
      {"content-type", "application/json"}
    ]
  )
 |> Finch.request(PlatformFinch)
 finch 0.16.0
 ssl_verify_fun 1.1.6
``
1 Like

I obtain some progress by changing my request otps like :

  def process_request_options(_options) do
    [
      ssl: [
        verify: :verify_peer,
        cacerts: :public_key.cacerts_get(),
        versions: [:"tlsv1.2"]
      ]
    ]

Now the error is :
%HTTPoison.Error{reason: {:tls_alert, {:handshake_failure, ~c"TLS client: In state certify at ssl_handshake.erl:2140 generated CLIENT ALERT: Fatal - Handshake Failure\n {bad_cert,hostname_check_failed}"}}, id: nil}

:wave: @GoulvenClech

Note that for wildcard certs you need to add an additional option, :customize_hostname_check

[
  ssl: [
    verify: :verify_peer,
    cacerts: :public_key.cacerts_get(),
    versions: [:"tlsv1.2"],
    customize_hostname_check: [
      match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
    ]
  ]
]

More info: Erlang standard library: ssl | EEF Security WG (note the depth option as well)

And for :public_key.cacerts_get() to work, you might need to first :public_key.cacerts_load() the certs (e.g. during application startup). Right now in your case it seems to be returning :undefined.

4 Likes

It looks like it was that!

Thanks a lot @ruslandoga , I clearly pay my lack of understanding of SSL and how OTP deal with it. I’ll work on that.

Where are you adding those ssl params at? Is it in your config files?

I’m getting:

12:48:40.937 [error] Postgrex.Protocol (#PID<0.1735.0>) failed to connect: ** (DBConnection.ConnectionError) ssl connect: TLS client: In state certify at ssl_handshake.erl:2138 generated CLIENT ALERT: Fatal - Unknown CA
 - {:tls_alert, {:unknown_ca, ~c"TLS client: In state certify at ssl_handshake.erl:2138 generated CLIENT ALERT: Fatal - Unknown CA\n"}}

from

ssl: true,
ssl_opts: [
      verify: :verify_peer,
      cacerts: :public_key.cacerts_get(),
      versions: [:"tlsv1.2"],
      customize_hostname_check: [
             match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
      ]
]