Skip to content

Commit 7823739

Browse files
authored
Merge pull request #835 from tomekowal/fix/cacertfile-ignored-with-conn
Fix/cacertfile ignored with conn
2 parents fdd1576 + 422f436 commit 7823739

6 files changed

Lines changed: 159 additions & 6 deletions

File tree

src/hackney_conn.erl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -761,10 +761,9 @@ connected({call, From}, {upgrade_to_ssl, _SslOpts}, #conn_data{transport = hackn
761761
{keep_state_and_data, [{reply, From, ok}]};
762762
connected({call, From}, {upgrade_to_ssl, SslOpts}, #conn_data{socket = Socket, host = Host, connect_options = ConnectOpts} = Data) ->
763763
%% Upgrade TCP socket to SSL (e.g., after CONNECT proxy tunnel)
764-
%% Get default SSL options with hostname verification
765-
DefaultSslOpts = hackney_ssl:check_hostname_opts(Host),
766-
%% Merge user-provided SSL options (they override defaults)
767-
MergedSslOpts = hackney_util:merge_opts(DefaultSslOpts, SslOpts),
764+
%% Use ssl_opts/2 to properly merge defaults with user options
765+
%% (handles cacertfile vs cacerts correctly)
766+
MergedSslOpts = hackney_ssl:ssl_opts(Host, [{ssl_options, SslOpts}]),
768767
%% Add ALPN options for HTTP/2 negotiation
769768
%% Check both SslOpts (from upgrade call) and ConnectOpts (from initial config)
770769
AlpnOpts = case hackney_ssl:alpn_opts(SslOpts) of
@@ -2271,8 +2270,9 @@ do_tcp_connect(From, Data) ->
22712270
TransportOpts = proplists:delete(protocols, ConnectOpts),
22722271
Opts = case Transport of
22732272
hackney_ssl ->
2274-
DefaultSslOpts = hackney_ssl:check_hostname_opts(Host),
2275-
MergedSslOpts = hackney_util:merge_opts(DefaultSslOpts, SslOpts0),
2273+
%% Use ssl_opts/2 to properly merge defaults with user options
2274+
%% (handles cacertfile vs cacerts correctly)
2275+
MergedSslOpts = hackney_ssl:ssl_opts(Host, [{ssl_options, SslOpts0}]),
22762276
AlpnOpts = hackney_ssl:alpn_opts(ConnectOpts),
22772277
FinalSslOpts = hackney_util:merge_opts(MergedSslOpts, AlpnOpts),
22782278
TransportOpts ++ [{ssl_options, FinalSslOpts}];

test/certs/ca.key

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCj1XbtZLafXR5i
3+
h513pkjAHfj+Ox8d5BgayydNru4zZVMdsamxZO02ZAvhM2CBlJ0T2XLuxE/4ULUD
4+
1nzH/58gz4ij78KMU5l77LRa2IwWYQSTotLIHR5CVkba8khgsj7sKrlOsvcEJW5+
5+
jl5Eov6a5LMr8eB13wBlWeqrID0kwo5Ebw+soTzUrJn57u5FsUzklm4N+W+WgwR+
6+
KawGss0Qg2YNA101tlIDWwH8VBqXxnr76T8fTdoEK+dqNYTUq2U7+HJlhHTJchj0
7+
OesoS4gZfat244MsyFagrZNPsTrofOeO5vRiKgCOss0iAnrn6/yjfiuoJ+1RqVYH
8+
FF9f9DFxAgMBAAECggEABqrerctYG8z3OjkM7WRC6G+n1RfcnLOuT/PV2AVfHBAa
9+
9XXII0gpV7nzWlFEE43z5Q2HzgQHAaLuNPdnCWAjrpsHk21j4GBcGhUgY2SV8ei1
10+
nhkFpT97HmXCuTEcRTQn16Zm93cU0rI/yI58c2RjQnQ9fvO3Z/ChBF7oDBoSJvNj
11+
zZlhkrfhCJgujf5deQjk2td5zVDKBB7OkrBHM7nGdOyqPWwRBWHRrpCj2v2MKQOu
12+
LjNcBwr7qE0jYA7D5y2M3AI2Bpof7IgFhnnvN9xkj8e6uvfMjd2y4gpOvxwdf3XO
13+
ya+vAtcbI1PVgIj8KY6rHgMXtqkYfmfn9fq1Zy5pAQKBgQDc162OFK8twi9rmz9w
14+
+H3dv8dEOtTbnviITEb9X8z7j6QcYpf3oJVcfGkNaz2Co4g/MDzkK7RD6XHeUvBo
15+
T28JvskghVNMYO6HKf/2oiTPlwtH1Wdm6I0n5fo8L29a8IkXlnfpNmstI0vJsXXo
16+
L5+pIyNLv6L9KfzhEq+UYK+ngQKBgQC96m24V7OAXw0YFbjEX/2P4CMlq2Wfe06r
17+
OiqVmZH4pUASQ0q1N8CMrSCu1Y7MZzLaUuLdL8GwL0NGZMBMSimTt7pfZDDeDda4
18+
xX22fYas+ZUZJAHu7CdvHH1MT9GEDp+G7XVY/LnruMSNwJ/lFOobNrcqRWz2ojru
19+
44IjovAB8QKBgGAYRT/Oxk8t8P5sxlU8+1/TRDzvMJIEAXclYbp8xjAsV6e2SxQI
20+
PxXIWNnq8Q/4Yp/EOKq8TatDWDX6dvucnN9rsg7BlPZmM0SDRQqnkUb3HYR7WowP
21+
4uQakSFBLr4ubijiY3kKIea5NhAkdP68QkgRrxkV4TEx5QR24gm5bJWBAoGAY9tO
22+
g54BcN8JiH9rXj3Gmg7VDCp5zYhNTfTQjUZpHR7ueGvPbUd6Q72IMMVzRwCAGZF5
23+
XamNovDG48132uUnxVbWdO++ThNislaNChYoaOz2O3jWV2TuOxr0utpBJLl3ob9b
24+
c0W3ED1fg9UjfZUontR/LIfCik+0wwT22XwDzFECgYBqcL9JWozhqVVOmEO6PqZe
25+
ysVWZInS9REFyOTBz7C3csUnzX+7aFljjSB7H8BeJ/Q4rmvNJ33ueqnXAg/CnyDO
26+
Ex+GiFHceCk5j5eS0r/uKhyrfL3wysYTDfOoN585n8unEJsif4hkRi6C52OhedwS
27+
Sg+tHFiu58ngrgxCrniA2Q==
28+
-----END PRIVATE KEY-----

test/certs/ca.pem

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDBTCCAe2gAwIBAgIUa2T0vunNiiG+w74wOqoY/wpfW1cwDQYJKoZIhvcNAQEL
3+
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjAzMTgwNzI3NDFaFw0zNjAzMTUw
4+
NzI3NDFaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
5+
DwAwggEKAoIBAQCj1XbtZLafXR5ih513pkjAHfj+Ox8d5BgayydNru4zZVMdsamx
6+
ZO02ZAvhM2CBlJ0T2XLuxE/4ULUD1nzH/58gz4ij78KMU5l77LRa2IwWYQSTotLI
7+
HR5CVkba8khgsj7sKrlOsvcEJW5+jl5Eov6a5LMr8eB13wBlWeqrID0kwo5Ebw+s
8+
oTzUrJn57u5FsUzklm4N+W+WgwR+KawGss0Qg2YNA101tlIDWwH8VBqXxnr76T8f
9+
TdoEK+dqNYTUq2U7+HJlhHTJchj0OesoS4gZfat244MsyFagrZNPsTrofOeO5vRi
10+
KgCOss0iAnrn6/yjfiuoJ+1RqVYHFF9f9DFxAgMBAAGjUzBRMB0GA1UdDgQWBBTc
11+
WObZ/TNAq4tQqy34nNCttoc5FDAfBgNVHSMEGDAWgBTcWObZ/TNAq4tQqy34nNCt
12+
toc5FDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAxnuB+uBwK
13+
jzS/muqhi8l3ccGh8WvQy0ABLVBbxQ+IElTNCxA+zh6zCnMdmv7LqN7Q1sFcTsoq
14+
aSsyvC5TPS6eVSuh4eB5ATY5bZD0J+X35OWd0+1vyDHFQN7fjgVGz09OFCbUyRfi
15+
U934qbTEcD3gpv9LKs9WQnuKKouAPlIqrIDak/z9o4PsYP52Av2h3O+ThO+suBbD
16+
a9IoNHru+I46xrqXsze8BzIVdnPYVpUxtsF440cErBeKonDzrQAWNIonut3ldsgG
17+
JxvJQFl+itRAcKhOI9GvN2nSstkbYC6VaZ0EpzPqhfazuXz4Re4Te5NGa8/j78GI
18+
V3UA40EJUz7c
19+
-----END CERTIFICATE-----

test/certs/server.key

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCoQRXFhsXPwITJ
3+
xvXpm6ShxKIE1BqSCqoaVPycpEHPEr0S3xTXgTfp53Xv0p2L65bucF2Yn4+1wonf
4+
qAh796po/8jSI+Fzoh51tHs5OTvEGcL2qHUFBXvdpleQuf4MeMU3ouDx1zMrXjuw
5+
DvCdwzCkOj6fA2v7+4BPfQATMosOy3vbTJKHv/HKXteg/Nbp1TZOwl3Pr9zGYXFw
6+
5+6k2Qh+cdg/l3qT2HxhMBSKtlmmn5X4ofmjWOelly1v8WpCn5psjfvuoJnitA4U
7+
Xc3dawdYp3/0rL+/BWTbXDDqxN7TE5DsTOhmDlWmCV8a1QSpzz8V+2MTEJ2F5+jr
8+
oldmmMDrAgMBAAECggEARrQzBfKApbDtHC2zoRt6r1AGFalcEQrSOIaGMP0FepMR
9+
SSDdjUIL0QsnEESdV/MEVeZ6Lmy+406Aya+/APkubzktlsDlOMZjrmrNbVqTtvBs
10+
cWKQ6i9HwfjoyzSdgXguyBZ2GKqqIgtTYcSlcGZZxmmDbybs9dLWNJD+uxJ+RRSm
11+
Qt/UYrPk9Ldkzze9wDRTv5bnMDgOayah4nYITBIsmbFEd+QBQYkjjzDecwHA3JrC
12+
7qkRHK/YXerAUDfSu+kiilwaG9rL01p+GGduoB3T0/Ba/+xryOTiJxHAv/lbiG1C
13+
cjOQBdj2NZvt37dZ56On7fZelh/z0VAxsNL5VbH24QKBgQDiDYTi+llYDQclnjf1
14+
7owq+GNLtnqkOoxgCLL09H/4yvhpGRyl/sTWcJUmN30BIGf3JbsEdUlw8LpjbQqg
15+
xG/+quoJzWhUdERE7ws077q7FS6oIuWR+Ebne010jOxR53K9TtG5fGka6krxmt/m
16+
k5ZyGwoKWWrZaUVhUi1uEZTVvwKBgQC+i1pgcS1Exb4Ux6TkoD5pHKPLf1rJo4t5
17+
gd0NAQwzBoWvKtzoFrWHmJc8EwumZjTylRFsCQ4dhunRwTyAga6st1qBp1102yLX
18+
lTCfLeXz0+LlkwUNhu7fbOLmSsn6xwiTXoNONQE3xabSYO+i/FQYtJwVH4phwMHi
19+
NARqPKFX1QKBgCwL+lLH+VTA5R2dYMYY/1L4J1D/c5JAnk2wJD66zZzK3/CKphxq
20+
Miyer1FNCpyHlfqAbZqGyBKrtYXeH24IGNKEtynFzoh2Rz8vXP2poLcHf5nfguAY
21+
gqhkTEljlEC5WpAspY0BAvHtqUC+rtYc9/mv7xrpJXrLmmtGOffykQ+9AoGAb5rL
22+
usVPkIKKDT3KdSbupz5hKeZUVNp37RmFUgKVFKXzU2A1t7LlbKCRpFw7bKFczeFG
23+
LRM4s068UWFvgI10tDFIz7wp3zIjPEZkDjgiAijPM0xjn0KzUyZB2EVh/ILroPWw
24+
zvP43KPmTD7+3WYSE85lxXGN6ieu6EEzfM46amkCgYAyRF2RA/CsbdK0+aAQpcVB
25+
51H36BpItwcGh2/CF84hEm7Xq8FZN2Xv0a3aZfMD/4gWIDR5GN/HqBhIe0rzSV9B
26+
ijVoF8P8JuYkB9FbZnK5srLqrh/uZb3AsHOuEzt3YnF1Xkq2h+UggE3e6izA5tXh
27+
32zpCo8kSaPbLDyQR191og==
28+
-----END PRIVATE KEY-----

test/certs/server.pem

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDEjCCAfqgAwIBAgIUeHOpIj5w7HfcnQw9T0ShnnZ3BjMwDQYJKoZIhvcNAQEL
3+
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjAzMTgwNzI3NDFaFw0zNjAzMTUw
4+
NzI3NDFaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQAD
5+
ggEPADCCAQoCggEBAKhBFcWGxc/AhMnG9embpKHEogTUGpIKqhpU/JykQc8SvRLf
6+
FNeBN+nnde/SnYvrlu5wXZifj7XCid+oCHv3qmj/yNIj4XOiHnW0ezk5O8QZwvao
7+
dQUFe92mV5C5/gx4xTei4PHXMyteO7AO8J3DMKQ6Pp8Da/v7gE99ABMyiw7Le9tM
8+
koe/8cpe16D81unVNk7CXc+v3MZhcXDn7qTZCH5x2D+XepPYfGEwFIq2Waaflfih
9+
+aNY56WXLW/xakKfmmyN++6gmeK0DhRdzd1rB1inf/Ssv78FZNtcMOrE3tMTkOxM
10+
6GYOVaYJXxrVBKnPPxX7YxMQnYXn6OuiV2aYwOsCAwEAAaNeMFwwGgYDVR0RBBMw
11+
EYcEfwAAAYIJbG9jYWxob3N0MB0GA1UdDgQWBBQGilSdZDqBe3dZwUVJU+dZSwWF
12+
ZTAfBgNVHSMEGDAWgBTcWObZ/TNAq4tQqy34nNCttoc5FDANBgkqhkiG9w0BAQsF
13+
AAOCAQEALQzkn2Ggt2UhtqpLDMaYwY3h5+beqOVeZ8MYp+IOeJWNfDF072TE6pYH
14+
xiZYfJ+RdZePe7VqnQJEQsMMJ/6p5VEGSCNN0Zryz4by3Vu/JA3RX6JqRq55Al9I
15+
1CPwn2Ii+dE8CJYRXG63tqqYzchmPVwfing7/YpT8D7zUpRmsd9oU8PTlpnxonvc
16+
SmvZEBTf8PVEm6HlO/nPxOi0MvN8AkQ8EBH23qDWPsiAd6LN+nySVnukAPRlt+8Q
17+
ZTM6KneidFluqTzbacnw/9njlH7fyQktsUDQh3wzQ6+x10PjfFa25Vi1L+kRvk7x
18+
e3h+4kGHJQpRtEBQ3NoeaS0KccGahg==
19+
-----END CERTIFICATE-----
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
%%% Test that cacertfile option is respected when making HTTPS requests.
2+
%%%
3+
%%% Currently fails because hackney_conn.erl bypasses hackney_ssl:ssl_opts/2
4+
%%% and calls hackney_ssl:check_hostname_opts/1 directly, which always sets
5+
%%% {cacerts, certifi:cacerts()}. Erlang SSL then ignores cacertfile.
6+
7+
-module(hackney_cacertfile_bug_test).
8+
-include_lib("eunit/include/eunit.hrl").
9+
10+
-define(HTTPS_PORT, 8126).
11+
12+
cacertfile_test_() ->
13+
{setup,
14+
fun setup/0,
15+
fun teardown/1,
16+
[
17+
{"cacertfile respected in ssl_opts/2", fun test_ssl_opts_handles_cacertfile/0},
18+
{"cacertfile respected in request", fun test_request_with_cacertfile/0}
19+
]}.
20+
21+
setup() ->
22+
error_logger:tty(false),
23+
{ok, _} = application:ensure_all_started(cowboy),
24+
{ok, _} = application:ensure_all_started(hackney),
25+
26+
CertDir = cert_dir(),
27+
CertFile = filename:join(CertDir, "server.pem"),
28+
KeyFile = filename:join(CertDir, "server.key"),
29+
30+
Dispatch = cowboy_router:compile([{'_', [{"/[...]", test_http_resource, []}]}]),
31+
{ok, _} = cowboy:start_tls(cacertfile_test_server,
32+
[{certfile, CertFile},
33+
{keyfile, KeyFile},
34+
{port, ?HTTPS_PORT}],
35+
#{env => #{dispatch => Dispatch}}),
36+
ok.
37+
38+
teardown(_) ->
39+
cowboy:stop_listener(cacertfile_test_server),
40+
application:stop(cowboy),
41+
application:stop(hackney),
42+
error_logger:tty(true),
43+
ok.
44+
45+
test_ssl_opts_handles_cacertfile() ->
46+
CACertFile = filename:join(cert_dir(), "ca.pem"),
47+
Options = [{ssl_options, [{cacertfile, CACertFile}]}],
48+
SslOpts = hackney_ssl:ssl_opts("localhost", Options),
49+
?assert(lists:keymember(cacertfile, 1, SslOpts)),
50+
?assertNot(lists:keymember(cacerts, 1, SslOpts)).
51+
52+
test_request_with_cacertfile() ->
53+
CACertFile = filename:join(cert_dir(), "ca.pem"),
54+
Url = "https://localhost:" ++ integer_to_list(?HTTPS_PORT) ++ "/get",
55+
Opts = [{ssl_options, [{cacertfile, CACertFile}]}, {pool, false}],
56+
{ok, 200, _, _} = hackney:request(get, Url, [], <<>>, Opts).
57+
58+
cert_dir() ->
59+
filename:join([filename:dirname(code:which(?MODULE)), "..", "test", "certs"]).

0 commit comments

Comments
 (0)