Skip to content

Commit

Permalink
Merge pull request #56923 from ClickHouse/cherrypick/23.10/5f9db3b248…
Browse files Browse the repository at this point in the history
…25cfc9c68d16ad3506d0fab5436438

Cherry pick #56794 to 23.10: Early disconnect if there is authentication failure with interserver secret
  • Loading branch information
robot-clickhouse-ci-2 committed Nov 17, 2023
2 parents 1253583 + 5f9db3b commit 884c287
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 12 deletions.
29 changes: 27 additions & 2 deletions src/Server/TCPHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <mutex>
#include <vector>
#include <string_view>
#include <cstring>
#include <Poco/Net/NetException.h>
#include <Poco/Net/SocketAddress.h>
#include <Poco/Util/LayeredConfiguration.h>
Expand Down Expand Up @@ -588,6 +587,21 @@ void TCPHandler::runImpl()
}
catch (const Exception & e)
{
/// Authentication failure with interserver secret
/// - early exit without trying to send the exception to the client.
/// Because the server should not try to skip (parse, decompress) the remaining packets sent by the client,
/// as it will lead to additional work and unneeded exposure to unauthenticated connections.

/// Note that the exception AUTHENTICATION_FAILED can be here in two cases:
/// 1. The authentication in receiveHello is skipped with "interserver secret",
/// postponed to receiving the query, and then failed.
/// 2. Receiving exception from a query using a table function to authenticate with another server.
/// In this case, the user is already authenticated with this server,
/// is_interserver_mode is false, and we can send the exception to the client normally.

if (is_interserver_mode && e.code() == ErrorCodes::AUTHENTICATION_FAILED)
throw;

state.io.onException();
exception.reset(e.clone());

Expand Down Expand Up @@ -1717,7 +1731,18 @@ void TCPHandler::receiveQuery()
{
client_info.interface = ClientInfo::Interface::TCP_INTERSERVER;
#if USE_SSL
String cluster_secret = server.context()->getCluster(cluster)->getSecret();

String cluster_secret;
try
{
cluster_secret = server.context()->getCluster(cluster)->getSecret();
}
catch (const Exception & e)
{
auto exception = Exception::createRuntime(ErrorCodes::AUTHENTICATION_FAILED, e.message());
session->onAuthenticationFailure(/* user_name= */ std::nullopt, socket().peerAddress(), exception);
throw exception; /// NOLINT
}

if (salt.empty() || cluster_secret.empty())
{
Expand Down
14 changes: 4 additions & 10 deletions tests/integration/test_distributed_inter_server_secret/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,26 +304,20 @@ def test_secure_insert_buffer_async():


def test_secure_disagree():
with pytest.raises(
QueryRuntimeException, match=".*Interserver authentication failed.*"
):
with pytest.raises(QueryRuntimeException):
n1.query("SELECT * FROM dist_secure_disagree")


def test_secure_disagree_insert():
n1.query("TRUNCATE TABLE data")
n1.query("INSERT INTO dist_secure_disagree SELECT * FROM numbers(2)")
with pytest.raises(
QueryRuntimeException, match=".*Interserver authentication failed.*"
):
with pytest.raises(QueryRuntimeException):
n1.query(
"SYSTEM FLUSH DISTRIBUTED ON CLUSTER secure_disagree dist_secure_disagree"
)
# check the the connection will be re-established
# check that the connection will be re-established
# IOW that we will not get "Unknown BlockInfo field"
with pytest.raises(
QueryRuntimeException, match=".*Interserver authentication failed.*"
):
with pytest.raises(QueryRuntimeException):
assert int(n1.query("SELECT count() FROM dist_secure_disagree")) == 0


Expand Down

0 comments on commit 884c287

Please sign in to comment.