You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by gn...@apache.org on 2026/04/25 19:16:23 UTC
(camel-spring-boot) 01/01: CAMEL-23250: Add Spring Boot auto-configuration for security policy properties
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch swift-school
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
commit 27c4054b365c99bab49d2e2f1a6e3b002a5f3938
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sat Apr 25 21:16:04 2026 +0200
CAMEL-23250: Add Spring Boot auto-configuration for security policy properties
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
.../CamelSecurityPolicyAutoConfiguration.java | 151 +++++++++++++++++++
...CamelSecurityPolicyConfigurationProperties.java | 119 +++++++++++++++
...rk.boot.autoconfigure.AutoConfiguration.imports | 1 +
.../CamelSecurityPolicyAutoConfigurationTest.java | 163 +++++++++++++++++++++
4 files changed, 434 insertions(+)
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfiguration.java
new file mode 100644
index 00000000000..c0c18a84a9f
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfiguration.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.spring.boot.security;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.main.SecurityConfigurationProperties;
+import org.apache.camel.main.SecurityPolicyResult;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.util.SecurityUtils;
+import org.apache.camel.util.SecurityViolation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.EnumerablePropertySource;
+import org.springframework.core.env.Environment;
+
+@AutoConfiguration(after = CamelAutoConfiguration.class)
+@ConditionalOnBean(CamelAutoConfiguration.class)
+@EnableConfigurationProperties(CamelSecurityPolicyConfigurationProperties.class)
+public class CamelSecurityPolicyAutoConfiguration {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CamelSecurityPolicyAutoConfiguration.class);
+
+ @Bean
+ SecurityPolicyResult camelSecurityPolicyResult(
+ CamelContext camelContext,
+ CamelSecurityPolicyConfigurationProperties config,
+ Environment environment) {
+
+ SecurityConfigurationProperties securityConfig = applySecurityProperties(camelContext, config);
+
+ Map<String, Object> camelProperties = extractCamelProperties(environment);
+
+ List<SecurityViolation> violations = SecurityUtils.detectViolations(
+ camelProperties,
+ (k, v) -> containsSensitive(camelContext, k, v),
+ securityConfig::resolvePolicy,
+ securityConfig.getAllowedPropertySet());
+
+ SecurityPolicyResult result = new SecurityPolicyResult(violations);
+ camelContext.getCamelContextExtension().addContextPlugin(SecurityPolicyResult.class, result);
+
+ enforceViolations(violations);
+
+ return result;
+ }
+
+ private SecurityConfigurationProperties applySecurityProperties(
+ CamelContext camelContext,
+ CamelSecurityPolicyConfigurationProperties config) {
+
+ // get the security config from camel-main's MainConfigurationProperties
+ // which is already bound by CamelAutoConfiguration via CamelConfigurationProperties
+ SecurityConfigurationProperties securityConfig
+ = new SecurityConfigurationProperties(null);
+
+ securityConfig.setPolicy(config.getPolicy());
+ if (config.getSecretPolicy() != null) {
+ securityConfig.setSecretPolicy(config.getSecretPolicy());
+ }
+ if (config.getInsecureSslPolicy() != null) {
+ securityConfig.setInsecureSslPolicy(config.getInsecureSslPolicy());
+ }
+ if (config.getInsecureSerializationPolicy() != null) {
+ securityConfig.setInsecureSerializationPolicy(config.getInsecureSerializationPolicy());
+ }
+ if (config.getInsecureDevPolicy() != null) {
+ securityConfig.setInsecureDevPolicy(config.getInsecureDevPolicy());
+ }
+ if (config.getAllowedProperties() != null) {
+ securityConfig.setAllowedProperties(config.getAllowedProperties());
+ }
+
+ return securityConfig;
+ }
+
+ private static Map<String, Object> extractCamelProperties(Environment environment) {
+ Map<String, Object> properties = new LinkedHashMap<>();
+
+ if (environment instanceof ConfigurableEnvironment ce) {
+ ce.getPropertySources().forEach(ps -> {
+ if (ps instanceof EnumerablePropertySource<?> eps) {
+ for (String name : eps.getPropertyNames()) {
+ if (name.startsWith("camel.") && !name.startsWith("camel.security.")) {
+ Object value = environment.getProperty(name);
+ if (value != null) {
+ properties.putIfAbsent(name, value);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ return properties;
+ }
+
+ private static boolean containsSensitive(CamelContext camelContext, String key, Object value) {
+ boolean answer = CamelContextHelper.containsSensitive(camelContext, key);
+ if (!answer && value != null) {
+ String v = value.toString();
+ answer = v.startsWith("RAW(");
+ }
+ return answer;
+ }
+
+ private static void enforceViolations(List<SecurityViolation> violations) {
+ List<String> failures = new ArrayList<>();
+ for (SecurityViolation v : violations) {
+ if ("fail".equals(v.policy())) {
+ failures.add(v.toString());
+ } else {
+ LOG.warn("SECURITY WARNING: {}", v);
+ }
+ }
+
+ if (!failures.isEmpty()) {
+ throw new RuntimeCamelException(
+ "Security policy violations detected (policy=fail):\n - " + String.join("\n - ", failures)
+ + "\nTo allow specific properties, add them to camel.security.allowed-properties"
+ + " or change the policy to 'warn' or 'allow'.");
+ }
+ }
+
+}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyConfigurationProperties.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyConfigurationProperties.java
new file mode 100644
index 00000000000..d358776e600
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyConfigurationProperties.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.spring.boot.security;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Security policy configuration for Camel Spring Boot applications.
+ * <p>
+ * Controls how Camel reacts to insecure configuration at startup. Policies can be set globally or per security
+ * category:
+ * <ul>
+ * <li>{@code allow} — no warnings, allow the configuration</li>
+ * <li>{@code warn} — log a warning at startup (default)</li>
+ * <li>{@code fail} — throw an exception and prevent startup</li>
+ * </ul>
+ */
+@ConfigurationProperties(prefix = "camel.security")
+public class CamelSecurityPolicyConfigurationProperties {
+
+ /**
+ * Global security policy applied to all categories unless overridden. Controls how Camel reacts when insecure
+ * configuration is detected at startup.
+ */
+ private String policy = "warn";
+
+ /**
+ * Security policy for plain-text secrets. When set, overrides the global policy for properties that contain
+ * sensitive values configured as plain text.
+ */
+ private String secretPolicy;
+
+ /**
+ * Security policy for insecure SSL/TLS configuration. When set, overrides the global policy for options that
+ * disable certificate validation or hostname verification.
+ */
+ private String insecureSslPolicy;
+
+ /**
+ * Security policy for insecure deserialization configuration. When set, overrides the global policy for options that
+ * enable dangerous deserialization of untrusted data.
+ */
+ private String insecureSerializationPolicy;
+
+ /**
+ * Security policy for development-only features. When set, overrides the global policy for options intended only for
+ * development environments.
+ */
+ private String insecureDevPolicy;
+
+ /**
+ * Comma-separated list of property keys to exclude from security policy checks. Use full property paths (e.g.,
+ * camel.component.aws2-s3.trustAllCertificates) to allow specific properties regardless of the configured policy.
+ */
+ private String allowedProperties;
+
+ public String getPolicy() {
+ return policy;
+ }
+
+ public void setPolicy(String policy) {
+ this.policy = policy;
+ }
+
+ public String getSecretPolicy() {
+ return secretPolicy;
+ }
+
+ public void setSecretPolicy(String secretPolicy) {
+ this.secretPolicy = secretPolicy;
+ }
+
+ public String getInsecureSslPolicy() {
+ return insecureSslPolicy;
+ }
+
+ public void setInsecureSslPolicy(String insecureSslPolicy) {
+ this.insecureSslPolicy = insecureSslPolicy;
+ }
+
+ public String getInsecureSerializationPolicy() {
+ return insecureSerializationPolicy;
+ }
+
+ public void setInsecureSerializationPolicy(String insecureSerializationPolicy) {
+ this.insecureSerializationPolicy = insecureSerializationPolicy;
+ }
+
+ public String getInsecureDevPolicy() {
+ return insecureDevPolicy;
+ }
+
+ public void setInsecureDevPolicy(String insecureDevPolicy) {
+ this.insecureDevPolicy = insecureDevPolicy;
+ }
+
+ public String getAllowedProperties() {
+ return allowedProperties;
+ }
+
+ public void setAllowedProperties(String allowedProperties) {
+ this.allowedProperties = allowedProperties;
+ }
+
+}
diff --git a/core/camel-spring-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/core/camel-spring-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index e2dd68b6d89..31f55659dc8 100644
--- a/core/camel-spring-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/core/camel-spring-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -25,6 +25,7 @@ org.apache.camel.spring.boot.actuate.health.CamelAvailabilityCheckAutoConfigurat
org.apache.camel.spring.boot.actuate.info.CamelInfoAutoConfiguration
org.apache.camel.spring.boot.cluster.ClusteredRouteControllerAutoConfiguration
org.apache.camel.spring.boot.routecontroller.SupervisingRouteControllerAutoConfiguration
+org.apache.camel.spring.boot.security.CamelSecurityPolicyAutoConfiguration
org.apache.camel.spring.boot.security.CamelSSLAutoConfiguration
org.apache.camel.spring.boot.threadpool.CamelThreadPoolAutoConfiguration
org.apache.camel.spring.boot.trace.CamelTraceAutoConfiguration
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfigurationTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfigurationTest.java
new file mode 100644
index 00000000000..31d1585b95b
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfigurationTest.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.spring.boot.security;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.main.SecurityPolicyResult;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+public class CamelSecurityPolicyAutoConfigurationTest {
+
+ private final ApplicationContextRunner runner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(
+ CamelAutoConfiguration.class,
+ CamelSecurityPolicyAutoConfiguration.class));
+
+ @Test
+ public void noSecurityPropertiesShouldStartNormally() {
+ runner.run(context -> {
+ assertThat(context).hasNotFailed();
+ SecurityPolicyResult result = context.getBean(SecurityPolicyResult.class);
+ assertThat(result).isNotNull();
+ assertThat(result.hasViolations()).isFalse();
+ });
+ }
+
+ @Test
+ public void policyAllowShouldIgnoreInsecureConfig() {
+ runner.withPropertyValues(
+ "camel.security.policy=allow",
+ "camel.component.http.trustAllCertificates=true")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ SecurityPolicyResult result = context.getBean(SecurityPolicyResult.class);
+ assertThat(result.hasViolations()).isFalse();
+ });
+ }
+
+ @Test
+ public void policyWarnShouldStartWithViolations() {
+ runner.withPropertyValues(
+ "camel.security.policy=warn",
+ "camel.component.http.trustAllCertificates=true")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ SecurityPolicyResult result = context.getBean(SecurityPolicyResult.class);
+ assertThat(result.hasViolations()).isTrue();
+ assertThat(result.getViolations()).anyMatch(
+ v -> v.propertyKey().contains("trustAllCertificates") && "warn".equals(v.policy()));
+ });
+ }
+
+ @Test
+ public void policyFailShouldPreventStartup() {
+ runner.withPropertyValues(
+ "camel.security.policy=fail",
+ "camel.component.http.trustAllCertificates=true")
+ .run(context -> {
+ assertThat(context).hasFailed();
+ assertThat(context.getStartupFailure())
+ .rootCause()
+ .isInstanceOf(RuntimeCamelException.class)
+ .hasMessageContaining("Security policy violations detected");
+ });
+ }
+
+ @Test
+ public void categoryOverrideShouldTakePrecedence() {
+ runner.withPropertyValues(
+ "camel.security.policy=fail",
+ "camel.security.insecure-ssl-policy=allow",
+ "camel.component.http.trustAllCertificates=true")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ SecurityPolicyResult result = context.getBean(SecurityPolicyResult.class);
+ assertThat(result.hasViolations()).isFalse();
+ });
+ }
+
+ @Test
+ public void categoryOverrideWarnWhileGlobalFail() {
+ runner.withPropertyValues(
+ "camel.security.policy=fail",
+ "camel.security.insecure-ssl-policy=warn",
+ "camel.component.http.trustAllCertificates=true")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ SecurityPolicyResult result = context.getBean(SecurityPolicyResult.class);
+ assertThat(result.hasViolations()).isTrue();
+ assertThat(result.getViolations()).anyMatch(
+ v -> "warn".equals(v.policy()));
+ });
+ }
+
+ @Test
+ public void allowedPropertiesShouldExcludeFromChecks() {
+ runner.withPropertyValues(
+ "camel.security.policy=fail",
+ "camel.security.allowed-properties=camel.component.http.trustAllCertificates",
+ "camel.component.http.trustAllCertificates=true")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ SecurityPolicyResult result = context.getBean(SecurityPolicyResult.class);
+ assertThat(result.hasViolations()).isFalse();
+ });
+ }
+
+ @Test
+ public void multipleViolationsDetected() {
+ runner.withPropertyValues(
+ "camel.security.policy=warn",
+ "camel.component.http.trustAllCertificates=true",
+ "camel.component.netty.allowJavaSerializedObject=true")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ SecurityPolicyResult result = context.getBean(SecurityPolicyResult.class);
+ assertThat(result.getViolationCount()).isGreaterThanOrEqualTo(2);
+ });
+ }
+
+ @Test
+ public void insecureDevPolicyOverride() {
+ runner.withPropertyValues(
+ "camel.security.policy=fail",
+ "camel.security.insecure-dev-policy=allow",
+ "camel.main.devConsoleEnabled=true")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ });
+ }
+
+ @Test
+ public void insecureSerializationPolicyOverride() {
+ runner.withPropertyValues(
+ "camel.security.policy=fail",
+ "camel.security.insecure-serialization-policy=warn",
+ "camel.component.netty.allowJavaSerializedObject=true")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ SecurityPolicyResult result = context.getBean(SecurityPolicyResult.class);
+ assertThat(result.hasViolations()).isTrue();
+ });
+ }
+
+}