Open docapotamus opened 8 years ago
Sorry for the late reply but I'm pretty sure this is a hackney/erlang SSL issue :/
No problem @edgurgel
I will open the issue there.
Thanks for the reply.
I ran into a similar issue when trying to hit a service internally at my company. I'm pretty sure something funky is up with our cert chain, but I was able to find a workaround since all my traffic is behind our VPN:
HTTPoison.get("https://example.com/", [], [ssl: [{:versions, [:'tlsv1.2']}]])
# or worst case:
HTTPoison.get("https://example.com/", [], [ssl: [{:verify, :verify_none}]])
Hopefully that helps someone.
Updated based on a better recommendation
Thanks! @jondlm Probably saved me few hours :)
Just for reference - it is better to force TLS v1.2 with .., [ssl: [{:versions, [:'tlsv1.2']}]]
I would suggest that this issue be re-opened as it's going to become more common as HTTPS endpoints upgrade their supported TLS versions. I ran into this today with the Slack API.
👍 for @teamon's workaround as it's much better than turning off SSL verification completely.
Cool I will leave this open so people can jump in here if the README is not enough. Let's leave it open till Erlang 19 is past
Neither of @jondlm's suggestions worked for me.
Erlang/OTP: 19 Elixir: 1.3.4 httpoison: 0.10.0 hackney: 1.6.3
I'm going to downgrade to Erlang 18 since that's when this last worked for me.
@teamon hello =)
Could you share some reference about what you said?
Just for reference - it is better to force TLS v1.2 with .., [ssl: [{:versions, [:'tlsv1.2']}]]
I would like to understand more about it.
Thank you
@adrianotadao See https://github.com/teamon/tesla/issues/35.
Basically, when using verify_none you give up host verification which is kind of against the purpose of using SSL at all. The issue here is that there are many TLS versions and erlang have(had) some issues with selecting the correct version and it can be overcome with forcing the usage of TLS 1.2.
Hi I fell in that case when using otp 18 and httpotion (ibrowse) but it's the same thing with httpoison (hackney). A TCP socket is created using gen_tcp:connect(Hostname, Port), and then upgraded to a SSL one with ssl:connect(Sock,..). After a look at inets code, I've noticed that when we set a Hostname instead of an IP and upgrading a socket, we have to set the SSL option {server_name_indication, Hostname} in order to perform a successful handshake.
$ erl
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [kernel-poll:false]
Eshell V7.3 (abort with ^G)
1> ssl:start().
ok
2> Sock = fun() ->
2> {ok, S} = gen_tcp:connect("requestb.in", 443, []),
2> S
2> end.
#Fun<erl_eval.20.50752066>
3> ssl:connect(Sock(), []).
=ERROR REPORT==== 31-May-2017::11:20:54 ===
SSL: hello: src/ssl_alert.erl:93:Fatal error: handshake failure
{error,{tls_alert,"handshake failure"}}
4> ssl:connect(Sock(), [{server_name_indication, "requestb.in"}]).
{ok,{sslsocket,{gen_tcp,#Port<0.1052>,tls_connection,
undefined},
<0.52.0>}}
You will notice that doing directly ssl:connect(Hostname, Port, []) will work. This issue only occurs when upgrading a normal TCP socket.
5> ssl:connect("requestb.in", 443, []).
{ok,{sslsocket,{gen_tcp,#Port<0.1183>,tls_connection,
undefined},
<0.56.0>}}
Hope it helps. FYI here is the piece of code of inets (httpc_handler.erl) that helped me fix the issue:
maybe_add_sni(Host, Options) ->
case http_util:is_hostname(Host) andalso
not lists:keymember(server_name_indication, 1, Options) of
true ->
[{server_name_indication, Host} | Options];
false ->
Options
end.
Same problem with HTTPoison 0.12, Elixir 1.4.5, Erlang 20
The only difference is that I'm connecting through a proxy server, not directly
I applied the fix explained in this post and it worked flawlessly
Basically you have to pass the server_name_indication
to the underlying ssl
subsystem
HTTPoison.get("https://hostname", [], [proxy: "http://proxy:port",
ssl: [server_name_indication: 'hostname']])
Note that server_name_indication
is passed as char list, not as binary string.
I think the issue can be closed now but that sni option tip really deserves to be documented somewhere. A lot of people may experience the same issue in the future.
Not recommended but:
You can disable it with the disable option: {server_name_indication, disable}
Can it be configured via some option in config
file ?
Since so many libraries are depending on HTTPoison
, it would be useful if we can do that via config.
I got this error too, with Erlang 20, elixir 1.6,1
The same, Erlang 20, elixir 1.5.2
As I said earlier, the only way to fix this issue for now is to add in your code the equivalent in Elixir of the maybe_add_sni erlang function. It would look like this:
def get(url) do
options = [...] ++ ssl_options(url)
HTTPoison.get(url, [], options)
end
defp ssl_options(url) do
{:ok, {scheme, _, host, _, _, _}} = :http_uri.parse(url |> to_charlist)
is_ssl = (scheme == :https)
is_hostname = :http_util.is_hostname(host)
cond do
is_ssl and is_hostname -> [ssl: true, server_name_indication: host]
is_ssl -> [ssl: true]
true -> []
end.
end
I ran into a similar issue when trying to hit a service internally at my company. I'm pretty sure something funky is up with our cert chain, but I was able to find a workaround since all my traffic is behind our VPN:
HTTPoison.get("https://example.com/", [], [ssl: [{:versions, [:'tlsv1.2']}]]) # or worst case: HTTPoison.get("https://example.com/", [], [ssl: [{:verify, :verify_none}]])
Hopefully that helps someone.
Updated based on a better recommendation
Thanks! @jondlm
I ran into a similar issue when trying to hit a service internally at my company. I'm pretty sure something funky is up with our cert chain, but I was able to find a workaround since all my traffic is behind our VPN:
HTTPoison.get("https://example.com/", [], [ssl: [{:versions, [:'tlsv1.2']}]]) # or worst case: HTTPoison.get("https://example.com/", [], [ssl: [{:verify, :verify_none}]])
Hopefully that helps someone.
Updated based on a better recommendation
thanks, It's helpful.
This code is golden and worked for me with no issues
url ="https://myexample.come/auth"
body = "{\"username\": \"robin\", \"password\": \"password\"}"
headers = [
{"Content-Type", "application/json"}
]
defaults = :ssl.cipher_suites(:default, :"tlsv1.2")
rsa_kx =
:ssl.cipher_suites(:all, :"tlsv1.2")
|> :ssl.filter_cipher_suites(
key_exchange: &(&1 == :rsa),
cipher: &(&1 in [:aes_128_cbc, :aes_128_gcm, :aes_256_cbc, :aes_256_gcm]),
)
options = [ssl: [ciphers: defaults ++ rsa_kx]]
HTTPoison.post(url, body, headers, options)
Reference URL: https://elixirforum.com/t/https-handshake-error-fatal-handshake-failure/36877/10
Hello,
Sorry I wasn't sure if to post this here or on hackney. I don't know all the ins-and-outs of SSL and I'm using Elixir so guessed this was the right place. If you would like me to move it too or reopen on hackney please let me know.
I am trying to check
https://www.bbc.co.uk
from HTTPoison and receiving:[error] SSL: :hello: ssl_alert.erl:97:Fatal error: handshake failure
With the following tuple returned:
{:error, %HTTPoison.Error{id: nil, reason: {:tls_alert, 'handshake failure'}}}}
I can't see anything wrong with the certificate when using
openssl s_client -connect www.bbc.co.uk:443
:I can provide anymore details that are needed. Its seems to be working in all browsers and via curl. I tested this against a fresh home brew installation of OpenSSL but I am also getting the issue on up to date Debian Jessie Machine.
Not sure how the Erlang TLS/SSL module works but I'm wondering if it is an issue there.
Thanks in advance