Secure Polyglot System with Identity Federation and Encryption Lifecycle: A Complete Integration Tutorial¶
Objective: Build a production-ready secure polyglot system that integrates identity federation, encryption lifecycle management, secrets management, secure-by-design patterns, and multi-tenant isolation. This tutorial demonstrates how to secure systems across multiple languages and environments.
This tutorial combines: - Cross-Domain Identity Federation, AuthZ/AuthN Architecture - Unified identity across systems - Secret Supply Chains, Encryption Lifecycle Management - Encryption and key rotation - End-to-End Secrets Management - Secrets lifecycle - Secure-by-Design Lifecycle Architecture - Security as lifecycle concern - Secure Computes, Sandboxing, and Multi-Tenant Isolation - Isolation patterns
1) Prerequisites¶
# Required tools
docker --version # >= 20.10
docker compose --version # >= 2.0
kubectl --version # >= 1.28
vault --version # Hashicorp Vault
python --version # >= 3.10
go --version # >= 1.21
rust --version # >= 1.70
# Python packages
pip install fastapi python-jose[cryptography] passlib[bcrypt] \
opentelemetry-api opentelemetry-sdk vault-python
# Go packages
go get github.com/hashicorp/vault/api
go get golang.org/x/crypto/bcrypt
go get github.com/golang-jwt/jwt/v5
Why: Secure polyglot systems require identity management (OIDC, JWT), secrets management (Vault), encryption (TLS, application-level), and isolation patterns across Python, Go, and Rust services.
2) Architecture Overview¶
We'll build a Secure Multi-Tenant API Platform with comprehensive security:
graph TB
subgraph "Client Layer"
WEB[Web Client]
MOBILE[Mobile App]
API_CLIENT[API Client]
end
subgraph "Identity & Access"
OIDC[OIDC Provider]
AUTH_SERVICE[Auth Service<br/>Python]
RBAC[RBAC Engine]
ABAC[ABAC Engine]
end
subgraph "API Gateway"
GATEWAY[Kong Gateway]
JWT_VALIDATE[JWT Validation]
RATE_LIMIT[Rate Limiter]
end
subgraph "Microservices"
USER_SERVICE[User Service<br/>Python/FastAPI]
DATA_SERVICE[Data Service<br/>Go]
PROCESSING[Processing Service<br/>Rust]
end
subgraph "Secrets & Encryption"
VAULT[Hashicorp Vault]
KMS[KMS]
KEY_ROTATION[Key Rotation]
ENCRYPTION[Encryption Layer]
end
subgraph "Isolation"
TENANT_ISOLATION[Tenant Isolation]
SANDBOX[Sandboxing]
NETWORK_POLICY[Network Policies]
end
subgraph "Data Layer"
POSTGRES[(PostgreSQL<br/>Encrypted)]
REDIS[(Redis<br/>Encrypted)]
end
WEB --> GATEWAY
MOBILE --> GATEWAY
API_CLIENT --> GATEWAY
GATEWAY --> JWT_VALIDATE
GATEWAY --> RATE_LIMIT
GATEWAY --> USER_SERVICE
GATEWAY --> DATA_SERVICE
GATEWAY --> PROCESSING
USER_SERVICE --> AUTH_SERVICE
DATA_SERVICE --> AUTH_SERVICE
PROCESSING --> AUTH_SERVICE
AUTH_SERVICE --> OIDC
AUTH_SERVICE --> RBAC
AUTH_SERVICE --> ABAC
USER_SERVICE --> VAULT
DATA_SERVICE --> VAULT
PROCESSING --> VAULT
VAULT --> KMS
VAULT --> KEY_ROTATION
USER_SERVICE --> ENCRYPTION
DATA_SERVICE --> ENCRYPTION
PROCESSING --> ENCRYPTION
USER_SERVICE --> TENANT_ISOLATION
DATA_SERVICE --> TENANT_ISOLATION
PROCESSING --> TENANT_ISOLATION
USER_SERVICE --> POSTGRES
DATA_SERVICE --> POSTGRES
PROCESSING --> POSTGRES
POSTGRES --> ENCRYPTION
REDIS --> ENCRYPTION Security Layers: 1. Identity: OIDC, JWT, RBAC/ABAC 2. Secrets: Vault, KMS, key rotation 3. Encryption: TLS, application-level, at-rest 4. Isolation: Tenant isolation, sandboxing, network policies 5. Access Control: API gateway, rate limiting, authorization
3) Repository Layout¶
secure-polyglot/
├── docker-compose.yaml
├── services/
│ ├── auth-service/
│ │ ├── Dockerfile
│ │ ├── requirements.txt
│ │ ├── app/
│ │ │ ├── __init__.py
│ │ │ ├── main.py
│ │ │ ├── oidc.py
│ │ │ ├── rbac.py
│ │ │ ├── abac.py
│ │ │ └── jwt.py
│ │ └── tests/
│ ├── user-service/
│ │ ├── Dockerfile
│ │ ├── requirements.txt
│ │ └── app/
│ ├── data-service/
│ │ ├── Dockerfile
│ │ ├── go.mod
│ │ └── main.go
│ └── processing-service/
│ ├── Dockerfile
│ ├── Cargo.toml
│ └── src/
├── vault/
│ ├── policies/
│ │ ├── user-service.hcl
│ │ ├── data-service.hcl
│ │ └── processing-service.hcl
│ └── init.sh
├── encryption/
│ ├── key-rotation/
│ │ └── rotate-keys.sh
│ └── kms/
│ └── setup.sh
└── scripts/
├── setup-vault.sh
└── rotate-secrets.sh
4) Identity Federation (Python)¶
Create services/auth-service/app/oidc.py:
"""OIDC identity federation."""
from typing import Optional, Dict, Any
from datetime import datetime, timedelta
import jwt
from jose import JWTError
from fastapi import HTTPException, Depends
from fastapi.security import OAuth2PasswordBearer
from passlib.context import CryptContext
from app.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class OIDCProvider:
"""OIDC provider for identity federation."""
def __init__(self, issuer: str, client_id: str, client_secret: str):
self.issuer = issuer
self.client_id = client_id
self.client_secret = client_secret
self.jwks_uri = f"{issuer}/.well-known/jwks.json"
def verify_token(self, token: str) -> Dict[str, Any]:
"""Verify OIDC token."""
try:
# In production, fetch JWKS and verify
payload = jwt.decode(
token,
options={"verify_signature": False} # Simplified for demo
)
return payload
except JWTError as e:
raise HTTPException(
status_code=401,
detail=f"Invalid token: {str(e)}"
)
def get_user_info(self, token: str) -> Dict[str, Any]:
"""Get user info from OIDC provider."""
payload = self.verify_token(token)
return {
"sub": payload.get("sub"),
"email": payload.get("email"),
"name": payload.get("name"),
"roles": payload.get("roles", [])
}
class JWTManager:
"""JWT token management."""
def __init__(self, secret_key: str, algorithm: str = "HS256"):
self.secret_key = secret_key
self.algorithm = algorithm
def create_token(
self,
data: Dict[str, Any],
expires_delta: Optional[timedelta] = None
) -> str:
"""Create JWT token."""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(hours=24)
to_encode.update({
"exp": expire,
"iat": datetime.utcnow(),
"iss": settings.JWT_ISSUER
})
return jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
def verify_token(self, token: str) -> Dict[str, Any]:
"""Verify JWT token."""
try:
payload = jwt.decode(
token,
self.secret_key,
algorithms=[self.algorithm],
options={"verify_exp": True}
)
return payload
except JWTError as e:
raise HTTPException(
status_code=401,
detail=f"Invalid token: {str(e)}"
)
async def get_current_user(token: str = Depends(oauth2_scheme)) -> Dict[str, Any]:
"""Get current user from token."""
oidc = OIDCProvider(
issuer=settings.OIDC_ISSUER,
client_id=settings.OIDC_CLIENT_ID,
client_secret=settings.OIDC_CLIENT_SECRET
)
return oidc.get_user_info(token)
5) RBAC/ABAC (Python)¶
Create services/auth-service/app/rbac.py:
"""RBAC and ABAC authorization."""
from typing import List, Dict, Any, Optional
from enum import Enum
from dataclasses import dataclass
from app.oidc import get_current_user
from fastapi import Depends, HTTPException
class Permission(Enum):
"""Permissions."""
READ = "read"
WRITE = "write"
DELETE = "delete"
ADMIN = "admin"
@dataclass
class Role:
"""Role definition."""
name: str
permissions: List[Permission]
resources: List[str]
class RBACEngine:
"""RBAC authorization engine."""
def __init__(self):
self.roles = {
"admin": Role(
name="admin",
permissions=[Permission.READ, Permission.WRITE, Permission.DELETE, Permission.ADMIN],
resources=["*"]
),
"user": Role(
name="user",
permissions=[Permission.READ, Permission.WRITE],
resources=["users", "data"]
),
"viewer": Role(
name="viewer",
permissions=[Permission.READ],
resources=["users", "data"]
)
}
def has_permission(
self,
user_roles: List[str],
permission: Permission,
resource: str
) -> bool:
"""Check if user has permission."""
for role_name in user_roles:
if role_name in self.roles:
role = self.roles[role_name]
if permission in role.permissions:
if "*" in role.resources or resource in role.resources:
return True
return False
class ABACEngine:
"""ABAC authorization engine."""
def evaluate_policy(
self,
subject: Dict[str, Any],
resource: Dict[str, Any],
action: str,
environment: Dict[str, Any]
) -> bool:
"""Evaluate ABAC policy."""
# Example policy: user can access their own data or admin can access all
if subject.get("roles", []).__contains__("admin"):
return True
if action == "read" and subject.get("sub") == resource.get("owner"):
return True
if action == "write" and subject.get("sub") == resource.get("owner"):
return True
return False
def require_permission(permission: Permission, resource: str):
"""Decorator for permission checking."""
def decorator(func):
async def wrapper(
*args,
current_user: Dict[str, Any] = Depends(get_current_user),
**kwargs
):
rbac = RBACEngine()
user_roles = current_user.get("roles", [])
if not rbac.has_permission(user_roles, permission, resource):
raise HTTPException(
status_code=403,
detail=f"Permission denied: {permission.value} on {resource}"
)
return await func(*args, current_user=current_user, **kwargs)
return wrapper
return decorator
6) Secrets Management (Python)¶
Create services/user-service/app/secrets.py:
"""Secrets management with Vault."""
import hvac
from typing import Optional, Dict, Any
import os
from observability.metrics import secrets_metrics
class VaultClient:
"""Hashicorp Vault client."""
def __init__(self, vault_url: str, vault_token: str):
self.client = hvac.Client(url=vault_url, token=vault_token)
self.client.is_authenticated()
def get_secret(self, path: str, key: Optional[str] = None) -> Any:
"""Get secret from Vault."""
try:
response = self.client.secrets.kv.v2.read_secret_version(path=path)
data = response["data"]["data"]
if key:
return data.get(key)
return data
except Exception as e:
secrets_metrics.secret_access_failures.labels(
path=path,
error_type=type(e).__name__
).inc()
raise
def rotate_secret(self, path: str, new_secret: str) -> bool:
"""Rotate secret in Vault."""
try:
self.client.secrets.kv.v2.create_or_update_secret(
path=path,
secret={"value": new_secret}
)
secrets_metrics.secret_rotations.labels(path=path).inc()
return True
except Exception as e:
secrets_metrics.secret_rotation_failures.labels(
path=path,
error_type=type(e).__name__
).inc()
raise
# Global Vault client
vault_client = VaultClient(
vault_url=os.getenv("VAULT_ADDR", "http://vault:8200"),
vault_token=os.getenv("VAULT_TOKEN", "")
)
7) Encryption Lifecycle (Go)¶
Create services/data-service/encryption.go:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"time"
"github.com/hashicorp/vault/api"
)
type EncryptionManager struct {
vaultClient *api.Client
keyVersion string
keyID string
}
func NewEncryptionManager(vaultAddr, vaultToken string) (*EncryptionManager, error) {
client, err := api.NewClient(&api.Config{
Address: vaultAddr,
})
if err != nil {
return nil, err
}
client.SetToken(vaultToken)
return &EncryptionManager{
vaultClient: client,
keyVersion: "v1",
keyID: "data-encryption-key",
}, nil
}
func (em *EncryptionManager) getEncryptionKey() ([]byte, error) {
// Get key from Vault KMS
secret, err := em.vaultClient.Logical().Read(
fmt.Sprintf("transit/keys/%s", em.keyID),
)
if err != nil {
return nil, err
}
// In production, use Vault Transit for encryption
// For demo, derive key from secret
keyData := secret.Data["keys"].(map[string]interface{})[em.keyVersion].(map[string]interface{})
keyStr := keyData["key"].(string)
hash := sha256.Sum256([]byte(keyStr))
return hash[:], nil
}
func (em *EncryptionManager) Encrypt(plaintext []byte) ([]byte, error) {
key, err := em.getEncryptionKey()
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
return ciphertext, nil
}
func (em *EncryptionManager) Decrypt(ciphertext []byte) ([]byte, error) {
key, err := em.getEncryptionKey()
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, fmt.Errorf("ciphertext too short")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
func (em *EncryptionManager) RotateKey() error {
// Rotate key in Vault
_, err := em.vaultClient.Logical().Write(
fmt.Sprintf("transit/keys/%s/rotate", em.keyID),
nil,
)
if err != nil {
return err
}
// Update key version
em.keyVersion = fmt.Sprintf("v%d", time.Now().Unix())
return nil
}
8) Multi-Tenant Isolation (Rust)¶
Create services/processing-service/src/isolation.rs:
/// Multi-tenant isolation patterns.
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tenant {
pub id: String,
pub name: String,
pub isolation_level: IsolationLevel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum IsolationLevel {
Shared, // Shared resources
Dedicated, // Dedicated resources
Isolated, // Fully isolated
}
pub struct TenantIsolator {
tenants: HashMap<String, Tenant>,
}
impl TenantIsolator {
pub fn new() -> Self {
Self {
tenants: HashMap::new(),
}
}
pub fn register_tenant(&mut self, tenant: Tenant) {
self.tenants.insert(tenant.id.clone(), tenant);
}
pub fn get_tenant(&self, tenant_id: &str) -> Option<&Tenant> {
self.tenants.get(tenant_id)
}
pub fn is_isolated(&self, tenant_id: &str) -> bool {
match self.get_tenant(tenant_id) {
Some(tenant) => matches!(tenant.isolation_level, IsolationLevel::Isolated),
None => false,
}
}
pub fn validate_tenant_access(
&self,
tenant_id: &str,
resource_tenant_id: &str,
) -> bool {
// Tenant can only access their own resources
tenant_id == resource_tenant_id
}
}
pub struct Sandbox {
pub tenant_id: String,
pub resource_limits: ResourceLimits,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceLimits {
pub cpu_millis: u64,
pub memory_mb: u64,
pub max_execution_time_secs: u64,
}
impl Sandbox {
pub fn new(tenant_id: String, limits: ResourceLimits) -> Self {
Self {
tenant_id,
resource_limits: limits,
}
}
pub fn enforce_limits(&self) -> Result<(), String> {
// In production, enforce resource limits
// For demo, just validate
if self.resource_limits.cpu_millis == 0 {
return Err("CPU limit must be > 0".to_string());
}
if self.resource_limits.memory_mb == 0 {
return Err("Memory limit must be > 0".to_string());
}
Ok(())
}
}
9) Key Rotation Script¶
Create encryption/key-rotation/rotate-keys.sh:
#!/bin/bash
# Automated key rotation script
set -euo pipefail
KEY_ID="${1:-data-encryption-key}"
VAULT_ADDR="${VAULT_ADDR:-http://localhost:8200}"
VAULT_TOKEN="${VAULT_TOKEN:-}"
if [[ -z "$VAULT_TOKEN" ]]; then
echo "Error: VAULT_TOKEN not set"
exit 1
fi
echo "Rotating encryption key: $KEY_ID"
# Rotate key in Vault
vault write -f "transit/keys/$KEY_ID/rotate"
# Get new key version
NEW_VERSION=$(vault read -format=json "transit/keys/$KEY_ID" | \
jq -r '.data.latest_version')
echo "Key rotated to version: $NEW_VERSION"
# Update services (in production, use service discovery or config management)
kubectl set env deployment/user-service ENCRYPTION_KEY_VERSION="$NEW_VERSION"
kubectl set env deployment/data-service ENCRYPTION_KEY_VERSION="$NEW_VERSION"
kubectl set env deployment/processing-service ENCRYPTION_KEY_VERSION="$NEW_VERSION"
echo "Services updated with new key version"
# Verify rotation
echo "Verifying key rotation..."
vault read "transit/keys/$KEY_ID"
echo "Key rotation completed successfully!"
10) Testing Security¶
10.1) Setup Vault¶
# Start Vault
docker compose up -d vault
# Initialize Vault
./scripts/setup-vault.sh
# Create encryption key
vault write -f transit/keys/data-encryption-key
10.2) Test Identity Federation¶
# Get token from OIDC provider
TOKEN=$(curl -X POST http://localhost:8080/token \
-d "username=user&password=pass" | jq -r '.access_token')
# Access protected resource
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/users
10.3) Test Key Rotation¶
# Rotate encryption key
./encryption/key-rotation/rotate-keys.sh data-encryption-key
# Verify services are using new key
kubectl get pods -l app=user-service
11) Best Practices Integration Summary¶
This tutorial demonstrates:
- Identity Federation: OIDC, JWT, RBAC/ABAC across services
- Secrets Management: Vault integration with automatic rotation
- Encryption Lifecycle: Key rotation and cryptographic agility
- Secure-by-Design: Security built into service architecture
- Multi-Tenant Isolation: Tenant isolation and sandboxing
Key Integration Points: - Identity tokens propagate across services - Secrets are fetched from Vault at runtime - Encryption keys rotate without downtime - Tenant isolation enforced at service boundaries - Security metrics track all security operations
12) Next Steps¶
- Add mTLS between services
- Implement audit logging
- Add security scanning to CI/CD
- Implement zero-trust networking
- Add security dashboards
This tutorial demonstrates how multiple best practices integrate to create a production-ready secure polyglot system.