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.
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/
.JavaScript
Python
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);
}
})();
src/app.py
import os
import sys
import json
from pyfiglet import Figlet
iexec_out = os.environ['IEXEC_OUT']
# Do whatever you want (let's write hello world here)
text = 'Hello, {}!'.format(sys.argv[1] if len(sys.argv) > 1 else "World")
text = Figlet().renderText(text) # Let's add some art for e.g.
print(text)
# Append some results in /iexec_out/
with open(iexec_out + '/result.txt', 'w+') as fout:
fout.write(text)
# Declare everything is computed
with open(iexec_out + '/computed.json', 'w+') as f:
json.dump({ "deterministic-output-path" : iexec_out + '/result.txt' }, f)
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:
Javascript
Python
Dockerfile
# Starting from a base image supported by SCONE
FROM node:14-alpine3.11
# install your dependencies
RUN mkdir /app && cd /app && npm install [email protected]
COPY ./src /app
ENTRYPOINT [ "node", "/app/app.js"]
Dockerfile
FROM python:3.7.3-alpine3.10
### install python dependencies if you have some
RUN pip3 install pyfiglet
COPY ./src /app
ENTRYPOINT ["python3", "/app/app.py"]
You will need to register a free SCONE Account to access SCONE build tools and curated images from the SCONE registry.
# when your account is ready, run `docker login` to connect the SCONE registry
docker login registry.scontain.com:5050
We will use the following script to wrap the sconification process, copy the
sconify.sh
script in the current directory:Javascript
Python
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:5050/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:5050/scone-production/iexec-sconify-image:5.3.15-v4 \
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})"
sconify.sh
#!/bin/bash
# declare the app entrypoint
ENTRYPOINT="python3 /app/app.py"
# 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}
# run the sconifier to build the TEE image based on the non-TEE image
docker run -it \
-v /var/run/docker.sock:/var/run/docker.sock \
registry.scontain.com:5050/scone-production/iexec-sconify-image:5.3.15-v4 \
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/python3.7 \
--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!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:
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
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.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 modified 28d ago