# Namespace Infrastructure Operator

The namespace infrastructure operator provisions per-namespace security and resource governance via `NamespaceInfrastructure` CRDs. It creates CiliumNetworkPolicies (default-deny + tenant-allow + peer policies), ResourceQuotas, and LimitRanges.

## Quick Start

```bash
# Create the target namespace
kubectl create ns team-alpha

# Apply infrastructure definition
cat <<EOF | kubectl apply -f -
apiVersion: citadel.dev/v1alpha1
kind: NamespaceInfrastructure
metadata:
  name: team-alpha-infra
  namespace: citadel-system
spec:
  targetNamespace: team-alpha
  networkPolicy:
    defaultDeny: true
    allowedPeers: ["shared-services"]
  resourceQuota:
    cpu: "4"
    memory: "8Gi"
    pods: "20"
EOF

# Watch provisioning
kubectl get nsinfra -n citadel-system -w
```

## NamespaceInfrastructure CRD

### Minimal Example

```yaml
apiVersion: citadel.dev/v1alpha1
kind: NamespaceInfrastructure
metadata:
  name: minimal-infra
  namespace: citadel-system
spec:
  targetNamespace: my-namespace
```

This creates a default-deny CiliumNetworkPolicy allowing only DNS and API server access, plus kube-system and monitoring ingress. No quotas or limits.

### Full Spec

```yaml
apiVersion: citadel.dev/v1alpha1
kind: NamespaceInfrastructure
metadata:
  name: full-infra
  namespace: citadel-system
spec:
  targetNamespace: team-beta
  networkPolicy:
    defaultDeny: true                        # Default-deny CiliumNetworkPolicy (default: true)
    ingressRules:                            # Additional ingress rules for tenant-allow policy
      - fromEndpoints:
          - matchLabels:
              app: frontend
        toPorts:
          - ports:
              - port: "8080"
                protocol: TCP
    egressRules:                             # Additional egress rules for tenant-allow policy
      - toFQDNs:
          - matchName: "api.example.com"
        toPorts:
          - ports:
              - port: "443"
                protocol: TCP
    allowedPeers:                            # Namespaces with bidirectional traffic
      - shared-services
      - monitoring
    allowKubeSystem: true                    # Allow ingress from kube-system (default: true)
    allowMonitoring: true                    # Allow ingress from monitoring (default: true)
  resourceQuota:
    cpu: "8"                                 # CPU limit
    memory: "16Gi"                           # Memory limit
    pods: "50"                               # Max pods
    storage: "200Gi"                         # Storage request limit
    pvcs: "10"                               # Max PersistentVolumeClaims
    custom:                                  # Additional quota entries
      services: "20"
  limitRange:
    defaultCpu: "500m"                       # Container default CPU (default: "500m")
    defaultMemory: "512Mi"                   # Container default memory (default: "512Mi")
    defaultRequestCpu: "100m"               # Container default request CPU (default: "100m")
    defaultRequestMemory: "128Mi"           # Container default request memory (default: "128Mi")
    maxCpu: "4"                             # Container max CPU (default: "4")
    maxMemory: "8Gi"                        # Container max memory (default: "8Gi")
```

Short names: `nsi`, `nsinfra`

## What Gets Created

For a NamespaceInfrastructure with all sections populated:

| Resource | Name | Namespace | Purpose |
|----------|------|-----------|---------|
| CiliumNetworkPolicy | `{target}-default-deny` | target | Block all traffic except DNS (kube-system:53) and API server (6443) |
| CiliumNetworkPolicy | `{target}-tenant-allow` | target | Custom ingress/egress + kube-system/monitoring access + intra-namespace |
| CiliumNetworkPolicy | `{target}-peer-{peer}` | target | Bidirectional traffic with each allowed peer namespace |
| ResourceQuota | `{target}-quota` | target | CPU, memory, pod, storage limits |
| LimitRange | `{target}-limits` | target | Container defaults and maximums |

All resources are labeled with `app.kubernetes.io/managed-by: citadel-operator`.

## Phase Lifecycle

```
┌─────────┐     ┌──────────────┐     ┌────────┐
│ Pending  │────>│ Provisioning │────>│ Active │
└─────────┘     └──────┬───────┘     └────────┘
                       │
                       │ (error)
                       ▼
                ┌────────┐
                │ Failed │
                └────────┘
```

| Phase | Meaning |
|-------|---------|
| **Pending** | Resource created, not yet reconciled |
| **Provisioning** | Reconciler is applying resources to the target namespace |
| **Active** | All resources applied successfully, `ready: true` |
| **Failed** | Error during provisioning (check conditions for details) |

## Drift Detection

The operator computes a SHA-256 hash (first 16 characters) of the network policy configuration and stores it in `status.policyHash`. Every 5 minutes, a timer recomputes the hash from the current spec and compares it. If the hash differs, the operator re-reconciles.

This catches:
- Manual edits to the NamespaceInfrastructure spec
- External modifications to managed resources

## Cleanup on Delete

When a NamespaceInfrastructure is deleted, the operator removes all managed resources in reverse order:

1. LimitRange
2. ResourceQuota
3. Peer CiliumNetworkPolicies
4. Tenant-allow CiliumNetworkPolicy
5. Default-deny CiliumNetworkPolicy

Resources are deleted safely — missing resources are ignored.

## Status Subresource

```yaml
status:
  phase: Active
  ready: true
  managedResources:
    defaultDenyPolicy: true
    tenantPolicy: true
    resourceQuota: true
    limitRange: true
  policyHash: "a1b2c3d4e5f6g7h8"
  conditions:
    - type: Reconciled
      status: "True"
      reason: ReconcileComplete
      message: "All resources applied successfully"
      lastTransitionTime: "2024-01-15T10:30:00Z"
```

Printer columns: Target, Phase, Ready, Age

## Network Policy Details

### Default-Deny Policy

Blocks all ingress and egress traffic with two exceptions:

1. **DNS**: Egress to kube-system on port 53 (UDP/TCP) — pods need name resolution
2. **API Server**: Egress to kube-apiserver entity on port 6443 — pods need K8s API access

### Tenant-Allow Policy

Grants namespace-specific permissions:

- **Intra-namespace**: All pods within the namespace can communicate
- **kube-system access**: If `allowKubeSystem: true`, allows ingress from kube-system (monitoring, DNS responses)
- **Monitoring access**: If `allowMonitoring: true`, allows ingress from monitoring namespace (Prometheus scraping)
- **Custom rules**: Any additional `ingressRules` and `egressRules` from the spec

### Peer Policies

For each namespace in `allowedPeers`, creates a bidirectional CiliumNetworkPolicy:

- Ingress from the peer namespace
- Egress to the peer namespace

## Kopf Event Handlers

| Event | Handler | Behavior |
|-------|---------|----------|
| CRD create/update/resume | `on_reconcile` | Full reconciliation |
| CRD delete | `on_delete` | Cleanup all managed resources |
| Timer (300s) | `on_drift_check` | Re-reconcile if hash changed |
| Startup | `on_startup` | Initialize reconciler |
| Health probe | `health_probe` | Return health dict |

## Python API

The builder functions are pure (no I/O) and can be used independently:

```python
from citadel_operator import (
    build_default_deny_policy,
    build_tenant_allow_policy,
    build_peer_policies,
    build_resource_quota,
    build_limit_range,
)
from citadel_operator.k3s.namespace_infra_models import (
    NetworkPolicyConfig,
    ResourceQuotaConfig,
    LimitRangeConfig,
)

config = NetworkPolicyConfig(
    default_deny=True,
    allowed_peers=("shared-services",),
    allow_kube_system=True,
    allow_monitoring=True,
)

# Generate CiliumNetworkPolicy dicts
deny = build_default_deny_policy("team-alpha", config)
allow = build_tenant_allow_policy("team-alpha", config)
peers = build_peer_policies("team-alpha", config)

# Generate ResourceQuota dict
quota_config = ResourceQuotaConfig(cpu="4", memory="8Gi", pods="20")
quota = build_resource_quota("team-alpha", quota_config)

# Generate LimitRange dict
limit_config = LimitRangeConfig()  # Uses defaults
limits = build_limit_range("team-alpha", limit_config)
```

## Prerequisites

- Cilium must be installed (CiliumNetworkPolicy CRD must exist)
- Target namespace must exist before creating the NamespaceInfrastructure resource
- Operator must have RBAC for CiliumNetworkPolicy CRUD in the `cilium.io` API group
