From 81ad5313856273630b7e4283d12bc93f42e6afe1 Mon Sep 17 00:00:00 2001 From: German Osin Date: Fri, 21 Nov 2025 16:02:24 +0100 Subject: [PATCH 1/2] BE: Fixes #854 Added skip ssl verfication for schemaregistry --- .../java/io/kafbat/ui/KafkaUiApplication.java | 9 ++++--- .../kafbat/ui/config/ClustersProperties.java | 11 +++----- .../builtin/sr/SchemaRegistrySerde.java | 17 +++++++++--- .../ui/service/ssl/SkipSecurityProvider.java | 12 +++++++++ .../ssl/SkipTrustManagerFactorySpi.java | 26 +++++++++++++++++++ .../ui/util/KafkaClientSslPropertiesUtil.java | 2 +- .../kafbat/ui/util/WebClientConfigurator.java | 5 ++-- contract-typespec/api/config.tsp | 2 +- 8 files changed, 66 insertions(+), 18 deletions(-) create mode 100644 api/src/main/java/io/kafbat/ui/service/ssl/SkipSecurityProvider.java create mode 100644 api/src/main/java/io/kafbat/ui/service/ssl/SkipTrustManagerFactorySpi.java diff --git a/api/src/main/java/io/kafbat/ui/KafkaUiApplication.java b/api/src/main/java/io/kafbat/ui/KafkaUiApplication.java index 51d693983..e6dc484a9 100644 --- a/api/src/main/java/io/kafbat/ui/KafkaUiApplication.java +++ b/api/src/main/java/io/kafbat/ui/KafkaUiApplication.java @@ -1,10 +1,11 @@ package io.kafbat.ui; +import io.kafbat.ui.service.ssl.SkipSecurityProvider; import io.kafbat.ui.util.DynamicConfigOperations; +import java.security.Security; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @@ -17,8 +18,10 @@ public static void main(String[] args) { startApplication(args); } - public static ConfigurableApplicationContext startApplication(String[] args) { - return new SpringApplicationBuilder(KafkaUiApplication.class) + public static void startApplication(String[] args) { + Security.addProvider(new SkipSecurityProvider()); + + new SpringApplicationBuilder(KafkaUiApplication.class) .initializers(DynamicConfigOperations.dynamicConfigPropertiesInitializer()) .build() .run(args); diff --git a/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java b/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java index e521f85a9..74e87e89d 100644 --- a/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java +++ b/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java @@ -159,7 +159,7 @@ public static class SchemaRegistryAuth { public static class TruststoreConfig { String truststoreLocation; String truststorePassword; - boolean verifySsl = true; + boolean verify = true; } @Data @@ -254,9 +254,7 @@ public boolean use(Boolean request) { if (enabled) { if (Boolean.TRUE.equals(request)) { return true; - } else if (request == null && defaultEnabled) { - return true; - } + } else return request == null && defaultEnabled; } return false; } @@ -287,7 +285,6 @@ private void flattenClusterProperties() { } } - @SuppressWarnings("unchecked") private Map flattenClusterProperties(@Nullable String prefix, @Nullable Map propertiesMap) { Map flattened = new HashMap<>(); @@ -306,8 +303,8 @@ private Map flattenClusterProperties(@Nullable String prefix, private void validateClusterNames() { // if only one cluster provided it is ok not to set name - if (clusters.size() == 1 && !StringUtils.hasText(clusters.get(0).getName())) { - clusters.get(0).setName("Default"); + if (clusters.size() == 1 && !StringUtils.hasText(clusters.getFirst().getName())) { + clusters.getFirst().setName("Default"); return; } diff --git a/api/src/main/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerde.java b/api/src/main/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerde.java index 91c2375d8..0778bc36e 100644 --- a/api/src/main/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerde.java +++ b/api/src/main/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerde.java @@ -24,6 +24,7 @@ import io.kafbat.ui.serde.api.PropertyResolver; import io.kafbat.ui.serde.api.SchemaDescription; import io.kafbat.ui.serdes.BuiltInSerde; +import io.kafbat.ui.service.ssl.SkipSecurityProvider; import io.kafbat.ui.util.jsonschema.AvroJsonSchemaConverter; import io.kafbat.ui.util.jsonschema.ProtobufSchemaConverter; import java.net.URI; @@ -76,7 +77,8 @@ public void autoConfigure(PropertyResolver kafkaClusterProperties, kafkaClusterProperties.getProperty("schemaRegistrySsl.keystoreLocation", String.class).orElse(null), kafkaClusterProperties.getProperty("schemaRegistrySsl.keystorePassword", String.class).orElse(null), kafkaClusterProperties.getProperty("ssl.truststoreLocation", String.class).orElse(null), - kafkaClusterProperties.getProperty("ssl.truststorePassword", String.class).orElse(null) + kafkaClusterProperties.getProperty("ssl.truststorePassword", String.class).orElse(null), + kafkaClusterProperties.getProperty("ssl.verify", Boolean.class).orElse(true) ), kafkaClusterProperties.getProperty("schemaRegistryKeySchemaNameTemplate", String.class).orElse("%s-key"), kafkaClusterProperties.getProperty("schemaRegistrySchemaNameTemplate", String.class).orElse("%s-value"), @@ -102,7 +104,8 @@ public void configure(PropertyResolver serdeProperties, serdeProperties.getProperty("keystoreLocation", String.class).orElse(null), serdeProperties.getProperty("keystorePassword", String.class).orElse(null), kafkaClusterProperties.getProperty("ssl.truststoreLocation", String.class).orElse(null), - kafkaClusterProperties.getProperty("ssl.truststorePassword", String.class).orElse(null) + kafkaClusterProperties.getProperty("ssl.truststorePassword", String.class).orElse(null), + kafkaClusterProperties.getProperty("ssl.verify", Boolean.class).orElse(true) ), serdeProperties.getProperty("keySchemaNameTemplate", String.class).orElse("%s-key"), serdeProperties.getProperty("schemaNameTemplate", String.class).orElse("%s-value"), @@ -132,7 +135,8 @@ private static SchemaRegistryClient createSchemaRegistryClient(List urls @Nullable String keyStoreLocation, @Nullable String keyStorePassword, @Nullable String trustStoreLocation, - @Nullable String trustStorePassword) { + @Nullable String trustStorePassword, + boolean verifySsl) { Map configs = new HashMap<>(); if (username != null && password != null) { configs.put(BASIC_AUTH_CREDENTIALS_SOURCE, "USER_INFO"); @@ -145,6 +149,13 @@ private static SchemaRegistryClient createSchemaRegistryClient(List urls "You specified password but do not specified username"); } + if (!verifySsl) { + configs.put( + SchemaRegistryClientConfig.CLIENT_NAMESPACE + SslConfigs.SSL_TRUSTMANAGER_ALGORITHM_CONFIG, + SkipSecurityProvider.NAME + ); + } + // We require at least a truststore. The logic is done similar to SchemaRegistryService.securedWebClientOnTLS if (trustStoreLocation != null && trustStorePassword != null) { configs.put(SchemaRegistryClientConfig.CLIENT_NAMESPACE + SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, diff --git a/api/src/main/java/io/kafbat/ui/service/ssl/SkipSecurityProvider.java b/api/src/main/java/io/kafbat/ui/service/ssl/SkipSecurityProvider.java new file mode 100644 index 000000000..51407d567 --- /dev/null +++ b/api/src/main/java/io/kafbat/ui/service/ssl/SkipSecurityProvider.java @@ -0,0 +1,12 @@ +package io.kafbat.ui.service.ssl; + +import java.security.Provider; + +public class SkipSecurityProvider extends Provider { + public static final String NAME = "Skip"; + + public SkipSecurityProvider() { + super(NAME, "1.0", "Skip TrustManagerFactory Provider"); + put("TrustManagerFactory." + NAME, "io.kafbat.ui.service.ssl.SkipTrustManagerFactorySpi"); + } +} diff --git a/api/src/main/java/io/kafbat/ui/service/ssl/SkipTrustManagerFactorySpi.java b/api/src/main/java/io/kafbat/ui/service/ssl/SkipTrustManagerFactorySpi.java new file mode 100644 index 000000000..9cef32d1d --- /dev/null +++ b/api/src/main/java/io/kafbat/ui/service/ssl/SkipTrustManagerFactorySpi.java @@ -0,0 +1,26 @@ +package io.kafbat.ui.service.ssl; + +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import java.security.KeyStore; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.TrustManager; + +@SuppressWarnings("unused") +public class SkipTrustManagerFactorySpi extends javax.net.ssl.TrustManagerFactorySpi { + + public SkipTrustManagerFactorySpi() { + } + + @Override + protected void engineInit(KeyStore ks) { + } + + @Override + protected void engineInit(ManagerFactoryParameters spec) { + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return InsecureTrustManagerFactory.INSTANCE.getTrustManagers(); + } +} diff --git a/api/src/main/java/io/kafbat/ui/util/KafkaClientSslPropertiesUtil.java b/api/src/main/java/io/kafbat/ui/util/KafkaClientSslPropertiesUtil.java index 384888aa1..c8fb4dca9 100644 --- a/api/src/main/java/io/kafbat/ui/util/KafkaClientSslPropertiesUtil.java +++ b/api/src/main/java/io/kafbat/ui/util/KafkaClientSslPropertiesUtil.java @@ -16,7 +16,7 @@ public static void addKafkaSslProperties(@Nullable ClustersProperties.Truststore return; } - if (!truststoreConfig.isVerifySsl()) { + if (!truststoreConfig.isVerify()) { sink.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, ""); } diff --git a/api/src/main/java/io/kafbat/ui/util/WebClientConfigurator.java b/api/src/main/java/io/kafbat/ui/util/WebClientConfigurator.java index 170530be1..7f37e15df 100644 --- a/api/src/main/java/io/kafbat/ui/util/WebClientConfigurator.java +++ b/api/src/main/java/io/kafbat/ui/util/WebClientConfigurator.java @@ -47,7 +47,7 @@ private static ObjectMapper defaultOM() { public WebClientConfigurator configureSsl(@Nullable ClustersProperties.TruststoreConfig truststoreConfig, @Nullable ClustersProperties.KeystoreConfig keystoreConfig) { - if (truststoreConfig != null && !truststoreConfig.isVerifySsl()) { + if (truststoreConfig != null && !truststoreConfig.isVerify()) { return configureNoSsl(); } @@ -130,14 +130,13 @@ public WebClientConfigurator configureBufferSize(DataSize maxBuffSize) { return this; } - public WebClientConfigurator configureObjectMapper(ObjectMapper mapper) { + public void configureObjectMapper(ObjectMapper mapper) { builder.codecs(codecs -> { codecs.defaultCodecs() .jackson2JsonEncoder(new Jackson2JsonEncoder(mapper, MediaType.APPLICATION_JSON)); codecs.defaultCodecs() .jackson2JsonDecoder(new Jackson2JsonDecoder(mapper, MediaType.APPLICATION_JSON)); }); - return this; } public WebClientConfigurator configureCodecs(Consumer configurer) { diff --git a/contract-typespec/api/config.tsp b/contract-typespec/api/config.tsp index 39647a3b1..3490f2dcc 100644 --- a/contract-typespec/api/config.tsp +++ b/contract-typespec/api/config.tsp @@ -146,7 +146,7 @@ model ApplicationConfig { ssl?: { truststoreLocation?: string; truststorePassword?: string; - verifySsl?: boolean = true; + verify?: boolean = true; }; schemaRegistry?: string; schemaRegistryAuth?: { From 34a6935976dc1081035bffc7d133d84d98b8cc02 Mon Sep 17 00:00:00 2001 From: German Osin Date: Fri, 21 Nov 2025 16:04:49 +0100 Subject: [PATCH 2/2] BE: Fixes #854 Added skip ssl verfication for schemaregistry --- api/src/main/java/io/kafbat/ui/config/ClustersProperties.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java b/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java index 74e87e89d..c9f84365c 100644 --- a/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java +++ b/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java @@ -254,7 +254,9 @@ public boolean use(Boolean request) { if (enabled) { if (Boolean.TRUE.equals(request)) { return true; - } else return request == null && defaultEnabled; + } else { + return request == null && defaultEnabled; + } } return false; }