diff options
author | HÃ¥vard Pettersen <havardpe@gmail.com> | 2018-09-28 13:54:54 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-28 13:54:54 +0200 |
commit | 54aa87b069c7717377003b127473e17fe447ad96 (patch) | |
tree | 2d9aa98e94dd71703f4d9e7466e5c3a0e52aaef4 | |
parent | 8a96354cead8df4d7810ec1336a374ce89645c9d (diff) | |
parent | 8d927c030a6d5bab4c1839b03f56ee987eb79baa (diff) |
Merge pull request #7131 from vespa-engine/havardpe/fnet-mixed-mode-tls
Havardpe/fnet mixed mode tls
16 files changed, 256 insertions, 12 deletions
diff --git a/fnet/src/tests/frt/rpc/CMakeLists.txt b/fnet/src/tests/frt/rpc/CMakeLists.txt index 2bacd37686a..6aa2e073e4e 100644 --- a/fnet/src/tests/frt/rpc/CMakeLists.txt +++ b/fnet/src/tests/frt/rpc/CMakeLists.txt @@ -8,6 +8,8 @@ vespa_add_executable(fnet_invoke_test_app TEST vespa_add_test(NAME fnet_invoke_test_app COMMAND fnet_invoke_test_app) vespa_add_test(NAME fnet_invoke_test_app_xor COMMAND fnet_invoke_test_app ENVIRONMENT "CRYPTOENGINE=xor") vespa_add_test(NAME fnet_invoke_test_app_tls COMMAND fnet_invoke_test_app ENVIRONMENT "CRYPTOENGINE=tls") +vespa_add_test(NAME fnet_invoke_test_app_tls_maybe_yes COMMAND fnet_invoke_test_app ENVIRONMENT "CRYPTOENGINE=tls_maybe_yes") +vespa_add_test(NAME fnet_invoke_test_app_tls_maybe_no COMMAND fnet_invoke_test_app ENVIRONMENT "CRYPTOENGINE=tls_maybe_no") vespa_add_executable(fnet_detach_return_invoke_test_app TEST SOURCES detach_return_invoke.cpp @@ -24,6 +26,8 @@ vespa_add_executable(fnet_session_test_app TEST vespa_add_test(NAME fnet_session_test_app COMMAND fnet_session_test_app) vespa_add_test(NAME fnet_session_test_app_xor COMMAND fnet_session_test_app ENVIRONMENT "CRYPTOENGINE=xor") vespa_add_test(NAME fnet_session_test_app_tls COMMAND fnet_session_test_app ENVIRONMENT "CRYPTOENGINE=tls") +vespa_add_test(NAME fnet_session_test_app_tls_maybe_yes COMMAND fnet_session_test_app ENVIRONMENT "CRYPTOENGINE=tls_maybe_yes") +vespa_add_test(NAME fnet_session_test_app_tls_maybe_no COMMAND fnet_session_test_app ENVIRONMENT "CRYPTOENGINE=tls_maybe_no") vespa_add_executable(fnet_sharedblob_test_app TEST SOURCES sharedblob.cpp diff --git a/fnet/src/tests/frt/rpc/my_crypto_engine.hpp b/fnet/src/tests/frt/rpc/my_crypto_engine.hpp index 6cd8d47e917..51811f4f70c 100644 --- a/fnet/src/tests/frt/rpc/my_crypto_engine.hpp +++ b/fnet/src/tests/frt/rpc/my_crypto_engine.hpp @@ -1,6 +1,8 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/vespalib/net/crypto_engine.h> #include <vespa/vespalib/net/tls/tls_crypto_engine.h> +#include <vespa/vespalib/net/tls/maybe_tls_crypto_engine.h> #include <vespa/vespalib/test/make_tls_options_for_testing.h> vespalib::CryptoEngine::SP my_crypto_engine() { @@ -16,6 +18,14 @@ vespalib::CryptoEngine::SP my_crypto_engine() { } else if (engine == "tls") { fprintf(stderr, "crypto engine: tls\n"); return std::make_shared<vespalib::TlsCryptoEngine>(vespalib::test::make_tls_options_for_testing()); + } else if (engine == "tls_maybe_yes") { + fprintf(stderr, "crypto engine: tls client, mixed server\n"); + auto tls = std::make_shared<vespalib::TlsCryptoEngine>(vespalib::test::make_tls_options_for_testing()); + return std::make_shared<vespalib::MaybeTlsCryptoEngine>(std::move(tls), true); + } else if (engine == "tls_maybe_no") { + fprintf(stderr, "crypto engine: null client, mixed server\n"); + auto tls = std::make_shared<vespalib::TlsCryptoEngine>(vespalib::test::make_tls_options_for_testing()); + return std::make_shared<vespalib::MaybeTlsCryptoEngine>(std::move(tls), false); } TEST_FATAL(("invalid crypto engine: " + engine).c_str()); abort(); diff --git a/searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp b/searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp index b85e706397d..9dd4e81f91a 100644 --- a/searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp +++ b/searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp @@ -297,7 +297,7 @@ Fdispatch::Init() LOG(debug, "Creating FNET transport"); - _transport = std::make_unique<FNET_Transport>(std::make_shared<vespalib::NullCryptoEngine>(), _config->transportthreads); // disable encryption + _transport = std::make_unique<FNET_Transport>(_config->transportthreads); // grab node slowness limit defaults diff --git a/searchlib/src/vespa/searchlib/engine/transportserver.cpp b/searchlib/src/vespa/searchlib/engine/transportserver.cpp index bc739a7bf48..0fa7d44bbad 100644 --- a/searchlib/src/vespa/searchlib/engine/transportserver.cpp +++ b/searchlib/src/vespa/searchlib/engine/transportserver.cpp @@ -359,7 +359,7 @@ TransportServer::TransportServer(SearchServer &searchServer, : _searchServer(searchServer), _docsumServer(docsumServer), _monitorServer(monitorServer), - _transport(std::make_shared<vespalib::NullCryptoEngine>(), 1), // disable encryption + _transport(), _ready(false), _failed(false), _doListen(true), diff --git a/vespalib/src/vespa/vespalib/net/crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/crypto_engine.cpp index 3dd800dadf0..d45b0cea1c5 100644 --- a/vespalib/src/vespa/vespalib/net/crypto_engine.cpp +++ b/vespalib/src/vespa/vespalib/net/crypto_engine.cpp @@ -9,6 +9,7 @@ #include <vespa/vespalib/net/tls/transport_security_options.h> #include <vespa/vespalib/net/tls/transport_security_options_reading.h> #include <vespa/vespalib/net/tls/tls_crypto_engine.h> +#include <vespa/vespalib/net/tls/maybe_tls_crypto_engine.h> #include <vespa/vespalib/data/smart_buffer.h> #include <assert.h> @@ -175,10 +176,22 @@ CryptoEngine::SP create_default_crypto_engine() { if (cfg_file.empty()) { return std::make_shared<NullCryptoEngine>(); } - LOG(debug, "Using TLS crypto engine with config file '%s'", cfg_file.c_str()); auto tls_opts = net::tls::read_options_from_json_file(cfg_file); - return std::make_shared<TlsCryptoEngine>(*tls_opts); + auto tls = std::make_shared<TlsCryptoEngine>(*tls_opts); + env = getenv("VESPA_TLS_INSECURE_MIXED_MODE"); + vespalib::string mixed_mode = env ? env : ""; + if (mixed_mode == "plaintext_client_mixed_server") { + LOG(debug, "tls insecure mixed-mode activated: plaintext client, mixed server"); + return std::make_shared<MaybeTlsCryptoEngine>(std::move(tls), false); + } else if (mixed_mode == "tls_client_mixed_server") { + LOG(debug, "tls insecure mixed-mode activated: tls client, mixed server"); + return std::make_shared<MaybeTlsCryptoEngine>(std::move(tls), true); + } else if (!mixed_mode.empty()) { + LOG(warning, "bad tls insecure mixed-mode specified: '%s' (ignoring)", + mixed_mode.c_str()); + } + return tls; } } // namespace vespalib::<unnamed> diff --git a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt index 17abd82366d..87b7cff1c13 100644 --- a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt @@ -4,9 +4,12 @@ vespa_add_library(vespalib_vespalib_net_tls OBJECT crypto_codec.cpp crypto_codec_adapter.cpp crypto_exception.cpp + maybe_tls_crypto_engine.cpp + maybe_tls_crypto_socket.cpp protocol_snooping.cpp tls_context.cpp tls_crypto_engine.cpp + tls_crypto_socket.cpp transport_security_options.cpp transport_security_options_reading.cpp DEPENDS diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp index 494919f449f..28aede69d22 100644 --- a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp +++ b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp @@ -56,6 +56,16 @@ CryptoCodecAdapter::flush_all() return res; } +void +CryptoCodecAdapter::inject_read_data(const char *buf, size_t len) +{ + if (len > 0) { + auto dst = _input.reserve(len); + memcpy(dst.data, buf, len); + _input.commit(len); + } +} + CryptoSocket::HandshakeResult CryptoCodecAdapter::handshake() { diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h index f17693cabff..af2db3525d3 100644 --- a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h +++ b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h @@ -2,7 +2,7 @@ #pragma once -#include <vespa/vespalib/net/crypto_socket.h> +#include "tls_crypto_socket.h" #include <vespa/vespalib/net/socket_handle.h> #include <vespa/vespalib/data/smart_buffer.h> #include "crypto_codec.h" @@ -12,11 +12,8 @@ namespace vespalib::net::tls { /** * Component adapting an underlying CryptoCodec to the CryptoSocket * interface by performing buffer and socket management. - * - * NOTE: initial implementation is for functionality/proof-of-concept - * purposes, not performance. **/ -class CryptoCodecAdapter : public CryptoSocket +class CryptoCodecAdapter : public TlsCryptoSocket { private: SmartBuffer _input; @@ -34,6 +31,7 @@ private: public: CryptoCodecAdapter(SocketHandle socket, std::unique_ptr<CryptoCodec> codec) : _input(64 * 1024), _output(64 * 1024), _socket(std::move(socket)), _codec(std::move(codec)) {} + void inject_read_data(const char *buf, size_t len) override; int get_fd() const override { return _socket.get(); } HandshakeResult handshake() override; size_t min_read_buffer_size() const override { return _codec->min_decode_buffer_size(); } diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.cpp new file mode 100644 index 00000000000..891f8cdab23 --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "maybe_tls_crypto_engine.h" +#include "maybe_tls_crypto_socket.h" + +namespace vespalib { + +CryptoSocket::UP +MaybeTlsCryptoEngine::create_crypto_socket(SocketHandle socket, bool is_server) +{ + if (is_server) { + return std::make_unique<MaybeTlsCryptoSocket>(std::move(socket), _tls_engine); + } else if (_use_tls_when_client) { + return _tls_engine->create_crypto_socket(std::move(socket), false); + } else { + return _null_engine->create_crypto_socket(std::move(socket), false); + } +} + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h new file mode 100644 index 00000000000..8e76460231c --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h @@ -0,0 +1,34 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <memory> +#include "tls_crypto_engine.h" +#include <vespa/vespalib/net/crypto_engine.h> + +namespace vespalib { + +/** + * A crypto engine that supports both tls encrypted connections and + * unencrypted connections. The use of tls for incoming connections is + * auto-detected using clever heuristics. The use of tls for outgoing + * connections is controlled by the use_tls_when_client flag given to + * the constructor. + **/ +class MaybeTlsCryptoEngine : public CryptoEngine +{ +private: + std::shared_ptr<NullCryptoEngine> _null_engine; + std::shared_ptr<TlsCryptoEngine> _tls_engine; + bool _use_tls_when_client; + +public: + MaybeTlsCryptoEngine(std::shared_ptr<TlsCryptoEngine> tls_engine, + bool use_tls_when_client) + : _null_engine(std::make_shared<NullCryptoEngine>()), + _tls_engine(std::move(tls_engine)), + _use_tls_when_client(use_tls_when_client) {} + CryptoSocket::UP create_crypto_socket(SocketHandle socket, bool is_server) override; +}; + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.cpp b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.cpp new file mode 100644 index 00000000000..1e8fbce13eb --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.cpp @@ -0,0 +1,90 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "maybe_tls_crypto_socket.h" +#include "tls_crypto_socket.h" +#include "protocol_snooping.h" +#include <vespa/vespalib/data/smart_buffer.h> + +namespace vespalib { + +namespace { + +class MyCryptoSocket : public CryptoSocket +{ +private: + static constexpr size_t SNOOP_SIZE = net::tls::snooping::min_header_bytes_to_observe(); + + CryptoSocket::UP &_self; + SocketHandle _socket; + std::shared_ptr<TlsCryptoEngine> _factory; + SmartBuffer _buffer; + + bool is_blocked(ssize_t res, int error) const { + return ((res < 0) && ((error == EWOULDBLOCK) || (error == EAGAIN))); + } + + bool looksLikeTlsToMe(const char *buf) { + return (net::tls::snooping::snoop_client_hello_header(buf) == net::tls::snooping::TlsSnoopingResult::ProbablyTls); + } + +public: + MyCryptoSocket(CryptoSocket::UP &self, SocketHandle socket, std::shared_ptr<TlsCryptoEngine> tls_engine) + : _self(self), _socket(std::move(socket)), _factory(std::move(tls_engine)), _buffer(4096) + { + static_assert(SNOOP_SIZE == 8); + } + int get_fd() const override { return _socket.get(); } + HandshakeResult handshake() override { + if (_factory) { + auto dst = _buffer.reserve(SNOOP_SIZE); + ssize_t res = _socket.read(dst.data, dst.size); + if (res > 0) { + _buffer.commit(res); + } else if (!is_blocked(res, errno)) { + return HandshakeResult::FAIL; + } + auto src = _buffer.obtain(); + if (src.size < SNOOP_SIZE) { + return HandshakeResult::NEED_READ; + } + if (looksLikeTlsToMe(src.data)) { + CryptoSocket::UP &self = _self; // need copy due to self destruction + auto tls_socket = _factory->create_tls_crypto_socket(std::move(_socket), true); + tls_socket->inject_read_data(src.data, src.size); + self = std::move(tls_socket); + return self->handshake(); + } else { + _factory.reset(); + } + } + return HandshakeResult::DONE; + } + size_t min_read_buffer_size() const override { return 1; } + ssize_t read(char *buf, size_t len) override { + int drain_result = drain(buf, len); + if (drain_result != 0) { + return drain_result; + } + return _socket.read(buf, len); + } + ssize_t drain(char *buf, size_t len) override { + auto src = _buffer.obtain(); + size_t frame = std::min(len, src.size); + if (frame > 0) { + memcpy(buf, src.data, frame); + _buffer.evict(frame); + } + return frame; + } + ssize_t write(const char *buf, size_t len) override { return _socket.write(buf, len); } + ssize_t flush() override { return 0; } +}; + +} // namespace vespalib::<unnamed> + +MaybeTlsCryptoSocket::MaybeTlsCryptoSocket(SocketHandle socket, std::shared_ptr<TlsCryptoEngine> tls_engine) + : _socket(std::make_unique<MyCryptoSocket>(_socket, std::move(socket), std::move(tls_engine))) +{ +} + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.h b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.h new file mode 100644 index 00000000000..3c7963cc948 --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.h @@ -0,0 +1,35 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <memory> +#include <vespa/vespalib/net/crypto_socket.h> +#include <vespa/vespalib/net/socket_handle.h> +#include "tls_crypto_engine.h" + +namespace vespalib { + +/** + * A crypto socket for the server side of a connection that + * auto-detects whether the connection is tls encrypted or unencrypted + * using clever heuristics. The assumption is that the client side + * will send at least 8 bytes of data before expecting anything from + * the server. These 8 bytes are inspected to see if they look like + * part of a tls handshake or not. + **/ +class MaybeTlsCryptoSocket : public CryptoSocket +{ +private: + CryptoSocket::UP _socket; +public: + MaybeTlsCryptoSocket(SocketHandle socket, std::shared_ptr<TlsCryptoEngine> tls_engine); + int get_fd() const override { return _socket->get_fd(); } + HandshakeResult handshake() override { return _socket->handshake(); } + size_t min_read_buffer_size() const override { return _socket->min_read_buffer_size(); } + ssize_t read(char *buf, size_t len) override { return _socket->read(buf, len); } + ssize_t drain(char *buf, size_t len) override { return _socket->drain(buf, len); } + ssize_t write(const char *buf, size_t len) override { return _socket->write(buf, len); } + ssize_t flush() override { return _socket->flush(); } +}; + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp index 72d9eacf37c..285a53dabc5 100644 --- a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp +++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp @@ -11,8 +11,8 @@ TlsCryptoEngine::TlsCryptoEngine(net::tls::TransportSecurityOptions tls_opts) { } -CryptoSocket::UP -TlsCryptoEngine::create_crypto_socket(SocketHandle socket, bool is_server) +std::unique_ptr<TlsCryptoSocket> +TlsCryptoEngine::create_tls_crypto_socket(SocketHandle socket, bool is_server) { auto mode = is_server ? net::tls::CryptoCodec::Mode::Server : net::tls::CryptoCodec::Mode::Client; auto codec = net::tls::CryptoCodec::create_default_codec(*_tls_ctx, mode); diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h index 58fda2b3b21..b6fda7fd577 100644 --- a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h +++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h @@ -3,6 +3,7 @@ #pragma once #include <vespa/vespalib/net/crypto_engine.h> +#include "tls_crypto_socket.h" #include "transport_security_options.h" #include "tls_context.h" @@ -17,7 +18,10 @@ private: std::unique_ptr<net::tls::TlsContext> _tls_ctx; public: TlsCryptoEngine(net::tls::TransportSecurityOptions tls_opts); - CryptoSocket::UP create_crypto_socket(SocketHandle socket, bool is_server) override; + std::unique_ptr<TlsCryptoSocket> create_tls_crypto_socket(SocketHandle socket, bool is_server); + CryptoSocket::UP create_crypto_socket(SocketHandle socket, bool is_server) override { + return create_tls_crypto_socket(std::move(socket), is_server); + } }; } // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.cpp b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.cpp new file mode 100644 index 00000000000..ee93827898e --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.cpp @@ -0,0 +1,9 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "tls_crypto_socket.h" + +namespace vespalib { + +TlsCryptoSocket::~TlsCryptoSocket() = default; + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.h b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.h new file mode 100644 index 00000000000..c98af6ee651 --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.h @@ -0,0 +1,14 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/vespalib/net/crypto_socket.h> + +namespace vespalib { + +struct TlsCryptoSocket : public CryptoSocket { + ~TlsCryptoSocket(); + virtual void inject_read_data(const char *buf, size_t len) = 0; +}; + +} // namespace vespalib |