First Steps with AWS Nitro: hello-world
At its core, the process of starting a Nitro Enclave includes the following steps:
-
identify a Docker image that contains an application,
-
convert the Docker image to an Enclave Image File (EIF),
-
start a Nitro Enclave using the previously created Enclave Image File.
Using the Anjuna Nitro Runtime, you can automatically start many of the existing containers in a Nitro Enclave, even if the applications in those containers were not updated to run on Nitro. The first benefit of using the Anjuna Nitro Runtime tools is that it allows you to lift-and-shift many of the existing containers into a Nitro Enclave. Without the Anjuna Nitro Runtime, the applications in those containers would have to be modified to be compatible with Nitro.
Depending on the needs of the application, other optional steps will be needed:
-
starting and configuring specific services (networking, configuration) designed to securely support the software running in the Nitro Enclaves
-
creating secrets (using KMS) that can securely be passed into the enclave for the application to use
-
automatically creating enclaves in an AWS EKS cluster
In this section, you will learn about the basic usage of the Anjuna Nitro Runtime tools to build and run a simple enclave.
Identify a Simple Docker Image
The Nitro development tools include a sample application (hello) to get started with Nitro. It is a minimal application that continuously prints Hello from the enclave side every 5 seconds, until the enclave is terminated.
It is important that the application running in the enclave does not exit immediately. When the application defined in the ENTRYPOINT or CMD directives finishes, the enclave also terminates, which will prevent us from observing its existence. |
Build the Docker Image
The sample application is located in the following folder:
/usr/share/nitro_enclaves/examples/hello
To build the container, run the following command:
$ docker build /usr/share/nitro_enclaves/examples/hello -t hello-enclave
If the command succeeded, you should see the newly built Docker image (which was tagged as hello-enclave) when running this command:
$ docker image ls hello-enclave
Build an enclave image file (EIF)
Once the Docker image is available, you can build an Enclave Image File from it:
$ anjuna-nitro-cli build-enclave --docker-uri hello-enclave:latest --output-file hello-enclave.eif
The command above should produce some output that looks like this:
Start building the Enclave Image... Enclave Image successfully created. { "Measurements": { "HashAlgorithm": "Sha384 { ... }", "PCR0": "00bad9b9d306537bc8b7059fa2f9c9e8a6501552f1325bda0454f28b0d2eb45c0782055f11c86275281f5b54be53511c", "PCR1": "cde04bd3a43c742edee3a7e9a323f67cbbaca4c765221419d1b53dd5eea1998ffaf65b324947f26655c25ebba2deec0a", "PCR2": "413bd40f7184f9ec42599ea2113da16eb8408f8491245e8aef05c3f3fcb435eb01a1f36b71f8716b379bf9e2b18cadcd" } }
If the command succeeded, it would print the enclave measurements, which are the hashes that uniquely identify an enclave. To learn more about enclave measurements, see this page: https://docs.aws.amazon.com/enclaves/latest/user/set-up-attestation.html#where
Any combination of the measurement values can be used to create policies that describe which operations are allowed by this enclave on AWS objects stored in services that are Nitro aware (AWS KMS for example).
Enable Networking for the Nitro Enclave
This command needs to be run once (it’s not necessary to run the command every time an enclave is created), but since the changes are not persistent, if the parent host is rebooted, it should be run again.
$ sudo /opt/anjuna/nitro/bin/routing-setup.sh $ sudo /opt/anjuna/nitro/bin/anjuna-nitro-netd-parent --daemonize
Run the enclave
Now that you have built the Enclave Image File, you can create and start an enclave that will run this hello-enclave application:
$ anjuna-nitro-cli run-enclave --cpu-count 2 --memory 1024 --eif-path hello-enclave.eif --debug-mode
The previous command is reserving 2 CPUS for running the enclave, as well as 1024MB of RAM. These values are appropriate for the sample application. |
If the command above succeeded, your should see some output that looks like this:
Start allocating memory... Started enclave with enclave-cid: 17, memory: 1024 MiB, cpu-ids: [1, 3] { "EnclaveID": "i-0f2c4b721470e92c4-enc1776bc0278e90b9", "ProcessID": 13823, "EnclaveCID": 17, "NumberOfCPUs": 2, "CPUIDs": [ 1, 3 ], "MemoryMiB": 1024 }
The EnclaveID value can be needed when it’s time to terminate the enclave. When an enclave is running, it’s basic attributes can be retrieved with the following command:
$ anjuna-nitro-cli describe-enclaves
Instead of using copy/paste to specify the value of EnclaveID, the rest of this document will automatically retrieve it by using the following command: anjuna-nitro-cli describe-enclaves | jq -r .[0].EnclaveID This command simply captures the description of the currently running enclave (anjuna-nitro-cli describe-enclaves) and filters the resulting JSON output to lookup the EnclaveID entry. |
Since the enclave was started in debug mode (the anjuna-nitro-cli run-enclave command was invoked with the –debug-mode flag), we can view the output of the application using the following command:
$ anjuna-nitro-cli console --enclave-id $(anjuna-nitro-cli describe-enclaves | jq -r .[0].EnclaveID)
If the enclave is running, you will see some output that looks like this:
[ 40] Hello from the enclave side! [ 41] Hello from the enclave side! [ 42] Hello from the enclave side! [ 43] Hello from the enclave side! [ 44] Hello from the enclave side! [ 45] Hello from the enclave side!
Press CTRL+C to stop watching the application output (this won’t stop the enclave, it will just stop inspecting the console log).
Congratulations, you have created a simple Nitro Enclave! |
Finally, to terminate the enclave, run the following command:
$ anjuna-nitro-cli terminate-enclave --all