Securing Microservices with SPIFFE and Spring Security
Microservices architectures introduce new security challenges, particularly in workload authentication and identity management. SPIFFE (Secure Production Identity Framework for Everyone) provides a standardized way to issue and verify identities in dynamic environments. Combined with Spring Security, we can build a robust authentication mechanism for microservices.
This article explores how to integrate SPIFFE with Spring Security to secure microservice communications.
1. What is SPIFFE?
SPIFFE is an open-source framework that provides:
- Secure identities for workloads (services, containers, VMs).
- SPIFFE Verifiable Identity Document (SVID) as a cryptographically verifiable identity.
- SPIRE (SPIFFE Runtime Environment) for issuing and managing identities.
1.1 Key Components:
- SPIFFE ID – A unique identifier (e.g.,
spiffe://example.org/myservice
). - SVID – A signed identity document (X.509 or JWT).
- SPIRE – The reference implementation for managing identities.
2. Why Use SPIFFE with Spring Security?
- Zero Trust Security: No implicit trust between services; every request must be authenticated.
- Dynamic Environments: Works seamlessly in Kubernetes, VMs, and cloud-native setups.
- Standardized Identity: Replaces ad-hoc solutions like API keys or static certificates.
3. Integrating SPIFFE with Spring Security
Step 1: Set Up SPIRE Server and Agents
Before integrating with Spring, deploy SPIRE to issue SVIDs:
1 2 3 4 5 | # Example SPIRE server in Kubernetes kubectl apply -f https: //raw .githubusercontent.com /spiffe/spire/main/examples/k8s/server .yaml # SPIRE agent kubectl apply -f https: //raw .githubusercontent.com /spiffe/spire/main/examples/k8s/agent .yaml |
Step 2: Configure Spring Security for SPIFFE
Spring Security can validate X.509 SVIDs from SPIFFE.
Add Dependencies (pom.xml
):
1 2 3 4 5 6 7 8 | < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-security</ artifactId > </ dependency > < dependency > < groupId >org.springframework.security</ groupId > < artifactId >spring-security-config</ artifactId > </ dependency > |
Configure application.yml
:
1 2 3 4 5 6 7 8 | server: ssl: enabled: true key-store: /path/to/keystore .p12 key-store-password: changeit trust-store: /path/to/truststore .jks trust-store-password: changeit client-auth: need # Enforces mTLS |
Custom Security Configuration:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .x509() .subjectPrincipalRegex( "CN=(.*?)(?:,|$)" ) .userDetailsService(userDetailsService()); } @Bean public UserDetailsService userDetailsService() { return username -> { if (username.equals( "spiffe://example.org/myservice" )) { return new User(username, "" , AuthorityUtils.createAuthorityList( "ROLE_SERVICE" )); } throw new UsernameNotFoundException( "Service not authorized" ); }; } } |
Step 3: Validate SPIFFE IDs in Requests
Use Spring AOP or Filters to verify SPIFFE IDs in headers or mTLS certificates.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | @Component public class SpiffeAuthFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { X509Certificate[] certs = (X509Certificate[]) request.getAttribute( "javax.servlet.request.X509Certificate" ); if (certs != null && certs.length > 0 ) { String spiffeId = extractSpiffeId(certs[ 0 ]); if (!spiffeId.startsWith( "spiffe://trusted-domain/" )) { response.sendError( 403 , "Unauthorized SPIFFE ID" ); return ; } } filterChain.doFilter(request, response); } private String extractSpiffeId(X509Certificate cert) { // Parse SPIFFE ID from SAN (Subject Alternative Name) return cert.getSubjectAlternativeNames() .stream() .filter(san -> san.get( 0 ).equals( 6 )) // URI type in SAN .map(san -> (String) san.get( 1 )) .findFirst() .orElseThrow(() -> new RuntimeException( "No SPIFFE ID found" )); } } |
4. Example: Securing a Spring Boot Microservice
A full example is available in:
5. Best Practices
- Rotate SVIDs Frequently: Use SPIRE’s automatic rotation.
- Limit Trust Domains: Only accept SPIFFE IDs from trusted issuers.
- Audit Logging: Log all authentication attempts.
6. Conclusion
By combining SPIFFE for workload identity and Spring Security for authentication, we can enforce Zero Trust principles in microservices. This approach eliminates static credentials and ensures secure, verifiable communication.