Build trusted applications

Prerequisites

Please make sure you have already checked the quickstart and Your first application tutorials before building your trusted application on iExec.

After understanding the fundamentals of Confidential Computing and explaining the technologies behind it, it is time to roll up our sleeves and get hands-on with enclaves. In this guide, we will focus on protecting an application - that is already compatible with the iExec platform - using SGX, and without changing the source code. That means we will use the same code we previously deployed for a basic iExec application.

How would the enclave verify the integrity of the code?

The short answer is: the application is protected by taking a snapshot of the file system's state. The TEE image will use the fspf feature of SCONE to authenticate the file system directories that would be used by the application (/bin, /lib...) as well as the code itself. It takes a snapshot of their state that will be later shared with the worker (via the Blockchain) to make sure everything is under control. If we change one bit of one of the authenticated files, the file system's state changes completely and the enclave will refuse to boot since it considers it as a possible attack.

Prepare your application

Create a directory tree for your application in ~/iexec-projects/.

cd ~/iexec-projects
mkdir tee-hello-world-app && cd tee-hello-world-app
iexec init --skip-wallet
mkdir src
touch Dockerfile
touch sconify.sh

Copy the following content in src/ .

src/app.js
const fsPromises = require("fs").promises;
const figlet = require("figlet");

(async () => {
  try {
    const iexecOut = process.env.IEXEC_OUT;
    // Do whatever you want (let's write hello world here)
    const message = process.argv.length > 2 ? process.argv[2] : "World";

    const text = figlet.textSync(`Hello, ${message}!`); // Let's add some art for e.g.
    console.log(text);
    // Append some results in /iexec_out/
    await fsPromises.writeFile(`${iexecOut}/result.txt`, text);
    // Declare everything is computed
    const computedJsonObj = {
      "deterministic-output-path": `${iexecOut}/result.txt`,
    };
    await fsPromises.writeFile(
      `${iexecOut}/computed.json`,
      JSON.stringify(computedJsonObj)
    );
  } catch (e) {
    console.log(e);
    process.exit(1);
  }
})();

As we mentioned earlier, the advantage of using SCONE is the ability to make the application Intel® SGX-enabled without changing the source code. The only thing we are going to do is rebuilding the app using the Trusted-Execution-Environment tooling provided by SCONE.

SCONE provides TEE conversion tooling (Python, Java, ..) plus eventually TEE base images for other languages (NodeJs).

Copy the Dockerfile of the non-TEE app:

Dockerfile
# Starting from a base image supported by SCONE
FROM node:14-alpine3.11

# install your dependencies
RUN mkdir /app && cd /app && npm install figlet@1.x

COPY ./src /app

ENTRYPOINT [ "node", "/app/app.js"]

Build the TEE docker image

You will need to register a free SCONE Account to access SCONE build tools and curated images from the SCONE registry.

Once your account is activated, you need to request access to the SCONE build tools for iExec.

# when your account is ready, run `docker login` to connect the SCONE registry
docker login registry.scontain.com

We will use the following script to wrap the sconification process, copy the sconify.sh script in the current directory:

sconify.sh
#!/bin/bash

# declare the app entrypoint
ENTRYPOINT="node /app/app.js"
# declare an image name
IMG_NAME=tee-hello-world

IMG_FROM=${IMG_NAME}:temp-non-tee
IMG_TO=${IMG_NAME}:tee-debug

# build the regular non-TEE image
docker build . -t ${IMG_FROM}

# pull the SCONE curated image corresponding to our base image
docker pull registry.scontain.com/sconecuratedimages/node:14.4.0-alpine3.11

# run the sconifier to build the TEE image based on the non-TEE image
docker run -it --rm \
            -v /var/run/docker.sock:/var/run/docker.sock \
            registry.scontain.com/scone-production/iexec-sconify-image:5.3.15-v12 \
            sconify_iexec \
            --name=${IMG_NAME} \
            --from=${IMG_FROM} \
            --to=${IMG_TO} \
            --binary-fs \
            --fs-dir=/app \
            --host-path=/etc/hosts \
            --host-path=/etc/resolv.conf \
            --binary=/usr/local/bin/node \
            --heap=1G \
            --dlopen=2 \
            --no-color \
            --verbose \
            --command=${ENTRYPOINT} \
            && echo -e "\n------------------\n" \
            && echo "successfully built TEE docker image => ${IMG_TO}" \
            && echo "application mrenclave.fingerprint is $(docker run -it --rm -e SCONE_HASH=1 ${IMG_TO})"
# make the script executable
chmod +x sconify.sh
# run the sconify script
./sconify.sh

Congratulation you just built your first TEE application.

The sconify.sh script prints the generated docker image name, you must retag this image and push it on dockerhub.

You may have noticed the tee-debug flag in the image name, the built image is actually in TEE debug mode, this allows you to have some debug features while developping the app. Once you are happy with the debug app, contact us to go to production!

Test your app on iExec

At this stage, your application is ready to be tested on iExec. The process is similar to testing any type of application on the platform, with these minor exceptions:

Deploy the TEE app on iExec

TEE applications require some additional information to be filled in during deployment.

# prepare the TEE application template
iexec app init --tee

Edit iexec.json and fill in the standard keys and the mrenclave object:

{
  ...
  "app": {
    "owner": "0xF048eF3d7E3B33A465E0599E641BB29421f7Df92", // your address
    "name": "tee-hello-world", // application name
    "type": "DOCKER",
    "multiaddr": "docker.io/username/tee-hello-world:1.0.0", // app image
    "checksum": "0x15bed530c76f1f3b05b2db8d44c417128b8934899bc85804a655a01b441bfa78", // image digest
    "mrenclave": {
      "provider": "SCONE", // TEE provider (keep default value)
      "version": "v5", // Scone version (keep default value)
      "entrypoint": "node /app/app.js" OR "python3 /app/app.py", // your app image entrypoint
      "heapSize": 1073741824, // heap size in bytes (1GB)
      "fingerprint": "eca3ace86f1e8a5c47123c8fd271319e9eb25356803d36666dc620f30365c0c1" // fingerprint of the enclave code (mrenclave), see how to retrieve it below
    }
  },
  ...
}

Run your TEE image with SCONE_HASH=1 to get the enclave fingerprint (mrenclave):

docker run -it --rm -e SCONE_HASH=1 tee-hello-world:tee-debug

Deploy the app with the standard command:

iexec app deploy --chain bellecour

Run the TEE app

Specify the tag --tag tee in iexec app run command to run a tee app.

One last thing, in order to run a TEE-debug app you will also need to select a debug workerpool, use the debug workerpool v7-debug.main.pools.iexec.eth.

The debug workerpool is connected to a debug Secret Management Service (this is fine for debugging but do not use to store production secrets), we will need to init the storage token on this SMS.

These sed commands will do the trick:

# set a custom bellecour SMS in chain.json
sed -i 's|"bellecour": {},|"bellecour": { "sms": "https://v7.sms.debug-tee-services.bellecour.iex.ec" },|g' chain.json
# initialize the storage
iexec storage init --chain bellecour
# restore the default configuration in chain.json
sed -i 's|"bellecour": { "sms": "https://v7.sms.debug-tee-services.bellecour.iex.ec" },|"bellecour": {},|g' chain.json

You are now ready to run the app

iexec app run --tag tee --workerpool v7-debug.main.pools.iexec.eth --watch --chain bellecour

You noticed we used v7-debug.main.pools.iexec.eth instead of an ethereum address, this is an ENS name. The ENS (Ethereum Name Service) protocol enables associating decentralized naming to ethereum addresses.

Next step?

In this tutorial, you learned how to leverage your application with the power of Trusted Execution Environments using iExec. But according to your use case, you may need to use some confidential data to get the full potential of the Confidential Computing paradigm. Check out the next chapter to see how.

Last updated