apiVersion: v1
kind: Namespace
metadata:
  name: overdeck-factory
  labels:
    overdeck.dev/factory-admission: enabled
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: overdeck-factory-controller
  namespace: overdeck-factory
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: overdeck-factory-runner
  namespace: overdeck-factory
automountServiceAccountToken: false
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: overdeck-factory-egress
  namespace: overdeck-factory
data:
  tinyproxy.conf: |
    Port 8888
    Listen 0.0.0.0
    Timeout 600
    LogFile "/tmp/tinyproxy.log"
    LogLevel Info
    PidFile "/tmp/tinyproxy.pid"
    MaxClients 100
    Allow 10.42.0.0/16
    ConnectPort 22
    ConnectPort 443
    Filter "/etc/tinyproxy/filter"
    FilterURLs No
    FilterType ere
    FilterDefaultDeny Yes
  filter: |
    ^github\.com$
    ^ssh\.github\.com$
    ^api\.github\.com$
    ^chatgpt\.com$
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: overdeck-factory-egress
  namespace: overdeck-factory
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: overdeck-factory-egress
  template:
    metadata:
      labels:
        app.kubernetes.io/name: overdeck-factory-egress
    spec:
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: proxy
          image: ghcr.io/alexcodeplace/overdeck-agent-sandbox@sha256:1db62fb686815054b86a658bed943d81faa71fc23908a325a952ae50df36c9db
          imagePullPolicy: Always
          command: ["/usr/bin/tinyproxy"]
          args: ["-d", "-c", "/etc/tinyproxy/tinyproxy.conf"]
          ports:
            - name: proxy
              containerPort: 8888
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
            limits:
              cpu: 250m
              memory: 128Mi
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: config
              mountPath: /etc/tinyproxy
              readOnly: true
            - name: tmp
              mountPath: /tmp
      imagePullSecrets:
        - name: overdeck-ghcr-pull
      volumes:
        - name: config
          configMap:
            name: overdeck-factory-egress
            defaultMode: 292
        - name: tmp
          emptyDir:
            sizeLimit: 16Mi
---
apiVersion: v1
kind: Service
metadata:
  name: overdeck-factory-egress
  namespace: overdeck-factory
spec:
  clusterIP: 10.43.200.8
  selector:
    app.kubernetes.io/name: overdeck-factory-egress
  ports:
    - name: proxy
      port: 8888
      targetPort: proxy
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: overdeck-factory-default-deny-egress
  namespace: overdeck-factory
spec:
  podSelector: {}
  policyTypes: ["Egress"]
  egress: []
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: overdeck-factory-workers
  namespace: overdeck-factory
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: overdeck-factory
      app.kubernetes.io/component: worker
  policyTypes: ["Egress"]
  egress:
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: overdeck-factory-egress
      ports:
        - protocol: TCP
          port: 8888
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: overdeck-factory-egress
  namespace: overdeck-factory
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: overdeck-factory-egress
  policyTypes: ["Ingress", "Egress"]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: overdeck-factory
              app.kubernetes.io/component: worker
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: overdeck
          podSelector:
            matchLabels:
              app.kubernetes.io/name: overdeck-cdx
              app.kubernetes.io/component: worker
      ports:
        - protocol: TCP
          port: 8888
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
      ports:
        - protocol: TCP
          port: 22
        - protocol: TCP
          port: 443
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: overdeck-cdx-egress
  namespace: overdeck-factory
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: overdeck-cdx-egress
  template:
    metadata:
      labels:
        app.kubernetes.io/name: overdeck-cdx-egress
    spec:
      automountServiceAccountToken: false
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: kubernetes.io/hostname
                    operator: In
                    values: [debian2, debian3]
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: proxy
          image: ghcr.io/alexcodeplace/overdeck-agent-sandbox@sha256:1db62fb686815054b86a658bed943d81faa71fc23908a325a952ae50df36c9db
          imagePullPolicy: Always
          command: ["/usr/bin/tinyproxy"]
          args: ["-d", "-c", "/etc/tinyproxy/tinyproxy.conf"]
          ports:
            - name: proxy
              containerPort: 8888
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
            limits:
              cpu: 250m
              memory: 128Mi
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: config
              mountPath: /etc/tinyproxy
              readOnly: true
            - name: tmp
              mountPath: /tmp
      imagePullSecrets:
        - name: overdeck-ghcr-pull
      volumes:
        - name: config
          configMap:
            name: overdeck-factory-egress
            defaultMode: 292
        - name: tmp
          emptyDir:
            sizeLimit: 16Mi
---
apiVersion: v1
kind: Service
metadata:
  name: overdeck-cdx-egress
  namespace: overdeck-factory
spec:
  type: NodePort
  externalTrafficPolicy: Local
  selector:
    app.kubernetes.io/name: overdeck-cdx-egress
  ports:
    - name: proxy
      port: 8888
      targetPort: proxy
      nodePort: 31888
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: overdeck-cdx-egress
  namespace: overdeck-factory
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: overdeck-cdx-egress
  policyTypes: ["Ingress", "Egress"]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: overdeck
          podSelector:
            matchLabels:
              app.kubernetes.io/name: overdeck-cdx
              app.kubernetes.io/component: worker
      ports:
        - protocol: TCP
          port: 8888
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
      ports:
        - protocol: TCP
          port: 443
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: overdeck-factory-controller
  namespace: overdeck-factory
rules:
  - apiGroups: ["batch"]
    resources: ["jobs"]
    verbs: ["create", "delete", "get", "list", "watch", "patch"]
  - apiGroups: ["batch"]
    resources: ["jobs/finalizers"]
    verbs: ["update"]
  - apiGroups: [""]
    resources: ["pods", "pods/log", "pods/exec"]
    verbs: ["create", "get", "list"]
  - apiGroups: ["networking.k8s.io"]
    resources: ["networkpolicies"]
    verbs: ["create"]
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: overdeck-factory-controller
  namespace: overdeck-factory
subjects:
  - kind: ServiceAccount
    name: overdeck-factory-controller
    namespace: overdeck-factory
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: overdeck-factory-controller
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: overdeck-factory-upload-secret
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["secrets"]
  matchConditions:
    - name: reserved-upload-secret-or-controller
      expression: >-
        object.metadata.name.startsWith('factory-upload-') ||
        request.userInfo.username == 'system:serviceaccount:overdeck-factory:overdeck-factory-controller'
  validations:
    - expression: >-
        request.userInfo.username == 'system:serviceaccount:overdeck-factory:overdeck-factory-controller'
      message: Factory upload Secrets require the trusted Factory controller
    - expression: >-
        object.metadata.namespace == 'overdeck-factory' &&
        object.metadata.name.matches('^factory-upload-[0-9a-f]{16}$') &&
        object.metadata.labels['app.kubernetes.io/name'] == 'overdeck-factory' &&
        object.metadata.labels['overdeck.dev/factory-job'] == 'factory-' + object.metadata.name.substring(15) &&
        object.immutable == true && object.type == 'Opaque' &&
        size(object.data) == 1 && 'upload-json' in object.data &&
        size(object.data['upload-json']) > 0 && size(object.data['upload-json']) <= 174764 &&
        size(object.metadata.ownerReferences) == 1 &&
        object.metadata.ownerReferences[0].apiVersion == 'batch/v1' &&
        object.metadata.ownerReferences[0].kind == 'Job' &&
        object.metadata.ownerReferences[0].name == object.metadata.labels['overdeck.dev/factory-job'] &&
        object.metadata.ownerReferences[0].uid.matches('^[0-9a-f-]{36}$') &&
        object.metadata.ownerReferences[0].controller == true &&
        object.metadata.ownerReferences[0].blockOwnerDeletion == true
      message: Factory upload Secret shape is not approved
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: overdeck-factory-upload-secret
spec:
  policyName: overdeck-factory-upload-secret
  validationActions: [Deny, Audit]
  matchResources:
    namespaceSelector:
      matchLabels:
        overdeck.dev/factory-admission: enabled
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: overdeck-factory-mailbox-policy
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: ["networking.k8s.io"]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["networkpolicies"]
  matchConditions:
    - name: reserved-mailbox-policy-or-controller
      expression: >-
        object.metadata.name.startsWith('factory-mailbox-') ||
        request.userInfo.username == 'system:serviceaccount:overdeck-factory:overdeck-factory-controller'
  validations:
    - expression: >-
        request.userInfo.username == 'system:serviceaccount:overdeck-factory:overdeck-factory-controller'
      message: Factory mailbox policies require the trusted Factory controller
    - expression: >-
        object.metadata.namespace == 'overdeck-factory' &&
        object.metadata.name.matches('^factory-mailbox-[0-9a-f]{16}$') &&
        object.metadata.labels['app.kubernetes.io/name'] == 'overdeck-factory' &&
        object.metadata.labels['overdeck.dev/factory-job'] == 'factory-' + object.metadata.name.substring(16) &&
        size(object.metadata.ownerReferences) == 1 &&
        object.metadata.ownerReferences[0].apiVersion == 'batch/v1' &&
        object.metadata.ownerReferences[0].kind == 'Job' &&
        object.metadata.ownerReferences[0].name == object.metadata.labels['overdeck.dev/factory-job'] &&
        object.metadata.ownerReferences[0].uid.matches('^[0-9a-f-]{36}$') &&
        object.metadata.ownerReferences[0].controller == true &&
        object.metadata.ownerReferences[0].blockOwnerDeletion == true &&
        object.spec.policyTypes == ['Egress'] &&
        size(object.spec.podSelector.matchLabels) == 1 &&
        object.spec.podSelector.matchLabels['overdeck.dev/factory-job'] == object.metadata.labels['overdeck.dev/factory-job'] &&
        size(object.spec.egress) == 1 && size(object.spec.egress[0].to) == 1 &&
        object.spec.egress[0].to[0].ipBlock.cidr == '100.126.128.50/32' &&
        !has(object.spec.egress[0].to[0].ipBlock.except) &&
        size(object.spec.egress[0].ports) == 1 &&
        object.spec.egress[0].ports[0].protocol == 'TCP' &&
        type(object.spec.egress[0].ports[0].port) == int &&
        object.spec.egress[0].ports[0].port >= 1024 && object.spec.egress[0].ports[0].port <= 65535
      message: Factory mailbox NetworkPolicy shape is not approved
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: overdeck-factory-mailbox-policy
spec:
  policyName: overdeck-factory-mailbox-policy
  validationActions: [Deny, Audit]
  matchResources:
    namespaceSelector:
      matchLabels:
        overdeck.dev/factory-admission: enabled
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: overdeck-factory-job-shape
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: ["batch"]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["jobs"]
  matchConditions:
    - name: live-job
      expression: "!has(object.metadata.deletionTimestamp)"
  validations:
    - expression: >-
        request.userInfo.username == 'system:serviceaccount:overdeck-factory:overdeck-factory-controller'
      message: Factory Jobs require the trusted Factory controller
    - expression: >-
        object.metadata.namespace == 'overdeck-factory' &&
        object.metadata.name.matches('^factory-[0-9a-f]{16}$') &&
        object.metadata.labels['app.kubernetes.io/name'] == 'overdeck-factory' &&
        object.metadata.labels['app.kubernetes.io/component'] == 'worker' &&
        object.metadata.labels['overdeck.dev/factory-job'] == object.metadata.name &&
        size(object.spec.template.metadata.labels) == 7 &&
        object.spec.template.metadata.labels['app.kubernetes.io/name'] == 'overdeck-factory' &&
        object.spec.template.metadata.labels['app.kubernetes.io/component'] == 'worker' &&
        object.spec.template.metadata.labels['overdeck.dev/factory-job'] == object.metadata.name &&
        object.spec.template.metadata.labels['batch.kubernetes.io/job-name'] == object.metadata.name &&
        object.spec.template.metadata.labels['job-name'] == object.metadata.name &&
        object.spec.template.metadata.labels['batch.kubernetes.io/controller-uid'] == object.metadata.uid &&
        object.spec.template.metadata.labels['controller-uid'] == object.metadata.uid
      message: Factory Job identity is not approved
    - expression: >-
        object.spec.backoffLimit == 0 &&
        has(object.spec.suspend) && object.spec.suspend in [true, false] &&
        object.spec.activeDeadlineSeconds >= 60 && object.spec.activeDeadlineSeconds <= 86400 &&
        object.spec.template.spec.restartPolicy == 'Never' &&
        object.spec.template.spec.serviceAccountName == 'overdeck-factory-runner' &&
        object.spec.template.spec.automountServiceAccountToken == false &&
        size(object.spec.template.spec.imagePullSecrets) == 1 &&
        object.spec.template.spec.imagePullSecrets[0].name == 'overdeck-ghcr-pull' &&
        size(object.spec.template.spec.hostAliases) == 1 &&
        object.spec.template.spec.hostAliases[0].ip == '10.43.200.8' &&
        object.spec.template.spec.hostAliases[0].hostnames == ['overdeck-factory-egress']
      message: Factory Job execution policy is not approved
    - expression: >-
        object.spec.template.spec.securityContext.runAsNonRoot == true &&
        object.spec.template.spec.securityContext.runAsUser == 1000 &&
        object.spec.template.spec.securityContext.runAsGroup == 1000 &&
        object.spec.template.spec.securityContext.fsGroup == 1000 &&
        object.spec.template.spec.securityContext.seccompProfile.type == 'RuntimeDefault'
      message: Factory pod security context is not restricted
    - expression: >-
        size(object.spec.template.spec.initContainers) == 1 &&
        object.spec.template.spec.initContainers[0].name == 'prepare-home' &&
        object.spec.template.spec.initContainers[0].image == 'ghcr.io/alexcodeplace/overdeck-agent-sandbox@sha256:1db62fb686815054b86a658bed943d81faa71fc23908a325a952ae50df36c9db' &&
        object.spec.template.spec.initContainers[0].command == ['/bin/mkdir'] &&
        object.spec.template.spec.initContainers[0].args == ['-p', '/home/factory/.pi/agent', '/home/factory/.ssh'] &&
        object.spec.template.spec.initContainers[0].securityContext.allowPrivilegeEscalation == false &&
        object.spec.template.spec.initContainers[0].securityContext.readOnlyRootFilesystem == true &&
        object.spec.template.spec.initContainers[0].securityContext.capabilities.drop == ['ALL']
      message: Factory init container is not approved
    - expression: >-
        size(object.spec.template.spec.containers) == 2 &&
        object.spec.template.spec.containers.exists(c,
          c.name == 'factory' &&
          c.image == 'ghcr.io/alexcodeplace/overdeck-agent-sandbox@sha256:1db62fb686815054b86a658bed943d81faa71fc23908a325a952ae50df36c9db' &&
          c.image.matches('^ghcr\\.io/alexcodeplace/overdeck-agent-sandbox@sha256:[0-9a-f]{64}$') &&
          c.command == ['/usr/local/bin/factory-kubernetes-worker'] &&
          size(c.resources.requests) == 2 && size(c.resources.limits) == 2 &&
          c.resources.requests.cpu == '1' && c.resources.limits.cpu == '4' &&
          c.resources.requests.memory == '2Gi' && c.resources.limits.memory == '8Gi' &&
          c.securityContext.allowPrivilegeEscalation == false &&
          c.securityContext.readOnlyRootFilesystem == true &&
          c.securityContext.capabilities.drop == ['ALL'])
      message: Factory workload containers are not approved
    - expression: >-
        object.spec.template.spec.containers.exists(c,
          c.name == 'result-reader' &&
          c.image == 'ghcr.io/alexcodeplace/overdeck-agent-sandbox@sha256:1db62fb686815054b86a658bed943d81faa71fc23908a325a952ae50df36c9db' &&
          c.command == ['/bin/sh', '-c', 'while :; do sleep 30; done'] &&
          c.resources.requests.cpu == '1m' && c.resources.requests.memory == '8Mi' &&
          c.resources.limits.cpu == '10m' && c.resources.limits.memory == '16Mi' &&
          c.securityContext.allowPrivilegeEscalation == false &&
          c.securityContext.readOnlyRootFilesystem == true &&
          c.securityContext.capabilities.drop == ['ALL'] &&
          size(c.volumeMounts) == 1 && c.volumeMounts[0].name == 'result' &&
          c.volumeMounts[0].mountPath == '/result' && c.volumeMounts[0].readOnly == true)
      message: Factory result reader is not approved
    - expression: size(object.spec.template.spec.volumes) == 6
      message: Factory Job volume count is not approved
    - expression: >-
        object.spec.template.spec.volumes.exists(v, v.name == 'workspace' && has(v.emptyDir) && quantity(v.emptyDir.sizeLimit).compareTo(quantity('8Gi')) == 0) &&
        object.spec.template.spec.volumes.exists(v, v.name == 'home' && has(v.emptyDir) && quantity(v.emptyDir.sizeLimit).compareTo(quantity('2Gi')) == 0) &&
        object.spec.template.spec.volumes.exists(v, v.name == 'tmp' && has(v.emptyDir) && quantity(v.emptyDir.sizeLimit).compareTo(quantity('256Mi')) == 0) &&
        object.spec.template.spec.volumes.exists(v, v.name == 'result' && has(v.emptyDir) && quantity(v.emptyDir.sizeLimit).compareTo(quantity('256Mi')) == 0)
      message: Factory Job temporary volumes are not approved
    - expression: >-
        object.spec.template.spec.volumes.exists(v, v.name == 'repository-key' &&
          v.secret.secretName.matches('^factory-git-[a-z0-9]([-a-z0-9]*[a-z0-9])?$') &&
          size(v.secret.secretName) <= 63 && v.secret.defaultMode == 256 &&
          object.spec.template.spec.containers[0].env.exists(e,
            e.name == 'FACTORY_CREDENTIAL_HANDLE' && e.value == v.secret.secretName))
      message: Factory repository credential volume is not approved
    - expression: >-
        object.spec.template.spec.volumes.exists(v, v.name == 'runtime-credentials' &&
          v.secret.secretName == 'overdeck-factory-runtime' && v.secret.defaultMode == 288)
      message: Factory runtime credential volume is not approved
    - expression: >-
        size(object.spec.template.spec.containers[0].volumeMounts) == 9 &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'workspace' && m.mountPath == '/workspace') &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'home' && m.mountPath == '/home/factory') &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'tmp' && m.mountPath == '/tmp') &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'result' && m.mountPath == '/result') &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'repository-key' && m.mountPath == '/home/factory/.ssh/id_ed25519' && m.subPath == 'id_ed25519' && m.readOnly == true) &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'repository-key' && m.mountPath == '/home/factory/.ssh/known_hosts' && m.subPath == 'known_hosts' && m.readOnly == true) &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'runtime-credentials' && m.mountPath == '/home/factory/.pi/agent/auth.json' && m.subPath == 'pi-auth.json' && m.readOnly == true) &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'runtime-credentials' && m.mountPath == '/home/factory/.pi/agent/models.json' && m.subPath == 'pi-models.json' && m.readOnly == true) &&
        object.spec.template.spec.containers[0].volumeMounts.exists(m, m.name == 'runtime-credentials' && m.mountPath == '/home/factory/attempt-signing-public-key.pem' && m.subPath == 'attempt-signing-public-key.pem' && m.readOnly == true)
      message: Factory credential mounts are not approved
    - expression: >-
        size(object.spec.template.spec.containers[0].env) == 15 &&
        object.spec.template.spec.containers[0].env.all(e, e.name in [
          'FACTORY_REPOSITORY_ID', 'FACTORY_CREDENTIAL_HANDLE', 'FACTORY_ATTEMPT_ENVELOPE', 'FACTORY_EXECUTION_REF',
          'FACTORY_RESULT_REF', 'FACTORY_INVOCATION_JSON', 'FACTORY_RESULT_UPLOAD_JSON', 'FACTORY_DATA_DIR',
          'FACTORY_DB_PATH', 'PI_AUTH_PATH', 'PI_MODELS_PATH', 'HTTPS_PROXY', 'HTTP_PROXY', 'NODE_USE_ENV_PROXY', 'UV_NO_SYNC'
        ]) &&
        [
          'FACTORY_REPOSITORY_ID', 'FACTORY_CREDENTIAL_HANDLE', 'FACTORY_ATTEMPT_ENVELOPE', 'FACTORY_EXECUTION_REF',
          'FACTORY_RESULT_REF', 'FACTORY_INVOCATION_JSON', 'FACTORY_RESULT_UPLOAD_JSON', 'FACTORY_DATA_DIR',
          'FACTORY_DB_PATH', 'PI_AUTH_PATH', 'PI_MODELS_PATH', 'HTTPS_PROXY', 'HTTP_PROXY', 'NODE_USE_ENV_PROXY', 'UV_NO_SYNC'
        ].all(name, object.spec.template.spec.containers[0].env.exists(e, e.name == name)) &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'FACTORY_REPOSITORY_ID' && e.value.matches('^[a-z0-9][a-z0-9-]{0,62}$')) &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'FACTORY_CREDENTIAL_HANDLE' && e.value.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') && size(e.value) <= 63) &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'FACTORY_ATTEMPT_ENVELOPE' && size(e.value) > 0 && size(e.value) <= 131072) &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'FACTORY_EXECUTION_REF' && e.value.matches('^refs/heads/factory-exec/[0-9a-f]{24}$')) &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'FACTORY_RESULT_REF' && e.value.matches('^refs/heads/factory-result/[0-9a-f]{24}$')) &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'HTTPS_PROXY' && e.value == 'http://10.43.200.8:8888') &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'HTTP_PROXY' && e.value == 'http://10.43.200.8:8888') &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'NODE_USE_ENV_PROXY' && e.value == '1') &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'UV_NO_SYNC' && e.value == '1') &&
        object.spec.template.spec.containers[0].env.exists(e, e.name == 'FACTORY_RESULT_UPLOAD_JSON' &&
          e.valueFrom.secretKeyRef.name == 'factory-upload-' + object.metadata.name.substring(8) &&
          e.valueFrom.secretKeyRef.key == 'upload-json' &&
          !has(e.valueFrom.secretKeyRef.optional))
      message: Factory Job environment is not approved
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: overdeck-factory-job-shape
spec:
  policyName: overdeck-factory-job-shape
  validationActions: [Deny, Audit]
  matchResources:
    namespaceSelector:
      matchLabels:
        overdeck.dev/factory-admission: enabled
