Providing an Enclave Image File for the Pod’s Enclave

Confidential Pods run workloads by executing an EIF (Enclave Image File) inside an enclave. The EIF can be provided in two ways: built on the fly from the container image, or supplied as a pre-built file.

Pre-built EIFs must be referenced in the Pod specification using the imageLocation annotation (nitro.k8s.anjuna.io/imageLocation). The annotation value depends on where the EIF is stored:

  • S3 bucket - the path must begin with s3://, for example: s3://eifs-bucket/my-app.eif

  • Local filesystem - the path must be absolute, for example: /anjuna/files/eifs/${EIF_PATH}

If no imageLocation is specified, the Anjuna Toolset will automatically build the EIF on the fly from the container image.

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, is transformed into an EIF by the launcher Pod. While convenient, this option is not recommended for production use cases for 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

Pre-built EIFs are the recommended option for most production use cases. They allow a faster start of the Confidential Pod, as well being much more secure, since they enable leveraging remote attestation with KMS and the Anjuna Encrypted Configuration. For more details about how to build the EIF, 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.

Use the following annotation to upload your pre-built EIF to an S3 bucket:

apiVersion: v1
kind: Pod
metadata:
  name: secure-eif-pod
  labels:
    name: secure-eif-pod
    nitro.k8s.anjuna.io/managed: "yes"
  annotations:
    nitro.k8s.anjuna.io/imageLocation: "s3://your-eif-bucket/your-eif-file"
    <snip>...

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 Kubernetes 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.):

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/your-eif-file"
    <snip>...

The nitro.k8s.anjuna.io/imageLocation must be specified with an absolute path, as you can see in lines 8 and 9 in the Preparing a Pod specification example below.

The volume used to pass the EIF to the enclave must have a name starting with anjuna-system-, as you can see in lines 21, 34, and 37 in the Preparing a Pod specification example below. This prefix tells the Anjuna Kubernetes Toolset for AWS EKS to mount the volume directly on the launcher Pod rather than forwarding it to the enclave.

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 docker.io/library/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, an init container, and an exposed port for Nginx. 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"
    ports:
    - containerPort: 80
    volumeMounts:
    - name: anjuna-system-eifs
      mountPath: "/anjuna/files/eifs"
  volumes:
  - name: anjuna-system-eifs
    emptyDir: {}
EOF
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 in the following format:

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