Skip to content

Commit 767fc60

Browse files
committed
Added examples
1 parent 0b5776d commit 767fc60

5 files changed

Lines changed: 178 additions & 27 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.clickhouse.client.api.enums;
2+
3+
/**
4+
* Defines how strictly the client verifies a server identity when a secure protocol is used.
5+
*
6+
* <p>The mode affects only connections that are already using a secure transport (for example,
7+
* an {@code https://} endpoint). It does <b>not</b> enable encryption for plain protocols - an
8+
* {@code http://} endpoint stays unencrypted whatever the mode is.</p>
9+
*
10+
* <p>Modes from the least to the most strict:</p>
11+
* <ul>
12+
* <li>{@link #Disabled} - SSL is not used. Plain protocols only.</li>
13+
* <li>{@link #Trust} - encryption is used, but the server certificate chain is not validated
14+
* and the hostname is not verified. Susceptible to MITM attacks - use only for testing or in
15+
* fully trusted environments.</li>
16+
* <li>{@link #VerifyCa} - the server certificate chain is validated against the trust material
17+
* (default JVM trust store, configured trust store, or a CA certificate), but the hostname is
18+
* not checked against the certificate.</li>
19+
* <li>{@link #Strict} - full verification (default): certificate chain is validated and the
20+
* hostname must match the certificate.</li>
21+
* </ul>
22+
*/
23+
public enum SSLMode {
24+
25+
/**
26+
* SSL is not used. Connection is not encrypted.
27+
*/
28+
Disabled,
29+
30+
/**
31+
* Encryption without verification: any server certificate is accepted and
32+
* the hostname is not verified.
33+
*/
34+
Trust,
35+
36+
/**
37+
* Server certificate chain is validated, but the hostname is not verified.
38+
*/
39+
VerifyCa,
40+
41+
/**
42+
* Full verification: certificate chain is validated and the hostname must match
43+
* the certificate. Default mode.
44+
*/
45+
Strict;
46+
47+
/**
48+
* Case-insensitive variant of {@link #valueOf(String)}.
49+
*
50+
* @param value mode name in any case
51+
* @return matching mode
52+
* @throws IllegalArgumentException when the value does not match any mode
53+
*/
54+
public static SSLMode fromValue(String value) {
55+
for (SSLMode mode : values()) {
56+
if (mode.name().equalsIgnoreCase(value)) {
57+
return mode;
58+
}
59+
}
60+
throw new IllegalArgumentException("Unknown SSL mode '" + value + "'");
61+
}
62+
}

examples/client-v2/README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,15 @@ Notes:
7878

7979
## SSL Examples
8080

81-
`com.clickhouse.examples.client_v2.SSLExamples` shows how to connect securely to a server whose
82-
certificate is signed by a custom (private) CA. Only the CA certificate is passed to the client
83-
with `Client.Builder.setRootCertificate()` - no trust store configuration is required, and the JVM
84-
default trust store stays untouched.
81+
`com.clickhouse.examples.client_v2.SSLExamples` shows how to connect securely to a server:
82+
83+
- **Custom CA certificate** - the server certificate is signed by a custom (private) CA. Only the
84+
CA certificate is passed to the client with `Client.Builder.setRootCertificate()` (as a file path
85+
or directly as a PEM string) - no trust store configuration is required, and the JVM default
86+
trust store stays untouched.
87+
- **Self-signed certificate without verification** - `Client.Builder.setSSLMode(SSLMode.Trust)`
88+
accepts any server certificate and skips hostname verification. The connection is encrypted, but
89+
the server identity is not verified - use it only for testing or in fully trusted environments.
8590

8691
The example runs in one of two modes.
8792

@@ -113,7 +118,8 @@ mvn exec:java -Dexec.mainClass="com.clickhouse.examples.client_v2.SSLExamples" \
113118
-DchRootCert="/path/to/ca.crt"
114119
```
115120

116-
`-DchRootCert` is required in this mode and must point to the CA certificate in PEM format.
121+
`-DchRootCert` must point to the CA certificate in PEM format. When it is omitted, only the
122+
self-signed (`SSLMode.Trust`) example runs - useful when you do not have the CA certificate at hand.
117123

118124
### Setting up a Docker dev instance with a self-signed certificate manually
119125

examples/client-v2/src/main/java/com/clickhouse/examples/client_v2/SSLExamples.java

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.clickhouse.examples.client_v2;
22

33
import com.clickhouse.client.api.Client;
4+
import com.clickhouse.client.api.enums.SSLMode;
45
import com.clickhouse.client.api.query.GenericRecord;
56
import lombok.extern.slf4j.Slf4j;
67

@@ -22,6 +23,9 @@
2223
* <li>Passing the CA certificate as a PEM string instead of a file path - useful when the
2324
* certificate comes from an environment variable or a secret manager (typical for
2425
* Kubernetes/cloud deployments) and you do not want to write it to disk.</li>
26+
* <li>Connecting to a server with a self-signed certificate without any trust material -
27+
* {@link SSLMode#Trust} accepts any server certificate and skips hostname verification.
28+
* Use it only for testing or in fully trusted environments.</li>
2529
* </ul>
2630
*
2731
* <p>More SSL examples (mTLS, trust stores, SNI) will be added to this class later.</p>
@@ -41,7 +45,8 @@
4145
* <li>{@code chPort} - ClickHouse HTTPS port, default {@code 8443}</li>
4246
* <li>{@code chDatabase} - database name, default {@code default}</li>
4347
* <li>{@code chUser} and {@code chPassword} - credentials (standalone mode)</li>
44-
* <li>{@code chRootCert} - path to the root CA certificate in PEM format (required in standalone mode)</li>
48+
* <li>{@code chRootCert} - path to the root CA certificate in PEM format. When omitted in
49+
* standalone mode, only the self-signed (Trust) example runs</li>
4550
* <li>{@code chImage} - Docker image for local mode, default {@code clickhouse/clickhouse-server:latest}</li>
4651
* </ul>
4752
*/
@@ -58,16 +63,17 @@ public static void main(String[] args) {
5863
final String user = System.getProperty("chUser", "default");
5964
final String password = System.getProperty("chPassword", "");
6065
final String rootCert = trimToNull(System.getProperty("chRootCert"));
61-
if (rootCert == null) {
62-
log.error("chRootCert is required when chHost is set. "
63-
+ "Pass the path to the CA certificate (PEM) that signed the server certificate.");
64-
return;
65-
}
6666

6767
log.info("Running in standalone mode against {}:{}", host, port);
6868
String endpoint = "https://" + host + ":" + port;
69-
connectWithCustomRootCertificate(endpoint, database, user, password, rootCert);
70-
connectWithRootCertificateAsString(endpoint, database, user, password, rootCert);
69+
connectToSelfSignedServer(endpoint, database, user, password);
70+
if (rootCert != null) {
71+
connectWithCustomRootCertificate(endpoint, database, user, password, rootCert);
72+
connectWithRootCertificateAsString(endpoint, database, user, password, rootCert);
73+
} else {
74+
log.info("chRootCert is not set - skipping the custom CA certificate examples. "
75+
+ "Pass the path to the CA certificate (PEM) that signed the server certificate to run them.");
76+
}
7177
return;
7278
}
7379

@@ -76,6 +82,8 @@ public static void main(String[] args) {
7682
final String image = System.getProperty("chImage", "clickhouse/clickhouse-server:latest");
7783
log.info("Running in local mode (set -DchHost to verify your own server)");
7884
try (SecureServerSupport server = SecureServerSupport.start(image)) {
85+
connectToSelfSignedServer(server.getEndpoint(), database,
86+
SecureServerSupport.USER, SecureServerSupport.PASSWORD);
7987
connectWithCustomRootCertificate(server.getEndpoint(), database,
8088
SecureServerSupport.USER, SecureServerSupport.PASSWORD, server.getCaCertPath());
8189
connectWithRootCertificateAsString(server.getEndpoint(), database,
@@ -88,6 +96,35 @@ public static void main(String[] args) {
8896
Runtime.getRuntime().exit(0);
8997
}
9098

99+
/**
100+
* Connects to a ClickHouse server with a self-signed certificate without providing
101+
* any trust material. {@link SSLMode#Trust} makes the client accept any server
102+
* certificate and skip hostname verification.
103+
*
104+
* <p><b>Warning:</b> the connection is encrypted, but the server identity is NOT verified,
105+
* which makes it susceptible to man-in-the-middle attacks. Use this mode only for testing
106+
* or in fully trusted environments. Prefer {@link Client.Builder#setRootCertificate(String)}
107+
* with the signing CA certificate whenever possible.</p>
108+
*/
109+
static void connectToSelfSignedServer(String endpoint, String database, String user, String password) {
110+
log.info("Connecting to {} accepting any server certificate (SSLMode.Trust)", endpoint);
111+
try (Client client = new Client.Builder()
112+
.addEndpoint(endpoint)
113+
.setUsername(user)
114+
.setPassword(password)
115+
.setDefaultDatabase(database)
116+
// Accept the self-signed certificate and skip hostname verification.
117+
.setSSLMode(SSLMode.Trust)
118+
.build()) {
119+
120+
List<GenericRecord> rows = client.queryAll("SELECT currentUser() AS user, version() AS version");
121+
log.info("Connected (server certificate not verified) as '{}' to ClickHouse {}",
122+
rows.get(0).getString("user"), rows.get(0).getString("version"));
123+
} catch (Exception e) {
124+
log.error("Connection with SSLMode.Trust failed", e);
125+
}
126+
}
127+
91128
/**
92129
* Connects to a ClickHouse server using a custom root CA certificate.
93130
* Use this when the server certificate is signed by a private CA (corporate CA,

examples/jdbc/README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ Addition options can be passed to the application:
2424

2525
## SSL Examples
2626

27-
`com.clickhouse.examples.jdbc.SSLExamples` shows how to connect securely to a server whose
28-
certificate is signed by a custom (private) CA. Only the CA certificate is passed with the
29-
`sslrootcert` connection property - no trust store configuration is required, and the JVM default
30-
trust store stays untouched.
27+
`com.clickhouse.examples.jdbc.SSLExamples` shows how to connect securely to a server:
28+
29+
- **Custom CA certificate** - the server certificate is signed by a custom (private) CA. Only the
30+
CA certificate is passed with the `sslrootcert` connection property (as a file path or directly
31+
as a PEM string) - no trust store configuration is required, and the JVM default trust store
32+
stays untouched.
33+
- **Self-signed certificate without verification** - the `ssl_mode=trust` connection property
34+
(`ssl_mode=none` is accepted as an alias) accepts any server certificate and skips hostname
35+
verification. The connection is encrypted, but the server identity is not verified - use it only
36+
for testing or in fully trusted environments.
3137

3238
The example runs in one of two modes.
3339

@@ -57,7 +63,8 @@ mvn exec:java -Dexec.mainClass="com.clickhouse.examples.jdbc.SSLExamples" \
5763
-DchRootCert="/path/to/ca.crt"
5864
```
5965

60-
`-DchRootCert` is required in this mode and must point to the CA certificate in PEM format.
66+
`-DchRootCert` must point to the CA certificate in PEM format. When it is omitted, only the
67+
self-signed (`ssl_mode=trust`) example runs - useful when you do not have the CA certificate at hand.
6168

6269
### Setting up a Docker dev instance with a self-signed certificate manually
6370

examples/jdbc/src/main/java/com/clickhouse/examples/jdbc/SSLExamples.java

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
* <li>Passing the CA certificate as a PEM string instead of a file path - useful when the
2828
* certificate comes from an environment variable or a secret manager (typical for
2929
* Kubernetes/cloud deployments) and you do not want to write it to disk.</li>
30+
* <li>Connecting to a server with a self-signed certificate without any trust material -
31+
* the {@code ssl_mode=trust} connection property accepts any server certificate and skips
32+
* hostname verification ({@code ssl_mode=none} is accepted as an alias). Use it only for
33+
* testing or in fully trusted environments.</li>
3034
* </ul>
3135
*
3236
* <p>More SSL examples (mTLS, trust stores, SNI) will be added to this class later.</p>
@@ -45,7 +49,8 @@
4549
* <li>{@code chUrl} - ClickHouse JDBC URL, e.g. {@code jdbc:clickhouse://my-host:8443/default}.
4650
* When set, standalone mode is used</li>
4751
* <li>{@code chUser} and {@code chPassword} - credentials (standalone mode)</li>
48-
* <li>{@code chRootCert} - path to the root CA certificate in PEM format (required in standalone mode)</li>
52+
* <li>{@code chRootCert} - path to the root CA certificate in PEM format. When omitted in
53+
* standalone mode, only the self-signed (trust) example runs</li>
4954
* <li>{@code chImage} - Docker image for local mode, default {@code clickhouse/clickhouse-server:latest}</li>
5055
* </ul>
5156
*/
@@ -60,18 +65,19 @@ public static void main(String[] args) {
6065
final String user = System.getProperty("chUser", "default");
6166
final String password = System.getProperty("chPassword", "");
6267
final String rootCert = trimToNull(System.getProperty("chRootCert"));
63-
if (rootCert == null) {
64-
log.error("chRootCert is required when chUrl is set. "
65-
+ "Pass the path to the CA certificate (PEM) that signed the server certificate.");
66-
return;
67-
}
6868

6969
log.info("Running in standalone mode against {}", url);
7070
try {
71-
connectWithCustomRootCertificate(url, user, password, rootCert);
72-
connectWithRootCertificateAsString(url, user, password, rootCert);
71+
connectToSelfSignedServer(url, user, password);
72+
if (rootCert != null) {
73+
connectWithCustomRootCertificate(url, user, password, rootCert);
74+
connectWithRootCertificateAsString(url, user, password, rootCert);
75+
} else {
76+
log.info("chRootCert is not set - skipping the custom CA certificate examples. "
77+
+ "Pass the path to the CA certificate (PEM) that signed the server certificate to run them.");
78+
}
7379
} catch (SQLException | IOException e) {
74-
log.error("Secure connection with a custom root CA certificate failed", e);
80+
log.error("Secure connection failed", e);
7581
}
7682
return;
7783
}
@@ -81,6 +87,8 @@ public static void main(String[] args) {
8187
final String image = System.getProperty("chImage", "clickhouse/clickhouse-server:latest");
8288
log.info("Running in local mode (set -DchUrl to verify your own server)");
8389
try (SecureServerSupport server = SecureServerSupport.start(image)) {
90+
connectToSelfSignedServer(server.getJdbcUrl(),
91+
SecureServerSupport.USER, SecureServerSupport.PASSWORD);
8492
connectWithCustomRootCertificate(server.getJdbcUrl(),
8593
SecureServerSupport.USER, SecureServerSupport.PASSWORD, server.getCaCertPath());
8694
connectWithRootCertificateAsString(server.getJdbcUrl(),
@@ -93,6 +101,37 @@ public static void main(String[] args) {
93101
Runtime.getRuntime().exit(0);
94102
}
95103

104+
/**
105+
* Connects to a ClickHouse server with a self-signed certificate without providing
106+
* any trust material. The {@code ssl_mode=trust} connection property makes the driver
107+
* accept any server certificate and skip hostname verification. The traditional JDBC
108+
* value {@code ssl_mode=none} is accepted as an alias.
109+
*
110+
* <p><b>Warning:</b> the connection is encrypted, but the server identity is NOT verified,
111+
* which makes it susceptible to man-in-the-middle attacks. Use this mode only for testing
112+
* or in fully trusted environments. Prefer {@code sslrootcert} with the signing CA
113+
* certificate whenever possible.</p>
114+
*/
115+
static void connectToSelfSignedServer(String url, String user, String password) throws SQLException {
116+
log.info("Connecting to {} accepting any server certificate (ssl_mode=trust)", url);
117+
118+
Properties properties = new Properties();
119+
properties.setProperty(ClientConfigProperties.USER.getKey(), user); // user
120+
properties.setProperty(ClientConfigProperties.PASSWORD.getKey(), password); // password
121+
properties.setProperty("ssl", "true"); // enable TLS even if the URL has no https scheme
122+
// Accept the self-signed certificate and skip hostname verification.
123+
properties.setProperty(ClientConfigProperties.SSL_MODE.getKey(), "trust"); // ssl_mode
124+
125+
try (Connection connection = DriverManager.getConnection(url, properties);
126+
Statement stmt = connection.createStatement();
127+
ResultSet rs = stmt.executeQuery("SELECT currentUser() AS user, version() AS version")) {
128+
if (rs.next()) {
129+
log.info("Connected (server certificate not verified) as '{}' to ClickHouse {}",
130+
rs.getString("user"), rs.getString("version"));
131+
}
132+
}
133+
}
134+
96135
/**
97136
* Connects to a ClickHouse server using a custom root CA certificate.
98137
* Use this when the server certificate is signed by a private CA (corporate CA,

0 commit comments

Comments
 (0)