Providing an Enclave Image File for the Pod’s Enclave

Confidential Pods launch the workload by running the EIF (Enclave Image File) for the Pod inside an enclave. This EIF can be provided by using one of the following methods:

  • Built on the fly, based on the container image provided

  • Fetched from S3 buckets

  • Taken from the local filesystem of the Pod

Built on-the-fly EIFs

EIFs may be built on the fly, based on the Pod configuration provided, such as the container image, container ports, and any volume mounts it may use. In this case, the Pod configuration is converted into an enclave configuration. This configuration, combined with the container image, are transformed into an EIF by the launcher Pod. While convenient, this option is not recommended for production use cases, because of the following reasons:

  1. It extends the launch time of the Pod, as the EIF is built during Pod launch.

  2. It is less secure, since it is not possible to enable the remote attestation in this case.

Pre-built EIFs

This is the recommended option for most production use cases. It allows a faster start of the Confidential Pod, as well being much more secure, since it enables leveraging remote attestation with KMS and the Anjuna Encrypted Configuration. For more details, see the Anjuna Nitro Enclave configuration and anjuna-nitro-cli build-enclave.

Getting the pre-built EIF from an S3 Bucket

The Anjuna Kubernetes Toolset for AWS EKS tools has built-in support for S3 storage. This allows you to upload your pre-built EIF to an S3 bucket, and point the launcher Pod to fetch them from there. For an example, see Using pre-built EIFs.

Getting the pre-built EIF from source other than S3

Sometimes, using S3 storage is not the preferred approach. When not using S3 storage, you can load the EIF to the launcher Pod’s filesystem, and indicate from where to fetch the EIF. All standard K8s methods of loading the EIF to the filesystem are supported, including through the use of Volume Mounts. These Volume Mounts may be populated in any way, including the following:

  • Through a Secret/ConfigMap (though this may not be suitable, since EIF files can be large)

  • Init Containers populating shared volumes

  • Persistent Volume Claims

Use the following annotation to specify the absolute path of the EIF on the filesystem. (This annotation can be applied with any method that was used to make the file visible to the launcher Pod.):

nitro.k8s.anjuna.io/imageLocation

Example for getting the pre-built EIF from source other than S3

In this example you will:

  1. Create a MinIO server that will hold an EIF.

  2. Add a shared EmptyDir volume to the Pod.

  3. Add an init container to the Pod specification that will download the EIF from the MinIO server.

  4. Tell the launcher Pod where the EIF is located (where the volume mount is mounted, and the filename of the EIF inside of it).

First steps

Define the environment variables that will be used throughout this example (change the values to match your environment):

$ export EIF_PATH=nginx.eif
$ export NAMESPACE=default
$ export MINIO_SERVER_NAME=minio-server
$ export MINIO_USER=admin
$ export MINIO_PASSWORD=miniopassword
$ export MINIO_BUCKET_NAME=eifs
$ export MINIO_SERVICE_PORT=9000

Next, build an EIF for a simple Nginx container:

$ anjuna-nitro-cli build-enclave --docker-uri nginx:latest --output-file ${EIF_PATH}

Creating a MinIO Server and uploading the EIF to it

Launch a MinIO server Pod:

$ kubectl run --image minio/minio:RELEASE.2025-04-08T15-41-24Z --port 9000 \
          --env "MINIO_ROOT_USER=${MINIO_USER}" \
          --env "MINIO_ROOT_PASSWORD=${MINIO_PASSWORD}" \
          -n ${NAMESPACE} \
          ${MINIO_SERVER_NAME} server /data

Create a service that exposes the MinIO server to the cluster:

$ kubectl expose pod ${MINIO_SERVER_NAME} --port=${MINIO_SERVICE_PORT} \
          --target-port=9000 --name=${MINIO_SERVER_NAME} --type=ClusterIP \
          -n ${NAMESPACE}

Using a MinIO client to upload the EIF to the MinIO server

Forward the MinIO Server port using kubectl port-forward, launch a MinIO client container, and upload the EIF to the MinIO server:

$ kubectl port-forward pod/minio-server 9000:9000 &
# Wait for port-forward to take affect
$ sleep 3
$ docker run -i --rm --entrypoint bash -v $(realpath ${EIF_PATH}):/${EIF_PATH} --net=host minio/mc:RELEASE.2025-04-08T15-39-49Z <<EOF
  mc alias set minioserver "http://localhost:9000" ${MINIO_USER} ${MINIO_PASSWORD}
  mc mb minioserver/${MINIO_BUCKET_NAME}
  mc cp /${EIF_PATH} minioserver/${MINIO_BUCKET_NAME}/${EIF_PATH}
EOF
$ kill %1

Preparing a Pod specification

Create a Pod specification for the Nginx Pod, which will include a shared volume mount, and an init container. This init container will download the EIF from the MinIO server, and will load it into the shared volume.

$ cat > pod_spec.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: confidential-nginx-pod
  labels:
    nitro.k8s.anjuna.io/managed: "yes"
  annotations:
    nitro.k8s.anjuna.io/imageLocation: "/anjuna/files/eifs/${EIF_PATH}"
spec:
  initContainers:
  - name: anjuna-eif-downloader
    image: minio/mc:RELEASE.2025-04-08T15-39-49Z
    command:
    - /bin/sh
    - -c
    - |
      mc alias set minioserver http://minio-server.default.svc.cluster.local:${MINIO_SERVICE_PORT} ${MINIO_USER} ${MINIO_PASSWORD} &&
      mc cp minioserver/${MINIO_BUCKET_NAME}/${EIF_PATH} /anjuna/files/eifs/${EIF_PATH}
    volumeMounts:
    - name: anjuna-system-eifs
      mountPath: "/anjuna/files/eifs"
  containers:
  - name: confidential-nginx
    image: DOES-NOT-MATTER
    imagePullPolicy: Always
    resources:
      limits:
        memory: "2048Mi"
        cpu: "2"
    volumeMounts:
    - name: anjuna-system-eifs
      mountPath: "/anjuna/files/eifs"
  volumes:
  - name: anjuna-system-eifs
    emptyDir: {}
EOF

The nitro.k8s.anjuna.io/imageLocation is specified with an absolute path.

The volume used to pass the EIF to the enclave has a name that starts with anjuna-system, which tells the Anjuna Kubernetes Toolset for AWS EKS tools to not forward it to the enclave, but rather mount it directly on the launcher Pod.

Launching the Pod and validating it

First, launch the Pod:

$ kubectl apply -f pod_spec.yaml -n ${NAMESPACE}

Then, make sure that the Pod started correctly (this may take a few seconds):

$ kubectl logs -n ${NAMESPACE} -f confidential-nginx-pod

The output should be of the following form:

2025/04/23 23:40:01 [notice] 471#471: using the "epoll" event method
2025/04/23 23:40:01 [notice] 471#471: nginx/1.27.3
2025/04/23 23:40:01 [notice] 471#471: built by gcc 12.2.0 (Debian 12.2.0-14)
2025/04/23 23:40:01 [notice] 471#471: OS: Linux 5.10.223
2025/04/23 23:40:01 [notice] 471#471: getrlimit(RLIMIT_NOFILE): 1024:4096
2025/04/23 23:40:01 [notice] 471#471: start worker processes
2025/04/23 23:40:01 [notice] 471#471: start worker process 495
2025/04/23 23:40:01 [notice] 471#471: start worker process 496

Cleaning up the example

Remove all created resources and files:

$ kubectl delete -f pod_spec.yaml -n ${NAMESPACE}
$ kubectl delete svc/${MINIO_SERVER_NAME} pod/${MINIO_SERVER_NAME}
$ rm -rf ${EIF_PATH} pod_spec.yaml