Skip to content
Back to skills

Multitenancy Spiffe

ASecurity

Expert in multi-tenant architecture with SPIFFE/SPIRE, mTLS, certificate rotation, and Rust SDK implementation. Use when performing security analysis, auditing, or hardening with multitenancy spiffe.

  • 9 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 8, 2026
devopsrustgobashsqlnodekubernetesapidatabasesecurity

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill multitenancy-spiffe --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Multitenancy Spiffe?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Multitenancy Spiffe
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-multitenancy-spiffe/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-multitenancy-spiffe)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: multitenancy-spiffe
description: Expert in multi-tenant architecture with SPIFFE/SPIRE, mTLS, certificate rotation, and Rust SDK implementation. Use when performing security analysis, auditing, or hardening with multitenancy spiffe.
license: CC-BY-NC-SA-4.0
metadata:
  risk: unknown
  source: community
  kind: mode
  category: security
---

# Multi-Tenancy Security Expert Mode

You are an expert in secure multi-tenant architecture. You specialize in SPIFFE/SPIRE for workload identity, mTLS for zero-trust communication, automatic certificate rotation, and Rust-based implementations.

## Core Competencies

### Multi-Tenancy Patterns

- Tenant isolation strategies
- Data segregation
- Resource quotas
- Identity federation
- Zero-trust architecture

### SPIFFE/SPIRE Stack

- SPIFFE identity framework
- SPIRE server and agents
- Workload attestation
- SVID (SPIFFE Verifiable Identity Document)
- Trust bundles

## SPIFFE Fundamentals

### SPIFFE ID Format

```
spiffe://trust-domain/path

Examples:
spiffe://acme.com/tenant/customer-a/service/api
spiffe://acme.com/tenant/customer-b/service/worker
spiffe://acme.com/platform/control-plane
```

### Multi-Tenant SPIFFE Architecture

```text
┌─────────────────────────────────────────────────────────┐
│                    SPIRE Server                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Tenant A    │  │ Tenant B    │  │ Platform    │     │
│  │ Registrar   │  │ Registrar   │  │ Services    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘
         │                  │                 │
    ┌────┴────┐        ┌────┴────┐       ┌────┴────┐
    │ Agent A │        │ Agent B │       │ Agent P │
    └────┬────┘        └────┬────┘       └────┬────┘
         │                  │                 │
    ┌────┴────┐        ┌────┴────┐       ┌────┴────┐
    │Workloads│        │Workloads│       │Workloads│
    │Tenant A │        │Tenant B │       │Platform │
    └─────────┘        └─────────┘       └─────────┘
```

## Rust Implementation

### Dependencies (Cargo.toml)

```toml
[dependencies]
# SPIFFE/SPIRE
spiffe = "0.4"
spire-api = "0.2"

# TLS/mTLS
rustls = "0.23"
tokio-rustls = "0.26"
rcgen = "0.13"
x509-parser = "0.16"

# Async runtime
tokio = { version = "1", features = ["full"] }

# gRPC for SPIRE
tonic = "0.12"
prost = "0.13"

# Multi-tenancy
uuid = { version = "1", features = ["v4"] }
dashmap = "6"
```

### SPIFFE Workload API Client

```rust
use spiffe::workload_api::client::WorkloadApiClient;
use spiffe::svid::x509::X509Svid;
use spiffe::bundle::x509::X509Bundle;
use std::sync::Arc;
use tokio::sync::RwLock;

pub struct SpiffeIdentityManager {
    client: WorkloadApiClient,
    current_svid: Arc<RwLock<Option<X509Svid>>>,
    trust_bundles: Arc<RwLock<Vec<X509Bundle>>>,
    tenant_id: String,
}

impl SpiffeIdentityManager {
    pub async fn new(tenant_id: &str) -> Result<Self, SpiffeError> {
        // Connect to SPIRE Agent socket
        let socket_path = std::env::var("SPIFFE_ENDPOINT_SOCKET")
            .unwrap_or_else(|_| "/run/spire/sockets/agent.sock".to_string());

        let client = WorkloadApiClient::new_from_path(&socket_path).await?;

        let manager = Self {
            client,
            current_svid: Arc::new(RwLock::new(None)),
            trust_bundles: Arc::new(RwLock::new(Vec::new())),
            tenant_id: tenant_id.to_string(),
        };

        // Initial fetch
        manager.refresh_identity().await?;

        Ok(manager)
    }

    pub async fn refresh_identity(&self) -> Result<(), SpiffeError> {
        // Fetch X.509 SVID for this workload
        let svid = self.client.fetch_x509_svid().await?;

        // Validate tenant claim in SPIFFE ID
        let spiffe_id = svid.spiffe_id();
        if !spiffe_id.path().contains(&format!("/tenant/{}/", self.tenant_id)) {
            return Err(SpiffeError::TenantMismatch);
        }

        *self.current_svid.write().await = Some(svid);

        // Fetch trust bundles for mTLS validation
        let bundles = self.client.fetch_x509_bundles().await?;
        *self.trust_bundles.write().await = bundles;

        Ok(())
    }

    pub async fn get_mtls_config(&self) -> Result<MtlsConfig, SpiffeError> {
        let svid = self.current_svid.read().await;
        let svid = svid.as_ref().ok_or(SpiffeError::NoIdentity)?;

        let bundles = self.trust_bundles.read().await;

        Ok(MtlsConfig {
            cert_chain: svid.cert_chain().to_vec(),
            private_key: svid.private_key().clone(),
            trust_anchors: bundles.iter()
                .flat_map(|b| b.authorities().to_vec())
                .collect(),
        })
    }
}
```

### Automatic Certificate Rotation

```rust
use std::time::Duration;
use tokio::time::{interval, Instant};

pub struct CertificateRotator {
    identity_manager: Arc<SpiffeIdentityManager>,
    rotation_interval: Duration,
    expiry_buffer: Duration,
}

impl CertificateRotator {
    pub fn new(
        identity_manager: Arc<SpiffeIdentityManager>,
        rotation_interval: Duration,
    ) -> Self {
        Self {
            identity_manager,
            rotation_interval,
            // Rotate before expiry (e.g., 50% of lifetime)
            expiry_buffer: rotation_interval / 2,
        }
    }

    pub async fn start_rotation_loop(&self) {
        let mut interval = interval(self.rotation_interval);

        loop {
            interval.tick().await;

            match self.check_and_rotate().await {
                Ok(rotated) => {
                    if rotated {
                        tracing::info!("Certificate rotated successfully");
                    }
                }
                Err(e) => {
                    tracing::error!("Certificate rotation failed: {}", e);
                    // Retry sooner on failure
                    tokio::time::sleep(Duration::from_secs(30)).await;
                }
            }
        }
    }

    async fn check_and_rotate(&self) -> Result<bool, SpiffeError> {
        let svid = self.identity_manager.current_svid.read().await;

        if let Some(svid) = svid.as_ref() {
            let expiry = svid.cert_chain()
                .first()
                .map(|c| c.not_after())
                .ok_or(SpiffeError::InvalidCert)?;

            let now = Instant::now();
            let time_until_expiry = expiry.duration_since(now)?;

            if time_until_expiry < self.expiry_buffer {
                drop(svid); // Release read lock
                self.identity_manager.refresh_identity().await?;
                return Ok(true);
            }
        } else {
            self.identity_manager.refresh_identity().await?;
            return Ok(true);
        }

        Ok(false)
    }
}
```

### mTLS Server with Tenant Validation

```rust
use tokio_rustls::TlsAcceptor;
use rustls::{ServerConfig, server::WebPkiClientVerifier};
use std::sync::Arc;

pub struct MtlsServer {
    identity_manager: Arc<SpiffeIdentityManager>,
    allowed_tenant_ids: Vec<String>,
}

impl MtlsServer {
    pub async fn create_tls_acceptor(&self) -> Result<TlsAcceptor, TlsError> {
        let mtls_config = self.identity_manager.get_mtls_config().await?;

        // Build client verifier with trust anchors
        let mut root_store = rustls::RootCertStore::empty();
        for anchor in &mtls_config.trust_anchors {
            root_store.add(anchor.clone())?;
        }

        let client_verifier = WebPkiClientVerifier::builder(Arc::new(root_store))
            .build()?;

        // Server config with mTLS
        let config = ServerConfig::builder()
            .with_client_cert_verifier(client_verifier)
            .with_single_cert(
                mtls_config.cert_chain,
                mtls_config.private_key,
            )?;

        Ok(TlsAcceptor::from(Arc::new(config)))
    }

    pub fn validate_client_tenant(
        &self,
        client_cert: &x509_parser::certificate::X509Certificate,
    ) -> Result<TenantContext, AuthError> {
        // Extract SPIFFE ID from SAN
        let spiffe_id = extract_spiffe_id_from_san(client_cert)?;

        // Parse tenant from SPIFFE ID path
        // e.g., spiffe://trust.domain/tenant/customer-a/service/api
        let tenant_id = extract_tenant_from_spiffe_id(&spiffe_id)?;

        // Validate tenant is allowed
        if !self.allowed_tenant_ids.contains(&tenant_id) {
            return Err(AuthError::UnauthorizedTenant(tenant_id));
        }

        Ok(TenantContext {
            tenant_id,
            spiffe_id,
            authenticated_at: chrono::Utc::now(),
        })
    }
}

fn extract_spiffe_id_from_san(
    cert: &x509_parser::certificate::X509Certificate,
) -> Result<String, AuthError> {
    use x509_parser::extensions::GeneralName;

    for ext in cert.extensions() {
        if let Ok(san) = ext.parsed_extension().as_subject_alternative_name() {
            for name in &san.general_names {
                if let GeneralName::URI(uri) = name {
                    if uri.starts_with("spiffe://") {
                        return Ok(uri.to_string());
                    }
                }
            }
        }
    }

    Err(AuthError::NoSpiffeId)
}
```

### Multi-Tenant Request Context

```rust
use std::collections::HashMap;
use dashmap::DashMap;

#[derive(Clone, Debug)]
pub struct TenantContext {
    pub tenant_id: String,
    pub spiffe_id: String,
    pub authenticated_at: chrono::DateTime<chrono::Utc>,
    pub permissions: Vec<String>,
    pub resource_quotas: ResourceQuotas,
}

#[derive(Clone, Debug)]
pub struct ResourceQuotas {
    pub max_requests_per_second: u32,
    pub max_connections: u32,
    pub max_storage_bytes: u64,
}

/// Thread-safe tenant context storage
pub struct TenantRegistry {
    contexts: DashMap<String, TenantContext>,
    rate_limiters: DashMap<String, RateLimiter>,
}

impl TenantRegistry {
    pub fn new() -> Self {
        Self {
            contexts: DashMap::new(),
            rate_limiters: DashMap::new(),
        }
    }

    pub fn register_tenant(&self, ctx: TenantContext) {
        let tenant_id = ctx.tenant_id.clone();
        let rps = ctx.resource_quotas.max_requests_per_second;

        self.contexts.insert(tenant_id.clone(), ctx);
        self.rate_limiters.insert(
            tenant_id,
            RateLimiter::new(rps),
        );
    }

    pub fn get_context(&self, tenant_id: &str) -> Option<TenantContext> {
        self.contexts.get(tenant_id).map(|r| r.clone())
    }

    pub fn check_rate_limit(&self, tenant_id: &str) -> Result<(), RateLimitError> {
        if let Some(limiter) = self.rate_limiters.get(tenant_id) {
            limiter.check()?;
        }
        Ok(())
    }
}
```

### Tenant-Isolated Database Connections

```rust
use sqlx::{PgPool, postgres::PgPoolOptions};

pub struct TenantDatabaseManager {
    pools: DashMap<String, PgPool>,
    connection_string_template: String,
}

impl TenantDatabaseManager {
    /// Each tenant gets isolated database/schema
    pub async fn get_pool(&self, tenant_id: &str) -> Result<PgPool, DbError> {
        // Check cache first
        if let Some(pool) = self.pools.get(tenant_id) {
            return Ok(pool.clone());
        }

        // Create new pool for tenant
        let conn_string = self.connection_string_template
            .replace("{tenant_id}", tenant_id);

        let pool = PgPoolOptions::new()
            .max_connections(10)
            .connect(&conn_string)
            .await?;

        // Set row-level security context
        sqlx::query(&format!("SET app.current_tenant = '{}'", tenant_id))
            .execute(&pool)
            .await?;

        self.pools.insert(tenant_id.to_string(), pool.clone());

        Ok(pool)
    }
}

// PostgreSQL Row-Level Security setup
/*
CREATE POLICY tenant_isolation ON resources
    USING (tenant_id = current_setting('app.current_tenant'));
*/
```

## SPIRE Configuration

### Server Configuration

```hcl
# spire-server.conf
server {
    bind_address = "0.0.0.0"
    bind_port = "8081"
    trust_domain = "acme.com"
    data_dir = "/var/spire/data"
    log_level = "INFO"

    ca_ttl = "24h"
    default_svid_ttl = "1h"
}

plugins {
    DataStore "sql" {
        plugin_data {
            database_type = "postgres"
            connection_string = "host=db user=spire dbname=spire"
        }
    }

    KeyManager "disk" {
        plugin_data {
            keys_path = "/var/spire/keys"
        }
    }

    NodeAttestor "k8s_psat" {
        plugin_data {
            clusters = {
                "production" = {
                    service_account_allow_list = ["spire:spire-agent"]
                }
            }
        }
    }
}
```

### Registration Entries for Multi-Tenancy

```bash
# Register tenant A workloads
spire-server entry create \
    -spiffeID spiffe://acme.com/tenant/customer-a/service/api \
    -parentID spiffe://acme.com/spire/agent/k8s_psat/production/node1 \
    -selector k8s:ns:tenant-a \
    -selector k8s:sa:api-service \
    -ttl 3600

# Register tenant B workloads
spire-server entry create \
    -spiffeID spiffe://acme.com/tenant/customer-b/service/api \
    -parentID spiffe://acme.com/spire/agent/k8s_psat/production/node1 \
    -selector k8s:ns:tenant-b \
    -selector k8s:sa:api-service \
    -ttl 3600

# Cross-tenant communication rules
spire-server entry create \
    -spiffeID spiffe://acme.com/platform/gateway \
    -parentID spiffe://acme.com/spire/agent/k8s_psat/production/node1 \
    -selector k8s:ns:platform \
    -selector k8s:sa:gateway \
    -federatesWith spiffe://acme.com/tenant/customer-a \
    -federatesWith spiffe://acme.com/tenant/customer-b
```

## Production-Ready Implementation

### Complete Service with Health Checks & Graceful Shutdown

```rust
use std::sync::Arc;
use std::time::Duration;
use tokio::signal;
use tokio::sync::watch;
use tracing::{info, error, warn};
use metrics::{counter, gauge, histogram};

pub struct MultiTenantService {
    identity_manager: Arc<SpiffeIdentityManager>,
    certificate_rotator: Arc<CertificateRotator>,
    tenant_registry: Arc<TenantRegistry>,
    db_manager: Arc<TenantDatabaseManager>,
    shutdown_tx: watch::Sender<bool>,
    shutdown_rx: watch::Receiver<bool>,
}

impl MultiTenantService {
    pub async fn new(config: ServiceConfig) -> Result<Self, ServiceError> {
        let (shutdown_tx, shutdown_rx) = watch::channel(false);

        // Initialize SPIFFE identity
        let identity_manager = Arc::new(
            SpiffeIdentityManager::new(&config.tenant_id)
                .await
                .map_err(|e| {
                    error!("Failed to initialize SPIFFE identity: {}", e);
                    ServiceError::IdentityInit(e)
                })?
        );

        // Initialize certificate rotator
        let certificate_rotator = Arc::new(CertificateRotator::new(
            identity_manager.clone(),
            Duration::from_secs(config.cert_rotation_interval_secs),
        ));

        let tenant_registry = Arc::new(TenantRegistry::new());
        let db_manager = Arc::new(TenantDatabaseManager::new(&config.db_template));

        Ok(Self {
            identity_manager,
            certificate_rotator,
            tenant_registry,
            db_manager,
            shutdown_tx,
            shutdown_rx,
        })
    }

    pub async fn run(&self) -> Result<(), ServiceError> {
        info!("Starting multi-tenant service");

        // Spawn certificate rotation background task
        let rotator = self.certificate_rotator.clone();
        let mut shutdown_rx = self.shutdown_rx.clone();
        tokio::spawn(async move {
            tokio::select! {
                _ = rotator.start_rotation_loop() => {},
                _ = shutdown_rx.changed() => {
                    info!("Certificate rotator shutting down");
                }
            }
        });

        // Spawn metrics reporter
        self.spawn_metrics_reporter();

        // Start mTLS server
        let server = self.create_mtls_server().await?;

        // Handle graceful shutdown
        let shutdown_rx = self.shutdown_rx.clone();
        tokio::select! {
            result = server.serve() => {
                if let Err(e) = result {
                    error!("Server error: {}", e);
                    return Err(ServiceError::Server(e));
                }
            }
            _ = Self::shutdown_signal() => {
                info!("Shutdown signal received");
                self.shutdown_tx.send(true).ok();
            }
        }

        // Graceful shutdown with timeout
        self.graceful_shutdown(Duration::from_secs(30)).await;

        info!("Service shutdown complete");
        Ok(())
    }

    async fn shutdown_signal() {
        let ctrl_c = async {
            signal::ctrl_c()
                .await
                .expect("Failed to install Ctrl+C handler");
        };

        #[cfg(unix)]
        let terminate = async {
            signal::unix::signal(signal::unix::SignalKind::terminate())
                .expect("Failed to install SIGTERM handler")
                .recv()
                .await;
        };

        #[cfg(not(unix))]
        let terminate = std::future::pending::<()>();

        tokio::select! {
            _ = ctrl_c => {},
            _ = terminate => {},
        }
    }

    async fn graceful_shutdown(&self, timeout: Duration) {
        info!("Starting graceful shutdown (timeout: {:?})", timeout);

        // Wait for in-flight requests
        let deadline = tokio::time::Instant::now() + timeout;

        while tokio::time::Instant::now() < deadline {
            let active = self.tenant_registry.active_connections();
            if active == 0 {
                break;
            }
            warn!("Waiting for {} active connections", active);
            tokio::time::sleep(Duration::from_millis(500)).await;
        }

        // Close database pools
        self.db_manager.close_all().await;
    }

    fn spawn_metrics_reporter(&self) {
        let identity_manager = self.identity_manager.clone();
        let tenant_registry = self.tenant_registry.clone();

        tokio::spawn(async move {
            let mut interval = tokio::time::interval(Duration::from_secs(15));
            loop {
                interval.tick().await;

                // Certificate expiry metric
                if let Ok(expiry_secs) = identity_manager.seconds_until_expiry().await {
                    gauge!("spiffe_cert_expiry_seconds").set(expiry_secs as f64);
                }

                // Tenant metrics
                let tenant_count = tenant_registry.tenant_count();
                gauge!("active_tenants").set(tenant_count as f64);

                let total_connections = tenant_registry.total_connections();
                gauge!("total_tenant_connections").set(total_connections as f64);
            }
        });
    }
}
```

### Production Error Handling

```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum SpiffeError {
    #[error("Failed to connect to SPIRE agent: {0}")]
    AgentConnection(#[from] std::io::Error),

    #[error("No identity available")]
    NoIdentity,

    #[error("Tenant mismatch in SPIFFE ID")]
    TenantMismatch,

    #[error("Invalid certificate: {0}")]
    InvalidCert(String),

    #[error("SVID fetch failed: {0}")]
    SvidFetch(String),

    #[error("Trust bundle fetch failed: {0}")]
    BundleFetch(String),
}

#[derive(Error, Debug)]
pub enum AuthError {
    #[error("No SPIFFE ID in certificate")]
    NoSpiffeId,

    #[error("Unauthorized tenant: {0}")]
    UnauthorizedTenant(String),

    #[error("Certificate validation failed: {0}")]
    CertValidation(String),

    #[error("mTLS handshake failed: {0}")]
    MtlsHandshake(String),
}

#[derive(Error, Debug)]
pub enum ServiceError {
    #[error("Identity initialization failed: {0}")]
    IdentityInit(#[source] SpiffeError),

    #[error("Server error: {0}")]
    Server(#[source] Box<dyn std::error::Error + Send + Sync>),

    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),

    #[error("Configuration error: {0}")]
    Config(String),
}

impl SpiffeError {
    pub fn is_retryable(&self) -> bool {
        matches!(self,
            SpiffeError::AgentConnection(_) |
            SpiffeError::SvidFetch(_) |
            SpiffeError::BundleFetch(_)
        )
    }
}
```

### Retry Logic with Exponential Backoff

```rust
use std::time::Duration;
use tokio::time::sleep;

pub struct RetryConfig {
    pub max_attempts: u32,
    pub initial_delay: Duration,
    pub max_delay: Duration,
    pub multiplier: f64,
}

impl Default for RetryConfig {
    fn default() -> Self {
        Self {
            max_attempts: 5,
            initial_delay: Duration::from_millis(100),
            max_delay: Duration::from_secs(30),
            multiplier: 2.0,
        }
    }
}

pub async fn retry_with_backoff<T, E, F, Fut>(
    config: &RetryConfig,
    operation: F,
) -> Result<T, E>
where
    F: Fn() -> Fut,
    Fut: std::future::Future<Output = Result<T, E>>,
    E: std::fmt::Display,
{
    let mut delay = config.initial_delay;
    let mut attempts = 0;

    loop {
        attempts += 1;

        match operation().await {
            Ok(result) => {
                if attempts > 1 {
                    info!("Operation succeeded after {} attempts", attempts);
                }
                return Ok(result);
            }
            Err(e) if attempts < config.max_attempts => {
                warn!(
                    "Attempt {}/{} failed: {}. Retrying in {:?}",
                    attempts, config.max_attempts, e, delay
                );
                counter!("retry_attempts_total").increment(1);

                sleep(delay).await;

                delay = Duration::from_secs_f64(
                    (delay.as_secs_f64() * config.multiplier)
                        .min(config.max_delay.as_secs_f64())
                );
            }
            Err(e) => {
                error!("All {} attempts failed. Last error: {}", attempts, e);
                counter!("retry_exhausted_total").increment(1);
                return Err(e);
            }
        }
    }
}

// Usage in identity manager
impl SpiffeIdentityManager {
    pub async fn refresh_identity_with_retry(&self) -> Result<(), SpiffeError> {
        let config = RetryConfig::default();

        retry_with_backoff(&config, || async {
            self.refresh_identity().await
        }).await
    }
}
```

### Health Check Endpoints

```rust
use axum::{routing::get, Router, Json};
use serde::Serialize;

#[derive(Serialize)]
pub struct HealthStatus {
    pub status: String,
    pub checks: HealthChecks,
    pub version: String,
}

#[derive(Serialize)]
pub struct HealthChecks {
    pub spiffe_identity: CheckResult,
    pub database: CheckResult,
    pub certificate_expiry: CertExpiryCheck,
}

#[derive(Serialize)]
pub struct CheckResult {
    pub healthy: bool,
    pub message: Option<String>,
    pub latency_ms: u64,
}

#[derive(Serialize)]
pub struct CertExpiryCheck {
    pub healthy: bool,
    pub expires_in_seconds: i64,
    pub warning_threshold_seconds: i64,
}

impl MultiTenantService {
    pub fn health_router(&self) -> Router {
        let identity_manager = self.identity_manager.clone();
        let db_manager = self.db_manager.clone();

        Router::new()
            .route("/health", get({
                let im = identity_manager.clone();
                let db = db_manager.clone();
                move || health_check(im.clone(), db.clone())
            }))
            .route("/health/live", get(|| async {
                Json(serde_json::json!({"status": "ok"}))
            }))
            .route("/ready", get({
                let im = identity_manager.clone();
                move || readiness_check(im.clone())
            }))
    }
}

async fn health_check(
    identity_manager: Arc<SpiffeIdentityManager>,
    db_manager: Arc<TenantDatabaseManager>,
) -> Json<HealthStatus> {
    let start = std::time::Instant::now();

    // Check SPIFFE identity
    let spiffe_check = match identity_manager.get_mtls_config().await {
        Ok(_) => CheckResult {
            healthy: true,
            message: None,
            latency_ms: start.elapsed().as_millis() as u64,
        },
        Err(e) => CheckResult {
            healthy: false,
            message: Some(e.to_string()),
            latency_ms: start.elapsed().as_millis() as u64,
        },
    };

    // Check database connectivity
    let db_start = std::time::Instant::now();
    let db_check = match db_manager.health_check().await {
        Ok(_) => CheckResult {
            healthy: true,
            message: None,
            latency_ms: db_start.elapsed().as_millis() as u64,
        },
        Err(e) => CheckResult {
            healthy: false,
            message: Some(e.to_string()),
            latency_ms: db_start.elapsed().as_millis() as u64,
        },
    };

    // Check certificate expiry
    let warning_threshold = 3600; // 1 hour
    let expires_in = identity_manager
        .seconds_until_expiry()
        .await
        .unwrap_or(0);

    let cert_check = CertExpiryCheck {
        healthy: expires_in > warning_threshold,
        expires_in_seconds: expires_in,
        warning_threshold_seconds: warning_threshold,
    };

    let all_healthy = spiffe_check.healthy
        && db_check.healthy
        && cert_check.healthy;

    Json(HealthStatus {
        status: if all_healthy { "healthy" } else { "degraded" }.to_string(),
        checks: HealthChecks {
            spiffe_identity: spiffe_check,
            database: db_check,
            certificate_expiry: cert_check,
        },
        version: env!("CARGO_PKG_VERSION").to_string(),
    })
}
```

### Observability: Structured Logging & Tracing

```rust
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
use opentelemetry::global;
use opentelemetry_otlp::WithExportConfig;

pub fn init_observability(config: &ObservabilityConfig) -> Result<(), ObsError> {
    // Initialize OpenTelemetry tracer
    let tracer = opentelemetry_otlp::new_pipeline()
        .tracing()
        .with_exporter(
            opentelemetry_otlp::new_exporter()
                .tonic()
                .with_endpoint(&config.otlp_endpoint)
        )
        .with_trace_config(
            opentelemetry_sdk::trace::config()
                .with_resource(opentelemetry_sdk::Resource::new(vec![
                    opentelemetry::KeyValue::new("service.name", &config.service_name),
                    opentelemetry::KeyValue::new("service.version", env!("CARGO_PKG_VERSION")),
                ]))
        )
        .install_batch(opentelemetry_sdk::runtime::Tokio)?;

    // Set up tracing subscriber
    let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);

    let fmt_layer = tracing_subscriber::fmt::layer()
        .json()
        .with_current_span(true)
        .with_span_list(true);

    tracing_subscriber::registry()
        .with(EnvFilter::from_default_env())
        .with(telemetry)
        .with(fmt_layer)
        .init();

    // Initialize metrics
    let meter_provider = opentelemetry_otlp::new_pipeline()
        .metrics(opentelemetry_sdk::runtime::Tokio)
        .with_exporter(
            opentelemetry_otlp::new_exporter()
                .tonic()
                .with_endpoint(&config.otlp_endpoint)
        )
        .build()?;

    global::set_meter_provider(meter_provider);

    Ok(())
}

// Request tracing middleware
pub async fn trace_request<B>(
    tenant_ctx: Extension<TenantContext>,
    request: axum::http::Request<B>,
    next: axum::middleware::Next<B>,
) -> axum::response::Response {
    let span = tracing::info_span!(
        "http_request",
        tenant_id = %tenant_ctx.tenant_id,
        method = %request.method(),
        path = %request.uri().path(),
        trace_id = tracing::field::Empty,
    );

    let _guard = span.enter();

    let start = std::time::Instant::now();
    let response = next.run(request).await;
    let latency = start.elapsed();

    histogram!("http_request_duration_seconds",
        "tenant_id" => tenant_ctx.tenant_id.clone(),
        "status" => response.status().as_u16().to_string(),
    ).record(latency.as_secs_f64());

    response
}
```

### Kubernetes Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: multitenant-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: multitenant-service
  template:
    metadata:
      labels:
        app: multitenant-service
    spec:
      serviceAccountName: multitenant-service
      containers:
        - name: service
          image: acme/multitenant-service:latest
          ports:
            - containerPort: 8443
              name: https
            - containerPort: 9090
              name: metrics
          env:
            - name: SPIFFE_ENDPOINT_SOCKET
              value: /run/spire/sockets/agent.sock
            - name: RUST_LOG
              value: info,tower_http=debug
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://otel-collector:4317
          volumeMounts:
            - name: spire-agent-socket
              mountPath: /run/spire/sockets
              readOnly: true
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 1000m
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /health/live
              port: 9090
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 9090
            initialDelaySeconds: 5
            periodSeconds: 5
          securityContext:
            runAsNonRoot: true
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
      volumes:
        - name: spire-agent-socket
          hostPath:
            path: /run/spire/sockets
            type: Directory
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: multitenant-service
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multitenant-service
spec:
  podSelector:
    matchLabels:
      app: multitenant-service
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: istio-system
      ports:
        - port: 8443
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: spire
      ports:
        - port: 8081 # SPIRE server
    - to:
        - namespaceSelector:
            matchLabels:
              name: database
      ports:
        - port: 5432
```

## Best Practices

### Tenant Isolation Checklist

- [ ] Unique SPIFFE IDs per tenant
- [ ] Separate namespaces/network policies
- [ ] Row-level security in databases
- [ ] Tenant-scoped encryption keys
- [ ] Isolated secret stores
- [ ] Per-tenant rate limiting
- [ ] Audit logging with tenant context

### Certificate Rotation

- Rotate before 50% of TTL
- Handle rotation failures gracefully
- Monitor certificate expiry metrics
- Test rotation under load

### Zero Trust Principles

- Verify every request (mTLS)
- Least privilege access
- Assume breach mentality
- Continuous validation

## Output Format

Provide:

- Secure multi-tenant architecture designs
- Rust implementation code
- SPIFFE/SPIRE configuration
- mTLS setup and validation
- Certificate rotation strategies

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.