Skip to content

Commit bc04005

Browse files
committed
ADR about the usage of the TLS registry in client extensions establishing TLS connection
1 parent 3ab88f5 commit bc04005

File tree

1 file changed

+189
-0
lines changed

1 file changed

+189
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
= Using the TLS registry for clients
2+
3+
* Status: accepted
4+
* Date: 2024-06-12 by @cescoffier, @geoand, @gsmet
5+
// * Revised:
6+
7+
== Context and Problem Statement
8+
9+
With https://github.com/quarkusio/quarkus/pull/39825[#39825], we introduced the concept of a TLS registry in Quarkus.
10+
The TLS registry is a way to configure TLS settings in a central place and reuse them across the application and extensions.
11+
This is particularly useful when an application needs to connect to multiple services using TLS.
12+
13+
Until now, each extension has _maintained_ its own way to configure TLS settings. This leads to a lot of duplication and inconsistencies.
14+
It's also difficult for the user to understand how to configure TLS settings in Quarkus.
15+
The TLS registry is a way to solve this problem.
16+
17+
The goal of this ADR is to explain how the registry should be used by client establishing a connection to a server using TLS.
18+
19+
It should be noted that TLS is a sensitive and complex topic.
20+
The TLS registry is not a silver bullet and does not solve all the problems related to TLS.
21+
It is a way to simplify the configuration of TLS settings in Quarkus applications.
22+
23+
== The TLS registry
24+
25+
The TLS registry is an extension processing the `quarkus.tls` configuration and proposing a runtime API to access it.
26+
Note that the configuration and the runtime API are different.
27+
The configuration is used to define the TLS settings, while the runtime API is used to access these pre-processed settings.
28+
29+
hen using the options directly under `quarkus.tls.`, one configures the default (_unamed_) configuration, while using options under `quarkus.tls.<name>.` configures a _named_ configuration.
30+
For each configuration, trust stores and key stores can be defined, as well as the default protocol, cipher suites, etc.
31+
More details can be found in the https://quarkus.io/version/main/guides/tls-registry-reference[documentation] and in the https://github.com/quarkusio/quarkus/blob/main/extensions/tls-registry/runtime/src/main/java/io/quarkus/tls/runtime/config/TlsBucketConfig.java[code].
32+
33+
At runtime, the https://github.com/quarkusio/quarkus/blob/main/extensions/tls-registry/runtime/src/main/java/io/quarkus/tls/TlsConfigurationRegistry.java[TlsConfigurationRegistry] CDI bean gives access to the default and named _TlsConfiguration_.
34+
These configurations have been verified during the application startup (password, alias, validity...) and are ready to be used.
35+
https://github.com/quarkusio/quarkus/blob/main/extensions/tls-registry/runtime/src/main/java/io/quarkus/tls/TlsConfiguration.java[TLSConfiguration] gives access to the key store, trust store, Vert.x options, tailored `SSLContext`...
36+
It also supports reloading the certificates.
37+
38+
== Configuring clients with the TLS registry
39+
40+
When configuring a _client_ establishing a connection to a server using TLS, the TLS registry should be used to configure the TLS settings.
41+
The TLS settings must be defined in the `quarkus.tls.<name>.` configuration.
42+
43+
WARNING: Using the default configuration is going to create conflict with the server configuration, and it's is generally not a good idea to use the same configuration for the client and the server.
44+
45+
The client extension should use the TLS registry to retrieve the _TlsConfiguration_ and use it to configure the connection.
46+
47+
Thus, the client configuration must be extended with a new configuration item: `tlsConfigurationName`:
48+
49+
[source, java]
50+
----
51+
/**
52+
* The name of the TLS configuration to use.
53+
* <p>
54+
* If a name is configured, it uses the configuration from {@code quarkus.tls.<name>.*}
55+
* If a name is configured, but no TLS configuration is found with that name then an error will be thrown.
56+
* <p>
57+
* The default TLS configuration is <strong>not</strong> used by default.
58+
*/
59+
Optional<String> tlsConfigurationName();
60+
----
61+
62+
Clients must not use the default TLS configuration, except for one case.
63+
For backward compatibility purpose, `quarkus.tls.trust-all` should be honored when configuring clients.
64+
65+
== Using the TLS registry from client extensions
66+
67+
=== Retrieving the registry from an extension
68+
69+
The TLS registry is a CDI bean and can be injected in other beans.
70+
The TLS registry is a _singleton_ CDI bean.
71+
72+
However, before configuring the client, you need to make sure the TLS registry is properly initialized.
73+
The recommended approach is to inject the `TlsConfigurationRegistry tlsConfigurationRegistry` bean in a `recorder`:
74+
75+
[source, java]
76+
----
77+
BasicWebSocketConnectorImpl(Vertx vertx, Codecs codecs, ClientConnectionManager connectionManager,
78+
WebSocketsClientRuntimeConfig config, TlsConfigurationRegistry tlsConfigurationRegistry) {
79+
// ...
80+
}
81+
----
82+
83+
Another approach is to use the `TlsRegistryBuildItem` in a _processor_:
84+
85+
[source, java]
86+
----
87+
@Record(ExecutionTime.RUNTIME_INIT)
88+
@BuildStep
89+
public SyntheticBeanBuildItem setup(OidcBuildTimeConfig oidcBuildTimeConfig, OidcConfig oidcRunTimeConfig,
90+
KeycloakPolicyEnforcerConfig keycloakConfig, KeycloakPolicyEnforcerRecorder recorder,
91+
HttpConfiguration httpConfiguration, TlsRegistryBuildItem tlsRegistryBuildItem) {
92+
if (oidcBuildTimeConfig.enabled) {
93+
return SyntheticBeanBuildItem.configure(PolicyEnforcerResolver.class).unremovable()
94+
.types(PolicyEnforcerResolver.class)
95+
.supplier(recorder.setup(oidcRunTimeConfig, keycloakConfig, httpConfiguration,
96+
tlsRegistryBuildItem.registry())) // Get the registry runtime value
97+
.scope(Singleton.class)
98+
.setRuntimeInit()
99+
.done();
100+
}
101+
return null;
102+
}
103+
----
104+
105+
=== Finding the named TLS configuration
106+
107+
Once the TLS registry is properly passed, the client extension can use it to retrieve the named TLS configuration:
108+
109+
[source, java]
110+
----
111+
TlsConfiguration configuration = null;
112+
Optional<TlsConfiguration> maybeTlsConfiguration = TlsConfiguration.from(tlsConfigurationRegistry,
113+
config.tlsConfigurationName());
114+
if (config.tlsConfigurationName.isPresent()) {
115+
configuration = maybeConfiguration.get();
116+
} else if (tlsRegistry.getDefault().isPresent() && tlsRegistry.getDefault().get().isTrustAll()) {
117+
defaultTrustAll = tlsRegistry.getDefault().get().isTrustAll();
118+
if (defaultTrustAll) {
119+
LOGGER.warn("The default TLS configuration is set to trust all certificates. This is a security risk."
120+
+ "Please use a named TLS configuration for the mailer " + name + " to avoid this warning.");
121+
}
122+
}
123+
----
124+
125+
Also, using the _default_ `trust-all` should be avoided, and a warning should be logged if it's used.
126+
127+
=== Configuring a Vert.x client
128+
129+
When configuring a Vert.x client (HTTP client, SMTP client, gRPC client, etc.), the `TlsConfiguration` should be used to configure the client:
130+
131+
- The `trustStore` and `keyStore` should be set on the `options.setTrustOptions(config.getTrustStoreOptions())` and `options.setKeyCertOptions(config.getKeyStoreOptions())`. Setting the keystore enables mutual TLS (`mTLS`).
132+
- `trustAll` can be set using `options.setTrustAll(true)`.
133+
- The hostname verification algorithm should be configured to a sensible default (`HTTPS` for every HTTP based protocol).
134+
To disable the hostname verification, the `NONE` value should be used.
135+
- `TlsConfigure.getSSLOptions()` allows configuring various TLS aspect like the protocol, cipher suites, handshake, ALPN, certificate revocation lists, etc:
136+
137+
[source, java]
138+
----
139+
SSLOptions sslOptions = configuration.getSSLOptions();
140+
if (sslOptions != null) {
141+
cfg.setSslHandshakeTimeout(sslOptions.getSslHandshakeTimeout());
142+
cfg.setSslHandshakeTimeoutUnit(sslOptions.getSslHandshakeTimeoutUnit());
143+
for (String suite : sslOptions.getEnabledCipherSuites()) {
144+
cfg.addEnabledCipherSuite(suite);
145+
}
146+
for (Buffer buffer : sslOptions.getCrlValues()) {
147+
cfg.addCrlValue(buffer);
148+
}
149+
cfg.setEnabledSecureTransportProtocols(sslOptions.getEnabledSecureTransportProtocols());
150+
151+
}
152+
----
153+
154+
- For protocol supporting SNI, the SNI should be configured using `options.setSNI(tlsConfiguration.usesSNI())`.
155+
156+
157+
=== Configuring non-Vert.x clients
158+
159+
When configuring a non-Vert.x client, the `TLSConfiguration` provides:
160+
161+
- `KeyStore` objects for the key store and trust store
162+
- A `SSLContext` object to configure the SSL context.
163+
164+
== Considered options
165+
166+
=== Continuing with the current approach
167+
168+
The current approach is to let each extension manage its TLS settings.
169+
This approach leads to a lot of duplication and inconsistencies.
170+
It's also difficult for the user to understand how to configure TLS settings in Quarkus.
171+
172+
Note that the TLS registry does **NOT** replace the extension-specific configuration.
173+
It's a way to simplify the configuration of TLS settings in Quarkus applications.
174+
The extension-specific configuration should be considered _deprecated_ and users should be invited to use the TLS registry.
175+
176+
== Consequences
177+
178+
=== Positive
179+
180+
* A centralized way to configure TLS settings in Quarkus applications.
181+
* A homogenization of the TLS configuration across Quarkus extensions.
182+
* A more complete and consistent TLS configuration.
183+
184+
=== Negative
185+
186+
* Dealing with 2 different ways to configure TLS settings in Quarkus applications until the extension-specific configurations are removed.
187+
188+
189+

0 commit comments

Comments
 (0)