fix: refining https file type detection (#33703)
also making common trustore logic align closes: #33649 Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
e3936a9b38
commit
af1a5ea2a8
24 changed files with 164 additions and 97 deletions
|
@ -39,7 +39,7 @@ public class KeystoreUtil {
|
|||
|
||||
public enum KeystoreFormat {
|
||||
JKS("jks"),
|
||||
PKCS12("p12", "pfx"),
|
||||
PKCS12("p12", "pfx", "pkcs12"),
|
||||
BCFKS("bcfks");
|
||||
|
||||
// Typical file extension for this keystore format
|
||||
|
@ -105,6 +105,17 @@ public class KeystoreUtil {
|
|||
throw new RuntimeException("Failed to load private key: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<KeystoreFormat> getKeystoreFormat(String path) {
|
||||
int lastDotIndex = path.lastIndexOf('.');
|
||||
if (lastDotIndex > -1) {
|
||||
String ext = path.substring(lastDotIndex + 1).toLowerCase();
|
||||
return Arrays.stream(KeystoreUtil.KeystoreFormat.values())
|
||||
.filter(ksFormat -> ksFormat.getFileExtensions().contains(ext))
|
||||
.findFirst();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -120,13 +131,9 @@ public class KeystoreUtil {
|
|||
if (preferredType != null) return preferredType;
|
||||
|
||||
// Fallback to path
|
||||
int lastDotIndex = path.lastIndexOf('.');
|
||||
if (lastDotIndex > -1) {
|
||||
String ext = path.substring(lastDotIndex + 1).toLowerCase();
|
||||
Optional<KeystoreFormat> detectedType = Arrays.stream(KeystoreUtil.KeystoreFormat.values())
|
||||
.filter(ksFormat -> ksFormat.getFileExtensions().contains(ext))
|
||||
.findFirst();
|
||||
if (detectedType.isPresent()) return detectedType.get().toString();
|
||||
Optional<KeystoreFormat> format = getKeystoreFormat(path);
|
||||
if (format.isPresent()) {
|
||||
return format.get().toString();
|
||||
}
|
||||
|
||||
// Fallback to default
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package org.keycloak.common.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class KeystoreUtilTest {
|
||||
|
||||
|
@ -11,6 +12,15 @@ public class KeystoreUtilTest {
|
|||
assertEquals("x", KeystoreUtil.getKeystoreType("x", "y", "z"));
|
||||
assertEquals("z", KeystoreUtil.getKeystoreType(null, "y", "z"));
|
||||
assertEquals(KeystoreUtil.KeystoreFormat.PKCS12.name(), KeystoreUtil.getKeystoreType(null, "y.pfx", "z"));
|
||||
assertEquals(KeystoreUtil.KeystoreFormat.PKCS12.name(), KeystoreUtil.getKeystoreType(null, "y.pkcs12", "z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFormat() {
|
||||
assertFalse(KeystoreUtil.getKeystoreFormat("some.file").isPresent());
|
||||
assertFalse(KeystoreUtil.getKeystoreFormat("somepfx").isPresent());
|
||||
assertEquals(KeystoreUtil.KeystoreFormat.PKCS12, KeystoreUtil.getKeystoreFormat("file.pfx").get());
|
||||
assertEquals(KeystoreUtil.KeystoreFormat.JKS, KeystoreUtil.getKeystoreFormat("file.jks").get());
|
||||
}
|
||||
|
||||
}
|
|
@ -275,7 +275,7 @@ To ensure proper TLS configuration, use the `tlsSecret` and `truststores` fields
|
|||
|
||||
If you need to provide trusted certificates, the Keycloak CR provides a top level feature for configuring the server's truststore as discussed in <@links.server id="keycloak-truststore"/>.
|
||||
|
||||
Use the truststores stanza of the Keycloak spec to specify Secrets containing PEM encoded files, or PKCS12 files with extension `.p12` or `.pfx`, for example:
|
||||
Use the truststores stanza of the Keycloak spec to specify Secrets containing PEM encoded files, or PKCS12 files with extension `.p12`, `.pfx`, or `.pkcs12`, for example:
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
|
|
|
@ -22,12 +22,20 @@ When you use a pair of matching certificate and private key files in PEM format,
|
|||
|
||||
{project_name} creates a keystore out of these files in memory and uses this keystore afterwards.
|
||||
|
||||
== Providing a Java Keystore
|
||||
== Providing a Keystore
|
||||
When no keystore file is explicitly configured, but `http-enabled` is set to false, {project_name} looks for a `conf/server.keystore` file.
|
||||
|
||||
As an alternative, you can use an existing keystore by running the following command:
|
||||
<@kc.start parameters="--https-key-store-file=/path/to/existing-keystore-file"/>
|
||||
|
||||
Recognized file extensions for a keystore:
|
||||
|
||||
* `.p12`, `.pkcs12`, and `.pfx` for a pkcs12 file
|
||||
* `.jks`, and `.keystore` for a jks file
|
||||
* `.key`, `.crt`, and `.pem` for a pem file
|
||||
|
||||
If your keystore does not have an extension matching its file type, you will also need to set the `https-key-store-type` option.
|
||||
|
||||
=== Setting the Keystore password
|
||||
You can set a secure password for your keystore using the `https-key-store-password` option:
|
||||
<@kc.start parameters="--https-key-store-password=<value>"/>
|
||||
|
|
|
@ -12,7 +12,7 @@ The certificates of these clients or servers, or the CA that signed these certif
|
|||
|
||||
== Configuring the System Truststore
|
||||
|
||||
The existing Java default truststore certs will always be trusted. If you need additional certificates, which will be the case if you have self-signed or internal certificate authorities that are not recognized by the JRE, they can be included in the `conf/truststores` directory or subdirectories. The certs may be in PEM files, or PKCS12 files with extension `.p12` or `.pfx`. If in PKCS12, the certs must be unencrypted - meaning no password is expected.
|
||||
The existing Java default truststore certs will always be trusted. If you need additional certificates, which will be the case if you have self-signed or internal certificate authorities that are not recognized by the JRE, they can be included in the `conf/truststores` directory or subdirectories. The certs may be in PEM files, or PKCS12 files with extension `.p12`, `.pfx`, or `.pkcs12`. If in PKCS12, the certs must be unencrypted - meaning no password is expected.
|
||||
|
||||
If you need an alternative path, use the `--truststore-paths` option to specify additional files or directories where PEM or PKCS12 files are located. Paths are relative to where you launched {project_name}, so absolute paths are recommended instead. If a directory is specified, it will be recursively scanned for truststore files.
|
||||
|
||||
|
|
|
@ -35,6 +35,14 @@ By default, {project_name} uses the System Truststore to validate certificates.
|
|||
If you need to use a dedicated truststore for mTLS, you can configure the location of this truststore by running the following command:
|
||||
<@kc.start parameters="--https-trust-store-file=/path/to/file --https-trust-store-password=<value>"/>
|
||||
|
||||
Recognized file extensions for a truststore:
|
||||
|
||||
* `.p12`, `.pkcs12`, and `.pfx` for a pkcs12 file
|
||||
* `.jks`, and `.truststore` for a jks file
|
||||
* `.ca`, `.crt`, and `.pem` for a pem file
|
||||
|
||||
If your truststore does not have an extension matching its file type, you will also need to set the `https-key-store-type` option.
|
||||
|
||||
== Additional resources
|
||||
|
||||
=== Using mTLS for outgoing HTTP requests
|
||||
|
|
|
@ -9,7 +9,7 @@ public class TruststoreOptions {
|
|||
|
||||
public static final Option<List<String>> TRUSTSTORE_PATHS = OptionBuilder.listOptionBuilder("truststore-paths", String.class)
|
||||
.category(OptionCategory.TRUSTSTORE)
|
||||
.description("List of pkcs12 (p12 or pfx file extensions), PEM files, or directories containing those files that will be used as a system truststore.")
|
||||
.description("List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or directories containing those files that will be used as a system truststore.")
|
||||
.build();
|
||||
|
||||
public static final Option<HostnameVerificationPolicy> HOSTNAME_VERIFICATION_POLICY = new OptionBuilder<>("tls-hostname-verifier", HostnameVerificationPolicy.class)
|
||||
|
|
|
@ -44,6 +44,9 @@ public final class ExecutionExceptionHandler implements CommandLine.IExecutionEx
|
|||
if (cause instanceof PropertyException) {
|
||||
PrintWriter writer = cmd.getErr();
|
||||
writer.println(cmd.getColorScheme().errorText(cause.getMessage()));
|
||||
if (verbose && cause.getCause() != null) {
|
||||
dumpException(writer, cause.getCause());
|
||||
}
|
||||
return ShortErrorMessageHandler.getInvalidInputExitCode(cause, cmd);
|
||||
}
|
||||
error(cmd.getErr(), "Failed to run '" + parseResult.subcommands().stream()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.quarkus.runtime.configuration.mappers;
|
||||
|
||||
import io.quarkus.runtime.util.ClassPathUtils;
|
||||
import io.quarkus.vertx.http.runtime.CertificateConfig;
|
||||
import io.quarkus.vertx.http.runtime.options.TlsUtils;
|
||||
import io.smallrye.config.ConfigSourceInterceptorContext;
|
||||
|
@ -14,9 +15,10 @@ import org.keycloak.quarkus.runtime.configuration.Configuration;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
|
||||
|
||||
|
@ -24,6 +26,10 @@ public final class HttpPropertyMappers {
|
|||
private static final int MIN_MAX_THREADS = 50;
|
||||
private static final String QUARKUS_HTTPS_CERT_FILES = "quarkus.http.ssl.certificate.files";
|
||||
private static final String QUARKUS_HTTPS_CERT_KEY_FILES = "quarkus.http.ssl.certificate.key-files";
|
||||
private static final String QUARKUS_HTTPS_KEY_STORE_FILE = "quarkus.http.ssl.certificate.key-store-file";
|
||||
private static final String QUARKUS_HTTPS_TRUST_STORE_FILE = "quarkus.http.ssl.certificate.trust-store-file";
|
||||
private static final String QUARKUS_HTTPS_TRUST_STORE_FILE_TYPE = "quarkus.http.ssl.certificate.trust-store-file-type";
|
||||
private static final String QUARKUS_HTTPS_KEY_STORE_FILE_TYPE = "quarkus.http.ssl.certificate.key-store-file-type";
|
||||
|
||||
private HttpPropertyMappers(){}
|
||||
|
||||
|
@ -72,17 +78,18 @@ public final class HttpPropertyMappers {
|
|||
.build(),
|
||||
fromOption(HttpOptions.HTTPS_CERTIFICATE_FILE)
|
||||
.to(QUARKUS_HTTPS_CERT_FILES)
|
||||
.transformer(HttpPropertyMappers.validatePath(QUARKUS_HTTPS_CERT_FILES))
|
||||
.transformer(HttpPropertyMappers::transformPath)
|
||||
.paramLabel("file")
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTPS_CERTIFICATE_KEY_FILE)
|
||||
.to(QUARKUS_HTTPS_CERT_KEY_FILES)
|
||||
.transformer(HttpPropertyMappers.validatePath(QUARKUS_HTTPS_CERT_KEY_FILES))
|
||||
.transformer(HttpPropertyMappers::transformPath)
|
||||
.paramLabel("file")
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTPS_KEY_STORE_FILE
|
||||
.withRuntimeSpecificDefault(getDefaultKeystorePathValue()))
|
||||
.to("quarkus.http.ssl.certificate.key-store-file")
|
||||
.to(QUARKUS_HTTPS_KEY_STORE_FILE)
|
||||
.transformer(HttpPropertyMappers::transformPath)
|
||||
.paramLabel("file")
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTPS_KEY_STORE_PASSWORD)
|
||||
|
@ -91,12 +98,13 @@ public final class HttpPropertyMappers {
|
|||
.isMasked(true)
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTPS_KEY_STORE_TYPE)
|
||||
.to("quarkus.http.ssl.certificate.key-store-file-type")
|
||||
.mapFrom(SecurityOptions.FIPS_MODE, HttpPropertyMappers::resolveKeyStoreType)
|
||||
.to(QUARKUS_HTTPS_KEY_STORE_FILE_TYPE)
|
||||
.paramLabel("type")
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTPS_TRUST_STORE_FILE)
|
||||
.to("quarkus.http.ssl.certificate.trust-store-file")
|
||||
.to(QUARKUS_HTTPS_TRUST_STORE_FILE)
|
||||
.transformer(HttpPropertyMappers::transformPath)
|
||||
.paramLabel("file")
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTPS_TRUST_STORE_PASSWORD)
|
||||
|
@ -105,8 +113,9 @@ public final class HttpPropertyMappers {
|
|||
.isMasked(true)
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTPS_TRUST_STORE_TYPE)
|
||||
.to("quarkus.http.ssl.certificate.trust-store-file-type")
|
||||
.mapFrom(SecurityOptions.FIPS_MODE, HttpPropertyMappers::resolveKeyStoreType)
|
||||
.to(QUARKUS_HTTPS_TRUST_STORE_FILE_TYPE)
|
||||
.transformer(HttpPropertyMappers::resolveKeyStoreType)
|
||||
.paramLabel("type")
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTP_MAX_QUEUED_REQUESTS)
|
||||
|
@ -130,68 +139,60 @@ public final class HttpPropertyMappers {
|
|||
|
||||
public static void validateConfig() {
|
||||
boolean enabled = isHttpEnabled(Configuration.getOptionalKcValue(HttpOptions.HTTP_ENABLED.getKey()).orElse(null));
|
||||
boolean trustStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_FILE.getKey()).isPresent();
|
||||
boolean keyStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_FILE.getKey()).isPresent();
|
||||
Optional<String> certFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_CERTIFICATE_FILE.getKey());
|
||||
Optional<String> keystoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_FILE.getKey());
|
||||
if (!enabled && certFile.isEmpty() && keystoreFile.isEmpty()) {
|
||||
throw new PropertyException(Messages.httpsConfigurationNotSet());
|
||||
}
|
||||
|
||||
if (trustStoreFile) {
|
||||
CertificateConfig config = new CertificateConfig();
|
||||
CertificateConfig config = new CertificateConfig();
|
||||
|
||||
config.trustStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_FILE.getKey()).map(Paths::get);
|
||||
config.trustStorePassword = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_PASSWORD.getKey());
|
||||
config.trustStoreFileType = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_TYPE.getKey());
|
||||
config.trustStoreProvider = Configuration.getOptionalValue("quarkus.http.ssl.certificate.trust-store-provider");
|
||||
config.trustStoreCertAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.trust-store-cert-alias");
|
||||
config.trustStoreFiles = Optional.empty();
|
||||
config.trustStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_FILE.getKey()).map(Paths::get);
|
||||
config.trustStorePassword = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_PASSWORD.getKey());
|
||||
config.trustStoreFileType = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_TYPE.getKey());
|
||||
config.trustStoreProvider = Configuration.getOptionalValue("quarkus.http.ssl.certificate.trust-store-provider");
|
||||
config.trustStoreCertAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.trust-store-cert-alias");
|
||||
config.trustStoreFiles = Optional.empty();
|
||||
|
||||
try {
|
||||
TlsUtils.computeTrustOptions(config, config.trustStorePassword);
|
||||
} catch (IOException e) {
|
||||
throw new PropertyException("Failed to load 'https-trust-store' material.", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
config.keyStoreFile = keystoreFile.map(Paths::get);
|
||||
config.keyStorePassword = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_PASSWORD.getKey());
|
||||
config.keyStoreFileType = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_TYPE.getKey());
|
||||
config.keyStoreProvider = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-provider");
|
||||
config.keyStoreAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias");
|
||||
config.keyStoreAliasPassword = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias-password");
|
||||
config.keyStoreAliasPasswordKey = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias-password-key");
|
||||
config.keyStoreKeyAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-key-alias");
|
||||
|
||||
config.keyFiles = Configuration.getOptionalKcValue(HttpOptions.HTTPS_CERTIFICATE_KEY_FILE.getKey()).map(Paths::get).map(List::of);
|
||||
config.files = certFile.map(Paths::get).map(List::of);
|
||||
|
||||
try {
|
||||
TlsUtils.computeTrustOptions(config, config.trustStorePassword);
|
||||
} catch (IOException e) {
|
||||
throw new PropertyException("Failed to load 'https-trust-store' material: " + e.getClass().getSimpleName() + " " + e.getMessage(), e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (e.getMessage().contains(QUARKUS_HTTPS_TRUST_STORE_FILE_TYPE)) {
|
||||
throw new PropertyException("Unable to determine 'https-trust-store-type' automatically. " +
|
||||
"Adjust the file extension or specify the property.", e);
|
||||
}
|
||||
throw new PropertyException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (keyStoreFile) {
|
||||
CertificateConfig config = new CertificateConfig();
|
||||
|
||||
config.keyStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_FILE.getKey()).map(Paths::get);
|
||||
config.keyStorePassword = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_PASSWORD.getKey());
|
||||
config.keyStoreFileType = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_TYPE.getKey());
|
||||
config.keyStoreProvider = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-provider");
|
||||
config.keyStoreAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias");
|
||||
config.keyStoreAliasPassword = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias-password");
|
||||
config.keyStoreAliasPasswordKey = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias-password-key");
|
||||
config.keyStoreKeyAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-key-alias");
|
||||
config.keyFiles = Optional.empty();
|
||||
config.files = Optional.empty();
|
||||
|
||||
try {
|
||||
TlsUtils.computeKeyStoreOptions(config, config.keyStorePassword, config.keyStoreAliasPassword);
|
||||
} catch (IOException e) {
|
||||
throw new PropertyException("Failed to load 'https-key-store' material.", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
TlsUtils.computeKeyStoreOptions(config, config.keyStorePassword, config.keyStoreAliasPassword);
|
||||
} catch (IOException e) {
|
||||
throw new PropertyException("Failed to load 'https-key-' material: " + e.getClass().getSimpleName() + " " + e.getMessage(), e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (e.getMessage().contains(QUARKUS_HTTPS_KEY_STORE_FILE_TYPE)) {
|
||||
throw new PropertyException("Unable to determine 'https-key-store-type' automatically. " +
|
||||
"Adjust the file extension or specify the property.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
Optional<String> value = Configuration.getOptionalKcValue(HttpOptions.HTTPS_CERTIFICATE_FILE.getKey());
|
||||
|
||||
if (value.isEmpty()) {
|
||||
value = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-file");
|
||||
}
|
||||
|
||||
if (value.isEmpty()) {
|
||||
throw new PropertyException(Messages.httpsConfigurationNotSet());
|
||||
}
|
||||
throw new PropertyException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static BiFunction<String, ConfigSourceInterceptorContext, String> validatePath(String key) {
|
||||
return (value, context) -> Environment.isWindows() && value != null && value.equals(context.proceed(key).getValue()) ? value.replace("\\", "/") : value;
|
||||
private static String transformPath(String value, ConfigSourceInterceptorContext context) {
|
||||
return value == null ? value : ClassPathUtils.toResourceName(Path.of(value));
|
||||
}
|
||||
|
||||
private static String getHttpEnabledTransformer(String value, ConfigSourceInterceptorContext context) {
|
||||
|
|
|
@ -288,6 +288,8 @@ public class PropertyMapper<T> {
|
|||
* NOTE: This transformer will not apply to the mapFrom value. When using
|
||||
* {@link #mapFrom} you generally need a transformer specifically for the parent
|
||||
* value, see {@link #mapFrom(Option, BiFunction)}
|
||||
* <p>
|
||||
* The value passed into the transformer may be null if the property has no value set, and no default
|
||||
*/
|
||||
public Builder<T> transformer(BiFunction<String, ConfigSourceInterceptorContext, String> mapper) {
|
||||
this.mapper = mapper;
|
||||
|
|
|
@ -206,6 +206,21 @@ public class PicocliTest extends AbstractConfigurationTest {
|
|||
.errorText("Unknown option: '--db-pasword'")
|
||||
+ "\nPossible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void httpStoreTypeValidation() {
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false");
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("Unable to determine 'https-key-store-type' automatically. Adjust the file extension or specify the property"));
|
||||
|
||||
nonRunningPicocli = pseudoLaunch("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false", "--https-key-store-type=jdk");
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("Failed to load 'https-key-' material: NoSuchFileException not-there.ks"));
|
||||
|
||||
nonRunningPicocli = pseudoLaunch("start", "--https-trust-store-file=not-there.jks", "--https-key-store-file=not-there.ks", "--hostname-strict=false", "--https-key-store-type=jdk");
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("No trust store password provided"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failSingleParamWithSpace() {
|
||||
|
|
|
@ -23,6 +23,8 @@ import static org.junit.Assert.assertTrue;
|
|||
import static org.keycloak.quarkus.runtime.Environment.isWindows;
|
||||
import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.CLI_ARGS;
|
||||
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
@ -491,7 +493,18 @@ public class ConfigurationTest extends AbstractConfigurationTest {
|
|||
ConfigArgsConfigSource.setCliArgs("--https-certificates-reload-period=2h");
|
||||
assertEquals("2h", createConfig().getConfigValue("quarkus.http.ssl.certificate.reload-period").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpsPaths() {
|
||||
ConfigArgsConfigSource.setCliArgs("--https-certificate-file=\\some\\file");
|
||||
|
||||
String expected = "\\some\\file";
|
||||
if (FileSystems.getDefault().getSeparator().equals("\\")) {
|
||||
expected = "/some/file";
|
||||
}
|
||||
assertEquals(expected, createConfig().getConfigValue("quarkus.http.ssl.certificate.files").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheMaxCount() {
|
||||
int maxCount = 500;
|
||||
|
|
|
@ -173,13 +173,13 @@ public class QuarkusPropertiesDistTest {
|
|||
@Test
|
||||
@BeforeStartDistribution(ForceRebuild.class)
|
||||
@DisabledOnOs(value = { OS.WINDOWS }, disabledReason = "Windows uses a different path separator.")
|
||||
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false",
|
||||
@Launch({ "start", "--verbose", "--http-enabled=true", "--hostname-strict=false",
|
||||
"--https-certificate-file=/tmp/kc/bin/../conf/server.crt.pem",
|
||||
"--https-certificate-key-file=/tmp/kc/bin/../conf/server.key.pem" })
|
||||
@Order(13)
|
||||
void testHttpCertsPathTransformer(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
assertThat(cliResult.getOutput(),containsString("ERROR: /tmp/kc/bin/../conf/server.crt.pem"));
|
||||
assertThat(cliResult.getErrorOutput(),containsString("Failed to load 'https-key-' material: NoSuchFileException /tmp/kc/bin/../conf/server.crt.pem"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -191,7 +191,7 @@ public class QuarkusPropertiesDistTest {
|
|||
@Order(14)
|
||||
void testHttpCertsPathTransformerOnWindows(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
assertThat(cliResult.getOutput(),containsString("ERROR: C:/tmp/kc/bin/../conf/server.crt.pem"));
|
||||
assertThat(cliResult.getErrorOutput(),containsString("Failed to load 'https-key-' material: NoSuchFileException C:/tmp/kc/bin/../conf/server.crt.pem"));
|
||||
}
|
||||
|
||||
public static class AddConsoleHandlerFromQuarkusProps implements Consumer<KeycloakDistribution> {
|
||||
|
|
|
@ -150,8 +150,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Export:
|
||||
|
||||
|
|
|
@ -248,8 +248,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Export:
|
||||
|
||||
|
|
|
@ -150,8 +150,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Import:
|
||||
|
||||
|
|
|
@ -248,8 +248,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Import:
|
||||
|
||||
|
|
|
@ -321,8 +321,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Security:
|
||||
|
||||
|
|
|
@ -454,8 +454,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Security:
|
||||
|
||||
|
|
|
@ -322,8 +322,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Security:
|
||||
|
||||
|
|
|
@ -455,8 +455,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Security:
|
||||
|
||||
|
|
|
@ -273,8 +273,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Bootstrap Admin:
|
||||
|
||||
|
|
|
@ -395,8 +395,8 @@ Truststore:
|
|||
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
|
||||
DEFAULT. Default: DEFAULT.
|
||||
--truststore-paths <truststore-paths>
|
||||
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
|
||||
containing those files that will be used as a system truststore.
|
||||
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
|
||||
directories containing those files that will be used as a system truststore.
|
||||
|
||||
Bootstrap Admin:
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.truststore;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.common.util.KeystoreUtil.KeystoreFormat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -101,15 +102,14 @@ public class TruststoreBuilder {
|
|||
if (f.isDirectory()) {
|
||||
mergeFiles(Stream.of(f.listFiles()).map(File::getAbsolutePath).toArray(String[]::new), truststore, false, discoveredFiles);
|
||||
} else {
|
||||
if (file.endsWith(".p12") || file.endsWith(".pfx")) {
|
||||
var format = KeystoreUtil.getKeystoreFormat(file).orElse(null);
|
||||
if (format == KeystoreFormat.PKCS12) {
|
||||
mergeTrustStore(truststore, file, loadStore(file, PKCS12, null));
|
||||
if (!topLevel) {
|
||||
discoveredFiles.add(f.getAbsolutePath());
|
||||
}
|
||||
} else {
|
||||
if (mergePemFile(truststore, file, topLevel) && !topLevel) {
|
||||
discoveredFiles.add(f.getAbsolutePath());
|
||||
}
|
||||
} else if (mergePemFile(truststore, file, topLevel) && !topLevel) {
|
||||
discoveredFiles.add(f.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue