SPIFFE Federation in Java: Secure Service Identity Across Trust Domains

SPIFFE (Secure Production Identity Framework For Everyone) and its companion specification SPIFFA provide a standardized approach to service identity in distributed systems. Federation extends this capability across organizational boundaries, enabling secure service-to-service communication between different trust domains.

What is SPIFFE Federation?

SPIFFE Federation allows SPIFFE identities from one trust domain to be recognized and trusted by another trust domain. For Java applications, this means your services can securely communicate across organizational boundaries, cloud providers, or Kubernetes clusters without managing complex certificate chains or shared secrets.

Key Concepts for Java Developers

  1. SPIFFE ID: A unique identifier for a workload (e.g., spiffe://prod.example.com/order-service)
  2. SVID (SPIFFE Verifiable Identity Document): A cryptographically-verified identity document (X.509 or JWT)
  3. Trust Domain: A security boundary (e.g., prod.example.com)
  4. Federation: Establishing trust between different trust domains

Java SPIFFE Implementation with SPIRE

Maven Dependencies:

<dependencies>
<dependency>
<groupId>io.spiffe</groupId>
<artifactId>spiffe-core</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>io.spiffe</groupId>
<artifactId>spiffe-provider</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>io.spiffe</groupId>
<artifactId>spiffe-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>

Federation Configuration

bundle-endpoints.yaml:

federates_with:
"acme-corp.com":
bundle_endpoint:
url: "https://spire.acme-corp.com:8443/bundle-endpoint"
authentication:
aws_iid:
region: "us-west-2"
access_key_id: "AKIAIOSFODNN7EXAMPLE"
secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
"partner-org.io":
bundle_endpoint:
url: "https://spire.partner-org.io:8443/bundle-endpoint"
authentication:
spiffe:
endpoint_spiffe_id: "spiffe://partner-org.io/federation-endpoint"

Java Service with SPIFFE Federation

FederatedServiceConfiguration.java:

@Configuration
@EnableSpiffe
public class FederatedServiceConfiguration {
@Value("${spiffe.trust.domain:prod.example.com}")
private String trustDomain;
@Bean
public SpiffeClient spiffeClient() throws Exception {
return SpiffeClient.newBuilder()
.trustDomain(trustDomain)
.federatedTrustDomains("acme-corp.com", "partner-org.io")
.spiffeSocketPath("/tmp/spire-agent/public/api.sock")
.build();
}
@Bean
public WorkloadApiClient workloadApiClient() {
return new DefaultWorkloadApiClient("/tmp/spire-agent/public/api.sock");
}
}

FederatedOrderService.java:

@Service
public class FederatedOrderService {
private final SpiffeClient spiffeClient;
private final RestTemplate restTemplate;
public FederatedOrderService(SpiffeClient spiffeClient) {
this.spiffeClient = spiffeClient;
this.restTemplate = createFederatedRestTemplate();
}
private RestTemplate createFederatedRestTemplate() {
try {
SslContext sslContext = GrpcSslContexts.forClient()
.trustManager(spiffeClient.getFederatedBundle())
.keyManager(spiffeClient.getPrivateKey(), spiffeClient.getCertificateChain())
.build();
return RestTemplateBuilder.builder()
.sslContext(sslContext)
.build();
} catch (Exception e) {
throw new RuntimeException("Failed to create federated REST template", e);
}
}
public Order processCrossDomainOrder(OrderRequest request) {
// Call service in federated trust domain
String acmeInventoryUrl = "https://inventory.acme-corp.com/api/stock";
ResponseEntity<StockInfo> response = restTemplate.exchange(
acmeInventoryUrl,
HttpMethod.POST,
createFederatedRequest(request),
StockInfo.class
);
return processOrderWithStockInfo(request, response.getBody());
}
private HttpEntity<OrderRequest> createFederatedRequest(OrderRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
try {
// Add SPIFFE identity header for JWT authentication
String jwtToken = spiffeClient.fetchJwtToken(
"spiffe://acme-corp.com/inventory-service",
Arrays.asList("inventory-read", "order-write")
);
headers.set("Authorization", "Bearer " + jwtToken);
} catch (Exception e) {
throw new RuntimeException("Failed to create federated authentication", e);
}
return new HttpEntity<>(request, headers);
}
}

Spring Security with SPIFFE Federation

SpiffeSecurityConfig.java:

@Configuration
@EnableWebSecurity
public class SpiffeSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/internal/**").hasAuthority("SPIFFE://prod.example.com/internal-service")
.requestMatchers("/api/partner/**").hasAuthority("SPIFFE://acme-corp.com/partner-service")
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
.x509(x509 -> x509
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.authenticationUserDetailsService(spiffeUserDetailsService())
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.decoder(spiffeJwtDecoder())
)
);
return http.build();
}
@Bean
public UserDetailsService spiffeUserDetailsService() {
return new SpiffeUserDetailsService();
}
@Bean
public JwtDecoder spiffeJwtDecoder() {
return new SpiffeFederatedJwtDecoder();
}
}
@Component
class SpiffeUserDetailsService implements UserDetailsService {
private final SpiffeClient spiffeClient;
public SpiffeUserDetailsService(SpiffeClient spiffeClient) {
this.spiffeClient = spiffeClient;
}
@Override
public UserDetails loadUserByUsername(String spiffeId) throws UsernameNotFoundException {
try {
// Validate the SPIFFE ID is from a federated trust domain
if (spiffeClient.isFederatedIdentity(spiffeId)) {
return new FederatedServiceUser(spiffeId, getAuthoritiesForSpiffeId(spiffeId));
}
throw new UsernameNotFoundException("Not a federated SPIFFE ID: " + spiffeId);
} catch (Exception e) {
throw new UsernameNotFoundException("Invalid SPIFFE ID: " + spiffeId, e);
}
}
private List<SimpleGrantedAuthority> getAuthoritiesForSpiffeId(String spiffeId) {
// Map SPIFFE ID to Spring Security authorities
// Example: spiffe://acme-corp.com/inventory-service -> ROLE_ACME_INVENTORY
String authority = "ROLE_" + spiffeId.replace("spiffe://", "").replace("/", "_").toUpperCase();
return Arrays.asList(new SimpleGrantedAuthority(authority));
}
}

gRPC with SPIFFE Federation

FederatedGrpcConfiguration.java:

@Configuration
public class FederatedGrpcConfiguration {
@Bean
public Server server(SpiffeClient spiffeClient) throws IOException {
return ServerBuilder.forPort(8080)
.addService(new OrderServiceImpl())
.useTransportSecurity(
spiffeClient.getCertificateChain(),
spiffeClient.getPrivateKey()
)
.build();
}
@Bean
public ManagedChannel federatedChannel(SpiffeClient spiffeClient) {
try {
SslContext sslContext = GrpcSslContexts.forClient()
.trustManager(spiffeClient.getFederatedBundle()) // Includes federated bundles
.keyManager(spiffeClient.getPrivateKey(), spiffeClient.getCertificateChain())
.build();
return Grpc.newChannelBuilder("inventory.acme-corp.com:50051", 
NettyChannelBuilder.usingNegotiator(sslContext))
.build();
} catch (Exception e) {
throw new RuntimeException("Failed to create federated gRPC channel", e);
}
}
}

Kubernetes Deployment with SPIRE

spire-agent-configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
name: spire-agent-config
namespace: spire
data:
agent.conf: |
agent {
data_dir = "/run/spire"
log_level = "DEBUG"
server_address = "spire-server"
server_port = "8081"
socket_path = "/run/spire/sockets/agent.sock"
trust_domain = "prod.example.com"
federation {
bundle_endpoint {
address = "0.0.0.0"
port = "8443"
}
federates_with = "acme-corp.com"
federates_with = "partner-org.io"
}
}

java-service-spiffe.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
labels:
app: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
serviceAccountName: order-service-account
containers:
- name: order-service
image: order-service:latest
ports:
- containerPort: 8080
env:
- name: SPIFFE_ENDPOINT_SOCKET
value: "/run/spire/sockets/agent.sock"
- name: TRUST_DOMAINS
value: "prod.example.com,acme-corp.com,partner-org.io"
volumeMounts:
- name: spire-agent-socket
mountPath: /run/spire/sockets
readOnly: true
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
volumes:
- name: spire-agent-socket
hostPath:
path: /run/spire/sockets
type: DirectoryOrCreate

Monitoring and Observability

SpiffeMetricsConfiguration.java:

@Component
public class SpiffeMetricsConfiguration {
private final MeterRegistry meterRegistry;
private final SpiffeClient spiffeClient;
public SpiffeMetricsConfiguration(MeterRegistry meterRegistry, SpiffeClient spiffeClient) {
this.meterRegistry = meterRegistry;
this.spiffeClient = spiffeClient;
initializeSpiffeMetrics();
}
private void initializeSpiffeMetrics() {
Gauge.builder("spiffe.federated_trust_domains")
.description("Number of federated trust domains")
.register(meterRegistry, this, config -> spiffeClient.getFederatedTrustDomains().size());
Counter.builder("spiffe.federated_requests")
.description("Federated service requests")
.tag("trust_domain", "")
.register(meterRegistry);
}
@EventListener
public void handleFederatedRequest(FederatedRequestEvent event) {
Counter.builder("spiffe.federated_requests")
.tag("trust_domain", event.getTrustDomain())
.tag("success", String.valueOf(event.isSuccess()))
.register(meterRegistry)
.increment();
}
}

Testing Federated Services

TestSpiffeFederation.java:

@SpringBootTest
@TestPropertySource(properties = {
"spiffe.trust.domain=test.example.com",
"spiffe.federation.enabled=true"
})
public class TestSpiffeFederation {
@Autowired
private SpiffeClient spiffeClient;
@Test
public void testFederatedIdentityValidation() {
String federatedSpiffeId = "spiffe://acme-corp.com/inventory-service";
assertTrue(spiffeClient.isFederatedIdentity(federatedSpiffeId));
assertTrue(spiffeClient.validateFederatedSvid(federatedSpiffeId));
}
@Test
public void testCrossDomainCommunication() {
FederatedOrderService orderService = new FederatedOrderService(spiffeClient);
OrderRequest request = new OrderRequest("product-123", 5);
Order order = orderService.processCrossDomainOrder(request);
assertNotNull(order);
assertEquals("product-123", order.getProductId());
}
}
@Configuration
static class TestSpiffeConfig {
@Bean
@Primary
public SpiffeClient testSpiffeClient() {
return Mockito.mock(SpiffeClient.class);
}
}

Best Practices for Java SPIFFE Federation

  1. Bundle Refresh Strategy: Implement automatic bundle refresh for federated trust domains
  2. Identity Caching: Cache validated identities with appropriate TTL
  3. Graceful Degradation: Handle federation failures gracefully
  4. Comprehensive Logging: Log federation events for audit and troubleshooting
  5. Security Boundaries: Clearly define which federated services can access which resources

Conclusion

SPIFFE Federation provides a robust foundation for secure service-to-service communication across organizational boundaries in Java applications. By implementing SPIFFE Federation:

  • Eliminate shared secret management across trust domains
  • Establish cryptographically-verified service identities
  • Enable zero-trust networking between federated services
  • Simplify multi-cloud and hybrid cloud security
  • Maintain compliance with strong identity verification

For Java teams operating in complex, multi-organizational environments, SPIFFE Federation offers a standardized, secure approach to service identity that scales with your distributed system architecture.

Java Logistics, Shipping Integration & Enterprise Inventory Automation (Tracking, ERP, RFID & Billing Systems)

https://macronepal.com/blog/aftership-tracking-in-java-enterprise-package-visibility/
Explains how to integrate AfterShip tracking services into Java applications to provide real-time shipment visibility, delivery status updates, and centralized tracking across multiple courier services.

https://macronepal.com/blog/shipping-integration-using-fedex-api-with-java-for-logistics-automation/
Explains how to integrate the FedEx API into Java systems to automate shipping tasks such as creating shipments, calculating delivery costs, generating shipping labels, and tracking packages.

https://macronepal.com/blog/shipping-and-logistics-integrating-ups-apis-with-java-applications/
Explains UPS API integration in Java to enable automated shipping operations including rate calculation, shipment scheduling, tracking, and delivery confirmation management.

https://macronepal.com/blog/generating-and-reading-qr-codes-for-products-in-java/
Explains how Java applications generate and read QR codes for product identification, tracking, and authentication, supporting faster inventory handling and product verification processes.

https://macronepal.com/blog/designing-a-robust-pick-and-pack-workflow-in-java/
Explains how to design an efficient pick-and-pack workflow in Java warehouse systems, covering order processing, item selection, packaging steps, and logistics preparation to improve fulfillment efficiency.

https://macronepal.com/blog/rfid-inventory-management-system-in-java-a-complete-guide/
Explains how RFID technology integrates with Java applications to automate inventory tracking, reduce manual errors, and enable real-time stock monitoring in warehouses and retail environments.

https://macronepal.com/blog/erp-integration-with-odoo-in-java/
Explains how Java applications connect with Odoo ERP systems to synchronize inventory, orders, customer records, and financial data across enterprise systems.

https://macronepal.com/blog/automated-invoice-generation-creating-professional-excel-invoices-with-apache-poi-in-java/
Explains how to automatically generate professional Excel invoices in Java using Apache POI, enabling structured billing documents and automated financial record creation.

https://macronepal.com/blog/enterprise-financial-integration-using-quickbooks-api-in-java-applications/
Explains QuickBooks API integration in Java to automate financial workflows such as invoice management, payment tracking, accounting synchronization, and financial reporting.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper