Skip to content
Merged
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
8 changes: 8 additions & 0 deletions clickhouse-client/src/test/resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ openssl x509 -req -in server.csr -CA myCA.crt -CAkey myCA.key -CAcreateserial -o
openssl req -nodes -subj "/CN=me" -newkey rsa:2048 -keyout client.key -out client.csr
openssl x509 -req -in client.csr -out client.crt -CAcreateserial -CA myCA.crt -CAkey myCA.key -days 36500
```

### Some_user

```bash
openssl req -nodes -subj "/CN=some_user" -newkey rsa:2048 -keyout some_user.key -out some_user.csr
openssl x509 -req -in some_user.csr -out some_user.crt -CAcreateserial -CA marsnet_ca.crt -CAkey marsnet_ca.key -days 36500

```
17 changes: 17 additions & 0 deletions clickhouse-client/src/test/resources/some_user.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICtDCCAZwCFBbI6UQK2g1r8o4XRXu+9wvQBHmnMA0GCSqGSIb3DQEBCwUAMBcx
FTATBgNVBAMMDGxvY2FsaG9zdCBDQTAgFw0yNDEwMTAxNjM3MzhaGA8yMTI0MDkx
NjE2MzczOFowFDESMBAGA1UEAwwJc29tZV91c2VyMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA68bBZlvBT64suwLa61eob9roTVXlJQmB9tGvX2cnJacP
NBx2h6W8Ow43doRLBRt32SopV06O1i2c0L84pRoliJcGrUhKyxAsVxVv11mFd4qg
962TeYe3VawSKK2w83GNfVhjQFwuNEDuzJT0I7J0jH/uNclMxAtFZNkKVMA2GOK2
c3Pib8zCmqITWAX5XXWUUvS0LWsASaBAEVh4R7StYbDl0L3VeiHCw6fKpdevVfw5
eDb+KuwMUOCPak0v31izEsXtcAyc7hxEZLfUMA+00zAdUENTC38GOJNTqirg0YmD
+wxPdp3quWwkF/b831UTczAHkK7GP3swPjfciMN8nwIDAQABMA0GCSqGSIb3DQEB
CwUAA4IBAQB+1poCA9p/XyKf5jxnAkaZQzoRW+fNqZvz8Eld2gGLqw7ZZUiBW6zo
d4aCAeuNehw5zJEOf1ew5EZzdWYRdxXUarjs3HOSQalfYTS8HqNI19sgWYD6Zcx+
sygJqswtplvPAB6phk9zyhQDLFNuJ8dp28xRgGuywYtVMnvLG1wapPf/fnqkRcOW
yTBS4BBvtmzKPzMMZl/qB4Ol/STgVphceMFmI71HQQFUPb56E5tAQ+m3fezjdAJ2
gZ+/LsApHLwhEV0ZGyIe/MNx0nDrkfWYWa7BsqvG6uuxyPXxgXSQofNjJN+RahRm
oHREhAYRL40BS1F20aLRFRupzLJngLBh
-----END CERTIFICATE-----
15 changes: 15 additions & 0 deletions clickhouse-client/src/test/resources/some_user.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICWTCCAUECAQAwFDESMBAGA1UEAwwJc29tZV91c2VyMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA68bBZlvBT64suwLa61eob9roTVXlJQmB9tGvX2cn
JacPNBx2h6W8Ow43doRLBRt32SopV06O1i2c0L84pRoliJcGrUhKyxAsVxVv11mF
d4qg962TeYe3VawSKK2w83GNfVhjQFwuNEDuzJT0I7J0jH/uNclMxAtFZNkKVMA2
GOK2c3Pib8zCmqITWAX5XXWUUvS0LWsASaBAEVh4R7StYbDl0L3VeiHCw6fKpdev
Vfw5eDb+KuwMUOCPak0v31izEsXtcAyc7hxEZLfUMA+00zAdUENTC38GOJNTqirg
0YmD+wxPdp3quWwkF/b831UTczAHkK7GP3swPjfciMN8nwIDAQABoAAwDQYJKoZI
hvcNAQELBQADggEBAA6cpW7rdV0a8FDxEBfZoStJPEVUisqS5pUT43UjFJ7M55kC
LGQ9Vl2Ua0nA4BwX5Le/IWVwwnhnnIJWvoPEbka9TWBVGujOPvt/WwBbEN2yHgGD
QgFrIq/zOaFVj3J3EuJtIXL2jOylDK14j+2k4MN4OJobVtQhyUHpmRTPgq4EVJIw
/PU6Lltgr2V4pTs3m9Ey2pIHF04HQIzr6Tt6MRJkKGEYWvOZlYuCbXA5bPLMyq5g
rs0kC1DMF5C3VsBND8oGQt0ULbc2AQy6AFJegdD/ZT+d4eeh+ejymc0nmB+kbxaM
tAxp2yTsRKUsGu7TBeMY1DxoP1xG5lAHkGznESg=
-----END CERTIFICATE REQUEST-----
28 changes: 28 additions & 0 deletions clickhouse-client/src/test/resources/some_user.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDrxsFmW8FPriy7
AtrrV6hv2uhNVeUlCYH20a9fZyclpw80HHaHpbw7Djd2hEsFG3fZKilXTo7WLZzQ
vzilGiWIlwatSErLECxXFW/XWYV3iqD3rZN5h7dVrBIorbDzcY19WGNAXC40QO7M
lPQjsnSMf+41yUzEC0Vk2QpUwDYY4rZzc+JvzMKaohNYBflddZRS9LQtawBJoEAR
WHhHtK1hsOXQvdV6IcLDp8ql169V/Dl4Nv4q7AxQ4I9qTS/fWLMSxe1wDJzuHERk
t9QwD7TTMB1QQ1MLfwY4k1OqKuDRiYP7DE92neq5bCQX9vzfVRNzMAeQrsY/ezA+
N9yIw3yfAgMBAAECggEAG+dVD976al/ehbAepkKkub7fxk33mrdc17qqNYbDlcux
2297lwMw2zu2qa1EzvvDZoKnC4ujEPWrUkiHP4Ga1pGqeyCL+tX/rBC/60Mk2L3M
iMjUpB9BPdTpqJch0uCUp7R/DpNk7nnkKSHUdlMSQxHdkyUEk6ESheRqj2wuGtSj
zVjXqUQa1yUiD1RZsg+o1v0Jk1bPvljMAblWslD4fBicX82MslExkuG4Kv411hss
EBfkbGAQAAVHGiQijaiJ1nuwuiiqHNgaNRUZSqnIZm6+TGCbCDbXjzRIVBzMEvfd
kB1DmpmxpzsVMDN6CK+RSpZXOgq2yTwcYAAbR5NHUQKBgQD/87ZGygLCHxzlj7T6
MpinZq8QEoB4OCqPn8gSvWI237U1Kr32KxjPHSHBu5DPDPNy6bf/upha3kqJ+53c
KVy3vZ9rEfkkXO9+5lNmlRWzG3+2TavH0SuihMQa1rK1aReyaDNG4xN8l9JxrVjx
iKo/lTdBKpSZjxC11mbbWBTPEQKBgQDr0hMoxazSMcCxYO2WXYVejVyiEueoQqSQ
BSDBqRKbv0+Gt5geM1dPxInRYCPJDhzgTbTvp33NSnl9LkhIBy/g2Z/jKZxxQB8f
LQiN+yoja6kYTagBfogCLHObdPl/VV5/hBEaffeM94KLwGfOxGXbWUzA/sdoHjxc
EcD/ncrwrwKBgQDw3H7WxPmthiviV8cegAip1/a8cDzXZTugJuPXxsKrEwBqxQs4
ojvZg/elYYYXYn+izxBpJkaDlJaenNtkOMRY4Kgp0SMcthxm1gb8DSX7g9A+VX9n
LY8bhEcrXomUMA6txGMkvUI0SIcwlMmTmmFkLl5uA80NaNV32Qi4N351kQKBgQCp
/Ic1B7D430ZAVldM4WMG8i1I4wm73zYSXq/rCT3RqQjhWiw78NRKOqkBlSSWhCbK
hRkc+4YSWlHSq28NBKk9koHPVKphdFA6v9J/zgHlAHEmhKvLT/MoZfR7pclHQTlZ
/8/4Yb71DWE77dimUin+AJP0NnN1GP53e5C8cXjdHwKBgCeCtZtNpj0O2dt4s6CC
392etUExPNvV5vyLfAlgTGI9SDPHApxomeu4wmsdBn8pIKY4apdP7MDXRgeU85Ql
DNOFDngldtgzvTS7PjyJ3JORqDdzidKnmQ0YLlLvzdoD1xQtI+YIkZoMB6dgugfC
tG/1B7aaPnbRlHz98DJBpEk4
-----END PRIVATE KEY-----
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class ClickHouseHttpProto {

public static final String HEADER_DB_PASSWORD = "X-ClickHouse-Key";

public static final String HEADER_SSL_CERT_AUTH = "x-clickhouse-ssl-certificate-auth";

/**
* Query parameter to specify the query ID.
*/
Expand Down
31 changes: 27 additions & 4 deletions client-v2/src/main/java/com/clickhouse/client/api/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,17 @@ public Builder setAccessToken(String accessToken) {
return this;
}

/**
* Makes client to use SSL Client Certificate to authenticate with server.
* Client certificate should be set as well. {@link Client.Builder#setClientCertificate(String)}
* @param useSSLAuthentication
* @return
*/
public Builder useSSLAuthentication(boolean useSSLAuthentication) {
this.configuration.put("ssl_authentication", String.valueOf(useSSLAuthentication));
return this;
}

/**
* Configures client to use build-in connection pool
* @param enable - if connection pool should be enabled
Expand Down Expand Up @@ -854,12 +865,24 @@ public Client build() {
throw new IllegalArgumentException("At least one endpoint is required");
}
// check if username and password are empty. so can not initiate client?
if (!this.configuration.containsKey("access_token") && (!this.configuration.containsKey("user") || !this.configuration.containsKey("password"))) {
throw new IllegalArgumentException("Username and password are required");
if (!this.configuration.containsKey("access_token") &&
(!this.configuration.containsKey("user") || !this.configuration.containsKey("password")) &&
!MapUtils.getFlag(this.configuration, "ssl_authentication")) {
throw new IllegalArgumentException("Username and password (or access token, or SSL authentication) are required");
}

if (this.configuration.containsKey("ssl_authentication") &&
(this.configuration.containsKey("password") || this.configuration.containsKey("access_token"))) {
throw new IllegalArgumentException("Only one of password, access token or SSL authentication can be used per client.");
}

if (this.configuration.containsKey("ssl_authentication") &&
!this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE.getKey())) {
throw new IllegalArgumentException("SSL authentication requires a client certificate");
}

if (this.configuration.containsKey(ClickHouseClientOption.TRUST_STORE) &&
this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE)) {
if (this.configuration.containsKey(ClickHouseClientOption.TRUST_STORE.getKey()) &&
this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE.getKey())) {
throw new IllegalArgumentException("Trust store and certificates cannot be used together");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.clickhouse.client.api.metrics.ServerMetrics;
import com.clickhouse.client.api.query.QueryResponse;

public class CommandResponse{
public class CommandResponse implements AutoCloseable {

private final QueryResponse response;

Expand Down Expand Up @@ -71,4 +71,9 @@ public long getWrittenBytes() {
public long getServerTime() {
return response.getServerTime();
}

@Override
public void close() throws Exception {
response.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -384,18 +384,19 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj

private void addHeaders(HttpPost req, Map<String, String> chConfig, Map<String, Object> requestConfig) {
req.addHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE.getMimeType());
if (requestConfig != null) {
if (requestConfig.containsKey(ClickHouseClientOption.FORMAT.getKey())) {
req.addHeader(ClickHouseHttpProto.HEADER_FORMAT, requestConfig.get(ClickHouseClientOption.FORMAT.getKey()));
}
if (requestConfig.containsKey(ClickHouseClientOption.QUERY_ID.getKey())) {
req.addHeader(ClickHouseHttpProto.HEADER_QUERY_ID, requestConfig.get(ClickHouseClientOption.QUERY_ID.getKey()).toString());
}
if (requestConfig.containsKey(ClickHouseClientOption.FORMAT.getKey())) {
req.addHeader(ClickHouseHttpProto.HEADER_FORMAT, requestConfig.get(ClickHouseClientOption.FORMAT.getKey()));
}
if (requestConfig.containsKey(ClickHouseClientOption.QUERY_ID.getKey())) {
req.addHeader(ClickHouseHttpProto.HEADER_QUERY_ID, requestConfig.get(ClickHouseClientOption.QUERY_ID.getKey()).toString());
}
req.addHeader(ClickHouseHttpProto.HEADER_DATABASE, chConfig.get(ClickHouseClientOption.DATABASE.getKey()));
req.addHeader(ClickHouseHttpProto.HEADER_DB_USER, chConfig.get(ClickHouseDefaults.USER.getKey()));
req.addHeader(ClickHouseHttpProto.HEADER_DB_PASSWORD, chConfig.get(ClickHouseDefaults.PASSWORD.getKey()));

if (MapUtils.getFlag(chConfig, "ssl_authentication", false)) {
req.addHeader(ClickHouseHttpProto.HEADER_SSL_CERT_AUTH, "on");
} else {
req.addHeader(ClickHouseHttpProto.HEADER_DB_PASSWORD, chConfig.get(ClickHouseDefaults.PASSWORD.getKey()));
}
if (proxyAuthHeaderValue != null) {
req.addHeader(HttpHeaders.PROXY_AUTHORIZATION, proxyAuthHeaderValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ public static boolean getFlag(Map<String, String> map, String key) {
throw new IllegalArgumentException("Invalid non-boolean value for the key '" + key + "': '" + val + "'");
}

public static boolean getFlag(Map<String, String> map, String key, boolean defaultValue) {
String val = map.get(key);
if (val == null) {
return defaultValue;
}
if (val.equalsIgnoreCase("true")) {
return true;
} else if (val.equalsIgnoreCase("false")) {
return false;
}

throw new IllegalArgumentException("Invalid non-boolean value for the key '" + key + "': '" + val + "'");
}


public static boolean getFlag(Map<String, ?> p1, Map<String, ?> p2, String key) {
Object val = p1.get(key);
if (val == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.clickhouse.client.api.ConnectionInitiationException;
import com.clickhouse.client.api.ConnectionReuseStrategy;
import com.clickhouse.client.api.ServerException;
import com.clickhouse.client.api.command.CommandResponse;
import com.clickhouse.client.api.enums.Protocol;
import com.clickhouse.client.api.enums.ProxyType;
import com.clickhouse.client.api.insert.InsertResponse;
Expand Down Expand Up @@ -42,6 +43,7 @@
import java.util.concurrent.atomic.AtomicInteger;

import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.junit.Assert.fail;

public class HttpTransportTests extends BaseIntegrationTest {

Expand Down Expand Up @@ -414,6 +416,63 @@ public void testServerSettings() {
Assert.fail("Unexpected exception", e);
}
}
}

static {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
}
@Test(groups = { "integration" })
public void testSSLAuthentication() throws Exception {
if (isCloud()) {
return; // Current test is working only with local server because of self-signed certificates.
}
ClickHouseNode server = getSecureServer(ClickHouseProtocol.HTTP);
try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
.setUsername("default")
.setPassword("")
.setRootCertificate("containers/clickhouse-server/certs/localhost.crt")
.build()) {

try (CommandResponse resp = client.execute("DROP USER IF EXISTS some_user").get()) {
}
try (CommandResponse resp = client.execute("CREATE USER some_user IDENTIFIED WITH ssl_certificate CN 'some_user'").get()) {
}
}

try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
.useSSLAuthentication(true)
.setUsername("some_user")
.setRootCertificate("containers/clickhouse-server/certs/localhost.crt")
.setClientCertificate("some_user.crt")
.setClientKey("some_user.key")
.compressServerResponse(false)
.build()) {

try (QueryResponse resp = client.query("SELECT 1").get()) {
Assert.assertEquals(resp.getReadRows(), 1);
}
}
}

@Test(groups = { "integration" })
public void testSSLAuthentication_invalidConfig() throws Exception {
if (isCloud()) {
return; // Current test is working only with local server because of self-signed certificates.
}
ClickHouseNode server = getSecureServer(ClickHouseProtocol.HTTP);
try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
.useSSLAuthentication(true)
.setUsername("some_user")
.setPassword("s3cret")
.setRootCertificate("containers/clickhouse-server/certs/localhost.crt")
.setClientCertificate("some_user.crt")
.setClientKey("some_user.key")
.compressServerResponse(false)
.build()) {
fail("Expected exception");
} catch (IllegalArgumentException e) {
e.printStackTrace();
Assert.assertTrue(e.getMessage().startsWith("Only one of password, access token or SSL authentication"));
}
}
}
Loading