CLI options to disable encryption and authentication to external Infinispan
Closes #28750 Signed-off-by: Pedro Ruivo <pruivo@redhat.com>
This commit is contained in:
parent
6977d58d27
commit
3de5357091
8 changed files with 114 additions and 128 deletions
|
@ -151,6 +151,16 @@ The configuration file is relative to the `conf/` directory.
|
||||||
For configuration of {project_name} server for high availability and multi-node clustered setup there was introduced following CLI options `cache-remote-host`, `cache-remote-port`, `cache-remote-username` and `cache-remote-password` simplifying configuration within the XML file.
|
For configuration of {project_name} server for high availability and multi-node clustered setup there was introduced following CLI options `cache-remote-host`, `cache-remote-port`, `cache-remote-username` and `cache-remote-password` simplifying configuration within the XML file.
|
||||||
Once any of declared CLI parameters are present, it is expected there is no configuration related to remote store present in the XML file.
|
Once any of declared CLI parameters are present, it is expected there is no configuration related to remote store present in the XML file.
|
||||||
|
|
||||||
|
==== Connecting to an insecure Infinispan server
|
||||||
|
|
||||||
|
WARNING: Disabling security is not recommended in production!
|
||||||
|
|
||||||
|
In development or test environment, it is easier to start an unsecured Infinispan server.
|
||||||
|
For these use case, the CLI options `cache-remote-tls-enabled` disables the encryption (SSL) between {project_name} and Infinispan.
|
||||||
|
{project_name} will fail to start if the Infinispan server is configured to accept only encrypted connections.
|
||||||
|
|
||||||
|
The CLI options `cache-remote-username` and `cache-remote-password` are optional and, if not set, {project_name} will connect to the Infinispan server without presenting any credentials.
|
||||||
|
If the Infinispan server has authentication enabled, {project_name} will fail to start.
|
||||||
|
|
||||||
== Transport stacks
|
== Transport stacks
|
||||||
Transport stacks ensure that distributed cache nodes in a cluster communicate in a reliable fashion.
|
Transport stacks ensure that distributed cache nodes in a cluster communicate in a reliable fashion.
|
||||||
|
|
|
@ -186,7 +186,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
|
||||||
throw new RuntimeException("Multiple " + org.keycloak.cluster.ManagedCacheManagerProvider.class + " providers found.");
|
throw new RuntimeException("Multiple " + org.keycloak.cluster.ManagedCacheManagerProvider.class + " providers found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
managedCacheManager = provider.getCacheManager(config);
|
managedCacheManager = provider.getEmbeddedCacheManager(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// store it in a locale variable first, so it is not visible to the outside, yet
|
// store it in a locale variable first, so it is not visible to the outside, yet
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class CachingOptions {
|
||||||
public static final String CACHE_REMOTE_PORT_PROPERTY = CACHE_REMOTE_PREFIX + "-port";
|
public static final String CACHE_REMOTE_PORT_PROPERTY = CACHE_REMOTE_PREFIX + "-port";
|
||||||
public static final String CACHE_REMOTE_USERNAME_PROPERTY = CACHE_REMOTE_PREFIX + "-username";
|
public static final String CACHE_REMOTE_USERNAME_PROPERTY = CACHE_REMOTE_PREFIX + "-username";
|
||||||
public static final String CACHE_REMOTE_PASSWORD_PROPERTY = CACHE_REMOTE_PREFIX + "-password";
|
public static final String CACHE_REMOTE_PASSWORD_PROPERTY = CACHE_REMOTE_PREFIX + "-password";
|
||||||
|
public static final String CACHE_REMOTE_TLS_ENABLED_PROPERTY = CACHE_REMOTE_PREFIX + "-tls-enabled";
|
||||||
|
|
||||||
private static final String CACHE_METRICS_PREFIX = "cache-metrics";
|
private static final String CACHE_METRICS_PREFIX = "cache-metrics";
|
||||||
public static final String CACHE_METRICS_HISTOGRAMS_ENABLED_PROPERTY = CACHE_METRICS_PREFIX + "-histograms-enabled";
|
public static final String CACHE_METRICS_HISTOGRAMS_ENABLED_PROPERTY = CACHE_METRICS_PREFIX + "-histograms-enabled";
|
||||||
|
@ -44,7 +45,7 @@ public class CachingOptions {
|
||||||
kubernetes,
|
kubernetes,
|
||||||
ec2,
|
ec2,
|
||||||
azure,
|
azure,
|
||||||
google;
|
google
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Option<Stack> CACHE_STACK = new OptionBuilder<>("cache-stack", Stack.class)
|
public static final Option<Stack> CACHE_STACK = new OptionBuilder<>("cache-stack", Stack.class)
|
||||||
|
@ -126,4 +127,9 @@ public class CachingOptions {
|
||||||
.description("Enable histograms for metrics for the embedded caches.")
|
.description("Enable histograms for metrics for the embedded caches.")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
public static final Option<Boolean> CACHE_REMOTE_TLS_ENABLED = new OptionBuilder<>(CACHE_REMOTE_TLS_ENABLED_PROPERTY, Boolean.class)
|
||||||
|
.category(OptionCategory.CACHE)
|
||||||
|
.description("Enable SSL support to communication with a secure remote Infinispan server. It is not recommended to disable in production!")
|
||||||
|
.defaultValue(Boolean.TRUE)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,37 +36,32 @@ import io.quarkus.agroal.DataSource;
|
||||||
import io.quarkus.arc.Arc;
|
import io.quarkus.arc.Arc;
|
||||||
import io.quarkus.arc.InstanceHandle;
|
import io.quarkus.arc.InstanceHandle;
|
||||||
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener;
|
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener;
|
||||||
|
import io.quarkus.runtime.RuntimeValue;
|
||||||
|
import io.quarkus.runtime.ShutdownContext;
|
||||||
|
import io.quarkus.runtime.annotations.Recorder;
|
||||||
import io.vertx.core.Handler;
|
import io.vertx.core.Handler;
|
||||||
import io.vertx.ext.web.RoutingContext;
|
import io.vertx.ext.web.RoutingContext;
|
||||||
import liquibase.Scope;
|
import liquibase.Scope;
|
||||||
|
import liquibase.servicelocator.ServiceLocator;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.infinispan.commons.util.FileLookupFactory;
|
import org.infinispan.commons.util.FileLookupFactory;
|
||||||
import org.infinispan.manager.DefaultCacheManager;
|
|
||||||
|
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.crypto.CryptoIntegration;
|
import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
import org.keycloak.common.crypto.CryptoProvider;
|
import org.keycloak.common.crypto.CryptoProvider;
|
||||||
import org.keycloak.common.crypto.FipsMode;
|
import org.keycloak.common.crypto.FipsMode;
|
||||||
import org.keycloak.config.MetricsOptions;
|
|
||||||
import org.keycloak.config.TruststoreOptions;
|
import org.keycloak.config.TruststoreOptions;
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.provider.Spi;
|
||||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||||
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
||||||
import org.keycloak.quarkus.runtime.storage.database.liquibase.FastServiceLocator;
|
import org.keycloak.quarkus.runtime.storage.database.liquibase.FastServiceLocator;
|
||||||
import org.keycloak.provider.Provider;
|
|
||||||
import org.keycloak.provider.ProviderFactory;
|
|
||||||
import org.keycloak.provider.Spi;
|
|
||||||
import org.keycloak.quarkus.runtime.storage.legacy.infinispan.CacheManagerFactory;
|
import org.keycloak.quarkus.runtime.storage.legacy.infinispan.CacheManagerFactory;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.theme.ClasspathThemeProviderFactory;
|
import org.keycloak.theme.ClasspathThemeProviderFactory;
|
||||||
import org.keycloak.truststore.TruststoreBuilder;
|
import org.keycloak.truststore.TruststoreBuilder;
|
||||||
|
|
||||||
import io.quarkus.runtime.RuntimeValue;
|
|
||||||
import io.quarkus.runtime.ShutdownContext;
|
|
||||||
import io.quarkus.runtime.annotations.Recorder;
|
|
||||||
import liquibase.servicelocator.ServiceLocator;
|
|
||||||
import org.keycloak.userprofile.DeclarativeUserProfileProviderFactory;
|
import org.keycloak.userprofile.DeclarativeUserProfileProviderFactory;
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getKcConfigValue;
|
import static org.keycloak.quarkus.runtime.configuration.Configuration.getKcConfigValue;
|
||||||
|
@ -123,17 +118,8 @@ public class KeycloakRecorder {
|
||||||
|
|
||||||
public RuntimeValue<CacheManagerFactory> createCacheInitializer(ShutdownContext shutdownContext) {
|
public RuntimeValue<CacheManagerFactory> createCacheInitializer(ShutdownContext shutdownContext) {
|
||||||
try {
|
try {
|
||||||
boolean isMetricsEnabled = Configuration.isTrue(MetricsOptions.METRICS_ENABLED);
|
CacheManagerFactory cacheManagerFactory = new CacheManagerFactory(getInfinispanConfigFile());
|
||||||
CacheManagerFactory cacheManagerFactory = new CacheManagerFactory(getInfinispanConfigFile(), isMetricsEnabled);
|
shutdownContext.addShutdownTask(cacheManagerFactory::shutdown);
|
||||||
|
|
||||||
shutdownContext.addShutdownTask(() -> {
|
|
||||||
DefaultCacheManager cacheManager = cacheManagerFactory.getOrCreate();
|
|
||||||
|
|
||||||
if (cacheManager != null) {
|
|
||||||
cacheManager.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new RuntimeValue<>(cacheManagerFactory);
|
return new RuntimeValue<>(cacheManagerFactory);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|
|
@ -137,13 +137,7 @@ public final class Configuration {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<Boolean> getOptionalBooleanKcValue(String propertyName) {
|
public static Optional<Boolean> getOptionalBooleanKcValue(String propertyName) {
|
||||||
Optional<String> value = getOptionalValue(NS_KEYCLOAK_PREFIX.concat(propertyName));
|
return getOptionalValue(NS_KEYCLOAK_PREFIX.concat(propertyName)).map(Boolean::parseBoolean);
|
||||||
|
|
||||||
if (value.isPresent()) {
|
|
||||||
return value.map(Boolean::parseBoolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<Boolean> getOptionalBooleanValue(String name) {
|
public static Optional<Boolean> getOptionalBooleanValue(String name) {
|
||||||
|
|
|
@ -19,13 +19,15 @@ package org.keycloak.quarkus.runtime.storage.legacy.infinispan;
|
||||||
|
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import io.micrometer.core.instrument.Metrics;
|
import io.micrometer.core.instrument.Metrics;
|
||||||
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
|
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
|
||||||
|
import org.infinispan.commons.api.Lifecycle;
|
||||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||||
import org.infinispan.configuration.cache.PersistenceConfigurationBuilder;
|
import org.infinispan.configuration.cache.PersistenceConfigurationBuilder;
|
||||||
import org.infinispan.configuration.global.GlobalConfiguration;
|
import org.infinispan.configuration.global.GlobalConfiguration;
|
||||||
|
@ -43,16 +45,16 @@ import org.jgroups.protocols.UDP;
|
||||||
import org.jgroups.util.TLS;
|
import org.jgroups.util.TLS;
|
||||||
import org.jgroups.util.TLSClientAuth;
|
import org.jgroups.util.TLSClientAuth;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.config.CachingOptions;
|
||||||
|
import org.keycloak.config.MetricsOptions;
|
||||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_ENABLED_PROPERTY;
|
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE_FILE_PROPERTY;
|
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE_FILE_PROPERTY;
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD_PROPERTY;
|
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD_PROPERTY;
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_FILE_PROPERTY;
|
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_FILE_PROPERTY;
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD_PROPERTY;
|
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD_PROPERTY;
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_METRICS_HISTOGRAMS_ENABLED_PROPERTY;
|
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_HOST_PROPERTY;
|
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_HOST_PROPERTY;
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_PASSWORD_PROPERTY;
|
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_PASSWORD_PROPERTY;
|
||||||
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_PORT_PROPERTY;
|
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_PORT_PROPERTY;
|
||||||
|
@ -68,44 +70,41 @@ public class CacheManagerFactory {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(CacheManagerFactory.class);
|
private static final Logger logger = Logger.getLogger(CacheManagerFactory.class);
|
||||||
|
|
||||||
private String config;
|
private final String config;
|
||||||
private final boolean metricsEnabled;
|
private final CompletableFuture<DefaultCacheManager> cacheManagerFuture;
|
||||||
private DefaultCacheManager cacheManager;
|
|
||||||
private Future<DefaultCacheManager> cacheManagerFuture;
|
|
||||||
private ExecutorService executor;
|
|
||||||
private boolean initialized;
|
|
||||||
|
|
||||||
public CacheManagerFactory(String config, boolean metricsEnabled) {
|
public CacheManagerFactory(String config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.metricsEnabled = metricsEnabled;
|
this.cacheManagerFuture = CompletableFuture.supplyAsync(this::startEmbeddedCacheManager);
|
||||||
this.executor = createThreadPool();
|
|
||||||
this.cacheManagerFuture = executor.submit(this::startCacheManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultCacheManager getOrCreate() {
|
public DefaultCacheManager getOrCreateEmbeddedCacheManager() {
|
||||||
if (cacheManager == null) {
|
return join(cacheManagerFuture);
|
||||||
if (initialized) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
logger.debug("Shutdown embedded cache manager");
|
||||||
|
cacheManagerFuture.thenAccept(CacheManagerFactory::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T join(Future<T> future) {
|
||||||
try {
|
try {
|
||||||
// for now, we don't have any explicit property for setting the cache start timeout
|
return future.get(getStartTimeout(), TimeUnit.SECONDS);
|
||||||
return cacheManager = cacheManagerFuture.get(getStartTimeout(), TimeUnit.SECONDS);
|
} catch (InterruptedException e) {
|
||||||
} catch (Exception e) {
|
Thread.currentThread().interrupt();
|
||||||
throw new RuntimeException("Failed to start caches", e);
|
return null;
|
||||||
} finally {
|
} catch (ExecutionException | TimeoutException e) {
|
||||||
shutdownThreadPool();
|
throw new RuntimeException("Failed to start embedded or remote cache manager", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cacheManager;
|
private static void close(Lifecycle lifecycle) {
|
||||||
|
if (lifecycle != null) {
|
||||||
|
lifecycle.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExecutorService createThreadPool() {
|
private DefaultCacheManager startEmbeddedCacheManager() {
|
||||||
return Executors.newSingleThreadExecutor(r -> new Thread(r, "keycloak-cache-init"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private DefaultCacheManager startCacheManager() {
|
|
||||||
ConfigurationBuilderHolder builder = new ParserRegistry().parse(config);
|
ConfigurationBuilderHolder builder = new ParserRegistry().parse(config);
|
||||||
|
|
||||||
if (builder.getNamedConfigurationBuilders().entrySet().stream().anyMatch(c -> c.getValue().clustering().cacheMode().isClustered())) {
|
if (builder.getNamedConfigurationBuilders().entrySet().stream().anyMatch(c -> c.getValue().clustering().cacheMode().isClustered())) {
|
||||||
|
@ -124,12 +123,12 @@ public class CacheManagerFactory {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (metricsEnabled) {
|
if (Configuration.isTrue(MetricsOptions.METRICS_ENABLED)) {
|
||||||
builder.getGlobalConfigurationBuilder().addModule(MicrometerMeterRegisterConfigurationBuilder.class);
|
builder.getGlobalConfigurationBuilder().addModule(MicrometerMeterRegisterConfigurationBuilder.class);
|
||||||
builder.getGlobalConfigurationBuilder().module(MicrometerMeterRegisterConfigurationBuilder.class).meterRegistry(Metrics.globalRegistry);
|
builder.getGlobalConfigurationBuilder().module(MicrometerMeterRegisterConfigurationBuilder.class).meterRegistry(Metrics.globalRegistry);
|
||||||
builder.getGlobalConfigurationBuilder().cacheContainer().statistics(true);
|
builder.getGlobalConfigurationBuilder().cacheContainer().statistics(true);
|
||||||
builder.getGlobalConfigurationBuilder().metrics().namesAsTags(true);
|
builder.getGlobalConfigurationBuilder().metrics().namesAsTags(true);
|
||||||
if (booleanProperty(CACHE_METRICS_HISTOGRAMS_ENABLED_PROPERTY)) {
|
if (Configuration.isTrue(CachingOptions.CACHE_METRICS_HISTOGRAMS_ENABLED)) {
|
||||||
builder.getGlobalConfigurationBuilder().metrics().histograms(true);
|
builder.getGlobalConfigurationBuilder().metrics().histograms(true);
|
||||||
}
|
}
|
||||||
builder.getNamedConfigurationBuilders().forEach((s, configurationBuilder) -> configurationBuilder.statistics().enabled(true));
|
builder.getNamedConfigurationBuilders().forEach((s, configurationBuilder) -> configurationBuilder.statistics().enabled(true));
|
||||||
|
@ -143,36 +142,35 @@ public class CacheManagerFactory {
|
||||||
return new DefaultCacheManager(builder, isStartEagerly());
|
return new DefaultCacheManager(builder, isStartEagerly());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStartEagerly() {
|
private static boolean isRemoteTLSEnabled() {
|
||||||
|
return Configuration.isTrue(CachingOptions.CACHE_REMOTE_TLS_ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRemoteAuthenticationEnabled() {
|
||||||
|
return Configuration.getOptionalValue(CACHE_REMOTE_USERNAME_PROPERTY).isPresent() ||
|
||||||
|
Configuration.getOptionalValue(CACHE_REMOTE_PASSWORD_PROPERTY).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SSLContext createSSLContext() {
|
||||||
|
try {
|
||||||
|
// uses the default Java Runtime TrustStore, or the one generated by Keycloak (see org.keycloak.truststore.TruststoreBuilder)
|
||||||
|
var sslContext = SSLContext.getInstance("TLS");
|
||||||
|
sslContext.init(null, null, null);
|
||||||
|
return sslContext;
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isStartEagerly() {
|
||||||
// eagerly starts caches by default
|
// eagerly starts caches by default
|
||||||
return Boolean.parseBoolean(System.getProperty("kc.cache-ispn-start-eagerly", Boolean.TRUE.toString()));
|
return Boolean.parseBoolean(System.getProperty("kc.cache-ispn-start-eagerly", Boolean.TRUE.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer getStartTimeout() {
|
private static int getStartTimeout() {
|
||||||
return Integer.getInteger("kc.cache-ispn-start-timeout", 120);
|
return Integer.getInteger("kc.cache-ispn-start-timeout", 120);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shutdownThreadPool() {
|
|
||||||
if (executor != null) {
|
|
||||||
executor.shutdown();
|
|
||||||
try {
|
|
||||||
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
|
|
||||||
executor.shutdownNow();
|
|
||||||
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
|
|
||||||
Logger.getLogger(CacheManagerFactory.class).warn("Cache init thread pool not terminated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception cause) {
|
|
||||||
executor.shutdownNow();
|
|
||||||
} finally {
|
|
||||||
executor = null;
|
|
||||||
cacheManagerFuture = null;
|
|
||||||
config = null;
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureTransportStack(ConfigurationBuilderHolder builder) {
|
private void configureTransportStack(ConfigurationBuilderHolder builder) {
|
||||||
String transportStack = Configuration.getRawValue("kc.cache-stack");
|
String transportStack = Configuration.getRawValue("kc.cache-stack");
|
||||||
|
|
||||||
|
@ -181,7 +179,7 @@ public class CacheManagerFactory {
|
||||||
transportConfig.defaultTransport().stack(transportStack);
|
transportConfig.defaultTransport().stack(transportStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (booleanProperty(CACHE_EMBEDDED_MTLS_ENABLED_PROPERTY)) {
|
if (Configuration.isTrue(CachingOptions.CACHE_REMOTE_TLS_ENABLED)) {
|
||||||
validateTlsAvailable(transportConfig.build());
|
validateTlsAvailable(transportConfig.build());
|
||||||
var tls = new TLS()
|
var tls = new TLS()
|
||||||
.enabled(true)
|
.enabled(true)
|
||||||
|
@ -218,25 +216,14 @@ public class CacheManagerFactory {
|
||||||
|
|
||||||
private void configureRemoteStores(ConfigurationBuilderHolder builder) {
|
private void configureRemoteStores(ConfigurationBuilderHolder builder) {
|
||||||
//if one of remote store command line parameters is defined, some other are required, otherwise assume it'd configured via xml only
|
//if one of remote store command line parameters is defined, some other are required, otherwise assume it'd configured via xml only
|
||||||
if (Configuration.getOptionalKcValue(CACHE_REMOTE_HOST_PROPERTY).isPresent() ||
|
if (Configuration.getOptionalKcValue(CACHE_REMOTE_HOST_PROPERTY).isPresent()) {
|
||||||
Configuration.getOptionalKcValue(CACHE_REMOTE_USERNAME_PROPERTY).isPresent() ||
|
|
||||||
Configuration.getOptionalKcValue(CACHE_REMOTE_PASSWORD_PROPERTY).isPresent()) {
|
|
||||||
|
|
||||||
String cacheRemoteHost = requiredStringProperty(CACHE_REMOTE_HOST_PROPERTY);
|
String cacheRemoteHost = requiredStringProperty(CACHE_REMOTE_HOST_PROPERTY);
|
||||||
Integer cacheRemotePort = Configuration.getOptionalKcValue(CACHE_REMOTE_PORT_PROPERTY)
|
Integer cacheRemotePort = Configuration.getOptionalKcValue(CACHE_REMOTE_PORT_PROPERTY)
|
||||||
.map(Integer::parseInt)
|
.map(Integer::parseInt)
|
||||||
.orElse(ConfigurationProperties.DEFAULT_HOTROD_PORT);
|
.orElse(ConfigurationProperties.DEFAULT_HOTROD_PORT);
|
||||||
String cacheRemoteUsername = requiredStringProperty(CACHE_REMOTE_USERNAME_PROPERTY);
|
|
||||||
String cacheRemotePassword = requiredStringProperty(CACHE_REMOTE_PASSWORD_PROPERTY);
|
|
||||||
|
|
||||||
SSLContext sslContext;
|
SSLContext sslContext = createSSLContext();
|
||||||
try {
|
|
||||||
// uses the default Java Runtime TrustStore, or the one generated by Keycloak (see org.keycloak.truststore.TruststoreBuilder)
|
|
||||||
sslContext = SSLContext.getInstance("TLS");
|
|
||||||
sslContext.init(null, null, null);
|
|
||||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
DISTRIBUTED_REPLICATED_CACHE_NAMES.forEach(cacheName -> {
|
DISTRIBUTED_REPLICATED_CACHE_NAMES.forEach(cacheName -> {
|
||||||
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS_NO_CACHE) &&
|
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS_NO_CACHE) &&
|
||||||
|
@ -251,7 +238,8 @@ public class CacheManagerFactory {
|
||||||
throw new RuntimeException(String.format("Remote store for cache '%s' is already configured via CLI parameters. It should not be present in the XML file.", cacheName));
|
throw new RuntimeException(String.format("Remote store for cache '%s' is already configured via CLI parameters. It should not be present in the XML file.", cacheName));
|
||||||
}
|
}
|
||||||
|
|
||||||
persistenceCB.addStore(RemoteStoreConfigurationBuilder.class)
|
var storeBuilder = persistenceCB.addStore(RemoteStoreConfigurationBuilder.class);
|
||||||
|
storeBuilder
|
||||||
.rawValues(true)
|
.rawValues(true)
|
||||||
.shared(true)
|
.shared(true)
|
||||||
.segmented(false)
|
.segmented(false)
|
||||||
|
@ -259,26 +247,29 @@ public class CacheManagerFactory {
|
||||||
.connectionPool()
|
.connectionPool()
|
||||||
.maxActive(16)
|
.maxActive(16)
|
||||||
.exhaustedAction(ExhaustedAction.CREATE_NEW)
|
.exhaustedAction(ExhaustedAction.CREATE_NEW)
|
||||||
.remoteSecurity()
|
|
||||||
.ssl()
|
|
||||||
.enable()
|
|
||||||
.sslContext(sslContext)
|
|
||||||
.sniHostName(cacheRemoteHost)
|
|
||||||
.authentication()
|
|
||||||
.enable()
|
|
||||||
.username(cacheRemoteUsername)
|
|
||||||
.password(cacheRemotePassword)
|
|
||||||
.realm("default")
|
|
||||||
.saslMechanism(SCRAM_SHA_512)
|
|
||||||
.addServer()
|
.addServer()
|
||||||
.host(cacheRemoteHost)
|
.host(cacheRemoteHost)
|
||||||
.port(cacheRemotePort);
|
.port(cacheRemotePort);
|
||||||
});
|
|
||||||
}
|
if (isRemoteTLSEnabled()) {
|
||||||
|
storeBuilder.remoteSecurity()
|
||||||
|
.ssl()
|
||||||
|
.enable()
|
||||||
|
.sslContext(sslContext)
|
||||||
|
.sniHostName(cacheRemoteHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean booleanProperty(String propertyName) {
|
if (isRemoteAuthenticationEnabled()) {
|
||||||
return Configuration.getOptionalKcValue(propertyName).map(Boolean::parseBoolean).orElse(Boolean.FALSE);
|
storeBuilder.remoteSecurity()
|
||||||
|
.authentication()
|
||||||
|
.enable()
|
||||||
|
.username(requiredStringProperty(CACHE_REMOTE_USERNAME_PROPERTY))
|
||||||
|
.password(requiredStringProperty(CACHE_REMOTE_PASSWORD_PROPERTY))
|
||||||
|
.realm("default")
|
||||||
|
.saslMechanism(SCRAM_SHA_512);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String requiredStringProperty(String propertyName) {
|
private static String requiredStringProperty(String propertyName) {
|
||||||
|
|
|
@ -17,10 +17,9 @@
|
||||||
|
|
||||||
package org.keycloak.quarkus.runtime.storage.legacy.infinispan;
|
package org.keycloak.quarkus.runtime.storage.legacy.infinispan;
|
||||||
|
|
||||||
import org.keycloak.cluster.ManagedCacheManagerProvider;
|
|
||||||
import org.keycloak.Config;
|
|
||||||
|
|
||||||
import io.quarkus.arc.Arc;
|
import io.quarkus.arc.Arc;
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.cluster.ManagedCacheManagerProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -28,7 +27,7 @@ import io.quarkus.arc.Arc;
|
||||||
public final class QuarkusCacheManagerProvider implements ManagedCacheManagerProvider {
|
public final class QuarkusCacheManagerProvider implements ManagedCacheManagerProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <C> C getCacheManager(Config.Scope config) {
|
public <C> C getEmbeddedCacheManager(Config.Scope config) {
|
||||||
return (C) Arc.container().instance(CacheManagerFactory.class).get().getOrCreate();
|
return (C) Arc.container().instance(CacheManagerFactory.class).get().getOrCreateEmbeddedCacheManager();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,11 @@ package org.keycloak.cluster;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Service Provider Interface (SPI) that allows to plug-in a cache manager instance.
|
* A Service Provider Interface (SPI) that allows to plug-in an embedded cache manager instance.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
public interface ManagedCacheManagerProvider {
|
public interface ManagedCacheManagerProvider {
|
||||||
|
|
||||||
<C> C getCacheManager(Config.Scope config);
|
<C> C getEmbeddedCacheManager(Config.Scope config);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue