apiVersion: v1
kind: Pod
metadata:
  name: encrypted-image-demo
  # setup.sh fills in the namespace, the image URI, the Region and the
  # sandbox.amazonaws.com/policy annotation at apply time. The policy annotation is one
  # long base64 string, so it is never kept in this file; the rendered pod setup.sh
  # applies is written to the repository's tmp/.
  labels:
    # The network policy in egress-policy.yaml selects this label.
    app: encrypted-image
    app.kubernetes.io/name: encrypted-image-demo
    app.kubernetes.io/managed-by: encrypted-image
  annotations:
    # Pin the node while this long-lived demo pod runs: Auto Mode's node consolidation
    # would otherwise remove the node, and nothing recreates a bare pod.
    karpenter.sh/do-not-disrupt: "true"
spec:
  # Confidential mode treats the node as untrusted. It is what binds key release to the
  # Cedar policy this pod carries: the guest measures that policy into PCR17, and the KMS
  # key policy releases the layer key only for the measurement it was set to expect.
  runtimeClassName: confidential-sandbox
  serviceAccountName: encrypted-image-pod
  # Pinned by the generated policy, so it must stay the same for the approved pod and the
  # changed pod: the only pinned difference between them is the extra environment
  # variable scripts/changed-pod.sh adds.
  hostname: encrypted-image-demo
  automountServiceAccountToken: false
  enableServiceLinks: false
  terminationGracePeriodSeconds: 0
  securityContext:
    # Numeric user and group IDs, and supplementalGroupsPolicy: Strict, are what let the
    # generator pin the identity exactly for an encrypted image. It cannot read
    # /etc/passwd or /etc/group out of encrypted layers, so a non-numeric image USER would
    # fail generation and the image's own group list would otherwise be unknown
    # (Amazon EKS User Guide, "Encrypted container images").
    runAsUser: 65534
    runAsGroup: 65534
    runAsNonRoot: true
    supplementalGroupsPolicy: Strict
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: main
      # The encrypted copy of the source image, in the sample's ECR repository. setup.sh
      # renders this placeholder with the tagged URI it pushed.
      image: <ENCRYPTED_IMAGE_URI>
      # Always, not IfNotPresent: the guest asks KMS for the layer key on each pull, and
      # that request is the thing this sample shows. A node that already holds prepared
      # state for this image would otherwise skip it.
      imagePullPolicy: Always
      env:
        # Both names, set to the same Region, on purpose. With only AWS_REGION declared,
        # the EKS Pod Identity webhook leaves out the AWS_DEFAULT_REGION it would
        # otherwise inject, while a policy generated with --with-pod-identity still
        # expects it in the pinned environment, and the guest then refuses to create the
        # container. Declaring both keeps the manifest and the policy agreed.
        - name: AWS_REGION
          value: <AWS_REGION>
        - name: AWS_DEFAULT_REGION
          value: <AWS_REGION>
      # The generated policy denies exec, so the container prints its own evidence at
      # start-up and the page reads it with kubectl logs, which the policy permits:
      #   kernel, uptime, disks, product  that it ran in a lightweight VM of its own
      #   kms, blocked                    that the network policy allows the KMS endpoint
      #                                   and refuses everything else
      # Nothing here prints a secret or any decrypted content: the layers were already
      # decrypted inside the guest before this command ran, which is the point.
      #
      # The two network readings come after a wait, and the readiness marker before it.
      # A pod's network policy is programmed on the node a few seconds after the pod
      # starts, so a probe in the pod's first seconds can run before the policy is in
      # force and reach a host the policy denies. Waiting means the readings are of the
      # policy, not of the gap before it.
      command:
        - /bin/sh
        - -c
        - >-
          echo "kernel:  $(uname -rv)";
          echo "uptime:  $(cut -d' ' -f1 /proc/uptime)s";
          echo "disks:   $(ls /sys/block | tr '\n' ' ')";
          echo "product: $(cat /sys/class/dmi/id/product_name)";
          echo encrypted-image-ready;
          sleep 60;
          probe() { echo "HTTP $(curl -s -o /dev/null -w '%{http_code}' --max-time 15 "$1"; echo " (curl exit $?)")"; };
          echo "kms:     $(probe https://kms.<AWS_REGION>.amazonaws.com/)";
          echo "blocked: $(probe https://example.com/)";
          echo encrypted-image-probes-done;
          exec sleep 3600
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop:
            - ALL
      # The guest decrypts and unpacks every layer inside the sandbox before the container
      # starts, so this pod does set a size: on the small default VM the unpack has
      # nowhere to go. Requests only, no limits; a sandbox is sized from its requests.
      resources:
        requests:
          cpu: "1"
          memory: 1Gi
