# ValidatingAdmissionPolicy to restrict images in sandbox namespace
# Requires Kubernetes 1.30+ (or 1.28+ with feature gate)
# This is the critical security control that prevents arbitrary image execution
# even if the gateway is compromised.
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: dexorder-sandbox-image-policy
spec:
  failurePolicy: Fail
  matchConstraints:
    namespaceSelector:
      matchLabels:
        dexorder.io/type: sandboxes
    resourceRules:
      - apiGroups: ["apps"]
        apiVersions: ["v1"]
        resources: ["deployments"]
        operations: ["CREATE", "UPDATE"]
  validations:
    # Only allow images from our approved registry with sandbox prefix
    - expression: |
        object.spec.template.spec.containers.all(c,
          c.image.startsWith('ghcr.io/dexorder/sandbox:') ||
          c.image.startsWith('ghcr.io/dexorder/sandbox-') ||
          c.image.startsWith('ghcr.io/dexorder/lifecycle-sidecar:') ||
          c.image.startsWith('dexorder/ai-sandbox:') ||
          c.image.startsWith('dexorder/ai-lifecycle-sidecar:') ||
          c.image.startsWith('git.dxod.org/dexorder/dexorder/ai-sandbox:') ||
          c.image.startsWith('git.dxod.org/dexorder/dexorder/ai-lifecycle-sidecar:'))
      message: "Only approved dexorder sandbox images are allowed in the sandboxes namespace"
      reason: Forbidden

    # No privileged containers
    - expression: |
        object.spec.template.spec.containers.all(c,
          !has(c.securityContext) ||
          !has(c.securityContext.privileged) ||
          c.securityContext.privileged == false)
      message: "Privileged containers are not allowed"
      reason: Forbidden

    # No hostPath volumes
    - expression: |
        !has(object.spec.template.spec.volumes) ||
        object.spec.template.spec.volumes.all(v,
          !has(v.hostPath))
      message: "hostPath volumes are not allowed"
      reason: Forbidden

    # No hostNetwork
    - expression: |
        !has(object.spec.template.spec.hostNetwork) ||
        object.spec.template.spec.hostNetwork == false
      message: "hostNetwork is not allowed"
      reason: Forbidden

    # No hostPID
    - expression: |
        !has(object.spec.template.spec.hostPID) ||
        object.spec.template.spec.hostPID == false
      message: "hostPID is not allowed"
      reason: Forbidden

    # Containers must run as non-root
    - expression: |
        object.spec.template.spec.containers.all(c,
          has(c.securityContext) &&
          has(c.securityContext.runAsNonRoot) &&
          c.securityContext.runAsNonRoot == true)
      message: "Containers must run as non-root"
      reason: Forbidden

    # Must drop all capabilities
    - expression: |
        object.spec.template.spec.containers.all(c,
          has(c.securityContext) &&
          has(c.securityContext.capabilities) &&
          has(c.securityContext.capabilities.drop) &&
          c.securityContext.capabilities.drop.exists(cap, cap == 'ALL'))
      message: "Containers must drop all capabilities"
      reason: Forbidden

    # Read-only root filesystem
    - expression: |
        object.spec.template.spec.containers.all(c,
          has(c.securityContext) &&
          has(c.securityContext.readOnlyRootFilesystem) &&
          c.securityContext.readOnlyRootFilesystem == true)
      message: "Containers must have read-only root filesystem"
      reason: Forbidden

    # Resource limits must be set
    - expression: |
        object.spec.template.spec.containers.all(c,
          has(c.resources) &&
          has(c.resources.limits) &&
          has(c.resources.limits.memory) &&
          has(c.resources.limits.cpu))
      message: "Containers must have resource limits set"
      reason: Forbidden
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: dexorder-sandbox-image-policy-binding
spec:
  policyName: dexorder-sandbox-image-policy
  validationActions:
    - Deny
  matchResources:
    namespaceSelector:
      matchLabels:
        dexorder.io/type: sandboxes
