Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/main/java/io/github/ktestify/config/KtestifyConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ private KtestifyConfig(Config config) {
this.schemaRegistry = new SchemaRegistryConfig(config.getConfig("ktestify.schema-registry"));
this.framework = new FrameworkConfig(config.getConfig("ktestify.framework"));
applyLogLevels(config);
applyJvmTruststore(config);
log.info("KtestifyConfig loaded successfully");
log.debug("Kafka bootstrap servers: {}", kafka.getBootstrapServers());
log.debug("Schema Registry URL: {}", schemaRegistry.getUrl());
Expand Down Expand Up @@ -105,6 +106,44 @@ private static void applyLogLevels(Config config) {
lc.getString("confluent-level"));
}

/**
* Applies the JVM-level truststore defined in {@code ktestify.jvm.truststore} HOCON section by setting the
* corresponding {@code javax.net.ssl.*} system properties. This truststore is shared by all plugins and underlying
* libraries that don't provide their own SSL configuration.
*
* <p>If {@code location} is blank, no system properties are set and the JVM's built-in cacerts are used.
*
* <p>Supported keys and their environment variable overrides:
*
* <ul>
* <li>{@code location} / {@code KTESTIFY_JVM_TRUSTSTORE_LOCATION}
* <li>{@code password} / {@code KTESTIFY_JVM_TRUSTSTORE_PASSWORD}
* <li>{@code type} / {@code KTESTIFY_JVM_TRUSTSTORE_TYPE} β€” {@code JKS} or {@code PKCS12}
* </ul>
*
* @param config the resolved Config object containing {@code ktestify.jvm.truststore}
*/
public static void applyJvmTruststore(Config config) {
String location = config.getString("ktestify.jvm.truststore.location");
if (location == null || location.isBlank()) {
log.debug("No JVM truststore location configured; keeping JVM default cacerts");
return;
}

System.setProperty("javax.net.ssl.trustStore", location);
System.setProperty("javax.net.ssl.trustStoreType", config.getString("ktestify.jvm.truststore.type"));

String password = config.getString("ktestify.jvm.truststore.password");
if (password != null && !password.isBlank()) {
System.setProperty("javax.net.ssl.trustStorePassword", password);
}

log.info(
"JVM truststore applied β€” location={} type={}",
location,
config.getString("ktestify.jvm.truststore.type"));
}

/**
* Loads the default configuration using the standard HOCON loading mechanism. Configuration is loaded from:
*
Expand Down
22 changes: 21 additions & 1 deletion src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,25 @@ ktestify {
confluent-level = "WARN"
confluent-level = ${?KTESTIFY_CONFLUENT_LOG_LEVEL}
}
}

# JVM Configuration
# =================
# Settings applied to the JVM itself at KtestifyConfig load time.
jvm {
# Default JVM truststore (javax.net.ssl.trustStore system properties).
# Used by any HTTPS/TLS client that doesn't have its own SSL config,
# e.g. plain HTTP calls, OAuth endpoints, etc.
# Leave location blank to keep the JVM's built-in cacerts.
truststore {
location = ""
location = ${?KTESTIFY_JVM_TRUSTSTORE_LOCATION}

password = ""
password = ${?KTESTIFY_JVM_TRUSTSTORE_PASSWORD}

# JKS or PKCS12
type = "JKS"
type = ${?KTESTIFY_JVM_TRUSTSTORE_TYPE}
}
}
}
108 changes: 108 additions & 0 deletions src/test/java/io/github/ktestify/config/KtestifyConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -693,4 +693,112 @@ void testFrameworkTimeoutHelpers() {
assertEquals(100L, fw.getPollIntervalMillis());
assertEquals(5000L, fw.getBufferTimeMillis());
}

// ==========================================
// JVM TRUSTSTORE TESTS
// ==========================================

@Nested
@DisplayName("JVM Truststore")
class JvmTruststoreTests {

@BeforeEach
void cleanSslProperties() {
System.clearProperty("javax.net.ssl.trustStore");
System.clearProperty("javax.net.ssl.trustStoreType");
System.clearProperty("javax.net.ssl.trustStorePassword");
}

@AfterEach
void restoreSslProperties() {
System.clearProperty("javax.net.ssl.trustStore");
System.clearProperty("javax.net.ssl.trustStoreType");
System.clearProperty("javax.net.ssl.trustStorePassword");
}

@Test
@DisplayName("Should not set truststore properties when location is blank")
void shouldNotSetTruststoreWhenLocationBlank() {
Config customConfig = ConfigFactory.parseString("""
ktestify {
jvm {
truststore {
location = ""
password = ""
type = "JKS"
}
}
}
""");

KtestifyConfig.load(customConfig);

assertNull(System.getProperty("javax.net.ssl.trustStore"));
assertNull(System.getProperty("javax.net.ssl.trustStoreType"));
assertNull(System.getProperty("javax.net.ssl.trustStorePassword"));
}

@Test
@DisplayName("Should set truststore location and type when location is provided")
void shouldSetTruststoreWhenLocationProvided() {
Config customConfig = ConfigFactory.parseString("""
ktestify {
jvm {
truststore {
location = "/path/to/truststore.jks"
password = ""
type = "PKCS12"
}
}
}
""");

KtestifyConfig.load(customConfig);

assertEquals("/path/to/truststore.jks", System.getProperty("javax.net.ssl.trustStore"));
assertEquals("PKCS12", System.getProperty("javax.net.ssl.trustStoreType"));
assertNull(System.getProperty("javax.net.ssl.trustStorePassword"));
}

@Test
@DisplayName("Should set truststore password when provided")
void shouldSetTruststorePasswordWhenProvided() {
Config customConfig = ConfigFactory.parseString("""
ktestify {
jvm {
truststore {
location = "/path/to/truststore.jks"
password = "secret"
type = "JKS"
}
}
}
""");

KtestifyConfig.load(customConfig);

assertEquals("/path/to/truststore.jks", System.getProperty("javax.net.ssl.trustStore"));
assertEquals("JKS", System.getProperty("javax.net.ssl.trustStoreType"));
assertEquals("secret", System.getProperty("javax.net.ssl.trustStorePassword"));
}

@Test
@DisplayName("Should default truststore type to JKS from reference.conf")
void shouldDefaultTruststoreTypeToJks() {
Config customConfig = ConfigFactory.parseString("""
ktestify {
jvm {
truststore {
location = "/path/to/truststore.jks"
}
}
}
""");

KtestifyConfig.load(customConfig);

assertEquals("/path/to/truststore.jks", System.getProperty("javax.net.ssl.trustStore"));
assertEquals("JKS", System.getProperty("javax.net.ssl.trustStoreType"));
}
}
}
Loading