Skip to content

Commit a34bd60

Browse files
committed
implemented ssl authentication
1 parent a499238 commit a34bd60

File tree

7 files changed

+91
-7
lines changed

7 files changed

+91
-7
lines changed

clickhouse-client/src/test/resources/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,11 @@ openssl x509 -req -in server.csr -CA myCA.crt -CAkey myCA.key -CAcreateserial -o
1818
openssl req -nodes -subj "/CN=me" -newkey rsa:2048 -keyout client.key -out client.csr
1919
openssl x509 -req -in client.csr -out client.crt -CAcreateserial -CA myCA.crt -CAkey myCA.key -days 36500
2020
```
21+
22+
### Some_user
23+
24+
```bash
25+
openssl req -nodes -subj "/CN=some_user" -newkey rsa:2048 -keyout some_user.key -out some_user.csr
26+
openssl x509 -req -in some_user.csr -out some_user.crt -CAcreateserial -CA marsnet_ca.crt -CAkey marsnet_ca.key -days 36500
27+
28+
```

clickhouse-http-client/src/main/java/com/clickhouse/client/http/ClickHouseHttpProto.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public class ClickHouseHttpProto {
4646

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

49+
public static final String HEADER_SSL_CERT_AUTH = "x-clickhouse-ssl-certificate-auth";
50+
4951
/**
5052
* Query parameter to specify the query ID.
5153
*/

client-v2/src/main/java/com/clickhouse/client/api/Client.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,17 @@ public Builder setAccessToken(String accessToken) {
320320
return this;
321321
}
322322

323+
/**
324+
* Makes client to use SSL Client Certificate to authenticate with server.
325+
* Client certificate should be set as well. {@link Client.Builder#setClientCertificate(String)}
326+
* @param useSSLAuthentication
327+
* @return
328+
*/
329+
public Builder useSSLAuthentication(boolean useSSLAuthentication) {
330+
this.configuration.put("ssl_authentication", String.valueOf(useSSLAuthentication));
331+
return this;
332+
}
333+
323334
/**
324335
* Configures client to use build-in connection pool
325336
* @param enable - if connection pool should be enabled
@@ -854,12 +865,19 @@ public Client build() {
854865
throw new IllegalArgumentException("At least one endpoint is required");
855866
}
856867
// check if username and password are empty. so can not initiate client?
857-
if (!this.configuration.containsKey("access_token") && (!this.configuration.containsKey("user") || !this.configuration.containsKey("password"))) {
858-
throw new IllegalArgumentException("Username and password are required");
868+
if (!this.configuration.containsKey("access_token") &&
869+
(!this.configuration.containsKey("user") || !this.configuration.containsKey("password")) &&
870+
!MapUtils.getFlag(this.configuration, "ssl_authentication")) {
871+
throw new IllegalArgumentException("Username and password (or access token, or SSL authentication) are required");
872+
}
873+
874+
if (this.configuration.containsKey("ssl_authentication") &&
875+
!this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE.getKey())) {
876+
throw new IllegalArgumentException("SSL authentication requires a client certificate");
859877
}
860878

861-
if (this.configuration.containsKey(ClickHouseClientOption.TRUST_STORE) &&
862-
this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE)) {
879+
if (this.configuration.containsKey(ClickHouseClientOption.TRUST_STORE.getKey()) &&
880+
this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE.getKey())) {
863881
throw new IllegalArgumentException("Trust store and certificates cannot be used together");
864882
}
865883

client-v2/src/main/java/com/clickhouse/client/api/command/CommandResponse.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.clickhouse.client.api.metrics.ServerMetrics;
66
import com.clickhouse.client.api.query.QueryResponse;
77

8-
public class CommandResponse{
8+
public class CommandResponse implements AutoCloseable {
99

1010
private final QueryResponse response;
1111

@@ -71,4 +71,9 @@ public long getWrittenBytes() {
7171
public long getServerTime() {
7272
return response.getServerTime();
7373
}
74+
75+
@Override
76+
public void close() throws Exception {
77+
response.close();
78+
}
7479
}

client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,11 @@ private void addHeaders(HttpPost req, Map<String, String> chConfig, Map<String,
394394
}
395395
req.addHeader(ClickHouseHttpProto.HEADER_DATABASE, chConfig.get(ClickHouseClientOption.DATABASE.getKey()));
396396
req.addHeader(ClickHouseHttpProto.HEADER_DB_USER, chConfig.get(ClickHouseDefaults.USER.getKey()));
397-
req.addHeader(ClickHouseHttpProto.HEADER_DB_PASSWORD, chConfig.get(ClickHouseDefaults.PASSWORD.getKey()));
398-
397+
if (MapUtils.getFlag(chConfig, "ssl_authentication", false)) {
398+
req.addHeader(ClickHouseHttpProto.HEADER_SSL_CERT_AUTH, "on");
399+
} else {
400+
req.addHeader(ClickHouseHttpProto.HEADER_DB_PASSWORD, chConfig.get(ClickHouseDefaults.PASSWORD.getKey()));
401+
}
399402
if (proxyAuthHeaderValue != null) {
400403
req.addHeader(HttpHeaders.PROXY_AUTHORIZATION, proxyAuthHeaderValue);
401404
}

client-v2/src/main/java/com/clickhouse/client/api/internal/MapUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ public static boolean getFlag(Map<String, String> map, String key) {
6868
throw new IllegalArgumentException("Invalid non-boolean value for the key '" + key + "': '" + val + "'");
6969
}
7070

71+
public static boolean getFlag(Map<String, String> map, String key, boolean defaultValue) {
72+
String val = map.get(key);
73+
if (val == null) {
74+
return defaultValue;
75+
}
76+
if (val.equalsIgnoreCase("true")) {
77+
return true;
78+
} else if (val.equalsIgnoreCase("false")) {
79+
return false;
80+
}
81+
82+
throw new IllegalArgumentException("Invalid non-boolean value for the key '" + key + "': '" + val + "'");
83+
}
84+
85+
7186
public static boolean getFlag(Map<String, ?> p1, Map<String, ?> p2, String key) {
7287
Object val = p1.get(key);
7388
if (val == null) {

client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.clickhouse.client.api.ConnectionInitiationException;
77
import com.clickhouse.client.api.ConnectionReuseStrategy;
88
import com.clickhouse.client.api.ServerException;
9+
import com.clickhouse.client.api.command.CommandResponse;
910
import com.clickhouse.client.api.enums.Protocol;
1011
import com.clickhouse.client.api.enums.ProxyType;
1112
import com.clickhouse.client.api.insert.InsertResponse;
@@ -414,6 +415,38 @@ public void testServerSettings() {
414415
Assert.fail("Unexpected exception", e);
415416
}
416417
}
418+
}
417419

420+
static {
421+
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
422+
}
423+
@Test(groups = { "integration" })
424+
public void testSSLAuthentication() throws Exception {
425+
ClickHouseNode server = getSecureServer(ClickHouseProtocol.HTTP);
426+
try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
427+
.setUsername("default")
428+
.setPassword("")
429+
.setRootCertificate("containers/clickhouse-server/certs/localhost.crt")
430+
.build()) {
431+
432+
try (CommandResponse resp = client.execute("DROP USER IF EXISTS some_user").get()) {
433+
}
434+
try (CommandResponse resp = client.execute("CREATE USER some_user IDENTIFIED WITH ssl_certificate CN 'some_user'").get()) {
435+
}
436+
}
437+
438+
try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
439+
.useSSLAuthentication(true)
440+
.setUsername("some_user")
441+
.setRootCertificate("containers/clickhouse-server/certs/localhost.crt")
442+
.setClientCertificate("some_user.crt")
443+
.setClientKey("some_user.key")
444+
.compressServerResponse(false)
445+
.build()) {
446+
447+
try (QueryResponse resp = client.query("SELECT 1").get()) {
448+
Assert.assertEquals(resp.getReadRows(), 1);
449+
}
450+
}
418451
}
419452
}

0 commit comments

Comments
 (0)