2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
20 * Copyright (C) 2015 Cloudius Systems, Ltd.
25 #include <seastar/core/do_with.hh>
26 #include <seastar/core/sstring.hh>
27 #include <seastar/core/reactor.hh>
28 #include <seastar/core/do_with.hh>
29 #include <seastar/core/loop.hh>
30 #include <seastar/core/sharded.hh>
31 #include <seastar/core/thread.hh>
32 #include <seastar/core/gate.hh>
33 #include <seastar/core/temporary_buffer.hh>
34 #include <seastar/core/iostream.hh>
35 #include <seastar/util/std-compat.hh>
36 #include <seastar/net/tls.hh>
37 #include <seastar/net/dns.hh>
38 #include <seastar/net/inet_address.hh>
39 #include <seastar/testing/test_case.hh>
40 #include <seastar/testing/thread_test_case.hh>
42 #include <boost/dll.hpp>
44 #include "loopback_socket.hh"
47 #include <gnutls/gnutls.h>
51 static void enable_gnutls_logging() {
52 gnutls_global_set_log_level(99);
53 gnutls_global_set_log_function([](int lv
, const char * msg
) {
54 std::cerr
<< "GNUTLS (" << lv
<< ") " << msg
<< std::endl
;
59 static const auto cert_location
= boost::dll::program_location().parent_path();
61 static std::string
certfile(const std::string
& file
) {
62 return (cert_location
/ file
).string();
65 using namespace seastar
;
67 static future
<> connect_to_ssl_addr(::shared_ptr
<tls::certificate_credentials
> certs
, socket_address addr
) {
68 return tls::connect(certs
, addr
, "www.google.com").then([](connected_socket s
) {
69 return do_with(std::move(s
), [](connected_socket
& s
) {
70 return do_with(s
.output(), [&s
](auto& os
) {
71 static const sstring
msg("GET / HTTP/1.0\r\n\r\n");
72 auto f
= os
.write(msg
);
73 return f
.then([&s
, &os
]() mutable {
75 return f
.then([&s
]() mutable {
76 return do_with(s
.input(), [](auto& in
) {
78 return f
.then([](temporary_buffer
<char> buf
) {
79 // std::cout << buf.get() << std::endl;
81 // Avoid passing a nullptr as an argument of strncmp().
82 // If the temporary_buffer is empty (e.g. due to the underlying TCP connection
83 // being reset) passing the buf.get() (which would be a nullptr) to strncmp()
84 // causes a runtime error which masks the actual issue.
86 BOOST_CHECK(strncmp(buf
.get(), "HTTP/", 5) == 0);
88 BOOST_CHECK(buf
.size() > 8);
100 static future
<> connect_to_ssl_google(::shared_ptr
<tls::certificate_credentials
> certs
) {
101 static socket_address google
;
103 if (google
.is_unspecified()) {
104 return net::dns::resolve_name("www.google.com", net::inet_address::family::INET
).then([certs
](net::inet_address addr
) {
105 google
= socket_address(addr
, 443);
106 return connect_to_ssl_google(certs
);
109 return connect_to_ssl_addr(std::move(certs
), google
);
112 SEASTAR_TEST_CASE(test_simple_x509_client
) {
113 auto certs
= ::make_shared
<tls::certificate_credentials
>();
114 return certs
->set_x509_trust_file(certfile("tls-ca-bundle.pem"), tls::x509_crt_format::PEM
).then([certs
]() {
115 return connect_to_ssl_google(certs
);
119 SEASTAR_TEST_CASE(test_x509_client_with_system_trust
) {
120 auto certs
= ::make_shared
<tls::certificate_credentials
>();
121 return certs
->set_system_trust().then([certs
]() {
122 return connect_to_ssl_google(certs
);
126 SEASTAR_TEST_CASE(test_x509_client_with_builder_system_trust
) {
127 tls::credentials_builder b
;
128 (void)b
.set_system_trust();
129 return connect_to_ssl_google(b
.build_certificate_credentials());
132 SEASTAR_TEST_CASE(test_x509_client_with_builder_system_trust_multiple
) {
133 tls::credentials_builder b
;
134 (void)b
.set_system_trust();
135 auto creds
= b
.build_certificate_credentials();
137 return parallel_for_each(boost::irange(0, 20), [creds
](auto i
) { return connect_to_ssl_google(creds
); });
140 SEASTAR_TEST_CASE(test_x509_client_with_priority_strings
) {
141 static std::vector
<sstring
> prios( { "NONE:+VERS-TLS-ALL:+MAC-ALL:+RSA:+AES-128-CBC:+SIGN-ALL:+COMP-NULL",
142 "NORMAL:+ARCFOUR-128", // means normal ciphers plus ARCFOUR-128.
143 "SECURE128:-VERS-SSL3.0:+COMP-DEFLATE", // means that only secure ciphers are enabled, SSL3.0 is disabled, and libz compression enabled.
144 "NONE:+VERS-TLS-ALL:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL:+SIGN-RSA-SHA1",
145 "SECURE256:+SECURE128",
148 "NONE:+VERS-TLS-ALL:+MAC-ALL:+RSA:+AES-128-CBC:+SIGN-ALL:+COMP-NULL",
149 "NORMAL:+ARCFOUR-128",
150 "SECURE128:-VERS-TLS1.0:+COMP-DEFLATE",
151 "SECURE128:+SECURE192:-VERS-TLS-ALL:+VERS-TLS1.2"
153 return do_for_each(prios
, [](const sstring
& prio
) {
154 tls::credentials_builder b
;
155 (void)b
.set_system_trust();
156 b
.set_priority_string(prio
);
157 return connect_to_ssl_google(b
.build_certificate_credentials());
161 SEASTAR_TEST_CASE(test_x509_client_with_priority_strings_fail
) {
162 static std::vector
<sstring
> prios( { "NONE",
163 "NONE:+CURVE-SECP256R1"
165 return do_for_each(prios
, [](const sstring
& prio
) {
166 tls::credentials_builder b
;
167 (void)b
.set_system_trust();
168 b
.set_priority_string(prio
);
170 return connect_to_ssl_google(b
.build_certificate_credentials()).then([] {
171 BOOST_FAIL("Expected exception");
172 }).handle_exception([](auto ep
) {
178 return make_ready_future
<>();
182 SEASTAR_TEST_CASE(test_failed_connect
) {
183 tls::credentials_builder b
;
184 (void)b
.set_system_trust();
185 return connect_to_ssl_addr(b
.build_certificate_credentials(), ipv4_addr()).handle_exception([](auto) {});
188 SEASTAR_TEST_CASE(test_non_tls
) {
189 ::listen_options opts
;
190 opts
.reuse_address
= true;
191 auto addr
= ::make_ipv4_address( {0x7f000001, 4712});
192 auto server
= server_socket(seastar::listen(addr
, opts
));
194 auto c
= server
.accept();
196 tls::credentials_builder b
;
197 (void)b
.set_system_trust();
199 auto f
= connect_to_ssl_addr(b
.build_certificate_credentials(), addr
);
202 return c
.then([f
= std::move(f
)](accept_result ar
) mutable {
203 ::connected_socket s
= std::move(ar
.connection
);
204 std::cerr
<< "Established connection" << std::endl
;
205 auto sp
= std::make_unique
<::connected_socket
>(std::move(s
));
206 timer
<> t([s
= std::ref(*sp
)] {
207 std::cerr
<< "Killing server side" << std::endl
;
208 s
.get() = ::connected_socket();
210 t
.arm(timer
<>::clock::now() + std::chrono::seconds(5));
211 return std::move(f
).finally([t
= std::move(t
), sp
= std::move(sp
)] {});
212 }).handle_exception([server
= std::move(server
)](auto ep
) {
213 std::cerr
<< "Got expected exception" << std::endl
;
217 SEASTAR_TEST_CASE(test_abort_accept_before_handshake
) {
218 auto certs
= ::make_shared
<tls::server_credentials
>(::make_shared
<tls::dh_params
>());
219 return certs
->set_x509_key_file(certfile("test.crt"), certfile("test.key"), tls::x509_crt_format::PEM
).then([certs
] {
220 ::listen_options opts
;
221 opts
.reuse_address
= true;
222 auto addr
= ::make_ipv4_address( {0x7f000001, 4712});
223 auto server
= server_socket(tls::listen(certs
, addr
, opts
));
224 auto c
= server
.accept();
225 BOOST_CHECK(!c
.available()); // should not be finished
227 server
.abort_accept();
229 return c
.then([](auto) { BOOST_FAIL("Should not reach"); }).handle_exception([](auto) {
231 }).finally([server
= std::move(server
)] {});
235 SEASTAR_TEST_CASE(test_abort_accept_after_handshake
) {
237 auto certs
= ::make_shared
<tls::server_credentials
>(::make_shared
<tls::dh_params
>());
238 certs
->set_x509_key_file(certfile("test.crt"), certfile("test.key"), tls::x509_crt_format::PEM
).get();
240 ::listen_options opts
;
241 opts
.reuse_address
= true;
242 auto addr
= ::make_ipv4_address( {0x7f000001, 4712});
243 auto server
= tls::listen(certs
, addr
, opts
);
244 auto sa
= server
.accept();
246 tls::credentials_builder b
;
247 b
.set_x509_trust_file(certfile("catest.pem"), tls::x509_crt_format::PEM
).get();
249 auto c
= tls::connect(b
.build_certificate_credentials(), addr
).get0();
250 server
.abort_accept(); // should not affect the socket we got.
253 auto out
= c
.output();
254 auto in
= s
.connection
.input();
256 out
.write("apa").get();
257 auto f
= out
.flush();
258 auto buf
= in
.read().get0();
260 BOOST_CHECK(sstring(buf
.begin(), buf
.end()) == "apa");
267 SEASTAR_TEST_CASE(test_abort_accept_on_server_before_handshake
) {
269 ::listen_options opts
;
270 opts
.reuse_address
= true;
271 auto addr
= ::make_ipv4_address( {0x7f000001, 4712});
272 auto server
= server_socket(seastar::listen(addr
, opts
));
273 auto sa
= server
.accept();
275 tls::credentials_builder b
;
276 b
.set_x509_trust_file(certfile("catest.pem"), tls::x509_crt_format::PEM
).get();
278 auto creds
= b
.build_certificate_credentials();
279 auto f
= tls::connect(creds
, addr
);
281 server
.abort_accept();
289 // the connect as such should succeed, but the handshare following it
292 auto out
= c
.output();
293 out
.write("apa").get();
297 BOOST_FAIL("Expected exception");
306 ::connected_socket s
;
307 input_stream
<char> in
;
308 output_stream
<char> out
;
310 // note: using custom output_stream, because we don't want polled flush
311 streams(::connected_socket cs
) : s(std::move(cs
)), in(s
.input()), out(s
.output().detach(), 8192)
315 static const sstring message
= "hej lilla fisk du kan dansa fint";
318 ::server_socket _socket
;
319 ::shared_ptr
<tls::server_credentials
> _certs
;
321 bool _stopped
= false;
323 std::exception_ptr _ex
;
325 echoserver(size_t message_size
, bool use_dh_params
= true)
328 ? ::make_shared
<tls::server_credentials
>(::make_shared
<tls::dh_params
>())
329 : ::make_shared
<tls::server_credentials
>()
331 , _size(message_size
)
334 future
<> listen(socket_address addr
, sstring crtfile
, sstring keyfile
, tls::client_auth ca
= tls::client_auth::NONE
, sstring trust
= {}) {
335 _certs
->set_client_auth(ca
);
336 auto f
= _certs
->set_x509_key_file(crtfile
, keyfile
, tls::x509_crt_format::PEM
);
337 if (!trust
.empty()) {
338 f
= f
.then([this, trust
= std::move(trust
)] {
339 return _certs
->set_x509_trust_file(trust
, tls::x509_crt_format::PEM
);
342 return f
.then([this, addr
] {
343 ::listen_options opts
;
344 opts
.reuse_address
= true;
346 _socket
= tls::listen(_certs
, addr
, opts
);
348 (void)try_with_gate(_gate
, [this] {
349 return _socket
.accept().then([this](accept_result ar
) {
350 ::connected_socket s
= std::move(ar
.connection
);
351 auto strms
= ::make_lw_shared
<streams
>(std::move(s
));
352 return repeat([strms
, this]() {
353 return strms
->in
.read_exactly(_size
).then([strms
](temporary_buffer
<char> buf
) {
355 return make_ready_future
<stop_iteration
>(stop_iteration::yes
);
357 sstring
tmp(buf
.begin(), buf
.end());
358 return strms
->out
.write(tmp
).then([strms
]() {
359 return strms
->out
.flush();
361 return make_ready_future
<stop_iteration
>(stop_iteration::no
);
365 return strms
->out
.close();
366 }).finally([strms
]{});
367 }).handle_exception([this](auto ep
) {
369 return make_ready_future
<>();
372 return make_ready_future
<>();
374 }).handle_exception_type([] (const gate_closed_exception
&) {/* ignore */});
375 return make_ready_future
<>();
381 _socket
.abort_accept();
382 return _gate
.close().handle_exception([this] (std::exception_ptr ignored
) {
384 std::rethrow_exception(_ex
);
390 static future
<> run_echo_test(sstring message
,
394 sstring crt
= certfile("test.crt"),
395 sstring key
= certfile("test.key"),
396 tls::client_auth ca
= tls::client_auth::NONE
,
397 sstring client_crt
= {},
398 sstring client_key
= {},
400 bool use_dh_params
= true,
401 tls::dn_callback distinguished_name_callback
= {}
404 static const auto port
= 4711;
406 auto msg
= ::make_shared
<sstring
>(std::move(message
));
407 auto certs
= ::make_shared
<tls::certificate_credentials
>();
408 auto server
= ::make_shared
<seastar::sharded
<echoserver
>>();
409 auto addr
= ::make_ipv4_address( {0x7f000001, port
});
411 assert(do_read
|| loops
== 1);
413 future
<> f
= make_ready_future();
415 if (!client_crt
.empty() && !client_key
.empty()) {
416 f
= certs
->set_x509_key_file(client_crt
, client_key
, tls::x509_crt_format::PEM
);
417 if (distinguished_name_callback
) {
418 certs
->set_dn_verification_callback(std::move(distinguished_name_callback
));
423 return certs
->set_x509_trust_file(trust
, tls::x509_crt_format::PEM
);
425 return server
->start(msg
->size(), use_dh_params
).then([=]() {
426 sstring server_trust
;
427 if (ca
!= tls::client_auth::NONE
) {
428 server_trust
= trust
;
430 return server
->invoke_on_all(&echoserver::listen
, addr
, crt
, key
, ca
, server_trust
);
432 return tls::connect(certs
, addr
, name
).then([loops
, msg
, do_read
](::connected_socket s
) {
433 auto strms
= ::make_lw_shared
<streams
>(std::move(s
));
434 auto range
= boost::irange(0, loops
);
435 return do_for_each(range
, [strms
, msg
](auto) {
436 auto f
= strms
->out
.write(*msg
);
437 return f
.then([strms
, msg
]() {
438 return strms
->out
.flush().then([strms
, msg
] {
439 return strms
->in
.read_exactly(msg
->size()).then([msg
](temporary_buffer
<char> buf
) {
441 throw std::runtime_error("Unexpected EOF");
443 sstring
tmp(buf
.begin(), buf
.end());
444 BOOST_CHECK(*msg
== tmp
);
448 }).then_wrapped([strms
, do_read
] (future
<> f1
) {
449 // Always call close()
450 return (do_read
? strms
->out
.close() : make_ready_future
<>()).then_wrapped([strms
, f1
= std::move(f1
)] (future
<> f2
) mutable {
451 // Verification errors will be reported by the call to output_stream::close(),
452 // which waits for the flush to actually happen. They can also be reported by the
453 // input_stream::read_exactly() call. We want to keep only one and avoid nested exception mess.
455 (void)f2
.handle_exception([] (std::exception_ptr ignored
) { });
456 return std::move(f1
);
458 (void)f1
.handle_exception([] (std::exception_ptr ignored
) { });
460 }).finally([strms
] { });
463 }).finally([server
] {
464 return server
->stop().finally([server
]{});
472 * make -f tests/unit/mkcert.gmk domain=scylladb.org server=test
479 * catest == snakeoil root authority for these self-signed certs
482 SEASTAR_TEST_CASE(test_simple_x509_client_server
) {
483 // Make sure we load our own auth trust pem file, otherwise our certs
485 // Must match expected name with cert CA or give empty name to ignore
487 return run_echo_test(message
, 20, certfile("catest.pem"), "test.scylladb.org");
490 SEASTAR_TEST_CASE(test_simple_x509_client_server_again
) {
491 return run_echo_test(message
, 20, certfile("catest.pem"), "test.scylladb.org");
494 #if GNUTLS_VERSION_NUMBER >= 0x030600
495 // Test #769 - do not set dh_params in server certs - let gnutls negotiate.
496 SEASTAR_TEST_CASE(test_simple_server_default_dhparams
) {
497 return run_echo_test(message
, 20, certfile("catest.pem"), "test.scylladb.org",
498 certfile("test.crt"), certfile("test.key"), tls::client_auth::NONE
,
499 {}, {}, true, /* use_dh_params */ false
504 SEASTAR_TEST_CASE(test_x509_client_server_cert_validation_fail
) {
505 // Load a real trust authority here, which out certs are _not_ signed with.
506 return run_echo_test(message
, 1, certfile("tls-ca-bundle.pem"), {}).then([] {
507 BOOST_FAIL("Should have gotten validation error");
508 }).handle_exception([](auto ep
) {
510 std::rethrow_exception(ep
);
511 } catch (tls::verification_error
&) {
514 BOOST_FAIL("Unexpected exception");
519 SEASTAR_TEST_CASE(test_x509_client_server_cert_validation_fail_name
) {
520 // Use trust store with our signer, but wrong host name
521 return run_echo_test(message
, 1, certfile("tls-ca-bundle.pem"), "nils.holgersson.gov").then([] {
522 BOOST_FAIL("Should have gotten validation error");
523 }).handle_exception([](auto ep
) {
525 std::rethrow_exception(ep
);
526 } catch (tls::verification_error
&) {
529 BOOST_FAIL("Unexpected exception");
534 SEASTAR_TEST_CASE(test_large_message_x509_client_server
) {
535 // Make sure we load our own auth trust pem file, otherwise our certs
537 // Must match expected name with cert CA or give empty name to ignore
539 sstring msg
= uninitialized_string(512 * 1024);
540 for (size_t i
= 0; i
< msg
.size(); ++i
) {
541 msg
[i
] = '0' + char(i
% 30);
543 return run_echo_test(std::move(msg
), 20, certfile("catest.pem"), "test.scylladb.org");
546 SEASTAR_TEST_CASE(test_simple_x509_client_server_fail_client_auth
) {
547 // Make sure we load our own auth trust pem file, otherwise our certs
549 // Must match expected name with cert CA or give empty name to ignore
551 // Server will require certificate auth. We supply none, so should fail connection
552 return run_echo_test(message
, 20, certfile("catest.pem"), "test.scylladb.org", certfile("test.crt"), certfile("test.key"), tls::client_auth::REQUIRE
).then([] {
553 BOOST_FAIL("Expected exception");
554 }).handle_exception([](auto ep
) {
559 SEASTAR_TEST_CASE(test_simple_x509_client_server_client_auth
) {
560 // Make sure we load our own auth trust pem file, otherwise our certs
562 // Must match expected name with cert CA or give empty name to ignore
564 // Server will require certificate auth. We supply one, so should succeed with connection
565 return run_echo_test(message
, 20, certfile("catest.pem"), "test.scylladb.org", certfile("test.crt"), certfile("test.key"), tls::client_auth::REQUIRE
, certfile("test.crt"), certfile("test.key"));
568 SEASTAR_TEST_CASE(test_simple_x509_client_server_client_auth_with_dn_callback
) {
569 // In addition to the above test, the certificate's subject and issuer
570 // Distinguished Names (DNs) will be checked for the occurrence of a specific
571 // substring (in this case, the test.scylladb.org url)
572 return run_echo_test(message
, 20, certfile("catest.pem"), "test.scylladb.org", certfile("test.crt"), certfile("test.key"), tls::client_auth::REQUIRE
, certfile("test.crt"), certfile("test.key"), true, true, [](tls::session_type t
, sstring subject
, sstring issuer
) {
573 BOOST_REQUIRE(t
== tls::session_type::CLIENT
);
574 BOOST_REQUIRE(subject
.find("test.scylladb.org") != sstring::npos
);
575 BOOST_REQUIRE(issuer
.find("test.scylladb.org") != sstring::npos
);
579 SEASTAR_TEST_CASE(test_simple_x509_client_server_client_auth_dn_callback_fails
) {
580 // Test throwing an exception from within the Distinguished Names callback
581 return run_echo_test(message
, 20, certfile("catest.pem"), "test.scylladb.org", certfile("test.crt"), certfile("test.key"), tls::client_auth::REQUIRE
, certfile("test.crt"), certfile("test.key"), true, true, [](tls::session_type
, sstring
, sstring
) {
582 throw tls::verification_error("to test throwing from within the callback");
584 BOOST_FAIL("Should have gotten a verification_error exception");
585 }).handle_exception([](auto) {
590 SEASTAR_TEST_CASE(test_many_large_message_x509_client_server
) {
591 // Make sure we load our own auth trust pem file, otherwise our certs
593 // Must match expected name with cert CA or give empty name to ignore
595 sstring msg
= uninitialized_string(4 * 1024 * 1024);
596 for (size_t i
= 0; i
< msg
.size(); ++i
) {
597 msg
[i
] = '0' + char(i
% 30);
599 // Sending a huge-ish message a and immediately closing the session (see params)
600 // provokes case where tls::vec_push entered race and asserted on broken IO state
602 auto range
= boost::irange(0, 20);
603 return do_for_each(range
, [msg
= std::move(msg
)](auto) {
604 return run_echo_test(std::move(msg
), 1, certfile("catest.pem"), "test.scylladb.org", certfile("test.crt"), certfile("test.key"), tls::client_auth::NONE
, {}, {}, false);
608 SEASTAR_THREAD_TEST_CASE(test_close_timout
) {
609 tls::credentials_builder b
;
611 b
.set_x509_key_file(certfile("test.crt"), certfile("test.key"), tls::x509_crt_format::PEM
).get();
612 b
.set_x509_trust_file(certfile("catest.pem"), tls::x509_crt_format::PEM
).get();
614 b
.set_system_trust().get();
616 auto creds
= b
.build_certificate_credentials();
617 auto serv
= b
.build_server_credentials();
621 class my_loopback_connected_socket_impl
: public loopback_connected_socket_impl
{
626 my_loopback_connected_socket_impl(semaphore
& s
, lw_shared_ptr
<loopback_buffer
> tx
, lw_shared_ptr
<loopback_buffer
> rx
)
627 : loopback_connected_socket_impl(tx
, rx
)
630 ~my_loopback_connected_socket_impl() {
633 class my_sink_impl
: public data_sink_impl
{
636 my_loopback_connected_socket_impl
& _impl
;
638 my_sink_impl(data_sink sink
, my_loopback_connected_socket_impl
& impl
)
639 : _sink(std::move(sink
))
642 future
<> flush() override
{
643 return _sink
.flush();
645 using data_sink_impl::put
;
646 future
<> put(net::packet p
) override
{
647 if (std::exchange(_impl
._close
, false)) {
648 return _p
.get_future().then([this, p
= std::move(p
)]() mutable {
649 return put(std::move(p
));
652 return _sink
.put(std::move(p
));
654 future
<> close() override
{
656 return make_ready_future
<>();
659 data_sink
sink() override
{
660 return data_sink(std::make_unique
<my_sink_impl
>(loopback_connected_socket_impl::sink(), *this));
664 auto constexpr iterations
= 500;
666 for (int i
= 0; i
< iterations
; ++i
) {
667 auto b1
= ::make_lw_shared
<loopback_buffer
>(nullptr, loopback_buffer::type::SERVER_TX
);
668 auto b2
= ::make_lw_shared
<loopback_buffer
>(nullptr, loopback_buffer::type::CLIENT_TX
);
669 auto ssi
= std::make_unique
<my_loopback_connected_socket_impl
>(sem
, b1
, b2
);
670 auto csi
= std::make_unique
<my_loopback_connected_socket_impl
>(sem
, b2
, b1
);
675 auto ss
= tls::wrap_server(serv
, connected_socket(std::move(ssi
))).get0();
676 auto cs
= tls::wrap_client(creds
, connected_socket(std::move(csi
))).get0();
678 auto os
= cs
.output().detach();
679 auto is
= ss
.input();
681 auto f1
= os
.put(temporary_buffer
<char>(10));
686 // block further writes
691 sem
.wait(2 * iterations
).get();
694 SEASTAR_THREAD_TEST_CASE(test_reload_certificates
) {
697 namespace fs
= std::filesystem
;
699 // copy the wrong certs. We don't trust these
700 // blocking calls, but this is a test and seastar does not have a copy
701 // util and I am lazy...
702 fs::copy_file(certfile("other.crt"), tmp
.path() / "test.crt");
703 fs::copy_file(certfile("other.key"), tmp
.path() / "test.key");
705 auto cert
= (tmp
.path() / "test.crt").native();
706 auto key
= (tmp
.path() / "test.key").native();
707 std::unordered_set
<sstring
> changed
;
710 tls::credentials_builder b
;
711 b
.set_x509_key_file(cert
, key
, tls::x509_crt_format::PEM
).get();
714 auto certs
= b
.build_reloadable_server_credentials([&](const std::unordered_set
<sstring
>& files
, std::exception_ptr ep
) {
718 changed
.insert(files
.begin(), files
.end());
719 if (changed
.count(cert
) && changed
.count(key
)) {
724 ::listen_options opts
;
725 opts
.reuse_address
= true;
726 auto addr
= ::make_ipv4_address( {0x7f000001, 4712});
727 auto server
= tls::listen(certs
, addr
, opts
);
729 tls::credentials_builder b2
;
730 b2
.set_x509_trust_file(certfile("catest.pem"), tls::x509_crt_format::PEM
).get();
733 auto sa
= server
.accept();
734 auto c
= tls::connect(b2
.build_certificate_credentials(), addr
).get0();
736 auto in
= s
.connection
.input();
738 output_stream
<char> out(c
.output().detach(), 4096);
741 out
.write("apa").get();
742 auto f
= out
.flush();
747 BOOST_FAIL("should not reach");
748 } catch (tls::verification_error
&) {
758 BOOST_FAIL("should not reach");
766 } catch (tls::verification_error
&) {
771 // copy the right (trusted) certs over the old ones.
772 fs::copy_file(certfile("test.crt"), tmp
.path() / "test0.crt");
773 fs::copy_file(certfile("test.key"), tmp
.path() / "test0.key");
775 rename_file((tmp
.path() / "test0.crt").native(), (tmp
.path() / "test.crt").native()).get();
776 rename_file((tmp
.path() / "test0.key").native(), (tmp
.path() / "test.key").native()).get();
778 p
.get_future().get();
780 // now it should work
782 auto sa
= server
.accept();
783 auto c
= tls::connect(b2
.build_certificate_credentials(), addr
).get0();
785 auto in
= s
.connection
.input();
787 output_stream
<char> out(c
.output().detach(), 4096);
789 out
.write("apa").get();
790 auto f
= out
.flush();
791 auto buf
= in
.read().get0();
794 in
.read().get(); // ignore - just want eof
797 BOOST_CHECK_EQUAL(sstring(buf
.begin(), buf
.end()), "apa");
801 SEASTAR_THREAD_TEST_CASE(test_reload_broken_certificates
) {
804 namespace fs
= std::filesystem
;
806 fs::copy_file(certfile("test.crt"), tmp
.path() / "test.crt");
807 fs::copy_file(certfile("test.key"), tmp
.path() / "test.key");
809 auto cert
= (tmp
.path() / "test.crt").native();
810 auto key
= (tmp
.path() / "test.key").native();
811 std::unordered_set
<sstring
> changed
;
814 tls::credentials_builder b
;
815 b
.set_x509_key_file(cert
, key
, tls::x509_crt_format::PEM
).get();
818 queue
<std::exception_ptr
> q(10);
820 auto certs
= b
.build_reloadable_server_credentials([&](const std::unordered_set
<sstring
>& files
, std::exception_ptr ep
) {
822 q
.push(std::move(ep
));
825 changed
.insert(files
.begin(), files
.end());
826 if (changed
.count(cert
) && changed
.count(key
)) {
831 // very intentionally use blocking calls. We want all our modifications to happen
832 // before any other continuation is allowed to process.
837 // should get one or two exceptions
838 q
.pop_eventually().get();
840 fs::copy_file(certfile("test.crt"), cert
);
841 fs::copy_file(certfile("test.key"), key
);
843 // now it should reload
844 p
.get_future().get();