Why Discovery Security Matters
When every microservice is reachable over the network, the service registry becomes the first line of defense. An unprotected Eureka instance can be used to:
- Inject rogue endpoints into the load-balancer
- Exfiltrate configuration metadata
- Trigger cascading failures via forged health checks
This guide shows how to turn the registry itself into a hardened security gateway.
Hardening the Registry
Step 1 – Lock Down the Dashboard
@EnableWebSecurity
class RegistrySecurityConfigurer {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/actuator/health").permitAll()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
UserDetailsService users() {
return new InMemoryUserDetailsManager(
User.withUsername("registry")
.password("{bcrypt}$2a$10$…")
.roles("ADMIN")
.build());
}
}
Step 2 – Mutual TLS Between Server and Clients
Create a PKCS12 keystore and truststore, then expose them via Spring Boot properties:
server:
port: 8761
ssl:
enabled: true
key-store: classpath:eureka-server.p12
key-store-password: changeit
trust-store: classpath:eureka-trust.p12
trust-store-password: changeit
client-auth: need
Step 3 – Client Side Configuration
eureka:
client:
serviceUrl:
defaultZone: https://registry:${REGISTRY_PASSWORD}@eureka:8761/eureka/
instance:
metadataMap:
zone: dmz
securePortEnabled: true
nonSecurePortEnabled: false
Token-Based Service-to-Service Auth
Instead of embedding passwords in defaultZone, issue short-lived JWTs:
@Component
class TokenRelayFilter implements ClientHttpRequestInterceptor {
private final OAuth2AuthorizedClientManager clientManager;
public ClientHttpRequestInterceptor interceptor() {
return (request, body, execution) -> {
OAuth2AuthorizeRequest req =
OAuth2AuthorizeRequest.withClientRegistrationId("eureka")
.principal("discovery-client")
.build();
OAuth2AuthorizedClient client = clientManager.authorize(req);
request.getHeaders().setBearerAuth(client.getAccessToken().getTokenValue());
return execution.execute(request, body);
};
}
}
Register the interceptor with the Eureka client’s RestTemplate via a EurekaClientHttpRequestFactorySupplier bean.
Least-Privilege Metadata
Use Eureka’s metadata map to carry role information:
eureka:
instance:
metadataMap:
roles: "orders,audit"
criticality: "high"
A sidecar filter can then enforce that only services with criticality=high may invoke a given endpoint.
Runtime Monitoring
Export security events to Prometheus:
@Component
public class SecurityAudit {
private final MeterRegistry registry;
@EventListener
public void onUnauthorized(AbstractAuthorizationEvent event) {
registry.counter("eureka.auth.denied",
"source", event.getSource().toString()).increment();
}
}
Pair the metric with an alert that fires when the rate of denied fetches exceeds 1 % in any 5-minute window.
Rotation Without Downtime
- Stage new certificates in an alternate truststore.
- Update clients to dual-trust both keystores.
- Roll the server to the new keystore.
- Remove the old truststore from clients in the next deployment wave.
Spring Cloud Config’s encryption endpoints can push the new truststore to all nodes atomically.