# Citadel Security

Security configurations for the Citadel platform. These resources close
parity gaps between Citadel and production-grade Kubernetes security baselines.

## mTLS via Cilium Mutual Authentication

**Problem:** Without mutual authentication, any pod can spoof identity labels
and impersonate another service. Network policies alone rely on label-based
identity, which is not cryptographically verified.

**Solution:** Cilium 1.14+ mutual authentication with SPIRE (SPIFFE Runtime
Environment). Every pod receives a SPIFFE Verifiable Identity Document (SVID)
from the SPIRE server. Cilium performs a transparent mTLS handshake before
allowing traffic, cryptographically proving both sides of every connection.

### How It Works

```
Pod A (frontend)                    Pod B (backend)
    |                                   |
    |-- 1. Connection attempt --------->|
    |                                   |
    |   [Cilium intercepts at eBPF]     |
    |                                   |
    |<- 2. TLS handshake (mutual) ----->|
    |   Pod A presents SVID             |
    |   Pod B presents SVID             |
    |   Both verified by SPIRE CA       |
    |                                   |
    |-- 3. Application data ----------->|
    |   (only after mutual auth)        |
```

### Prerequisites

Cilium must be installed with SPIRE integration. The install script
(`scripts/02-install-cilium.sh`) includes the required Helm values, or
you can enable it on an existing cluster:

```bash
helm upgrade cilium cilium/cilium \
  --namespace kube-system \
  --reuse-values \
  --set authentication.mutual.spire.enabled=true \
  --set authentication.mutual.spire.install.enabled=true \
  --set authentication.mutual.spire.install.server.dataStore=sqlite3 \
  --set authentication.mutual.spire.install.agent.socketPath=/run/spire/agent/agent.sock \
  --wait
```

### Applying Policies

```bash
# Apply mutual authentication policies
kubectl apply -f security/mutual-auth.yaml

# Verify SPIRE server is running
kubectl get pods -n cilium-spire

# Check SPIRE agent status
kubectl exec -n cilium-spire spire-agent-<pod> -- \
  /opt/spire/bin/spire-agent healthcheck

# Verify mutual auth is active on connections
hubble observe --namespace demo -o json | jq '.flow.auth_type'
```

### Verifying mTLS

```bash
# Check that connections without valid SVID are rejected
kubectl run --rm -it test-no-auth --image=busybox --restart=Never -- \
  wget -qO- http://backend.demo.svc:5678 --timeout=5
# Expected: connection refused or timeout (no SPIFFE identity)

# Check authenticated connections work between labeled pods
kubectl exec -n demo deploy/frontend -- \
  curl -s http://backend.demo.svc:5678
# Expected: success (both pods have valid SVIDs)
```

### Trust Domain

All SPIFFE identities use the trust domain `citadel.local`:

```
spiffe://citadel.local/ns/demo/sa/frontend
spiffe://citadel.local/ns/demo/sa/backend
spiffe://citadel.local/ns/demo/sa/database
```

### Files

| File | Purpose |
|------|---------|
| `security/mutual-auth.yaml` | CiliumNetworkPolicy resources enforcing mutual authentication |

## Data at Rest Encryption

**Problem:** Unencrypted etcd and persistent storage means that anyone with
disk access can read Kubernetes Secrets, ConfigMaps, and application data in
plaintext. This violates baseline security compliance requirements (SOC2,
FedRAMP, PCI-DSS).

**Solution:** Two layers of encryption at rest:
1. **etcd encryption** via Kubernetes EncryptionConfiguration (AES-GCM)
2. **Storage encryption** via MinIO SSE-S3 or host-level disk encryption

### Applying etcd Encryption

```bash
# Enable etcd encryption (requires K3s restart)
sudo ./scripts/08-enable-encryption.sh

# Verify secrets are encrypted
sudo ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cert=/var/lib/rancher/k3s/server/tls/etcd/server-client.crt \
  --key=/var/lib/rancher/k3s/server/tls/etcd/server-client.key \
  --cacert=/var/lib/rancher/k3s/server/tls/etcd/server-ca.crt \
  get /registry/secrets/default/my-secret | hexdump -C
# Should show 'k8s:enc:aesgcm:v1:citadel-key-1' prefix, not plaintext
```

### Files

| File | Purpose |
|------|---------|
| `security/encryption-config.yaml` | K8s EncryptionConfiguration for etcd (AES-GCM) |
| `security/storage-encryption.yaml` | MinIO SSE-S3 and StorageClass encryption config |
| `security/disk-encryption-guide.md` | Host-level disk encryption options (LUKS, dm-crypt) |
| `scripts/08-enable-encryption.sh` | Bootstrap script to deploy encryption config to K3s |
