Skip to content

Commit 5cf63e5

Browse files
committed
Add Database CLI options for TLS encryption for databases
Closes keycloak#46603 Signed-off-by: Pedro Ruivo <1492066+pruivo@users.noreply.github.com>
1 parent 23f6982 commit 5cf63e5

24 files changed

+1429
-19
lines changed

docs/guides/server/db.adoc

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,260 @@ The `transaction-default-timeout` option takes precedence over the unsupported `
439439
If you are using the Quarkus property, migrate to the supported `transaction-default-timeout` option and remove the Quarkus property from your configuration.
440440
====
441441

442+
== Secure the database connection
443+
444+
Encrypting the traffic between {project_name} and the database is recommended for increased security, as it prevents third parties from examining the network traffic.
445+
446+
It is recommended to go a step further and enable certificate verification to prevent more complex attacks such as DNS poisoning and address hijacking, whereby {project_name} could be directed to a different server than intended.
447+
To perform the certificate validation, the database certificate, or the Certificate Authority (CA) certificate, must be added to the {project_name} truststore.
448+
449+
This section provides guidance on how to enable these settings in {project_name} and configure the JDBC driver properly.
450+
Configuring the database server with the private keys and certificates is outside the scope of this section.
451+
Consult your vendor documentation on how to do it.
452+
453+
=== Using {project_name} CLI options
454+
455+
{project_name} provides unified CLI options to configure database TLS settings across different database vendors.
456+
These options simplify the configuration by abstracting vendor-specific JDBC properties and providing a consistent interface.
457+
458+
The following options are available:
459+
460+
`db-tls-mode`::
461+
Sets the TLS mode for the database connection.
462+
Valid values are `disabled` and `verify-server`.
463+
When set to `verify-server`, it enables encryption and server identity verification.
464+
Default: `disabled`
465+
466+
`db-tls-trust-store-file`::
467+
The path to the truststore file containing the database server certificates or Certificate Authority (CA) certificates used to verify the database server's identity.
468+
469+
`db-tls-trust-store-password`::
470+
The password to access the truststore file (if required and supported by the JDBC driver).
471+
472+
`db-tls-trust-store-type`::
473+
The type of the truststore file.
474+
Common values include `JKS` (Java KeyStore) and `PKCS12`.
475+
If not specified, the driver's default truststore type is used.
476+
477+
NOTE: These unified CLI options are the recommended approach for configuring database TLS.
478+
{project_name} automatically translates these options to the appropriate vendor-specific JDBC properties.
479+
480+
The following example demonstrates how to configure database TLS using these options for a PostgreSQL database.
481+
482+
<@kc.start parameters="--db=postgres --db-tls-mode=verify-server --db-tls-trust-store-file=/path/to/certificate.pem"/>
483+
484+
Alternatively, you can use `--truststore-paths` instead of the `db-tls-trust-store-*` options to add your certificate to the Java truststore.
485+
486+
<@kc.start parameters="--db=postgres --db-tls-mode=verify-server --truststore-paths=/path/to/certificate.pem"/>
487+
488+
=== Using the Java truststore
489+
490+
{project_name} allows adding certificates to the Java truststore using the CLI option `--truststore-paths`, which is supported by most drivers.
491+
The recommended settings for each database are provided below.
492+
493+
==== PostgreSQL
494+
// https://jdbc.postgresql.org/documentation/ssl/
495+
PostgreSQL requires the following JDBC properties to be configured.
496+
497+
[%autowidth]
498+
|===
499+
|JDBC Property |Value | Description
500+
501+
m|sslmode
502+
m|verify-full
503+
|Encrypts the network traffic and validates the server identity.
504+
505+
m|sslfactory
506+
m|org.postgresql.ssl.DefaultJavaSSLFactory
507+
|By default, the driver uses `LibPQFactory` which does not support the Java truststore.
508+
509+
|===
510+
511+
<@kc.start parameters="--truststore-paths=/path/to/database_cert.pem --db-url-properties=?sslmode=verify-full&sslfactory=org.postgresql.ssl.DefaultJavaSSLFactory"/>
512+
513+
==== MySQL
514+
// https://dev.mysql.com/doc/connector-j/en/connector-j-reference-using-ssl.html
515+
MySQL requires the following JDBC property to be configured.
516+
517+
[%autowidth]
518+
|===
519+
|JDBC Property |Value | Description
520+
521+
m|sslMode
522+
m|VERIFY_IDENTITY
523+
|Encrypts the network traffic and validates the server identity.
524+
525+
|===
526+
527+
<@kc.start parameters="--truststore-paths=/path/to/database_cert.pem --db-url-properties=?sslMode=VERIFY_IDENTITY"/>
528+
529+
[%autowidth]
530+
==== MariaDB
531+
// https://mariadb.com/docs/connectors/mariadb-connector-j/using-tls-ssl-with-mariadb-java-connector
532+
MariaDB requires the following JDBC property to be configured.
533+
534+
|===
535+
|JDBC Property |Value | Description
536+
537+
m|sslMode
538+
m|verify-full
539+
|Encrypts the network traffic and validates the server identity.
540+
541+
|===
542+
543+
<@kc.start parameters="--truststore-paths=/path/to/database_cert.pem --db-url-properties=?sslMode=verify-full"/>
544+
545+
[%autowidth]
546+
==== Microsoft SQL Server
547+
// https://learn.microsoft.com/en-us/sql/connect/jdbc/connecting-with-ssl-encryption?view=sql-server-ver17#configuring-the-connection
548+
Microsoft SQL Server requires the following JDBC properties to be configured.
549+
550+
|===
551+
|JDBC Property |Value | Description
552+
553+
m|encrypt
554+
m|true
555+
|Encrypts the network traffic.
556+
557+
m|trustServerCertificate
558+
m|false
559+
|Validates the server identity.
560+
561+
|===
562+
563+
<@kc.start parameters="--truststore-paths=/path/to/database_cert.pem --db-url-properties=;encrypt=true;trustServerCertificate=false"/>
564+
565+
[%autowidth]
566+
==== Oracle
567+
// https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleDriver.html
568+
Oracle by default verifies the server identity when the protocol `tcps` is used.
569+
570+
<@kc.start parameters="--truststore-paths=/path/to/database_cert.pem --db-url=jdbc:oracle:thin:@tcps://host:port/database"/>
571+
572+
=== Using database-specific certificate paths
573+
574+
Alternatively, instead of using the Java truststore with `--truststore-paths`, you can configure each database driver to use its own certificate file or truststore directly through JDBC properties.
575+
This approach is useful when you want to keep database certificates separate from the Java truststore.
576+
577+
==== PostgreSQL
578+
579+
PostgreSQL supports specifying the certificate file directly using the `sslrootcert` property.
580+
581+
[%autowidth]
582+
|===
583+
|JDBC Property |Value | Description
584+
585+
m|sslmode
586+
m|verify-full
587+
|Encrypts the network traffic and validates the server identity.
588+
589+
m|sslrootcert
590+
m|/path/to/cert.pem
591+
|The path to the server's certificate file on the client machine.
592+
593+
|===
594+
595+
<@kc.start parameters="--db-url-properties=?sslmode=verify-full&sslrootcert=/path/to/cert.pem"/>
596+
597+
==== MySQL
598+
599+
MySQL supports specifying the certificate file or truststore through dedicated SSL properties.
600+
601+
[%autowidth]
602+
|===
603+
|JDBC Property |Value | Description
604+
605+
m|sslMode
606+
m|VERIFY_IDENTITY
607+
|Encrypts the network traffic and validates the server identity.
608+
609+
m|trustCertificateKeyStoreUrl
610+
m|file:/path/to/truststore.jks
611+
|The URL to a custom truststore file containing the server certificate.
612+
613+
m|trustCertificateKeyStorePassword
614+
m|password
615+
|The password for the custom truststore (if required).
616+
617+
|===
618+
619+
<@kc.start parameters="--db-url-properties=?sslMode=VERIFY_IDENTITY&trustCertificateKeyStoreUrl=file:/path/to/truststore.jks&trustCertificateKeyStorePassword=password"/>
620+
621+
==== MariaDB
622+
623+
MariaDB supports specifying the certificate file directly using the `serverSslCert` property.
624+
625+
[%autowidth]
626+
|===
627+
|JDBC Property |Value | Description
628+
629+
m|sslMode
630+
m|verify-full
631+
|Encrypts the network traffic and validates the server identity.
632+
633+
m|serverSslCert
634+
m|/path/to/cert.pem
635+
|The path to the server's certificate file on the client machine.
636+
637+
|===
638+
639+
<@kc.start parameters="--db-url-properties=?sslMode=verify-full&serverSslCert=/path/to/cert.pem"/>
640+
641+
==== Microsoft SQL Server
642+
643+
Microsoft SQL Server supports specifying a custom truststore using the `trustStore` and `trustStorePassword` properties.
644+
645+
[%autowidth]
646+
|===
647+
|JDBC Property |Value | Description
648+
649+
m|encrypt
650+
m|true
651+
|Encrypts the network traffic.
652+
653+
m|trustServerCertificate
654+
m|false
655+
|Validates the server identity.
656+
657+
m|trustStore
658+
m|/path/to/truststore.jks
659+
|The path to a custom truststore file containing the server certificate.
660+
661+
m|trustStorePassword
662+
m|password
663+
|The password for the custom truststore.
664+
665+
|===
666+
667+
<@kc.start parameters="--db-url-properties=;encrypt=true;trustServerCertificate=false;trustStore=/path/to/truststore.jks;trustStorePassword=password"/>
668+
669+
==== Oracle
670+
671+
Oracle supports using Oracle Wallet or Java keystores for certificate management.
672+
When using `tcps` protocol, you can specify a custom keystore location.
673+
674+
[%autowidth]
675+
|===
676+
|JDBC Property |Value | Description
677+
678+
m|javax.net.ssl.trustStore
679+
m|/path/to/truststore.jks
680+
|The path to a custom truststore file containing the server certificate.
681+
682+
m|javax.net.ssl.trustStorePassword
683+
m|password
684+
|The password for the custom truststore.
685+
686+
m|javax.net.ssl.trustStoreType
687+
m|JKS
688+
|The type of truststore (JKS, PKCS12, etc.).
689+
690+
|===
691+
692+
<@kc.start parameters="--db-url=jdbc:oracle:thin:@tcps://host:port/database --db-url-properties=?javax.net.ssl.trustStore=/path/to/truststore.jks&javax.net.ssl.trustStorePassword=password&javax.net.ssl.trustStoreType=JKS"/>
693+
694+
Alternatively, you can use Oracle Wallet by configuring the wallet location in the JDBC URL or through Oracle-specific properties.
695+
442696
== Setting JPA provider configuration option for migrationStrategy
443697

444698
To setup the JPA migrationStrategy (manual/update/validate) you should setup JPA provider as follows:

quarkus/config-api/src/main/java/org/keycloak/config/DatabaseOptions.java

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.keycloak.config;
22

3+
import java.io.File;
4+
import java.util.Arrays;
35
import java.util.HashMap;
46
import java.util.List;
57
import java.util.Map;
@@ -121,6 +123,78 @@ public class DatabaseOptions {
121123
.hidden()
122124
.build();
123125

126+
public static final Option<String> DB_TLS_MODE = new OptionBuilder<>("db-tls-mode", String.class)
127+
.category(OptionCategory.DATABASE)
128+
.expectedValues(Arrays.stream(DatabaseTlsMode.values()).map(DatabaseTlsMode::toCliValue).toList())
129+
.defaultValue(DatabaseTlsMode.DISABLED.toCliValue())
130+
.description("Sets the TLS mode for the database connection. If disabled, it uses the driver's default value. When set to verify-server, it enables encryption and server identity verification. The database server certificate or Certificate Authority (CA) certificate is required.")
131+
.build();
132+
133+
public static final Option<File> DB_TLS_TRUST_STORE_FILE = new OptionBuilder<>("db-tls-trust-store-file", File.class)
134+
.category(OptionCategory.DATABASE)
135+
.description("The path to the truststore file containing the database server certificates or Certificate Authority (CA) certificates used to verify the database server's identity.")
136+
.build();
137+
138+
public static final Option<String> DB_TLS_TRUST_STORE_PASSWORD = new OptionBuilder<>("db-tls-trust-store-password", String.class)
139+
.category(OptionCategory.DATABASE)
140+
.description("The password to access the truststore file specified in db-tls-trust-store-file (if required and supported by the JDBC driver).")
141+
.build();
142+
public static final Option<String> DB_TLS_TRUST_STORE_TYPE = new OptionBuilder<>("db-tls-trust-store-type", String.class)
143+
.category(OptionCategory.DATABASE)
144+
.description("The type of the truststore file. Common values include 'JKS' (Java KeyStore) and 'PKCS12'. If not specified, it uses the driver's default.")
145+
.build();
146+
147+
// TLS hidden options, per vendor, to configure TLS in the driver
148+
public static final Option<String> DB_ORACLE_TLS_TRANSPORT = new OptionBuilder<>("db-oracle-protocol", String.class)
149+
.hidden()
150+
.build();
151+
public static final Option<String> DB_PROPERTY_SSL_SERVER_DN_MATCH = new OptionBuilder<>("db-property-ssl-server-dn-match", String.class)
152+
.hidden()
153+
.build();
154+
public static final Option<String> DB_PROPERTY_ENCRYPT = new OptionBuilder<>("db-property-encrypt", String.class)
155+
.hidden()
156+
.build();
157+
public static final Option<String> DB_PROPERTY_TRUST_SERVER_CERTIFICATE = new OptionBuilder<>("db-property-trust-server-certificate", String.class)
158+
.hidden()
159+
.build();
160+
public static final Option<String> DB_PROPERTY_SSLFACTORY = new OptionBuilder<>("db-property-sslfactory", String.class)
161+
.hidden()
162+
.build();
163+
public static final Option<String> DB_PROPERTY_SSLMODE = new OptionBuilder<>("db-property-sslmode", String.class)
164+
.hidden()
165+
.build();
166+
public static final Option<String> DB_PROPERTY_SSL_MODE = new OptionBuilder<>("db-property-sslMode", String.class)
167+
.hidden()
168+
.build();
169+
public static final Option<String> DB_PROPERTY_SSLROOTCERT = new OptionBuilder<>("db-property-sslrootcert", String.class)
170+
.hidden()
171+
.build();
172+
public static final Option<String> DB_PROPERTY_TRUST_CERTIFICATE_KEY_STORE_URL = new OptionBuilder<>("db-property-trustCertificateKeyStoreUrl", String.class)
173+
.hidden()
174+
.build();
175+
public static final Option<String> DB_PROPERTY_SERVER_SSL_CERT = new OptionBuilder<>("db-property-serverSslCert", String.class)
176+
.hidden()
177+
.build();
178+
public static final Option<String> DB_PROPERTY_TRUST_STORE = new OptionBuilder<>("db-property-trustStore", String.class)
179+
.hidden()
180+
.build();
181+
public static final Option<String> DB_PROPERTY_ORACLE_TRUST_STORE = new OptionBuilder<>("db-property-javax.net.ssl.trustStore", String.class)
182+
.hidden()
183+
.build();
184+
public static final Option<String> DB_PROPERTY_TRUST_CERTIFICATE_KEY_STORE_PASSWORD = new OptionBuilder<>("db-property-trustCertificateKeyStorePassword", String.class)
185+
.hidden()
186+
.build();
187+
public static final Option<String> DB_PROPERTY_TRUST_STORE_PASSWORD = new OptionBuilder<>("db-property-trustStorePassword", String.class)
188+
.hidden()
189+
.build();
190+
public static final Option<String> DB_PROPERTY_ORACLE_TRUST_STORE_PASSWORD = new OptionBuilder<>("db-property-javax.net.ssl.trustStorePassword", String.class)
191+
.hidden()
192+
.build();
193+
public static final Option<String> DB_PROPERTY_ORACLE_TRUST_STORE_TYPE = new OptionBuilder<>("db-property-javax.net.ssl.trustStoreType", String.class)
194+
.hidden()
195+
.build();
196+
197+
124198
public static final Option<String> DB_MSSQL_SEND_STRING_PARAMETER_AS_UNICODE = new OptionBuilder<>("db-mssql-send-string-parameter-as-unicode", String.class)
125199
.category(OptionCategory.DATABASE)
126200
.defaultValue("false")
@@ -148,7 +222,27 @@ public static final class Datasources {
148222
DB_POOL_MIN_SIZE,
149223
DB_POOL_MAX_SIZE,
150224
DB_SQL_JPA_DEBUG,
151-
DB_SQL_LOG_SLOW_QUERIES
225+
DB_SQL_LOG_SLOW_QUERIES,
226+
DB_TLS_MODE,
227+
DB_TLS_TRUST_STORE_FILE,
228+
DB_TLS_TRUST_STORE_PASSWORD,
229+
DB_TLS_TRUST_STORE_TYPE,
230+
DB_PROPERTY_SSLMODE,
231+
DB_PROPERTY_SSLFACTORY,
232+
DB_PROPERTY_SSL_MODE,
233+
DB_PROPERTY_ENCRYPT,
234+
DB_PROPERTY_TRUST_SERVER_CERTIFICATE,
235+
DB_PROPERTY_SSL_SERVER_DN_MATCH,
236+
DB_ORACLE_TLS_TRANSPORT,
237+
DB_PROPERTY_SSLROOTCERT,
238+
DB_PROPERTY_TRUST_CERTIFICATE_KEY_STORE_URL,
239+
DB_PROPERTY_SERVER_SSL_CERT,
240+
DB_PROPERTY_TRUST_STORE,
241+
DB_PROPERTY_ORACLE_TRUST_STORE,
242+
DB_PROPERTY_TRUST_CERTIFICATE_KEY_STORE_PASSWORD,
243+
DB_PROPERTY_TRUST_STORE_PASSWORD,
244+
DB_PROPERTY_ORACLE_TRUST_STORE_PASSWORD,
245+
DB_PROPERTY_ORACLE_TRUST_STORE_TYPE
152246
);
153247

154248
/**
@@ -246,4 +340,17 @@ public static Optional<String> getNamedKey(Option<?> option, String namedPropert
246340
return getKeyForDatasource(option).map(key -> getWildcardNamedKey(key, namedProperty));
247341
}
248342
}
343+
344+
public enum DatabaseTlsMode {
345+
DISABLED,
346+
VERIFY_SERVER;
347+
348+
public String toCliValue() {
349+
return name().toLowerCase().replace('_', '-');
350+
}
351+
352+
public static DatabaseTlsMode fromCliValue(String value) {
353+
return valueOf(value.toUpperCase().replace('-', '_'));
354+
}
355+
}
249356
}

0 commit comments

Comments
 (0)