Enhance masking around config-keystore (#30348)

Closes #30346

Signed-off-by: Václav Muzikář <vmuzikar@redhat.com>
This commit is contained in:
Václav Muzikář 2024-06-12 08:54:45 +02:00 committed by GitHub
parent e6df8a2866
commit 375ea9da03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 40 additions and 12 deletions

View file

@ -124,7 +124,7 @@ After executing the command, you will be prompted to *Enter the password to be s
When the KeyStore is created, you can start the server using the following parameters:
<@kc.start parameters="--config-keystore=/path/to/keystore.p12 --config-keystore-password=storepass --config-keystore-type=PKCS12"/>
<@kc.start parameters="--config-keystore=/path/to/keystore.p12 --config-keystore-password=keystorepass --config-keystore-type=PKCS12"/>
=== Format for raw Quarkus properties
In most cases, the available configuration options should suffice to configure the server.

View file

@ -34,7 +34,7 @@ import static org.keycloak.quarkus.runtime.configuration.Configuration.getCurren
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawPersistedProperty;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRuntimeProperty;
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.formatValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.maskValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.isBuildTimeProperty;
import static org.keycloak.utils.StringUtil.isNotBlank;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST;
@ -222,7 +222,7 @@ public final class Picocli {
return;
}
properties.add(key + "=" + formatValue(key, value));
properties.add(key + "=" + maskValue(key, value));
}
}, arg -> {
properties.add(arg);

View file

@ -22,7 +22,7 @@ import static org.keycloak.quarkus.runtime.Environment.setProfile;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getConfigValue;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getPropertyNames;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRuntimeProperty;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.formatValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.maskValue;
import java.util.HashSet;
import java.util.List;
@ -148,7 +148,9 @@ public final class ShowConfig extends AbstractCommand implements Runnable {
value = getRuntimeProperty(property).orElse(value);
}
spec.commandLine().getOut().printf("\t%s = %s (%s)%n", configValue.getName(), formatValue(configValue.getName(), value), KeycloakConfigSourceProvider.getConfigSourceDisplayName(configValue.getConfigSourceName()));
value = maskValue(configValue.getName(), value, configValue.getConfigSourceName());
spec.commandLine().getOut().printf("\t%s = %s (%s)%n", configValue.getName(), value, KeycloakConfigSourceProvider.getConfigSourceDisplayName(configValue.getConfigSourceName()));
}
private static String groupProperties(String property) {

View file

@ -100,9 +100,13 @@ public class KeycloakConfigSourceProvider implements ConfigSourceProvider, Confi
if (configSource == null) {
return "Derived";
}
if (configSource.startsWith("KeyStoreConfigSource")) {
if (isKeyStoreConfigSource(configSource)) {
return "config-keystore";
}
return CONFIG_SOURCE_DISPLAY_NAMES.getOrDefault(configSource, configSource);
}
public static boolean isKeyStoreConfigSource(String configSourceName) {
return configSourceName.contains("KeyStoreConfigSource");
}
}

View file

@ -29,6 +29,7 @@ final class ConfigKeystorePropertyMappers {
.to(SMALLRYE_KEYSTORE_PASSWORD)
.transformer(ConfigKeystorePropertyMappers::validatePassword)
.paramLabel("config-keystore-password")
.isMasked(true)
.build(),
fromOption(ConfigKeystoreOptions.CONFIG_KEYSTORE_TYPE)
.to("smallrye.config.source.keystore.kc-default.type")

View file

@ -34,6 +34,7 @@ import java.util.stream.Collectors;
import static org.keycloak.quarkus.runtime.Environment.isParsedCommand;
import static org.keycloak.quarkus.runtime.Environment.isRebuild;
import static org.keycloak.quarkus.runtime.Environment.isRebuildCheck;
import static org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider.isKeyStoreConfigSource;
public final class PropertyMappers {
@ -125,11 +126,15 @@ public final class PropertyMappers {
MAPPERS.sanitizeDisabledMappers();
}
public static String formatValue(String property, String value) {
public static String maskValue(String property, String value) {
return maskValue(property, value, null);
}
public static String maskValue(String property, String value, String configSourceName) {
property = removeProfilePrefixIfNeeded(property);
PropertyMapper<?> mapper = getMapper(property);
if (mapper != null && mapper.isMask()) {
if ((configSourceName != null && isKeyStoreConfigSource(configSourceName) || (mapper != null && mapper.isMask()))) {
return VALUE_MASK;
}

View file

@ -17,17 +17,19 @@
package org.keycloak.it.cli;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLITest;
import org.keycloak.it.junit5.extension.ConfigurationTestResource;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import org.keycloak.quarkus.runtime.cli.command.ShowConfig;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_LONG_NAME;
@QuarkusTestResource(value = ConfigurationTestResource.class, restrictToAnnotatedClass = true)
@ -59,4 +61,16 @@ public class ShowConfigCommandTest {
Assertions.assertFalse(output.contains("testpw3"));
Assertions.assertTrue(output.contains("kc.db-password = " + PropertyMappers.VALUE_MASK));
}
@Test
@Launch({ CONFIG_FILE_LONG_NAME+"=src/test/resources/ShowConfigCommandTest/keycloak-keystore.conf", ShowConfig.NAME, "all" })
void testSmallRyeKeyStoreConfigSource(LaunchResult result) {
// keystore is shared with QuarkusPropertiesDistTest#testSmallRyeKeyStoreConfigSource
String output = result.getOutput();
assertThat(output, containsString("kc.config-keystore-password = " + PropertyMappers.VALUE_MASK));
assertThat(output, containsString("kc.log-level = " + PropertyMappers.VALUE_MASK));
assertThat(output, not(containsString("secret")));
assertThat(output, not(containsString("debug")));
}
}

View file

@ -0,0 +1,2 @@
config-keystore=src/test/resources/keystore
config-keystore-password=secret