From 75fc71a35b08f093e37e07bdc25aaa044ee2ea73 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 13:22:01 +0900 Subject: [PATCH 1/3] test(config): prove anonymous config reads fail closed --- .../ConfigServerInboundSecurityTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 config-server/src/test/java/com/xtrmetl/config/ConfigServerInboundSecurityTest.java diff --git a/config-server/src/test/java/com/xtrmetl/config/ConfigServerInboundSecurityTest.java b/config-server/src/test/java/com/xtrmetl/config/ConfigServerInboundSecurityTest.java new file mode 100644 index 00000000..43db3ce1 --- /dev/null +++ b/config-server/src/test/java/com/xtrmetl/config/ConfigServerInboundSecurityTest.java @@ -0,0 +1,37 @@ +package com.xtrmetl.config; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Verifies the fail-closed inbound HTTP boundary for the reference-only Config Server profile. + */ +@SpringBootTest( + classes = ConfigServerApplication.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.profiles.active=native", + "spring.cloud.config.server.native.search-locations=classpath:/", + "eureka.client.enabled=false" + } +) +class ConfigServerInboundSecurityTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + void anonymousConfigurationReadIsDeniedWhileHealthRemainsPublic() { + ResponseEntity configuration = restTemplate.getForEntity("/application/default", String.class); + ResponseEntity health = restTemplate.getForEntity("/actuator/health", String.class); + + assertEquals(HttpStatus.FORBIDDEN, configuration.getStatusCode()); + assertEquals(HttpStatus.OK, health.getStatusCode()); + } +} From b407e6b89ba72b76afe6290f313837e915f03b7d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 14:04:20 +0900 Subject: [PATCH 2/3] build(security): add Config Server security starter --- config-server/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config-server/pom.xml b/config-server/pom.xml index faba3ee8..06c843de 100644 --- a/config-server/pom.xml +++ b/config-server/pom.xml @@ -21,6 +21,10 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-starter-security + org.springframework.cloud spring-cloud-starter-netflix-eureka-client From 1cd9a0a4cbab85f6674e006fa4c81288f0f8faea Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 14:04:45 +0900 Subject: [PATCH 3/3] fix(security): deny anonymous Config Server reads --- .../ConfigServerSecurityConfiguration.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 config-server/src/main/java/com/xtrmetl/config/ConfigServerSecurityConfiguration.java diff --git a/config-server/src/main/java/com/xtrmetl/config/ConfigServerSecurityConfiguration.java b/config-server/src/main/java/com/xtrmetl/config/ConfigServerSecurityConfiguration.java new file mode 100644 index 00000000..baa2359c --- /dev/null +++ b/config-server/src/main/java/com/xtrmetl/config/ConfigServerSecurityConfiguration.java @@ -0,0 +1,43 @@ +package com.xtrmetl.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +/** + * Defines the credential-free fail-closed HTTP posture for the reference-only Config Server. + * + *

The repository does not invent a production service identity. Until a deployment-owned + * authentication mechanism is selected and proven under issue #193, only health and info actuator + * endpoints are intentionally public and every configuration-resource request is denied. A future + * authenticated production profile must replace this reference-only posture through a separately + * reviewed security contract rather than weakening this default.

+ */ +@Configuration(proxyBeanMethods = false) +public class ConfigServerSecurityConfiguration { + + /** + * Builds the reference-only Config Server security chain. + * + * @param http Spring Security's servlet HTTP configuration builder + * @return the configured filter chain + * @throws Exception when Spring Security cannot build the filter chain + */ + @Bean + public SecurityFilterChain configServerSecurityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) + .httpBasic(httpBasic -> httpBasic.disable()) + .formLogin(formLogin -> formLogin.disable()) + .logout(logout -> logout.disable()) + .authorizeHttpRequests(authorize -> authorize + .requestMatchers( + "/actuator/health", + "/actuator/health/**", + "/actuator/info" + ).permitAll() + .anyRequest().denyAll()); + return http.build(); + } +}