Skip to main content

Building an enclave image

While enclave images can easily be built from scratch, we recommend starting with our default image and customizing it as per your needs. It comes with various components pre-configured to ensure the enclave behaves as close to a normal instance/VM as possible.

Default image

The default enclave image setup consists of 3 files:

  • Dockerfile, to specify how the enclave should be created
  • setup.sh, to do networking and DNS related configuration
  • supervisord.conf, to specify what programs will run inside the enclave

Dockerfile

The Dockerfile below uses pre-built binaries to set up various components, hence it is architecture specific.

# base image
FROM alpine:3.17

# install dependency tools
RUN apk add --no-cache net-tools iptables iproute2 wget

# working directory
WORKDIR /app

# supervisord to manage programs
RUN wget -O supervisord http://public.artifacts.marlin.pro/projects/enclaves/supervisord_master_linux_amd64
RUN chmod +x supervisord

# transparent proxy component inside the enclave to enable outgoing connections
RUN wget -O ip-to-vsock-transparent http://public.artifacts.marlin.pro/projects/enclaves/ip-to-vsock-transparent_v1.0.0_linux_amd64
RUN chmod +x ip-to-vsock-transparent

# key generator to generate static keys
RUN wget -O keygen http://public.artifacts.marlin.pro/projects/enclaves/keygen_v1.0.0_linux_amd64
RUN chmod +x keygen

# attestation server inside the enclave that generates attestations
RUN wget -O attestation-server http://public.artifacts.marlin.pro/projects/enclaves/attestation-server_v1.0.0_linux_amd64
RUN chmod +x attestation-server

# proxy to expose attestation server outside the enclave
RUN wget -O vsock-to-ip http://public.artifacts.marlin.pro/projects/enclaves/vsock-to-ip_v1.0.0_linux_amd64
RUN chmod +x vsock-to-ip

# dnsproxy to provide DNS services inside the enclave
RUN wget -O dnsproxy http://public.artifacts.marlin.pro/projects/enclaves/dnsproxy_v0.46.5_linux_amd64
RUN chmod +x dnsproxy

# supervisord config
COPY supervisord.conf /etc/supervisord.conf

# setup.sh script that will act as entrypoint
COPY setup.sh ./
RUN chmod +x setup.sh

# your custom setup goes here

# entry point
ENTRYPOINT [ "/app/setup.sh" ]

If you have used Docker previously, the above Dockerfile would look familiar. It starts with a base image, installs dependencies, fetches or copies relevant programs, scripts and config files into the enclave and finally designates the setup.sh script as the entrypoint for when the enclave starts executing.

setup.sh

#!/bin/sh

# setting an address for loopback
ifconfig lo 127.0.0.1
ifconfig

# adding a default route
ip route add default via 127.0.0.1 dev lo
route -n

# iptables rules to route traffic to transparent proxy
iptables -A OUTPUT -t nat -p tcp --dport 1:65535 ! -d 127.0.0.1 -j DNAT --to-destination 127.0.0.1:1200
iptables -t nat -A POSTROUTING -o lo -s 0.0.0.0 -j SNAT --to-source 127.0.0.1
iptables -L -t nat

# generate identity key
/app/keygen --secret /app/id.sec --public /app/id.pub

# your custom setup goes here

# starting supervisord
cat /etc/supervisord.conf
/app/supervisord

The setup.sh script sets up networking inside the enclave including outgoing connections support using a transparent proxy. At the end, it starts and hands over control to supervisord.

supervisord.conf

[supervisord]
loglevel=debug
logfile=/dev/stdout
logfile_maxbytes=0

# attestation server
[program:attestation-server]
command=/app/attestation-server --ip-addr 127.0.0.1:1300 --pub-key /app/id.pub
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0

# attestation server proxy
[program:attestation-proxy]
command=/app/vsock-to-ip --vsock-addr 88:1300 --ip-addr 127.0.0.1:1300
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0

# transparent proxy component inside enclave
[program:ip-to-vsock-transparent]
command=/app/ip-to-vsock-transparent --vsock-addr 3:1200 --ip-addr 127.0.0.1:1200
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0

# DNS-over-HTTPS provider
[program:dnsproxy]
command=/app/dnsproxy -u https://1.1.1.1/dns-query -v
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0

# your custom programs go here

The default programs running inside the enclave (in order) are

  • an attestation server to generate attestations and make them available through a HTTP interface
  • a proxy to expose the attestation server outside the enclave
  • a transparent proxy to enable programs inside the enclave to make outgoing TCP connections
  • a DNS provider that resolves queries using a DNS-over-HTTPS endpoint

Customizing the image

The files above specify customization points where you can do your custom setup and add your own programs and script to run. They represent three customization points depending on your needs:

  • one time commands before boot should go in Dockerfile
  • one time commands after boot should go in setup.sh
  • persistent commands/programs/daemons should go in supervisord.conf

This should be sufficient for most use cases. Be careful with altering any existing setup steps or programs since it may render your enclave unusable or unreachable over the network.

Building the image

First, build a docker image using the Dockerfile:

docker image build -t enclave:latest .

Next, use nitro-cli to convert the docker image into an enclave image. We recommend running below command within the container with image marlinorg/nitro-cli, which bundles all dependencies.

nitro-cli build-enclave --docker-uri enclave:latest --output-file enclave.eif

This creates an enclave image file (enclave.eif) and will print enclave measurements which should look something like

{
"Measurements": {
"HashAlgorithm": "Sha384 { ... }",
"PCR0": "cfa7554f87ba13620037695d62a381a2d876b74c2e1b435584fe5c02c53393ac1c5cd5a8b6f92e866f9a65af751e0462",
"PCR1": "bcdf05fefccaa8e55bf2c8d6dee9e79bbff31e34bf28a99aa19e6b29c37ee80b214a414b7607236edf26fcb78654e63f",
"PCR2": "20caae8a6a69d9b1aecdf01a0b9c5f3eafd1f06cb51892bf47cef476935bfe77b5b75714b68a69146d650683a217c5b3"
}
}

Make a note of the measurements, they are used to verify whether a particular deployed enclave actually matches the enclave image you expect.