fix: add a reload period property (#32715)

closes: #23771

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2024-09-13 03:47:21 -04:00 committed by GitHub
parent 92e435f192
commit f0bf290c28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 79 additions and 2 deletions

View file

@ -142,6 +142,10 @@ The deprecated `proxy` option was removed. This option was deprecated in {projec
The `proxy-trusted-addresses` can be used when the `proxy-headers` option is set to specify a allowlist of trusted proxy addresses. If the proxy address for a given request is not trusted, then the respective proxy header values will not be used. The `proxy-trusted-addresses` can be used when the `proxy-headers` option is set to specify a allowlist of trusted proxy addresses. If the proxy address for a given request is not trusted, then the respective proxy header values will not be used.
= Option to reload trust and key material added
The `https-certificates-reload-period` option can be set to define the reloading period of key store, trust store, and certificate files referenced by https-* options. Use -1 to disable reloading. Defaults to 1h (one hour).
= Property `origin` in the `UserRepresentation` is deprecated = Property `origin` in the `UserRepresentation` is deprecated
The `origin` property in the `UserRepresentation` is deprecated and planned to be removed in future releases. The `origin` property in the `UserRepresentation` is deprecated and planned to be removed in future releases.

View file

@ -85,4 +85,9 @@ NOTE: Management interface properties are inherited from the main HTTP server, i
It means when mTLS is set, it is also enabled for the management interface. It means when mTLS is set, it is also enabled for the management interface.
To override the behavior, use the `https-management-client-auth` property. To override the behavior, use the `https-management-client-auth` property.
== Certificate and Key Reloading
By default {project_name} will reload the certificates, keys, and keystores specified in `https-*` options every hour. For environments where your server keys may need frequent rotation, this allows that to happen without a server restart. You may override the default via the `https-certificates-reload-period` option. Interval on which to reload key store, trust store, and certificate files referenced by https-* options.
The value may be a java.time.Duration value, an integer number of seconds, or an integer followed by one of the time units [ms, h, m, s, d]. Must be greater than 30 seconds. Use -1 to disable.
</@tmpl.guide> </@tmpl.guide>

View file

@ -63,6 +63,12 @@ public class HttpOptions {
.defaultValue(Arrays.asList("TLSv1.3,TLSv1.2")) .defaultValue(Arrays.asList("TLSv1.3,TLSv1.2"))
.build(); .build();
public static final Option<String> HTTPS_CERTIFICATES_RELOAD_PERIOD = new OptionBuilder<>("https-certificates-reload-period", String.class)
.category(OptionCategory.HTTP)
.description("Interval on which to reload key store, trust store, and certificate files referenced by https-* options. May be a java.time.Duration value, an integer number of seconds, or an integer followed by one of [ms, h, m, s, d]. Must be greater than 30 seconds. Use -1 to disable.")
.defaultValue("1h")
.build();
public static final Option<File> HTTPS_CERTIFICATE_FILE = new OptionBuilder<>("https-certificate-file", File.class) public static final Option<File> HTTPS_CERTIFICATE_FILE = new OptionBuilder<>("https-certificate-file", File.class)
.category(OptionCategory.HTTP) .category(OptionCategory.HTTP)
.description("The file path to a server certificate or certificate chain in PEM format.") .description("The file path to a server certificate or certificate chain in PEM format.")

View file

@ -68,6 +68,12 @@ public final class HttpPropertyMappers {
.to("quarkus.http.ssl.protocols") .to("quarkus.http.ssl.protocols")
.paramLabel("protocols") .paramLabel("protocols")
.build(), .build(),
fromOption(HttpOptions.HTTPS_CERTIFICATES_RELOAD_PERIOD)
.to("quarkus.http.ssl.certificate.reload-period")
// -1 means no reload
.transformer((value, context) -> "-1".equals(value.get()) ? null : value)
.paramLabel("reload period")
.build(),
fromOption(HttpOptions.HTTPS_CERTIFICATE_FILE) fromOption(HttpOptions.HTTPS_CERTIFICATE_FILE)
.to(QUARKUS_HTTPS_CERT_FILES) .to(QUARKUS_HTTPS_CERT_FILES)
.transformer(HttpPropertyMappers.validatePath(QUARKUS_HTTPS_CERT_FILES)) .transformer(HttpPropertyMappers.validatePath(QUARKUS_HTTPS_CERT_FILES))

View file

@ -160,7 +160,12 @@ public class PropertyMapper<T> {
// we always fallback to the current value from the property we are mapping // we always fallback to the current value from the property we are mapping
if (transformedValue == null) { if (transformedValue == null) {
return context.proceed(name); return ConfigValue.builder()
.withName(name)
.withValue(null)
.withRawValue(config.getValue())
.withConfigSourceName(config.getConfigSourceName())
.build();
} }
return transformedValue; return transformedValue;

View file

@ -18,6 +18,7 @@
package org.keycloak.quarkus.runtime.configuration.test; package org.keycloak.quarkus.runtime.configuration.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.keycloak.quarkus.runtime.Environment.isWindows; import static org.keycloak.quarkus.runtime.Environment.isWindows;
import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.CLI_ARGS; import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.CLI_ARGS;
@ -475,4 +476,14 @@ public class ConfigurationTest extends AbstractConfigurationTest {
ConfigValue secret = config.getConfigValue("my.secret"); ConfigValue secret = config.getConfigValue("my.secret");
assertEquals("secret", secret.getValue()); assertEquals("secret", secret.getValue());
} }
@Test
public void testReloadPeriod() {
ConfigArgsConfigSource.setCliArgs("");
assertEquals("1h", createConfig().getConfigValue("quarkus.http.ssl.certificate.reload-period").getValue());
ConfigArgsConfigSource.setCliArgs("--https-certificates-reload-period=-1");
assertNull(createConfig().getConfigValue("quarkus.http.ssl.certificate.reload-period").getValue());
ConfigArgsConfigSource.setCliArgs("--https-certificates-reload-period=2h");
assertEquals("2h", createConfig().getConfigValue("quarkus.http.ssl.certificate.reload-period").getValue());
}
} }

View file

@ -31,12 +31,16 @@ import java.util.concurrent.CompletableFuture;
import static io.restassured.RestAssured.when; import static io.restassured.RestAssured.when;
import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
/** /**
* @author Vaclav Muzikar <vmuzikar@redhat.com> * @author Vaclav Muzikar <vmuzikar@redhat.com>
*/ */
@DistributionTest(keepAlive = true) @DistributionTest(keepAlive = true, enableTls = true)
@RawDistOnly(reason = "Containers are immutable") @RawDistOnly(reason = "Containers are immutable")
public class HttpDistTest { public class HttpDistTest {
@Test @Test
@ -55,4 +59,10 @@ public class HttpDistTest {
assertThat("Some of the requests should be properly rejected", statusCodes, hasItem(503)); assertThat("Some of the requests should be properly rejected", statusCodes, hasItem(503));
assertThat("None of the requests should throw an unhandled exception", statusCodes, not(hasItem(500))); assertThat("None of the requests should throw an unhandled exception", statusCodes, not(hasItem(500)));
} }
@Test
@Launch({"start-dev", "--https-certificates-reload-period=wrong"})
public void testHttpCertificateReloadPeriod(LaunchResult result) {
assertThat(result.getErrorOutput(), containsString("Text cannot be parsed to a Duration"));
}
} }

View file

@ -153,6 +153,11 @@ HTTP(S):
The file path to a server certificate or certificate chain in PEM format. The file path to a server certificate or certificate chain in PEM format.
--https-certificate-key-file <file> --https-certificate-key-file <file>
The file path to a private key in PEM format. The file path to a private key in PEM format.
--https-certificates-reload-period <reload period>
Interval on which to reload key store, trust store, and certificate files
referenced by https-* options. May be a java.time.Duration value, an integer
number of seconds, or an integer followed by one of [ms, h, m, s, d]. Must
be greater than 30 seconds. Use -1 to disable. Default: 1h.
--https-cipher-suites <ciphers> --https-cipher-suites <ciphers>
The cipher suites to use. If none is given, a reasonable default is selected. The cipher suites to use. If none is given, a reasonable default is selected.
--https-client-auth <auth> --https-client-auth <auth>

View file

@ -188,6 +188,11 @@ HTTP(S):
The file path to a server certificate or certificate chain in PEM format. The file path to a server certificate or certificate chain in PEM format.
--https-certificate-key-file <file> --https-certificate-key-file <file>
The file path to a private key in PEM format. The file path to a private key in PEM format.
--https-certificates-reload-period <reload period>
Interval on which to reload key store, trust store, and certificate files
referenced by https-* options. May be a java.time.Duration value, an integer
number of seconds, or an integer followed by one of [ms, h, m, s, d]. Must
be greater than 30 seconds. Use -1 to disable. Default: 1h.
--https-cipher-suites <ciphers> --https-cipher-suites <ciphers>
The cipher suites to use. If none is given, a reasonable default is selected. The cipher suites to use. If none is given, a reasonable default is selected.
--https-client-auth <auth> --https-client-auth <auth>

View file

@ -154,6 +154,11 @@ HTTP(S):
The file path to a server certificate or certificate chain in PEM format. The file path to a server certificate or certificate chain in PEM format.
--https-certificate-key-file <file> --https-certificate-key-file <file>
The file path to a private key in PEM format. The file path to a private key in PEM format.
--https-certificates-reload-period <reload period>
Interval on which to reload key store, trust store, and certificate files
referenced by https-* options. May be a java.time.Duration value, an integer
number of seconds, or an integer followed by one of [ms, h, m, s, d]. Must
be greater than 30 seconds. Use -1 to disable. Default: 1h.
--https-cipher-suites <ciphers> --https-cipher-suites <ciphers>
The cipher suites to use. If none is given, a reasonable default is selected. The cipher suites to use. If none is given, a reasonable default is selected.
--https-client-auth <auth> --https-client-auth <auth>

View file

@ -189,6 +189,11 @@ HTTP(S):
The file path to a server certificate or certificate chain in PEM format. The file path to a server certificate or certificate chain in PEM format.
--https-certificate-key-file <file> --https-certificate-key-file <file>
The file path to a private key in PEM format. The file path to a private key in PEM format.
--https-certificates-reload-period <reload period>
Interval on which to reload key store, trust store, and certificate files
referenced by https-* options. May be a java.time.Duration value, an integer
number of seconds, or an integer followed by one of [ms, h, m, s, d]. Must
be greater than 30 seconds. Use -1 to disable. Default: 1h.
--https-cipher-suites <ciphers> --https-cipher-suites <ciphers>
The cipher suites to use. If none is given, a reasonable default is selected. The cipher suites to use. If none is given, a reasonable default is selected.
--https-client-auth <auth> --https-client-auth <auth>

View file

@ -136,6 +136,11 @@ HTTP(S):
The file path to a server certificate or certificate chain in PEM format. The file path to a server certificate or certificate chain in PEM format.
--https-certificate-key-file <file> --https-certificate-key-file <file>
The file path to a private key in PEM format. The file path to a private key in PEM format.
--https-certificates-reload-period <reload period>
Interval on which to reload key store, trust store, and certificate files
referenced by https-* options. May be a java.time.Duration value, an integer
number of seconds, or an integer followed by one of [ms, h, m, s, d]. Must
be greater than 30 seconds. Use -1 to disable. Default: 1h.
--https-cipher-suites <ciphers> --https-cipher-suites <ciphers>
The cipher suites to use. If none is given, a reasonable default is selected. The cipher suites to use. If none is given, a reasonable default is selected.
--https-key-store-file <file> --https-key-store-file <file>

View file

@ -171,6 +171,11 @@ HTTP(S):
The file path to a server certificate or certificate chain in PEM format. The file path to a server certificate or certificate chain in PEM format.
--https-certificate-key-file <file> --https-certificate-key-file <file>
The file path to a private key in PEM format. The file path to a private key in PEM format.
--https-certificates-reload-period <reload period>
Interval on which to reload key store, trust store, and certificate files
referenced by https-* options. May be a java.time.Duration value, an integer
number of seconds, or an integer followed by one of [ms, h, m, s, d]. Must
be greater than 30 seconds. Use -1 to disable. Default: 1h.
--https-cipher-suites <ciphers> --https-cipher-suites <ciphers>
The cipher suites to use. If none is given, a reasonable default is selected. The cipher suites to use. If none is given, a reasonable default is selected.
--https-key-store-file <file> --https-key-store-file <file>