feature: add option for creating a global truststore (#24473)
closes #24148 Co-authored-by: Václav Muzikář <vaclav@muzikari.cz> Co-authored-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
parent
dd5b9b1c36
commit
8c3df19722
62 changed files with 1030 additions and 287 deletions
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.truststore;
|
||||
package org.keycloak.common.enums;
|
||||
|
||||
public enum HostnameVerificationPolicy {
|
||||
|
|
@ -13,6 +13,9 @@ include::topics/templates/document-attributes.adoc[]
|
|||
:release_header_latest_link: {releasenotes_link_latest}
|
||||
include::topics/templates/release-header.adoc[]
|
||||
|
||||
== {project_name_full} 24.0.0
|
||||
include::topics/24_0_0.adoc[leveloffset=2]
|
||||
|
||||
== {project_name_full} 23.0.0
|
||||
include::topics/23_0_0.adoc[leveloffset=2]
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
The Keycloak JS adapter now uses the https://webpack.js.org/guides/package-exports/[`exports` field] in `package.json`. This improves support for more modern bundlers like Webpack 5 and Vite, but comes with some unavoidable breaking changes. Consult the link:{upgradingguide_link}[{upgradingguide_name}] for more details.
|
||||
|
||||
= Truststore Improvements
|
||||
|
||||
Keycloak introduces an improved truststores configuration options. The Keycloak truststore is now used across the server: for outgoing connections, mTLS, database drivers and more. It's no longer needed to configure separate truststores for individual areas. To configure the truststore, you can put your truststores files or certificates in the default `conf/truststores`, or use the new `truststore-paths` config option. For details refer to the relevant https://www.keycloak.org/server/keycloak-truststore[guide].
|
||||
|
||||
== Automatic certificate management for SAML identity providers
|
||||
|
||||
The SAML identity providers can now be configured to automatically download the signing certificates from the IDP entity metadata descriptor endpoint. In order to use the new feature the option `Metadata descriptor URL` should be configured in the provider (URL where the IDP metadata information with the certificates is published) and `Use metadata descriptor URL` needs to be `ON`. The certificates are automatically downloaded and cached in the `public-key-storage` SPI from that URL. The certificates can also be reloaded or imported from the admin console, using the action combo in the provider page.
|
||||
|
|
|
@ -74,11 +74,9 @@ Hover the mouse pointer over the tooltips in the Admin Console to see more detai
|
|||
|
||||
==== Connecting to LDAP over SSL
|
||||
|
||||
When you configure a secure connection URL to your LDAP store (for example,`ldaps://myhost.com:636`), {project_name} uses SSL to communicate with the LDAP server. Configure a truststore on the {project_name} server side so that {project_name} can trust the SSL connection to LDAP.
|
||||
When you configure a secure connection URL to your LDAP store (for example,`ldaps://myhost.com:636`), {project_name} uses SSL to communicate with the LDAP server. Configure a truststore on the {project_name} server side so that {project_name} can trust the SSL connection to LDAP - see https://www.keycloak.org/server/keycloak-truststore[Configuring a Truststore] {section}.
|
||||
|
||||
Configure the global truststore for {project_name} with the Truststore SPI. For more information about configuring the global truststore, see the https://www.keycloak.org/server/keycloak-truststore[Configuring a Truststore] {section}. If you do not configure the Truststore SPI, the truststore falls back to the default mechanism provided by Java, which can be the file supplied by the `javax.net.ssl.trustStore` system property or the cacerts file from the JDK if the system property is unset.
|
||||
|
||||
The `Use Truststore SPI` configuration property, in the LDAP federation provider configuration, controls the truststore SPI. By default, {project_name} sets the property to `Always`, which is adequate for most deployments. {project_name} uses the Truststore SPI if the connection URL to LDAP starts with `ldaps` only.
|
||||
The `Use Truststore SPI` configuration property is deprecated. It should normally be left as `Always`.
|
||||
|
||||
==== Synchronizing LDAP users to {project_name}
|
||||
|
||||
|
|
|
@ -12,3 +12,9 @@ import AuthZ from 'keycloak-js/dist/keycloak-authz.js';
|
|||
import Keycloak from 'keycloak-js';
|
||||
import AuthZ from 'keycloak-js/authz';
|
||||
----
|
||||
|
||||
= Truststore Changes
|
||||
|
||||
The `spi-truststore-file-*` options and the truststore related options `https-trust-store-*` are deprecated, please use the new default location for truststore material, `conf/truststores`, or specify your desired paths via the `truststore-paths` option. For details refer to the relevant https://www.keycloak.org/server/keycloak-truststore[guide].
|
||||
|
||||
The `tls-hostname-verifier` property should be used instead of the `spi-truststore-file-hostname-verification-policy` property.
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
== Migration Changes
|
||||
|
||||
=== Migrating to 24.0.0
|
||||
|
||||
include::changes-24_0_0.adoc[leveloffset=3]
|
||||
|
||||
=== Migrating to 23.0.2
|
||||
|
||||
include::changes-23_0_2.adoc[leveloffset=3]
|
||||
|
|
|
@ -2,52 +2,36 @@
|
|||
<#import "/templates/kc.adoc" as kc>
|
||||
|
||||
<@tmpl.guide
|
||||
title="Configuring trusted certificates for outgoing requests"
|
||||
summary="How to configure the {project_name} Truststore to communicate with external services through TLS."
|
||||
includedOptions="">
|
||||
title="Configuring trusted certificates"
|
||||
summary="How to configure the {project_name} Truststore to communicate through TLS."
|
||||
includedOptions="truststore-paths tls-hostname-verifier">
|
||||
|
||||
When {project_name} communicates with external services through TLS, it has to validate the remote server’s certificate in order to ensure it is connecting to a trusted server. This is necessary in order to prevent man-in-the-middle attacks. The certificates of these remote server’s or the CA that signed these certificates must be put in a truststore. This truststore is managed by the Keycloak server.
|
||||
When {project_name} communicates with external services or has an incoming connection through TLS, it has to validate the remote certificate in order to ensure it is connecting to a trusted server. This is necessary in order to prevent man-in-the-middle attacks.
|
||||
|
||||
The truststore is used when connecting securely to identity brokers, LDAP identity providers, when sending emails, and for backchannel communication with client applications. It is also useful
|
||||
when you want to change the policy on how host names are verified and trusted by the server.
|
||||
The certificates of these clients or servers, or the CA that signed these certificates, must be put in a truststore. This truststore is then configured for use by Keycloak.
|
||||
|
||||
By default, a truststore provider is not configured, and any TLS/HTTPS connections fall back to standard Java Truststore configuration. If there is no trust established, then these outgoing requests will fail.
|
||||
== Configuring the System Truststore
|
||||
|
||||
== Configuring the {project_name} 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.
|
||||
|
||||
You can add your truststore configuration by entering this command:
|
||||
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.
|
||||
|
||||
<@kc.start parameters="--spi-truststore-file-file=myTrustStore.jks --spi-truststore-file-password=password --spi-truststore-file-hostname-verification-policy=ANY"/>
|
||||
After all applicable certs are included, the truststore will be used as the system default truststore via the `javax.net.ssl` properties, and as the default for internal usage within {project_name}.
|
||||
|
||||
The following are possible configuration options for this setting:
|
||||
For example:
|
||||
|
||||
file::
|
||||
The path to a Java keystore file.
|
||||
TLS requests need a way to verify the host of the server to which they are talking.
|
||||
This is what the truststore does.
|
||||
The keystore contains one or more trusted host certificates or certificate authorities.
|
||||
This truststore file should only contain public certificates of your secured hosts.
|
||||
This is _REQUIRED_ if any of these properties are defined.
|
||||
<@kc.start parameters="--truststore-paths=/opt/truststore/myTrustStore.pfx,/opt/other-truststore/myOtherTrustStore.pem" />
|
||||
|
||||
password::
|
||||
Password of the keystore.
|
||||
This option is _REQUIRED_ if any of these properties are defined.
|
||||
It is still possible to directly set your own `javax.net.ssl` truststore System properties, but it's recommended to use the `--truststore-paths` instead.
|
||||
|
||||
hostname-verification-policy::
|
||||
For HTTPS requests, this option verifies the hostname of the server's certificate. Default: `WILDCARD`
|
||||
== Hostname Verification Policy
|
||||
|
||||
You may refine how hostnames are verified by TLS connections with the `tls-hostname-verifier` property.
|
||||
|
||||
* `WILDCARD` (the default) allows wildcards in subdomain names, such as *.foo.com.
|
||||
* `ANY` means that the hostname is not verified.
|
||||
* `WILDCARD` allows wildcards in subdomain names, such as *.foo.com.
|
||||
* When using `STRICT`, the Common Name (CN) must match the hostname exactly.
|
||||
+
|
||||
Please note that this setting does not apply to LDAP secure connections, which require strict hostname checking.
|
||||
|
||||
type::
|
||||
The type of truststore, such as `jks`, `pkcs12` or `bcfks`. If not provided, the type would be detected based on the truststore
|
||||
file extension or platform default type.
|
||||
|
||||
=== Example of a truststore configuration
|
||||
The following is an example configuration for a truststore that allows you to create trustful connections to all `mycompany.org` domains and its subdomains:
|
||||
|
||||
<@kc.start parameters="--spi-truststore-file-file=path/to/truststore.jks --spi-truststore-file-password=change_me --spi-truststore-file-hostname-verification-policy=WILDCARD"/>
|
||||
|
||||
</@tmpl.guide>
|
||||
|
|
|
@ -89,17 +89,17 @@ public class HttpOptions {
|
|||
|
||||
public static final Option HTTPS_TRUST_STORE_FILE = new OptionBuilder<>("https-trust-store-file", File.class)
|
||||
.category(OptionCategory.HTTP)
|
||||
.description("The trust store which holds the certificate information of the certificates to trust.")
|
||||
.description("DEPRECATED: The trust store which holds the certificate information of the certificates to trust.")
|
||||
.build();
|
||||
|
||||
public static final Option HTTPS_TRUST_STORE_PASSWORD = new OptionBuilder<>("https-trust-store-password", String.class)
|
||||
.category(OptionCategory.HTTP)
|
||||
.description("The password of the trust store file.")
|
||||
.description("DEPRECATED: The password of the trust store file.")
|
||||
.build();
|
||||
|
||||
public static final Option<String> HTTPS_TRUST_STORE_TYPE = new OptionBuilder<>("https-trust-store-type", String.class)
|
||||
.category(OptionCategory.HTTP)
|
||||
.description("The type of the trust store file. " +
|
||||
.description("DEPRECATED: The type of the trust store file. " +
|
||||
"If not given, the type is automatically detected based on the file name. " +
|
||||
"If '" + SecurityOptions.FIPS_MODE.getKey() + "' is set to '" + FipsMode.STRICT + "' and no value is set, it defaults to 'BCFKS'.")
|
||||
.build();
|
||||
|
|
|
@ -7,13 +7,14 @@ public enum OptionCategory {
|
|||
TRANSACTION("Transaction",30, ConfigSupportLevel.SUPPORTED),
|
||||
FEATURE("Feature", 40, ConfigSupportLevel.SUPPORTED),
|
||||
HOSTNAME("Hostname", 50, ConfigSupportLevel.SUPPORTED),
|
||||
HTTP("HTTP/TLS", 60, ConfigSupportLevel.SUPPORTED),
|
||||
HTTP("HTTP(S)", 60, ConfigSupportLevel.SUPPORTED),
|
||||
HEALTH("Health", 70, ConfigSupportLevel.SUPPORTED),
|
||||
CONFIG("Config", 75, ConfigSupportLevel.SUPPORTED),
|
||||
METRICS("Metrics", 80, ConfigSupportLevel.SUPPORTED),
|
||||
PROXY("Proxy", 90, ConfigSupportLevel.SUPPORTED),
|
||||
VAULT("Vault", 100, ConfigSupportLevel.SUPPORTED),
|
||||
LOGGING("Logging", 110, ConfigSupportLevel.SUPPORTED),
|
||||
TRUSTSTORE("Truststore", 115, ConfigSupportLevel.SUPPORTED),
|
||||
SECURITY("Security", 120, ConfigSupportLevel.SUPPORTED),
|
||||
EXPORT("Export", 130, ConfigSupportLevel.SUPPORTED),
|
||||
IMPORT("Import", 140, ConfigSupportLevel.SUPPORTED),
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package org.keycloak.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.crypto.FipsMode;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class SecurityOptions {
|
||||
|
||||
public static final Option<FipsMode> FIPS_MODE = new OptionBuilder<>("fips-mode", FipsMode.class)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package org.keycloak.config;
|
||||
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
|
||||
public class TruststoreOptions {
|
||||
|
||||
public static final Option<String> TRUSTSTORE_PATHS = new OptionBuilder<>("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.")
|
||||
.build();
|
||||
|
||||
public static final Option<HostnameVerificationPolicy> HOSTNAME_VERIFICATION_POLICY = new OptionBuilder<>("tls-hostname-verifier", HostnameVerificationPolicy.class)
|
||||
.category(OptionCategory.TRUSTSTORE)
|
||||
.description("The TLS hostname verification policy for out-going HTTPS and SMTP requests.")
|
||||
.defaultValue(HostnameVerificationPolicy.WILDCARD)
|
||||
.build();
|
||||
|
||||
}
|
|
@ -230,6 +230,13 @@ class KeycloakProcessor {
|
|||
recorder.configureProfile(profile.getName(), profile.getFeatures());
|
||||
}
|
||||
|
||||
@Record(ExecutionTime.STATIC_INIT)
|
||||
@BuildStep
|
||||
@Consume(ConfigBuildItem.class)
|
||||
void configureTruststore(KeycloakRecorder recorder) {
|
||||
recorder.configureTruststore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether JDBC driver is present for the specified DB
|
||||
*
|
||||
|
|
8
quarkus/dist/assembly.xml
vendored
8
quarkus/dist/assembly.xml
vendored
|
@ -80,6 +80,14 @@
|
|||
<include>*.*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<!-- create an empty truststores directory -->
|
||||
<fileSet>
|
||||
<directory>src/main/content/conf</directory>
|
||||
<outputDirectory>conf/truststores</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>**/*</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>src/main/content/data</directory>
|
||||
<outputDirectory>data</outputDirectory>
|
||||
|
|
3
quarkus/dist/src/main/content/conf/README.md
vendored
3
quarkus/dist/src/main/content/conf/README.md
vendored
|
@ -2,3 +2,6 @@ Configure the server
|
|||
====================
|
||||
|
||||
Files in this directory are used to configure the server. Please consult the [configuration guides](https://www.keycloak.org/guides#server) for more information.
|
||||
|
||||
Use the truststores subdirectory to provide additional trusted certificates. Please consult the [truststore guide](https://www.keycloak.org/server/keycloak-truststore) for more information.
|
||||
|
||||
|
|
|
@ -17,11 +17,13 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.agroal.api.AgroalDataSource;
|
||||
import io.quarkus.agroal.DataSource;
|
||||
|
@ -32,13 +34,13 @@ import liquibase.Scope;
|
|||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.infinispan.manager.DefaultCacheManager;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.crypto.CryptoProvider;
|
||||
import org.keycloak.common.crypto.FipsMode;
|
||||
import org.keycloak.config.TruststoreOptions;
|
||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
||||
|
@ -48,6 +50,7 @@ import org.keycloak.provider.ProviderFactory;
|
|||
import org.keycloak.provider.Spi;
|
||||
import org.keycloak.quarkus.runtime.storage.legacy.infinispan.CacheManagerFactory;
|
||||
import org.keycloak.theme.ClasspathThemeProviderFactory;
|
||||
import org.keycloak.truststore.TruststoreBuilder;
|
||||
|
||||
import io.quarkus.runtime.RuntimeValue;
|
||||
import io.quarkus.runtime.ShutdownContext;
|
||||
|
@ -68,6 +71,23 @@ public class KeycloakRecorder {
|
|||
Profile.init(profileName, features);
|
||||
}
|
||||
|
||||
public void configureTruststore() {
|
||||
String[] truststores = Configuration.getOptionalKcValue(TruststoreOptions.TRUSTSTORE_PATHS.getKey())
|
||||
.map(s -> s.split(",")).orElse(new String[0]);
|
||||
|
||||
String dataDir = Environment.getDataDir();
|
||||
|
||||
File truststoresDir = Optional.ofNullable(Environment.getHomePath()).map(path -> path.resolve("conf").resolve("truststores").toFile()).orElse(null);
|
||||
|
||||
if (truststoresDir != null && truststoresDir.exists() && Optional.ofNullable(truststoresDir.list()).map(a -> a.length).orElse(0) > 0) {
|
||||
truststores = Stream.concat(Stream.of(truststoresDir.getAbsolutePath()), Stream.of(truststores)).toArray(String[]::new);
|
||||
} else if (truststores.length == 0) {
|
||||
return; // nothing to configure, we'll just use the system default
|
||||
}
|
||||
|
||||
TruststoreBuilder.setSystemTruststore(truststores, true, dataDir);
|
||||
}
|
||||
|
||||
public void configureLiquibase(Map<String, List<String>> services) {
|
||||
ServiceLocator locator = Scope.getCurrentScope().getServiceLocator();
|
||||
if (locator instanceof FastServiceLocator)
|
||||
|
|
|
@ -40,6 +40,7 @@ public final class PropertyMappers {
|
|||
MAPPERS.addAll(SecurityPropertyMappers.getMappers());
|
||||
MAPPERS.addAll(ExportPropertyMappers.getMappers());
|
||||
MAPPERS.addAll(ImportPropertyMappers.getMappers());
|
||||
MAPPERS.addAll(TruststorePropertyMappers.getMappers());
|
||||
}
|
||||
|
||||
public static ConfigValue getValue(ConfigSourceInterceptorContext context, String name) {
|
||||
|
|
|
@ -21,7 +21,7 @@ final class SecurityPropertyMappers {
|
|||
return new PropertyMapper[] {
|
||||
fromOption(SecurityOptions.FIPS_MODE).transformer(SecurityPropertyMappers::resolveFipsMode)
|
||||
.paramLabel("mode")
|
||||
.build()
|
||||
.build(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package org.keycloak.quarkus.runtime.configuration.mappers;
|
||||
|
||||
import org.keycloak.config.TruststoreOptions;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
|
||||
|
||||
public class TruststorePropertyMappers {
|
||||
|
||||
public static PropertyMapper[] getMappers() {
|
||||
return new PropertyMapper[] {
|
||||
fromOption(TruststoreOptions.TRUSTSTORE_PATHS)
|
||||
.paramLabel(TruststoreOptions.TRUSTSTORE_PATHS.getKey())
|
||||
.build(),
|
||||
fromOption(TruststoreOptions.HOSTNAME_VERIFICATION_POLICY)
|
||||
.paramLabel(TruststoreOptions.HOSTNAME_VERIFICATION_POLICY.getKey())
|
||||
.to("kc.spi-truststore-file-hostname-verification-policy")
|
||||
.build(),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -105,6 +105,20 @@ public class FipsDistTest {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnencryptedPkcs12TrustStoreInStrictMode(KeycloakDistribution dist) {
|
||||
runOnFipsEnabledDistribution(dist, () -> {
|
||||
String truststoreName = "keycloak-truststore.p12";
|
||||
dist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", truststoreName));
|
||||
|
||||
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
|
||||
Path truststorePath = rawDist.getDistPath().resolve("conf").resolve(truststoreName).toAbsolutePath();
|
||||
|
||||
CLIResult cliResult = dist.run("--verbose", "start", "--fips-mode=strict", "--truststore-paths=" + truststorePath);
|
||||
cliResult.assertStarted();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnsupportedHttpsPkcs12KeyStoreInStrictMode(KeycloakDistribution dist) {
|
||||
runOnFipsEnabledDistribution(dist, () -> {
|
||||
|
|
86
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TruststoreDistTest.java
vendored
Normal file
86
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TruststoreDistTest.java
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.it.cli.dist;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
import org.keycloak.it.utils.RawKeycloakDistribution;
|
||||
import org.keycloak.truststore.TruststoreBuilder;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
|
||||
@DistributionTest(keepAlive = true)
|
||||
@RawDistOnly(reason = "Containers are immutable")
|
||||
public class TruststoreDistTest {
|
||||
|
||||
@BeforeAll
|
||||
static void before() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMutualAuthWithTruststorePaths(KeycloakDistribution dist) {
|
||||
String[] truststoreNames = new String[] { "keycloak-truststore.p12", "self-signed.pem" };
|
||||
Stream.of(truststoreNames).forEach(truststoreName -> {
|
||||
dist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", truststoreName));
|
||||
});
|
||||
|
||||
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
|
||||
String paths = Stream.of(truststoreNames).map(truststoreName -> rawDist.getDistPath().resolve("conf")
|
||||
.resolve(truststoreName).toAbsolutePath().toString()).collect(Collectors.joining(","));
|
||||
dist.copyOrReplaceFileFromClasspath("/self-signed.p12", Path.of("conf", "self-signed.p12"));
|
||||
Path keyStore = rawDist.getDistPath().resolve("conf").resolve("self-signed.p12").toAbsolutePath();
|
||||
|
||||
rawDist.run("--verbose", "start", "--http-enabled=true", "--hostname=mykeycloak.org",
|
||||
"--truststore-paths=" + paths, "--https-client-auth=required", "--https-key-store-file=" + keyStore);
|
||||
|
||||
given().trustStore(TruststoreDistTest.class.getResource("/self-signed-truststore.p12").getPath(), TruststoreBuilder.DUMMY_PASSWORD)
|
||||
.keyStore(TruststoreDistTest.class.getResource("/self-signed.p12").getPath(), "password")
|
||||
.get("https://mykeycloak.org:8443").then().body(Matchers.containsString("https://mykeycloak.org"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMutualAuthWithDefaultTruststoresDir(KeycloakDistribution dist) {
|
||||
String[] truststoreNames = new String[] { "keycloak-truststore.p12", "self-signed.pem" };
|
||||
Stream.of(truststoreNames).forEach(truststoreName -> {
|
||||
dist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", "truststores", truststoreName));
|
||||
});
|
||||
|
||||
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
|
||||
dist.copyOrReplaceFileFromClasspath("/self-signed.p12", Path.of("conf", "self-signed.p12"));
|
||||
Path keyStore = rawDist.getDistPath().resolve("conf").resolve("self-signed.p12").toAbsolutePath();
|
||||
|
||||
rawDist.run("--verbose", "start", "--http-enabled=true", "--hostname=mykeycloak.org",
|
||||
"--https-client-auth=required", "--https-key-store-file=" + keyStore);
|
||||
|
||||
given().trustStore(TruststoreDistTest.class.getResource("/self-signed-truststore.p12").getPath(), TruststoreBuilder.DUMMY_PASSWORD)
|
||||
.keyStore(TruststoreDistTest.class.getResource("/self-signed.p12").getPath(), "password")
|
||||
.get("https://mykeycloak.org:8443").then().body(Matchers.containsString("https://mykeycloak.org"));
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
|
@ -60,7 +60,7 @@ Feature:
|
|||
multi-site, par, preview, recovery-codes, scripts, step-up-authentication,
|
||||
token-exchange, transient-users, update-email, web-authn.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-relative-path <path>
|
||||
Set the path relative to '/' for serving resources. The path must start with a
|
||||
|
|
|
@ -60,7 +60,7 @@ Feature:
|
|||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-relative-path <path>
|
||||
Set the path relative to '/' for serving resources. The path must start with a
|
||||
|
|
|
@ -130,6 +130,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Export:
|
||||
|
||||
--dir <dir> Set the path to a directory where files will be created with the exported data.
|
||||
|
|
|
@ -130,6 +130,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Export:
|
||||
|
||||
--dir <dir> Set the path to a directory where files will be created with the exported data.
|
||||
|
|
|
@ -130,6 +130,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Import:
|
||||
|
||||
--dir <dir> Set the path to a directory where files will be read from.
|
||||
|
|
|
@ -130,6 +130,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Import:
|
||||
|
||||
--dir <dir> Set the path to a directory where files will be read from.
|
||||
|
|
|
@ -116,7 +116,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -150,14 +150,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Health:
|
||||
|
||||
|
@ -245,6 +245,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Security:
|
||||
|
||||
--fips-mode <mode> Sets the FIPS mode. If 'non-strict' is set, FIPS is enabled but on
|
||||
|
|
|
@ -111,7 +111,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -145,14 +145,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Health:
|
||||
|
||||
|
@ -228,6 +228,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Security:
|
||||
|
||||
--fips-mode <mode> Sets the FIPS mode. If 'non-strict' is set, FIPS is enabled but on
|
||||
|
|
|
@ -116,7 +116,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -150,14 +150,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Health:
|
||||
|
||||
|
@ -245,6 +245,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Security:
|
||||
|
||||
--fips-mode <mode> Sets the FIPS mode. If 'non-strict' is set, FIPS is enabled but on
|
||||
|
|
|
@ -111,7 +111,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -145,14 +145,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Health:
|
||||
|
||||
|
@ -228,6 +228,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Security:
|
||||
|
||||
--fips-mode <mode> Sets the FIPS mode. If 'non-strict' is set, FIPS is enabled but on
|
||||
|
|
|
@ -117,7 +117,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -151,14 +151,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Health:
|
||||
|
||||
|
@ -246,6 +246,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Security:
|
||||
|
||||
--fips-mode <mode> Sets the FIPS mode. If 'non-strict' is set, FIPS is enabled but on
|
||||
|
|
|
@ -112,7 +112,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -146,14 +146,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Health:
|
||||
|
||||
|
@ -229,6 +229,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Security:
|
||||
|
||||
--fips-mode <mode> Sets the FIPS mode. If 'non-strict' is set, FIPS is enabled but on
|
||||
|
|
|
@ -117,7 +117,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -151,14 +151,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Health:
|
||||
|
||||
|
@ -246,6 +246,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Security:
|
||||
|
||||
--fips-mode <mode> Sets the FIPS mode. If 'non-strict' is set, FIPS is enabled but on
|
||||
|
|
|
@ -112,7 +112,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -146,14 +146,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Health:
|
||||
|
||||
|
@ -229,6 +229,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
Security:
|
||||
|
||||
--fips-mode <mode> Sets the FIPS mode. If 'non-strict' is set, FIPS is enabled but on
|
||||
|
|
|
@ -76,7 +76,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -107,14 +107,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Config:
|
||||
|
||||
|
@ -188,6 +188,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
By default, this command tries to update the server configuration by running a
|
||||
'build' before starting the server. You can disable this behavior by using the
|
||||
'--optimized' option:
|
||||
|
|
|
@ -69,7 +69,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -100,14 +100,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Proxy:
|
||||
|
||||
|
@ -169,6 +169,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
By default, this command tries to update the server configuration by running a
|
||||
'build' before starting the server. You can disable this behavior by using the
|
||||
'--optimized' option:
|
||||
|
|
|
@ -76,7 +76,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -107,14 +107,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Config:
|
||||
|
||||
|
@ -188,6 +188,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
By default, this command tries to update the server configuration by running a
|
||||
'build' before starting the server. You can disable this behavior by using the
|
||||
'--optimized' option:
|
||||
|
|
|
@ -69,7 +69,7 @@ Hostname:
|
|||
URL this option should be enabled. Default: false.
|
||||
--hostname-url <url> Set the base URL for frontend URLs, including scheme, host, port and path.
|
||||
|
||||
HTTP/TLS:
|
||||
HTTP(S):
|
||||
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Default: false.
|
||||
|
@ -100,14 +100,14 @@ HTTP/TLS:
|
|||
--https-protocols <protocols>
|
||||
The list of protocols to explicitly enable. Default: TLSv1.3,TLSv1.2.
|
||||
--https-trust-store-file <file>
|
||||
The trust store which holds the certificate information of the certificates to
|
||||
trust.
|
||||
DEPRECATED: The trust store which holds the certificate information of the
|
||||
certificates to trust.
|
||||
--https-trust-store-password <password>
|
||||
The password of the trust store file.
|
||||
DEPRECATED: The password of the trust store file.
|
||||
--https-trust-store-type <type>
|
||||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name. If 'fips-mode' is set to 'strict' and no
|
||||
value is set, it defaults to 'BCFKS'.
|
||||
DEPRECATED: The type of the trust store file. If not given, the type is
|
||||
automatically detected based on the file name. If 'fips-mode' is set to
|
||||
'strict' and no value is set, it defaults to 'BCFKS'.
|
||||
|
||||
Proxy:
|
||||
|
||||
|
@ -169,6 +169,15 @@ Logging:
|
|||
categories and their levels. For the root category, you don't need to
|
||||
specify a category. Default: info.
|
||||
|
||||
Truststore:
|
||||
|
||||
--tls-hostname-verifier <tls-hostname-verifier>
|
||||
The TLS hostname verification policy for out-going HTTPS and SMTP requests.
|
||||
Possible values are: ANY, WILDCARD, STRICT. Default: WILDCARD.
|
||||
--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.
|
||||
|
||||
By default, this command tries to update the server configuration by running a
|
||||
'build' before starting the server. You can disable this behavior by using the
|
||||
'--optimized' option:
|
||||
|
|
Binary file not shown.
BIN
quarkus/tests/integration/src/test/resources/self-signed.p12
Normal file
BIN
quarkus/tests/integration/src/test/resources/self-signed.p12
Normal file
Binary file not shown.
21
quarkus/tests/integration/src/test/resources/self-signed.pem
Normal file
21
quarkus/tests/integration/src/test/resources/self-signed.pem
Normal file
|
@ -0,0 +1,21 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDezCCAmOgAwIBAgIEM0VH1DANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQGEwJL
|
||||
QzERMA8GA1UECBMIa2V5Y2xvYWsxETAPBgNVBAcTCGtleWNsb2FrMREwDwYDVQQK
|
||||
EwhrZXljbG9hazERMA8GA1UECxMIa2V5Y2xvYWsxEjAQBgNVBAMTCUtleSBDbG9h
|
||||
azAgFw0yMzExMDkxMjMxNDZaGA8yMDUxMDMyNjEyMzE0NlowbTELMAkGA1UEBhMC
|
||||
S0MxETAPBgNVBAgTCGtleWNsb2FrMREwDwYDVQQHEwhrZXljbG9hazERMA8GA1UE
|
||||
ChMIa2V5Y2xvYWsxETAPBgNVBAsTCGtleWNsb2FrMRIwEAYDVQQDEwlLZXkgQ2xv
|
||||
YWswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv1M8ZiKSONnryBcTy
|
||||
u6u0d/cJ9s0kZAvO+Q62I5W6e6r0zFYlUjheRSTF7PdypAxhJPMRVGZoE0WCKUMn
|
||||
4D1E43ZC8bl8IgzpC8m2hbtJsVcbZs0H4D9jHjHvBiPgdkmTc7F/pU8jk8oD3L+Y
|
||||
olzOFLI/wpwC7kx9O1bzXVpTKOg12JqrKRHIfdvU4mbMlRTRX+AX6Iw5QMiy8vfA
|
||||
77uCkkxPInx18Z4MU6vqjvAWXos5gQE1fVUyHsmWlBp8Eis7jD7d52Th1uK3nJLo
|
||||
4wnKzNXDXq34o25xR6j1BpU+v8iqkcDdpJncQHuo/iHK2WZh8xgQMxGoc33nslxB
|
||||
JvuPAgMBAAGjITAfMB0GA1UdDgQWBBTp2UwAeIxtlKqNFrW4jUVnJ8CgAjANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEANfkQnompu7woA7fCJb5O+WCNTGoAdFO36tjyRczbRO2G
|
||||
W7GqPFupObQhAQTtZ55pBfhZTz30lrM9C6vAMDIhfqjYfdATeflALCZH4lbLRcxf
|
||||
bMq/FVRz1p+hEUGwNeguxCiZ7ZqfqchwiW8oeEeG4MYdYOfdR1pglF6Ak0pkWwqE
|
||||
KqfuTjllGJRkLJco6ApDA6viBnGUX9T4Xb+xsfzTiOj93k9Z1aWzLB4MCupS+Z1h
|
||||
VC39DBEUe04OEczu8ZT4A20hiOp+wA2YuToxVVncpnMdZhe1hbzKrTTflM22mcs1
|
||||
6Fqjo2BBJQ/sfhWMB7KYpnI78n/bkVP2S4hE3JChXA==
|
||||
-----END CERTIFICATE-----
|
|
@ -258,11 +258,12 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
|||
URL contextRoot = new URL(scheme + "://localhost:" + port + ("/" + relativePath + "/realms/master/").replace("//", "/"));
|
||||
HttpURLConnection connection = null;
|
||||
long startTime = System.currentTimeMillis();
|
||||
Exception ex = null;
|
||||
|
||||
while (true) {
|
||||
if (System.currentTimeMillis() - startTime > getStartTimeout()) {
|
||||
throw new IllegalStateException(
|
||||
"Timeout [" + getStartTimeout() + "] while waiting for Quarkus server");
|
||||
"Timeout [" + getStartTimeout() + "] while waiting for Quarkus server", ex);
|
||||
}
|
||||
|
||||
if (!keycloak.isAlive()) {
|
||||
|
@ -287,6 +288,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
|||
break;
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
ex = ignore;
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
|
@ -314,12 +316,15 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
|||
|
||||
private SSLSocketFactory createInsecureSslSocketFactory() throws IOException {
|
||||
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.truststore;
|
||||
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
|
|
@ -202,7 +202,7 @@ public class DefaultHttpClientFactory implements HttpClientFactory {
|
|||
if (disableTruststoreProvider) {
|
||||
logger.warn("TruststoreProvider is disabled");
|
||||
} else {
|
||||
builder.hostnameVerification(HttpClientBuilder.HostnameVerificationPolicy.valueOf(truststoreProvider.getPolicy().name()));
|
||||
builder.hostnameVerification(truststoreProvider.getPolicy());
|
||||
try {
|
||||
builder.trustStore(truststoreProvider.getTruststore());
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.http.conn.ssl.X509HostnameVerifier;
|
|||
import org.apache.http.impl.NoConnectionReuseStrategy;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
@ -53,35 +54,23 @@ import java.util.concurrent.TimeUnit;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HttpClientBuilder {
|
||||
public enum HostnameVerificationPolicy {
|
||||
/**
|
||||
* Hostname verification is not done on the server's certificate
|
||||
*/
|
||||
ANY,
|
||||
/**
|
||||
* Allows wildcards in subdomain names i.e. *.foo.com
|
||||
*/
|
||||
WILDCARD,
|
||||
/**
|
||||
* CN must match hostname connecting to
|
||||
*/
|
||||
STRICT
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
private static class PassthroughTrustManager implements X509TrustManager {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ package org.keycloak.email;
|
|||
|
||||
import jakarta.mail.internet.MimeUtility;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.truststore.HostnameVerificationPolicy;
|
||||
import org.keycloak.truststore.JSSETruststoreConfigurator;
|
||||
import org.keycloak.vault.VaultStringSecret;
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.keycloak.truststore;
|
||||
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Map;
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.truststore;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
@ -27,7 +28,6 @@ import org.keycloak.provider.ProviderConfigurationBuilder;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyStore;
|
||||
|
@ -53,6 +53,8 @@ import javax.security.auth.x500.X500Principal;
|
|||
*/
|
||||
public class FileTruststoreProviderFactory implements TruststoreProviderFactory {
|
||||
|
||||
static final String HOSTNAME_VERIFICATION_POLICY = "hostname-verification-policy";
|
||||
|
||||
private static final Logger log = Logger.getLogger(FileTruststoreProviderFactory.class);
|
||||
|
||||
private TruststoreProvider provider;
|
||||
|
@ -72,30 +74,41 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
|||
|
||||
String storepath = config.get("file");
|
||||
String pass = config.get("password");
|
||||
String policy = config.get("hostname-verification-policy");
|
||||
String policy = config.get(HOSTNAME_VERIFICATION_POLICY);
|
||||
String configuredType = config.get("type");
|
||||
|
||||
// if "truststore" . "file" is not configured then it is disabled
|
||||
if (storepath == null && pass == null && policy == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HostnameVerificationPolicy verificationPolicy = null;
|
||||
KeyStore truststore = null;
|
||||
|
||||
boolean system = false;
|
||||
if (storepath == null) {
|
||||
throw new RuntimeException("Attribute 'file' missing in 'truststore':'file' configuration");
|
||||
storepath = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY);
|
||||
if (storepath == null) {
|
||||
File defaultTrustStore = TruststoreBuilder.getJRETruststore();
|
||||
if (!defaultTrustStore.exists()) {
|
||||
throw new RuntimeException("Attribute 'file' missing in 'truststore':'file' configuration, and could not find the system truststore");
|
||||
}
|
||||
if (pass == null) {
|
||||
throw new RuntimeException("Attribute 'password' missing in 'truststore':'file' configuration");
|
||||
storepath = defaultTrustStore.getAbsolutePath();
|
||||
system = true;
|
||||
}
|
||||
// should there be an exception if pass / type are configured for the spi-truststore
|
||||
pass = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY, system ? "changeit" : null);
|
||||
configuredType = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY);
|
||||
}
|
||||
|
||||
String type = KeystoreUtil.getKeystoreType(configuredType, storepath, KeyStore.getDefaultType());
|
||||
try {
|
||||
truststore = loadStore(storepath, type, pass == null ? null :pass.toCharArray());
|
||||
} catch (Exception e) {
|
||||
// in fips mode the default truststore type can be pkcs12, but the cacerts file will still be jks
|
||||
if (system && !"jks".equalsIgnoreCase(type)) {
|
||||
try {
|
||||
truststore = loadStore(storepath, "jks", pass == null ? null :pass.toCharArray());
|
||||
} catch (Exception e1) {
|
||||
}
|
||||
}
|
||||
if (truststore == null) {
|
||||
throw new RuntimeException("Failed to initialize TruststoreProviderFactory: " + new File(storepath).getAbsolutePath() + ", truststore type: " + type, e);
|
||||
}
|
||||
}
|
||||
if (policy == null) {
|
||||
verificationPolicy = HostnameVerificationPolicy.WILDCARD;
|
||||
} else {
|
||||
|
@ -115,15 +128,9 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
|||
|
||||
private KeyStore loadStore(String path, String type, char[] password) throws Exception {
|
||||
KeyStore ks = KeyStore.getInstance(type);
|
||||
InputStream is = new FileInputStream(path);
|
||||
try {
|
||||
try (InputStream is = new FileInputStream(path)) {
|
||||
ks.load(is, password);
|
||||
return ks;
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,24 +153,24 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
|||
.property()
|
||||
.name("file")
|
||||
.type("string")
|
||||
.helpText("The file path of the trust store from where the certificates are going to be read from to validate TLS connections.")
|
||||
.helpText("DEPRECATED: The file path of the trust store from where the certificates are going to be read from to validate TLS connections.")
|
||||
.add()
|
||||
.property()
|
||||
.name("password")
|
||||
.type("string")
|
||||
.helpText("The trust store password.")
|
||||
.helpText("DEPRECATED: The trust store password.")
|
||||
.add()
|
||||
.property()
|
||||
.name("hostname-verification-policy")
|
||||
.name(HOSTNAME_VERIFICATION_POLICY)
|
||||
.type("string")
|
||||
.helpText("The hostname verification policy.")
|
||||
.helpText("DEPRECATED: The hostname verification policy.")
|
||||
.options(Arrays.stream(HostnameVerificationPolicy.values()).map(HostnameVerificationPolicy::name).map(String::toLowerCase).toArray(String[]::new))
|
||||
.defaultValue(HostnameVerificationPolicy.WILDCARD.name().toLowerCase())
|
||||
.add()
|
||||
.property()
|
||||
.name("type")
|
||||
.type("string")
|
||||
.helpText("Type of the truststore. If not provided, the type would be detected based on the truststore file extension or platform default type.")
|
||||
.helpText("DEPRECATED: Type of the truststore. If not provided, the type would be detected based on the truststore file extension or platform default type.")
|
||||
.add()
|
||||
.build();
|
||||
}
|
||||
|
@ -184,14 +191,14 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
|||
private void readTruststore(KeyStore truststore) {
|
||||
|
||||
//Reading truststore aliases & certificates
|
||||
Enumeration enumeration;
|
||||
Enumeration<String> enumeration;
|
||||
|
||||
try {
|
||||
|
||||
enumeration = truststore.aliases();
|
||||
log.trace("Checking " + truststore.size() + " entries from the truststore.");
|
||||
while(enumeration.hasMoreElements()) {
|
||||
String alias = (String)enumeration.nextElement();
|
||||
String alias = enumeration.nextElement();
|
||||
readTruststoreEntry(truststore, alias);
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
|
|
|
@ -17,14 +17,10 @@
|
|||
|
||||
package org.keycloak.truststore;
|
||||
|
||||
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.StrictHostnameVerifier;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
|
@ -94,29 +90,6 @@ public class JSSETruststoreConfigurator {
|
|||
return tm;
|
||||
}
|
||||
|
||||
public HostnameVerifier getHostnameVerifier() {
|
||||
if (provider == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HostnameVerificationPolicy policy = provider.getPolicy();
|
||||
switch (policy) {
|
||||
case ANY:
|
||||
return new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(String s, SSLSession sslSession) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
case WILDCARD:
|
||||
return new BrowserCompatHostnameVerifier();
|
||||
case STRICT:
|
||||
return new StrictHostnameVerifier();
|
||||
default:
|
||||
throw new IllegalStateException("Unknown policy: " + policy.name());
|
||||
}
|
||||
}
|
||||
|
||||
public TruststoreProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.truststore;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Builds a system-wide truststore from the given config options.
|
||||
*/
|
||||
public class TruststoreBuilder {
|
||||
|
||||
public static final String SYSTEM_TRUSTSTORE_KEY = "javax.net.ssl.trustStore";
|
||||
public static final String SYSTEM_TRUSTSTORE_PASSWORD_KEY = "javax.net.ssl.trustStorePassword";
|
||||
public static final String SYSTEM_TRUSTSTORE_TYPE_KEY = "javax.net.ssl.trustStoreType";
|
||||
private static final String CERT_PROTECTION_ALGORITHM_KEY = "keystore.pkcs12.certProtectionAlgorithm";
|
||||
public static final String DUMMY_PASSWORD = "keycloakchangeit"; // fips length compliant dummy password
|
||||
static final String PKCS12 = "PKCS12";
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(TruststoreBuilder.class);
|
||||
|
||||
public static void setSystemTruststore(String[] truststores, boolean trustStoreIncludeDefault, String dataDir) {
|
||||
KeyStore truststore = createMergedTruststore(truststores, trustStoreIncludeDefault);
|
||||
|
||||
// save with a dummy password just in case some logic that uses the system properties needs to have one
|
||||
File file = saveTruststore(truststore, dataDir, DUMMY_PASSWORD.toCharArray());
|
||||
|
||||
// finally update the system properties
|
||||
System.setProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY, file.getAbsolutePath());
|
||||
System.setProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY, PKCS12);
|
||||
System.setProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY, DUMMY_PASSWORD);
|
||||
}
|
||||
|
||||
static File saveTruststore(KeyStore truststore, String dataDir, char[] password) {
|
||||
File file = new File(dataDir, "keycloak-truststore.p12");
|
||||
file.getParentFile().mkdirs();
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
// this should inhibit the use of encryption in storing the certs
|
||||
// it's of course not concurrency safe, but it should only be run at startup
|
||||
String oldValue = System.setProperty(CERT_PROTECTION_ALGORITHM_KEY, "NONE");
|
||||
truststore.store(fos, password);
|
||||
if (oldValue != null) {
|
||||
System.setProperty(CERT_PROTECTION_ALGORITHM_KEY, oldValue);
|
||||
} else {
|
||||
System.getProperties().remove(CERT_PROTECTION_ALGORITHM_KEY);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to save truststore: " + file.getAbsolutePath(), e);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
static KeyStore createMergedTruststore(String[] truststores, boolean trustStoreIncludeDefault) {
|
||||
KeyStore truststore = createPkcs12KeyStore();
|
||||
|
||||
if (trustStoreIncludeDefault) {
|
||||
includeDefaultTruststore(truststore);
|
||||
}
|
||||
|
||||
mergeFiles(truststores, truststore, true);
|
||||
return truststore;
|
||||
}
|
||||
|
||||
private static void mergeFiles(String[] truststores, KeyStore truststore, boolean topLevel) {
|
||||
for (String file : truststores) {
|
||||
File f = new File(file);
|
||||
if (f.isDirectory()) {
|
||||
mergeFiles(Stream.of(f.listFiles()).map(File::getAbsolutePath).toArray(String[]::new), truststore, false);
|
||||
} else {
|
||||
if (file.endsWith(".p12") || file.endsWith(".pfx")) {
|
||||
mergeTrustStore(truststore, file, loadStore(file, PKCS12, null));
|
||||
} else {
|
||||
mergePemFile(truststore, file, topLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static KeyStore createPkcs12KeyStore() {
|
||||
try {
|
||||
KeyStore truststore = KeyStore.getInstance(PKCS12);
|
||||
truststore.load(null, null);
|
||||
return truststore;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to initialize truststore: cannot create a PKCS12 keystore", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Include the default truststore, if it can be found.
|
||||
* <p>
|
||||
* The existing system properties will be preserved so that this logic can be rerun without consuming
|
||||
* the newly created merged truststore.
|
||||
*/
|
||||
static void includeDefaultTruststore(KeyStore truststore) {
|
||||
String originalTruststoreKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY + ".orig";
|
||||
String originalTruststoreTypeKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY + ".orig";
|
||||
String originalTruststorePasswordKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY + ".orig";
|
||||
|
||||
String trustStorePath = System.getProperty(originalTruststoreKey);
|
||||
String type = PKCS12;
|
||||
String password = null;
|
||||
File defaultTrustStore = null;
|
||||
if (trustStorePath == null) {
|
||||
trustStorePath = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY);
|
||||
if (trustStorePath == null) {
|
||||
defaultTrustStore = getJRETruststore();
|
||||
} else {
|
||||
type = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY, KeyStore.getDefaultType());
|
||||
password = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY);
|
||||
// save the original information
|
||||
System.setProperty(originalTruststoreKey, trustStorePath);
|
||||
System.setProperty(originalTruststoreTypeKey, type);
|
||||
if (password == null) {
|
||||
System.getProperties().remove(originalTruststorePasswordKey);
|
||||
} else {
|
||||
System.setProperty(originalTruststorePasswordKey, password);
|
||||
}
|
||||
defaultTrustStore = new File(trustStorePath);
|
||||
}
|
||||
} else {
|
||||
type = System.getProperty(originalTruststoreTypeKey);
|
||||
password = System.getProperty(originalTruststorePasswordKey);
|
||||
defaultTrustStore = new File(trustStorePath);
|
||||
}
|
||||
|
||||
if (defaultTrustStore.exists()) {
|
||||
String path = defaultTrustStore.getAbsolutePath();
|
||||
mergeTrustStore(truststore, path,
|
||||
loadStore(path, type, Optional.ofNullable(password).map(String::toCharArray).orElse(null)));
|
||||
} else {
|
||||
LOGGER.warnf("Default truststore was to be included, but could not be found at: %s", defaultTrustStore);
|
||||
}
|
||||
}
|
||||
|
||||
static File getJRETruststore() {
|
||||
// try jre locations - there doesn't seem to be a good default mechanism for this
|
||||
String securityDirectory = System.getProperty("java.home") + File.separator + "lib" + File.separator
|
||||
+ "security";
|
||||
File jssecacertsFile = new File(securityDirectory, "jssecacerts");
|
||||
if (jssecacertsFile.exists() && jssecacertsFile.isFile()) {
|
||||
return jssecacertsFile;
|
||||
}
|
||||
return new File(securityDirectory, "cacerts");
|
||||
}
|
||||
|
||||
static KeyStore loadStore(String path, String type, char[] password) {
|
||||
try (InputStream is = new FileInputStream(path)) {
|
||||
KeyStore ks = KeyStore.getInstance(type);
|
||||
ks.load(is, password);
|
||||
return ks;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Failed to initialize truststore: " + new File(path).getAbsolutePath() + ", type: " + type, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void mergePemFile(KeyStore truststore, String file, boolean isPem) {
|
||||
try (FileInputStream pemInputStream = new FileInputStream(file)) {
|
||||
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
|
||||
boolean loadedAny = false;
|
||||
while (pemInputStream.available() > 0) {
|
||||
X509Certificate cert;
|
||||
try {
|
||||
cert = (X509Certificate) certFactory.generateCertificate(pemInputStream);
|
||||
loadedAny = true;
|
||||
} catch (CertificateException e) {
|
||||
if (pemInputStream.available() > 0 || !loadedAny) {
|
||||
// any remaining input means there is an actual problem with the key contents or
|
||||
// file format
|
||||
if (isPem) {
|
||||
throw e;
|
||||
}
|
||||
LOGGER.infof(e,
|
||||
"The file %s may not be in PEM format, it will not be used to create the merged truststore",
|
||||
new File(file).getAbsolutePath());
|
||||
continue;
|
||||
}
|
||||
LOGGER.debugf(e,
|
||||
"The trailing entry for %s generated a certificate exception, assuming instead that the file ends with comments",
|
||||
new File(file).getAbsolutePath());
|
||||
continue;
|
||||
}
|
||||
setCertificateEntry(truststore, cert);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Failed to initialize truststore, could not merge: " + new File(file).getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setCertificateEntry(KeyStore truststore, Certificate cert) throws KeyStoreException {
|
||||
String alias = null;
|
||||
if (cert instanceof X509Certificate) {
|
||||
X509Certificate x509Cert = (X509Certificate)cert;
|
||||
// use an alias that should be unique, yet deterministic
|
||||
alias = x509Cert.getSubjectX500Principal().getName() + "_" + x509Cert.getSerialNumber().toString(16);
|
||||
} else {
|
||||
// isn't expected
|
||||
alias = String.valueOf(Collections.list(truststore.aliases()).size());
|
||||
}
|
||||
truststore.setCertificateEntry(alias, cert);
|
||||
}
|
||||
|
||||
private static void mergeTrustStore(KeyStore truststore, String file, KeyStore additionalStore) {
|
||||
try {
|
||||
for (String alias : Collections.list(additionalStore.aliases())) {
|
||||
if (additionalStore.isCertificateEntry(alias)) {
|
||||
setCertificateEntry(truststore, additionalStore.getCertificate(alias));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Failed to initialize truststore, could not merge: " + new File(file).getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@
|
|||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
|
@ -26,7 +25,6 @@ import java.net.UnknownHostException;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
|
||||
|
@ -39,11 +37,7 @@ import org.junit.Test;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.DefaultKeycloakSession;
|
||||
import org.keycloak.services.DefaultKeycloakSessionFactory;
|
||||
import org.keycloak.services.util.JsonConfigProvider;
|
||||
import org.keycloak.services.util.JsonConfigProvider.JsonScope;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.keycloak.utils.ScopeUtil;
|
||||
|
||||
public class DefaultHttpClientFactoryTest {
|
||||
private static final String DISABLE_TRUST_MANAGER_PROPERTY = "disable-trust-manager";
|
||||
|
@ -54,12 +48,12 @@ public class DefaultHttpClientFactoryTest {
|
|||
Map<String, String> values = new HashMap<>();
|
||||
values.put(DISABLE_TRUST_MANAGER_PROPERTY, "true");
|
||||
DefaultHttpClientFactory factory = new DefaultHttpClientFactory();
|
||||
factory.init(scope(values));
|
||||
factory.init(ScopeUtil.createScope(values));
|
||||
KeycloakSession session = new DefaultKeycloakSession(new DefaultKeycloakSessionFactory());
|
||||
HttpClientProvider provider = factory.create(session);
|
||||
Optional<String> testURL = getTestURL();
|
||||
Assume.assumeTrue( "Could not get test url for domain", testURL.isPresent() );
|
||||
try (CloseableHttpClient httpClient = (CloseableHttpClient) provider.getHttpClient();
|
||||
try (CloseableHttpClient httpClient = provider.getHttpClient();
|
||||
CloseableHttpResponse response = httpClient.execute(new HttpGet(testURL.get()))) {
|
||||
assertEquals(HttpStatus.SC_NOT_FOUND,response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
@ -68,42 +62,16 @@ public class DefaultHttpClientFactoryTest {
|
|||
@Test(expected = SSLPeerUnverifiedException.class)
|
||||
public void createHttpClientProviderWithUnvailableURL() throws IOException {
|
||||
DefaultHttpClientFactory factory = new DefaultHttpClientFactory();
|
||||
factory.init(scope(new HashMap<>()));
|
||||
factory.init(ScopeUtil.createScope(new HashMap<>()));
|
||||
KeycloakSession session = new DefaultKeycloakSession(new DefaultKeycloakSessionFactory());
|
||||
HttpClientProvider provider = factory.create(session);
|
||||
try (CloseableHttpClient httpClient = (CloseableHttpClient) provider.getHttpClient()) {
|
||||
try (CloseableHttpClient httpClient = provider.getHttpClient()) {
|
||||
Optional<String> testURL = getTestURL();
|
||||
Assume.assumeTrue("Could not get test url for domain", testURL.isPresent());
|
||||
httpClient.execute(new HttpGet(testURL.get()));
|
||||
}
|
||||
}
|
||||
|
||||
private JsonScope scope(Map<String, String> properties) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
JsonNode config = mapper.readTree(json(properties));
|
||||
return new JsonConfigProvider(config,new Properties()).new JsonScope(config);
|
||||
} catch (IOException e) {
|
||||
fail("Could not parse json");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String json(Map<String, String> properties) {
|
||||
String[] params = properties.entrySet().stream().map(e -> param(e.getKey(), e.getValue())).toArray(String[]::new);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{");
|
||||
sb.append(String.join(",", params));
|
||||
sb.append("}");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String param(String key, String value) {
|
||||
return "\"" + key + "\"" + " : " + "\"" + value + "\"";
|
||||
}
|
||||
|
||||
private Optional<String> getTestURL() {
|
||||
try {
|
||||
// Convert domain name to ip to make request by ip
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
*/
|
||||
package org.keycloak.protocol.saml.profile.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
@ -28,8 +26,6 @@ import java.net.InetSocketAddress;
|
|||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.xml.soap.SOAPException;
|
||||
import jakarta.xml.soap.SOAPMessage;
|
||||
|
@ -57,8 +53,7 @@ import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
|
|||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.services.DefaultKeycloakSession;
|
||||
import org.keycloak.services.DefaultKeycloakSessionFactory;
|
||||
import org.keycloak.services.util.JsonConfigProvider;
|
||||
import org.keycloak.services.util.JsonConfigProvider.JsonScope;
|
||||
import org.keycloak.utils.ScopeUtil;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
|
@ -101,33 +96,6 @@ public class SoapTest {
|
|||
server.stop(0);
|
||||
}
|
||||
|
||||
|
||||
private String param(String key, String value) {
|
||||
return "\"" + key + "\"" + " : " + "\"" + value + "\"";
|
||||
}
|
||||
|
||||
private String json(Map<String, String> properties) {
|
||||
String[] params = properties.entrySet().stream().map(e -> param(e.getKey(), e.getValue())).toArray(String[]::new);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{");
|
||||
sb.append(String.join(",", params));
|
||||
sb.append("}");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private JsonScope createScope(Map<String, String> properties) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
JsonNode config = mapper.readTree(json(properties));
|
||||
return new JsonConfigProvider(config, new Properties()).new JsonScope(config);
|
||||
} catch (IOException e) {
|
||||
Assert.fail("Could not parse json");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private LogoutRequestType createLogoutRequestType() throws ConfigurationException {
|
||||
NameIDType nameId = new NameIDType();
|
||||
nameId.setFormat(URI.create(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get()));
|
||||
|
@ -181,9 +149,9 @@ public class SoapTest {
|
|||
@Override
|
||||
public Config.Scope scope(String... scope) {
|
||||
if (scope.length == 2 && "connectionsHttpClient".equals(scope[0]) && "default".equals(scope[1])) {
|
||||
return createScope(Collections.singletonMap("proxy-mappings", "localhost;http://localhost:8281"));
|
||||
return ScopeUtil.createScope(Collections.singletonMap("proxy-mappings", "localhost;http://localhost:8281"));
|
||||
}
|
||||
return createScope(new HashMap<>());
|
||||
return ScopeUtil.createScope(new HashMap<>());
|
||||
}
|
||||
});
|
||||
DefaultKeycloakSessionFactory sessionFactory = new DefaultKeycloakSessionFactory();
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.truststore;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
import org.keycloak.utils.ScopeUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class FileTruststoreProviderFactoryTest {
|
||||
|
||||
@Test
|
||||
public void testFallbackToSystemTruststore() throws IOException {
|
||||
FileTruststoreProviderFactory factory = new FileTruststoreProviderFactory();
|
||||
factory.init(ScopeUtil.createScope(new HashMap<>()));
|
||||
TruststoreProvider provider = factory.create(null);
|
||||
assertNotNull(provider.getTruststore());
|
||||
assertEquals(HostnameVerificationPolicy.WILDCARD, provider.getPolicy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFallbackToSystemTruststoreWithHostnameVerification() throws IOException {
|
||||
Map<String, String> values = new HashMap<>();
|
||||
values.put(FileTruststoreProviderFactory.HOSTNAME_VERIFICATION_POLICY,
|
||||
HostnameVerificationPolicy.ANY.name());
|
||||
FileTruststoreProviderFactory factory = new FileTruststoreProviderFactory();
|
||||
factory.init(ScopeUtil.createScope(values));
|
||||
TruststoreProvider provider = factory.create(null);
|
||||
assertNotNull(provider.getTruststore());
|
||||
assertEquals(HostnameVerificationPolicy.ANY, provider.getPolicy());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.truststore;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TruststoreBuilderTest {
|
||||
|
||||
@Test
|
||||
public void testMergedTrustStore() throws Exception {
|
||||
URL url = TruststoreBuilderTest.class.getResource("/truststores/keycloak.pem");
|
||||
|
||||
KeyStore storeWithoutDefaults = TruststoreBuilder.createMergedTruststore(new String[] { url.getPath() }, false);
|
||||
ArrayList<String> storeWithoutDefaultsAliases = Collections.list(storeWithoutDefaults.aliases());
|
||||
assertEquals(2, storeWithoutDefaultsAliases.size());
|
||||
|
||||
KeyStore storeWithDefaults = TruststoreBuilder.createMergedTruststore(new String[] { url.getPath() }, true);
|
||||
ArrayList<String> storeWithDefaultsAliases = Collections.list(storeWithDefaults.aliases());
|
||||
int certs = storeWithDefaultsAliases.size();
|
||||
assertTrue(certs > 2);
|
||||
assertTrue(storeWithDefaultsAliases.containsAll(storeWithoutDefaultsAliases));
|
||||
|
||||
// saving / loading should provide the certs even without a password
|
||||
char[] password = null;
|
||||
File saved = TruststoreBuilder.saveTruststore(storeWithDefaults, "target", password);
|
||||
|
||||
KeyStore savedLoaded = TruststoreBuilder.loadStore(saved.getAbsolutePath(), TruststoreBuilder.PKCS12, password);
|
||||
assertEquals(certs, Collections.list(savedLoaded.aliases()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergedTrustStoreFromDirectory() throws Exception {
|
||||
URL url = TruststoreBuilderTest.class.getResource("/truststores/keycloak.pem");
|
||||
|
||||
KeyStore storeWithoutDefaults = TruststoreBuilder
|
||||
.createMergedTruststore(new String[] { new File(url.getPath()).getParent() }, false);
|
||||
ArrayList<String> storeWithoutDefaultsAliases = Collections.list(storeWithoutDefaults.aliases());
|
||||
assertEquals(2, storeWithoutDefaultsAliases.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailsWithInvalidFile() throws Exception {
|
||||
URL url = TruststoreBuilderTest.class.getResource("/truststores/invalid");
|
||||
|
||||
assertThrows(RuntimeException.class, () -> TruststoreBuilder
|
||||
.createMergedTruststore(new String[] { new File(url.getPath()).getAbsolutePath() }, false));
|
||||
}
|
||||
|
||||
}
|
59
services/src/test/java/org/keycloak/utils/ScopeUtil.java
Normal file
59
services/src/test/java/org/keycloak/utils/ScopeUtil.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.utils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.keycloak.services.util.JsonConfigProvider;
|
||||
import org.keycloak.services.util.JsonConfigProvider.JsonScope;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public class ScopeUtil {
|
||||
|
||||
public static JsonScope createScope(Map<String, String> properties) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
JsonNode config = mapper.readTree(json(properties));
|
||||
return new JsonConfigProvider(config, new Properties()).new JsonScope(config);
|
||||
} catch (IOException e) {
|
||||
Assert.fail("Could not parse json");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String json(Map<String, String> properties) {
|
||||
String[] params = properties.entrySet().stream().map(e -> param(e.getKey(), e.getValue())).toArray(String[]::new);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{");
|
||||
sb.append(String.join(",", params));
|
||||
sb.append("}");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static String param(String key, String value) {
|
||||
return "\"" + key + "\"" + " : " + "\"" + value + "\"";
|
||||
}
|
||||
|
||||
}
|
1
services/src/test/resources/truststores/invalid
Normal file
1
services/src/test/resources/truststores/invalid
Normal file
|
@ -0,0 +1 @@
|
|||
I am not a PEM file
|
53
services/src/test/resources/truststores/keycloak.pem
Normal file
53
services/src/test/resources/truststores/keycloak.pem
Normal file
|
@ -0,0 +1,53 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF/jCCA+agAwIBAgIJAOMEN39fZf7uMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYDVQQGEwJVUzEL
|
||||
MAkGA1UECAwCTUExDzANBgNVBAcMBkJvc3RvbjEQMA4GA1UECgwHUmVkIEhhdDERMA8GA1UECwwI
|
||||
S2V5Y2xvYWsxFDASBgNVBAMMC0tleWNsb2FrIENBMSMwIQYJKoZIhvcNAQkBFhRjb250YWN0QGtl
|
||||
eWNsb2FrLm9yZzAeFw0xODAyMjAxOTQ3NTFaFw00NTA3MDgxOTQ3NTFaMIGLMQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECAwCTUExDzANBgNVBAcMBkJvc3RvbjEQMA4GA1UECgwHUmVkIEhhdDERMA8GA1UE
|
||||
CwwIS2V5Y2xvYWsxFDASBgNVBAMMC0tleWNsb2FrIENBMSMwIQYJKoZIhvcNAQkBFhRjb250YWN0
|
||||
QGtleWNsb2FrLm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJlGjg05FzCm3f3Y
|
||||
dIbMHNYuORfiP2n6YhX7vQyDjF/4gh7EYEYgE7spJ864/DySQenJ55Jn22K/1MQ1rOHcqfTioIgN
|
||||
3eEAyyuMDx60KU3frMBRYeCgLJVZQHr06x+Sh/+SbbIYq/558+g/6PSZjmPBindHsPzGuBPaLOW4
|
||||
Jz47CA73L/su2qnJGeAiUaK/tXmANs1bqJbiNRDr9IJFkdusx1mql2ElfknJT8U+LBPOOID/S7Xd
|
||||
83SKtpFIQ8Vikb6C0SKnopOJiG2uWg5g7CYlNYxJpAM25zhDqp71bl8zOsIL2tFfUAvvoBnhN31k
|
||||
DIl8RZJ5ELnh+t5SCfwbgdfMzS7uht8qVTeZ0/BG80Lzl1gfzNR8q45gsKC97mg7Voj68kt2aZr+
|
||||
E3Ng1guK69gePMxCpqLyjwlKz187mNUme+zxg2gL2egs4M6uffqsEd0c5QryrRSTcIXi8Bim6PDh
|
||||
L93dBsenAIg25DOJNA6Vt2LELoe9w0TkL48UwUvU6GYB7/zM/z3EW45ZkRhHWK+HZppqDAb05lgJ
|
||||
eeKUxxdUSy+ot7ls6cSqACYofVjPoVHPD5Ncx+6NGHPGM5N3FGvMMh64PYpChyVWDTEfrZIS7Yyj
|
||||
9Iz/2eCxV3cOcO4bU0K6kx/dWRic5B5ymVtRME93+Of/hQuta4uLhlo8ZxRpAgMBAAGjYzBhMB0G
|
||||
A1UdDgQWBBQiuPS7cwDHKT+TgKX2HFICast6UjAfBgNVHSMEGDAWgBQiuPS7cwDHKT+TgKX2HFIC
|
||||
ast6UjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEA
|
||||
VCVXdx79ooKyOaL+S49S4agP7mE4IxuDefDwQ2dm996wpk3nntg0y54Auu1Y2plJirBhTvYZ1Red
|
||||
LNBMVBypm6BQpNn37u5TI39/FYsoGFPINu1EzLTYl0bFKc0w7UFlFusje9zXLWISm8uTNzxJ1RGL
|
||||
rcnv9gfiXPKxAmN0cz9WY0vm+0+OV50HvLyUyqGKxyWmt2ek4jV+oEhsMMSO/MVNNXHEo2MAGcA2
|
||||
3XPe7FZkiFB1suDIMzzUFCrRBtoZjYSUeyN9Pd0Yg3twl96CLqld4xFjsKMIsz0ACGRI8OpzeHAs
|
||||
ePH4yS94E6nLwWH9YTi6pgTtoXSaVBLvIYpVHi8UAyIBFNqLMCukoq0OBlOdkO0zescmpEtp8GiU
|
||||
WMuB7x+kkaSxmsujEfL3mRWshkqaz/ZHPKXaNtPBUtIMjQnTMBF/wQjZxCGAps8dOMZ9pYnZcmVz
|
||||
0KeXpBJe1j+47MhItgt1wQNoyr4iBaxE3fAF/Arr/IZtIf0erXOjc7P6dEQW+WiKWvEA5Mp+4tV3
|
||||
Zj2pwSSX5bKDKx4RAkoW1jLTE1KN5RWvF8phStLty83gTd5wgykFSl65Lu7KIBW9HH3LIK46fb+c
|
||||
OBOZfSn3mdQXrbuXNUXgbhrsetnBfPNMAkJjaBQLNTxebIvXndiTIEsWqHS7h1x+kBkDOKhwSCc=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEsTCCApmgAwIBAgICC7kwDQYJKoZIhvcNAQELBQAwgYsxCzAJBgNVBAYTAlVTMQswCQYDVQQI
|
||||
DAJNQTEPMA0GA1UEBwwGQm9zdG9uMRAwDgYDVQQKDAdSZWQgSGF0MREwDwYDVQQLDAhLZXljbG9h
|
||||
azEUMBIGA1UEAwwLS2V5Y2xvYWsgQ0ExIzAhBgkqhkiG9w0BCQEWFGNvbnRhY3RAa2V5Y2xvYWsu
|
||||
b3JnMB4XDTE5MDMyMTE4MTk1MFoXDTQ2MDgwNjE4MTk1MFowZDELMAkGA1UEBhMCVVMxCzAJBgNV
|
||||
BAgMAk1BMRAwDgYDVQQKDAdSZWQgSGF0MREwDwYDVQQLDAhLZXljbG9hazEjMCEGA1UEAwwaS2V5
|
||||
Y2xvYWsgSW50ZXJtZWRpYXRlIENBIDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD1
|
||||
afusL2MqMMxg20HTGyVt5nxc9MvpzU9yWhKyper9hh/iEDkH06SZ+vInUWGTeRfGmoA3W+IGF4Tz
|
||||
SFO2BjOLz6bowNTqpWONZAK0swMauE23sbxA7XfKxmIt5pDkQWAWb2kzLoKEXkbmdlF+O/qcM8i+
|
||||
1U7fpg1NjQI1DHvBVMvul/aGyzP6q/HjNEg+7o3y21T/3gq/Yma9L+awX1wLLxtDlSGjP1Q2C19w
|
||||
87ijUIwFUo7AzTOn03SIt+Pfc1cdIsmjUG2lDqJTyo/VuqmfWWBMm6p+Ya/Z4Nipk87nxoW60y4E
|
||||
jRL7vxx9vyWiRUhP/2pEE5y9LQ0uzxxA4kIPAgMBAAGjRTBDMB0GA1UdDgQWBBQYXI8JjLfLFM+o
|
||||
Cghx8t/R+2iv0zASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0B
|
||||
AQsFAAOCAgEAB5SrTyWz95GqgG0zrtIwJArMY4EUuISGWEgX8GpVyP9xTM1he/vOXqFpAQHtygiq
|
||||
5yhyrrMJuu5/m0Ca5NZM1NaM6unJr7mIPEKKBP/85Fue/gBe/Q0Vx1VnmxZhszqQT4hLIfR8anLX
|
||||
gK9w8E6lB4HAXgK+VUzzxXLE43oc8/L9swEnvCKsLHKynexNxKRIZD3GPmLCciOz+101Fb6a0Nc8
|
||||
+A+5soiQi8gC/K9ygXvxqmHtnrTndZmrtJFOaeXwMDAw7q3j25MkRt4fBPcKX0soBuI6/cTE2veH
|
||||
GPYXW4QbVswrhJVD/vVuU0VFR1fLnuw9NgDkGz0qoi20x/Ew3HiVi++9EU9YsPgRStcfsCTfth2T
|
||||
LsPmSFSKeGMR3e+Hr6xr6fcahSl6QmKcI60EHw+kKqQ3bBq7QOxizEuQ0SunRyuEUewnhMT9s7sI
|
||||
rZ4M2fyiWH3GVm2971J0/ey3NEYSq/Y9fAO6+9xGWlvaps8X5GLs/XYt9HwZNtdb9F3YMNSFi10W
|
||||
BIKqdpppLT9uuIDSK5S+s5chuychy2TZiCBwKBt1lRdqEZ6J4vMj7M5eIdj8Pmvp+tmL1kewuTfK
|
||||
aU+2Bpy6l42sYkyMjy7Z0XyMITIBnAf9SmElQPUjq+IIYL6HK/i8mI15AHHIiwm1eaUdbvBKJnQS
|
||||
0++b0iwvQA4=
|
||||
-----END CERTIFICATE-----
|
Binary file not shown.
|
@ -21,6 +21,7 @@ import org.jboss.resteasy.annotations.cache.NoCache;
|
|||
import org.keycloak.http.HttpRequest;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
import org.keycloak.common.util.HtmlUtils;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
|
@ -86,7 +87,6 @@ import org.keycloak.testsuite.util.FeatureDeployerUtil;
|
|||
import org.keycloak.timer.TimerProvider;
|
||||
import org.keycloak.truststore.FileTruststoreProvider;
|
||||
import org.keycloak.truststore.FileTruststoreProviderFactory;
|
||||
import org.keycloak.truststore.HostnameVerificationPolicy;
|
||||
import org.keycloak.truststore.TruststoreProvider;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.utils.MediaType;
|
||||
|
|
|
@ -19,13 +19,13 @@ package org.keycloak.testsuite.client.resources;
|
|||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
import org.keycloak.representations.idm.AdminEventRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.components.TestProvider;
|
||||
import org.keycloak.testsuite.rest.representation.AuthenticatorState;
|
||||
import org.keycloak.truststore.HostnameVerificationPolicy;
|
||||
import org.keycloak.utils.MediaType;
|
||||
|
||||
import jakarta.ws.rs.Consumes;
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.junit.After;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.enums.HostnameVerificationPolicy;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
|
@ -39,7 +40,6 @@ import org.keycloak.testsuite.util.AccountHelper;
|
|||
import org.keycloak.testsuite.util.MailServerConfiguration;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.SslMailServer;
|
||||
import org.keycloak.truststore.HostnameVerificationPolicy;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
|
||||
|
|
Loading…
Reference in a new issue