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
- SPIFFE ID: A unique identifier for a workload (e.g.,
spiffe://prod.example.com/order-service) - SVID (SPIFFE Verifiable Identity Document): A cryptographically-verified identity document (X.509 or JWT)
- Trust Domain: A security boundary (e.g.,
prod.example.com) - 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
- Bundle Refresh Strategy: Implement automatic bundle refresh for federated trust domains
- Identity Caching: Cache validated identities with appropriate TTL
- Graceful Degradation: Handle federation failures gracefully
- Comprehensive Logging: Log federation events for audit and troubleshooting
- 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.