mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 23:52:03 +00:00
Add SSL authentication to the native protocol
This commit is contained in:
parent
44531e5f85
commit
eddda2eb73
@ -10,6 +10,8 @@
|
||||
#include <Poco/Net/NetException.h>
|
||||
#include <Poco/Net/SocketAddress.h>
|
||||
#include <Poco/Util/LayeredConfiguration.h>
|
||||
#include <Poco/Net/SecureStreamSocket.h>
|
||||
#include <Poco/Net/SecureStreamSocketImpl.h>
|
||||
#include <Common/CurrentThread.h>
|
||||
#include <Common/Stopwatch.h>
|
||||
#include <Common/NetException.h>
|
||||
@ -1224,6 +1226,20 @@ void TCPHandler::receiveHello()
|
||||
|
||||
session = makeSession();
|
||||
auto & client_info = session->getClientInfo();
|
||||
|
||||
/// Authentification with SSL user certificate
|
||||
if (dynamic_cast<Poco::Net::SecureStreamSocketImpl*>(socket().impl()))
|
||||
{
|
||||
Poco::Net::SecureStreamSocket secure_socket(socket());
|
||||
if (secure_socket.havePeerCertificate())
|
||||
{
|
||||
session->authenticate(
|
||||
SSLCertificateCredentials{user, secure_socket.peerCertificate().commonName()},
|
||||
getClientAddress(client_info));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
session->authenticate(user, password, getClientAddress(client_info));
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
You have to configure certificate to enable this interface.
|
||||
See the openSSL section below.
|
||||
-->
|
||||
<!-- <tcp_port_secure>9440</tcp_port_secure> -->
|
||||
<tcp_port_secure>9440</tcp_port_secure>
|
||||
|
||||
<!-- Used with https_port and tcp_port_secure. Full ssl options list: https://github.com/ClickHouse-Extras/poco/blob/master/NetSSL_OpenSSL/include/Poco/Net/SSLManager.h#L71 -->
|
||||
<openSSL replace="replace">
|
||||
|
@ -1,9 +1,12 @@
|
||||
import pytest
|
||||
from helpers.client import Client
|
||||
from helpers.cluster import ClickHouseCluster
|
||||
from helpers.ssl_context import WrapSSLContextWithSNI
|
||||
import urllib.request, urllib.parse
|
||||
import ssl
|
||||
import os.path
|
||||
from os import remove
|
||||
|
||||
|
||||
# The test cluster is configured with certificate for that host name, see 'server-ext.cnf'.
|
||||
# The client have to verify server certificate against that name. Client uses SNI
|
||||
@ -66,6 +69,54 @@ def execute_query_https(
|
||||
return response.decode("utf-8")
|
||||
|
||||
|
||||
config = """<clickhouse>
|
||||
<openSSL>
|
||||
<client>
|
||||
<verificationMode>none</verificationMode>
|
||||
|
||||
<certificateFile>{certificateFile}</certificateFile>
|
||||
<privateKeyFile>{privateKeyFile}</privateKeyFile>
|
||||
<caConfig>{caConfig}</caConfig>
|
||||
|
||||
<invalidCertificateHandler>
|
||||
<name>AcceptCertificateHandler</name>
|
||||
</invalidCertificateHandler>
|
||||
</client>
|
||||
</openSSL>
|
||||
</clickhouse>"""
|
||||
|
||||
|
||||
def execute_query_native(node, query, user, cert_name):
|
||||
|
||||
config_path = f"{SCRIPT_DIR}/configs/client.xml"
|
||||
|
||||
formatted = config.format(
|
||||
certificateFile=f"{SCRIPT_DIR}/certs/{cert_name}-cert.pem",
|
||||
privateKeyFile=f"{SCRIPT_DIR}/certs/{cert_name}-key.pem",
|
||||
caConfig=f"{SCRIPT_DIR}/certs/ca-cert.pem",
|
||||
)
|
||||
|
||||
file = open(config_path, "w")
|
||||
file.write(formatted)
|
||||
file.close()
|
||||
|
||||
client = Client(
|
||||
node.ip_address,
|
||||
9440,
|
||||
command=cluster.client_bin_path,
|
||||
secure=True,
|
||||
config=config_path,
|
||||
)
|
||||
|
||||
try:
|
||||
result = client.query(query, user=user)
|
||||
remove(config_path)
|
||||
return result
|
||||
except:
|
||||
remove(config_path)
|
||||
raise
|
||||
|
||||
|
||||
def test_https():
|
||||
assert (
|
||||
execute_query_https("SELECT currentUser()", user="john", cert_name="client1")
|
||||
@ -81,6 +132,27 @@ def test_https():
|
||||
)
|
||||
|
||||
|
||||
def test_native():
|
||||
assert (
|
||||
execute_query_native(
|
||||
instance, "SELECT currentUser()", user="john", cert_name="client1"
|
||||
)
|
||||
== "john\n"
|
||||
)
|
||||
assert (
|
||||
execute_query_native(
|
||||
instance, "SELECT currentUser()", user="lucy", cert_name="client2"
|
||||
)
|
||||
== "lucy\n"
|
||||
)
|
||||
assert (
|
||||
execute_query_native(
|
||||
instance, "SELECT currentUser()", user="lucy", cert_name="client3"
|
||||
)
|
||||
== "lucy\n"
|
||||
)
|
||||
|
||||
|
||||
def test_https_wrong_cert():
|
||||
# Wrong certificate: different user's certificate
|
||||
with pytest.raises(Exception) as err:
|
||||
@ -107,6 +179,23 @@ def test_https_wrong_cert():
|
||||
)
|
||||
|
||||
|
||||
def test_native_wrong_cert():
|
||||
# Wrong certificate: different user's certificate
|
||||
with pytest.raises(Exception) as err:
|
||||
execute_query_native(
|
||||
instance, "SELECT currentUser()", user="john", cert_name="client2"
|
||||
)
|
||||
assert "AUTHENTICATION_FAILED" in str(err.value)
|
||||
|
||||
# Wrong certificate: self-signed certificate.
|
||||
# In this case clickhouse-client itself will throw an error
|
||||
with pytest.raises(Exception) as err:
|
||||
execute_query_native(
|
||||
instance, "SELECT currentUser()", user="john", cert_name="wrong"
|
||||
)
|
||||
assert "UNKNOWN_CA" in str(err.value)
|
||||
|
||||
|
||||
def test_https_non_ssl_auth():
|
||||
# Users with non-SSL authentication are allowed, in this case we can skip sending a client certificate at all (because "verificationMode" is set to "relaxed").
|
||||
# assert execute_query_https("SELECT currentUser()", user="peter", enable_ssl_auth=False) == "peter\n"
|
||||
|
Loading…
Reference in New Issue
Block a user