Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,50 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// MySQL Proxy :ref:`configuration overview <config_network_filters_mysql_proxy>`.
// [#extension: envoy.filters.network.mysql_proxy]

// [#next-free-field: 4]
message MySQLProxy {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.filter.network.mysql_proxy.v1alpha1.MySQLProxy";

// Downstream SSL operational modes.
enum SSLMode {
// Do not terminate SSL session initiated by a client.
// The MySQL proxy filter will pass all encrypted and unencrypted packets to the upstream server.
DISABLE = 0;

// The MySQL proxy filter will terminate SSL session initiated by a client
// and close downstream connections that do not initiate SSL.
// The filter will mediate ``caching_sha2_password`` RSA authentication when
// the upstream MySQL server requires full authentication over the plaintext connection.
// The filter chain must use :ref:`starttls transport socket
// <envoy_v3_api_msg_extensions.transport_sockets.starttls.v3.StartTlsConfig>`.
REQUIRE = 1;

// The MySQL proxy filter will accept downstream client's encryption settings.
// If the client wants to use clear-text, Envoy will not enforce SSL encryption.
// If the client wants to use encryption, Envoy will terminate SSL and mediate
// ``caching_sha2_password`` RSA authentication when needed.
// The filter chain must use :ref:`starttls transport socket
// <envoy_v3_api_msg_extensions.transport_sockets.starttls.v3.StartTlsConfig>`.
ALLOW = 2;
}

// The human readable prefix to use when emitting :ref:`statistics
// <config_network_filters_mysql_proxy_stats>`.
string stat_prefix = 1 [(validate.rules).string = {min_len: 1}];

// [#not-implemented-hide:] The optional path to use for writing MySQL access logs.
// If the access log field is empty, access logs will not be written.
string access_log = 2;

// Controls whether to terminate SSL sessions initiated by downstream clients.
// If enabled, the filter chain must use
// :ref:`starttls transport socket <envoy_v3_api_msg_extensions.transport_sockets.starttls.v3.StartTlsConfig>`.
// Defaults to ``DISABLE``.
SSLMode downstream_ssl = 3;

// TODO: Add upstream_ssl (SSLMode) to support encrypting the connection from
// Envoy to the upstream MySQL server, similar to PostgresProxy.upstream_ssl.
// When implemented, the corresponding cluster should use
// :ref:`starttls transport socket <envoy_v3_api_msg_extensions.transport_sockets.starttls.v3.UpstreamStartTlsConfig>`.
}
12 changes: 12 additions & 0 deletions bazel/deps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ libprotobuf_mutator:
license: "Apache-2.0"
license_url: "https://github.com/google/libprotobuf-mutator/blob/v{version}/LICENSE"

libsodium:
project_name: "libsodium"
project_desc: "A modern, portable, easy-to-use crypto library for secure memory and zeroing"
project_url: "https://github.com/jedisct1/libsodium"
release_date: "2024-08-23"
use_category:
- other
extensions:
- envoy.filters.network.mysql_proxy
license: "ISC"
license_url: "https://github.com/jedisct1/libsodium/blob/{version}/LICENSE"

libsxg:
project_name: "libsxg"
project_desc: "Signed HTTP Exchange library"
Expand Down
21 changes: 21 additions & 0 deletions bazel/foreign_cc/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,27 @@ cc_library(
deps = ["unicode_icu_build"],
)

configure_make(
name = "libsodium",
configure_in_place = True,
configure_options = [
"--disable-shared",
"--enable-static",
"--disable-asm",
"--without-pthreads",
],
env = select({
"//bazel:clang_build": {
"AR": "$(AR)",
"RANLIB": "$(AR) -s",
},
"//conditions:default": {},
}),
lib_source = "@libsodium//:all",
out_static_libs = ["libsodium.a"],
tags = ["skip_on_windows"],
)

envoy_cmake(
name = "libsxg",
build_args = select({
Expand Down
7 changes: 7 additions & 0 deletions bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ def envoy_dependencies(skip_targets = []):
_aws_lc()

_aws_c_auth_testdata()
_libsodium()
_liburing()
_com_github_bazel_buildtools()
_c_ares()
Expand Down Expand Up @@ -323,6 +324,12 @@ def _aws_c_auth_testdata():
build_file = "@envoy//bazel/external:aws-c-auth.BUILD",
)

def _libsodium():
external_http_archive(
name = "libsodium",
build_file_content = BUILD_ALL_CONTENT,
)

def _liburing():
external_http_archive(
name = "liburing",
Expand Down
6 changes: 6 additions & 0 deletions bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ REPOSITORY_LOCATIONS_SPEC = dict(
strip_prefix = "aws-c-auth-{version}",
urls = ["https://github.com/awslabs/aws-c-auth/archive/refs/tags/v{version}.tar.gz"],
),
libsodium = dict(
version = "1.0.20",
sha256 = "ebb65ef6ca439333c2bb41a0c1990587288da07f6c7fd07cb3a18cc18d30ce19",
strip_prefix = "libsodium-{version}",
urls = ["https://download.libsodium.org/libsodium/releases/libsodium-{version}.tar.gz"],
),
liburing = dict(
version = "2.13",
sha256 = "618e34dbea408fc9e33d7c4babd746036dbdedf7fce2496b1178ced0f9b5b357",
Expand Down
9 changes: 9 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,15 @@ new_features:
Added :ref:`set() <config_http_filters_lua_stream_info_filter_state_wrapper>` to the Lua filter
state API, allowing Lua scripts to create and store filter state objects dynamically using
registered object factories.
- area: mysql_proxy
change: |
Added SSL termination support to the MySQL proxy filter with RSA-mediated ``caching_sha2_password``
authentication. The filter can now terminate downstream TLS connections using the
:ref:`starttls transport socket <envoy_v3_api_msg_extensions.transport_sockets.starttls.v3.StartTlsConfig>`
and transparently mediate MySQL 8.0+ ``caching_sha2_password`` full authentication by performing
RSA public key exchange on behalf of the client. Added a new
:ref:`downstream_ssl <envoy_v3_api_field_extensions.filters.network.mysql_proxy.v3.MySQLProxy.downstream_ssl>`
config option with ``DISABLE``, ``REQUIRE``, and ``ALLOW`` modes.
- area: http
change: |
Fixed an issue where filter chain execution could continue on HTTP streams that had been reset but not yet
Expand Down
3 changes: 3 additions & 0 deletions contrib/mysql_proxy/filters/network/source/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ envoy_cc_library(
"//envoy/server:filter_config_interface",
"//envoy/stats:stats_interface",
"//envoy/stats:stats_macros",
"//source/common/crypto:utility_lib",
"//source/common/network:filter_lib",
"//source/extensions/filters/network:well_known_names",
"@envoy_api//contrib/envoy/extensions/filters/network/mysql_proxy/v3:pkg_cc_proto",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
)
Expand Down Expand Up @@ -80,6 +82,7 @@ envoy_cc_library(
hdrs = ["mysql_utils.h"],
deps = [
":codec_interface",
"//bazel/foreign_cc:libsodium",
"//source/common/buffer:buffer_lib",
],
)
Expand Down
5 changes: 5 additions & 0 deletions contrib/mysql_proxy/filters/network/source/mysql_codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ constexpr uint8_t MYSQL_RESP_MORE = 0x01;
constexpr uint8_t MYSQL_RESP_AUTH_SWITCH = 0xfe;
constexpr uint8_t MYSQL_RESP_ERR = 0xff;

constexpr uint8_t MYSQL_CACHINGSHA2_FAST_AUTH_SUCCESS = 0x03;
constexpr uint8_t MYSQL_CACHINGSHA2_FULL_AUTH_REQUIRED = 0x04;
constexpr uint8_t MYSQL_REQUEST_PUBLIC_KEY = 0x02;

constexpr uint8_t EOF_MARKER = 0xfe;
constexpr uint8_t ERR_MARKER = 0xff;

Expand Down Expand Up @@ -102,6 +106,7 @@ constexpr uint8_t LENENCODINT_3BYTES = 0xfd;
constexpr uint8_t LENENCODINT_8BYTES = 0xfe;

constexpr uint32_t DEFAULT_MAX_PACKET_SIZE = (1 << 24) - 1; // 16M-1
constexpr uint32_t SSL_CONNECTION_REQUEST_PACKET_SIZE = 32;
constexpr uint8_t MIN_PROTOCOL_VERSION = 10;

constexpr char MYSQL_STR_END = '\0';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ DecodeStatus ClientLogin::parseMessage(Buffer::Instance& buffer, uint32_t len) {
return DecodeStatus::Failure;
}
setBaseClientCap(base_cap);
if (base_cap & CLIENT_SSL) {
if (len == SSL_CONNECTION_REQUEST_PACKET_SIZE && (base_cap & CLIENT_SSL)) {
return parseResponseSsl(buffer);
}
if (base_cap & CLIENT_PROTOCOL_41) {
Expand Down
4 changes: 2 additions & 2 deletions contrib/mysql_proxy/filters/network/source/mysql_config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ NetworkFilters::MySQLProxy::MySQLConfigFactory::createFilterFactoryFromProtoType

const std::string stat_prefix = fmt::format("mysql.{}", proto_config.stat_prefix());

MySQLFilterConfigSharedPtr filter_config(
std::make_shared<MySQLFilterConfig>(stat_prefix, context.scope()));
MySQLFilterConfigSharedPtr filter_config(std::make_shared<MySQLFilterConfig>(
stat_prefix, context.scope(), proto_config.downstream_ssl()));
return [filter_config](Network::FilterManager& filter_manager) -> void {
filter_manager.addFilter(std::make_shared<MySQLFilter>(filter_config));
};
Expand Down
25 changes: 22 additions & 3 deletions contrib/mysql_proxy/filters/network/source/mysql_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "contrib/mysql_proxy/filters/network/source/mysql_codec_greeting.h"
#include "contrib/mysql_proxy/filters/network/source/mysql_codec_switch_resp.h"
#include "contrib/mysql_proxy/filters/network/source/mysql_session.h"
#include "contrib/mysql_proxy/filters/network/source/mysql_utils.h"

namespace Envoy {
namespace Extensions {
Expand All @@ -21,14 +22,15 @@ class DecoderCallbacks {
virtual ~DecoderCallbacks() = default;

virtual void onProtocolError() PURE;
virtual void onNewMessage(MySQLSession::State) PURE;
virtual void onServerGreeting(ServerGreeting&) PURE;
virtual void onClientLogin(ClientLogin&) PURE;
virtual void onClientLogin(ClientLogin&, MySQLSession::State) PURE;
virtual void onClientLoginResponse(ClientLoginResponse&) PURE;
virtual void onClientSwitchResponse(ClientSwitchResponse&) PURE;
virtual void onMoreClientLoginResponse(ClientLoginResponse&) PURE;
virtual void onCommand(Command&) PURE;
virtual void onCommandResponse(CommandResponse&) PURE;
virtual void onAuthSwitchMoreClientData(std::unique_ptr<SecureBytes> data) PURE;
virtual bool onSSLRequest() PURE;
};

/**
Expand All @@ -38,16 +40,33 @@ class Decoder {
public:
virtual ~Decoder() = default;

virtual void onData(Buffer::Instance& data) PURE;
enum class Result {
ReadyForNext, // Decoder processed previous message and is ready for the next message.
Stopped // Received and processed message disrupts the current flow. Decoder stopped accepting
// data. This happens when decoder wants filter to perform some action, for example to
// call starttls transport socket to enable TLS.
};

struct PayloadMetadata {
uint8_t seq;
uint32_t len;
};

virtual Result onData(Buffer::Instance& data, bool is_upstream) PURE;
virtual MySQLSession& getSession() PURE;

const Extensions::Common::SQLUtils::SQLUtils::DecoderAttributes& getAttributes() const {
return attributes_;
}

const std::vector<PayloadMetadata>& getPayloadMetadataList() const {
return payload_metadata_list_;
}

protected:
// Decoder attributes.
Extensions::Common::SQLUtils::SQLUtils::DecoderAttributes attributes_;
std::vector<PayloadMetadata> payload_metadata_list_{};
};

using DecoderPtr = std::unique_ptr<Decoder>;
Expand Down
Loading