logo
Menu
Creating Attestable Builds with AWS Nitro Enclaves

Creating Attestable Builds with AWS Nitro Enclaves

We use TEE technology to generate reproducible builds and enhance trust, transparency, and security in the software development process!

Published Sep 5, 2024

Introduction to Attestable Builds

In December 2020, a major breach within SolarWinds' build system was uncovered. This breach enabled malicious actors to inject malicious code into signed software updates distributed to thousands of SolarWinds customers, spanning government agencies and private organizations globally, granting unauthorized access to the systems and sensitive data.
Incorporating third-party dependencies is a common practice in today’s software development. However, while these dependencies enable developers to accelerate development and leverage specialized expertise, they may also introduce malicious code that could compromise the integrity and security of the entire system.
In this post, we explore how to use attestable builds to prevent bad actors from inserting malicious code and strengthen supply chain security. We’ll introduce attestable builds and showcase TEE Compile, a framework built by Automata Network that leverages AWS Nitro Enclaves to generate attestable builds, followed by a step-by-step tutorial on how to use TEE Compile to generate an attestable build for Reth.
The SolarWinds breach underscores software supply chain attack risks, emphasizing the critical need for robust defense mechanisms. Auditing source code or making it public is often insufficient as users generally rely on pre-compiled binaries. Ensuring the absence of vulnerabilities or backdoors in these binaries demands that we ensure they are produced by compiling the authentic code. To decide if a binary is compiled from a specific source code, we can compute a checksum over it and compare it against the checksum of the legitimate binary, since the two checksums can only match if the two binaries also match. Doing this requires solving two challenges:
  1. Having a build system that can produce the same binary regardless of who does the compilation and
  2. Knowing the value of the expected checksum 
The first challenge is solved by ensuring the build is reproducible. By enabling deterministic computation of a binary’s checksum, reproducible builds are a key strategy for supply chain security, allowing independent verification of software authenticity and integrity. All a verifier has to do is compute a checksum over the distributed software and check if the value matches the expected value.
The second challenge is identifying a trusted source for the expected checksum value. External parties can be compromised, as observed in the SolarWinds incident. Requiring each user to compile the source code themselves is a straightforward solution, but that defeats the purpose of having pre-compiled binaries. Moreover, it burdens the user to have access to the build environment required to compile the source code.
We turn to TEEs (Trusted Execution Environments) to solve this challenge by introducing attestable builds. An attestable build is a reproducible build compiled inside a TEE that produces a report attesting to the expected checksum. The integrity guarantees provided by the TEE ensure the authenticity of the build artifacts, allowing anyone to verify their correctness without relying on external parties or self-building. TEE Compile is a solution built by Automata Network to generate attestable builds inside an AWS Nitro Enclave. Nitro Enclaves are a great fit for TEE Compile due to their seamless integration within the AWS Nitro System, providing easy deployment and management within AWS. In addition, Nitro Enclaves support various hardware architectures (Intel, AMD, and ARM) and are compatible with any programming language, offering a versatile environment for building diverse source codes.

How TEE Compile could have prevented the SolarWinds attack

During the SolarWinds hack, the integrity of the software development and delivery pipeline was compromised, enabling attackers to inject a malicious library into SolarWinds’ Orion platform. TEE Compile would have countered this threat by building the Orion platform inside the Nitro Enclave, requiring the attackers to breach the security of the trusted enclave to introduce their malicious code. TEE Compile would have also produced a report detailing the correct binary checksum. End users could then verify that the received binary matched the checksum in the attested report, rejecting binaries with different checksums and preventing the exploit.

Setting up TEE Compile inside a Nitro Enclave

Now that we know what attestable builds are and how they can mitigate supply chain attacks, let’s go through the steps required to set up TEE Compile to produce attestable builds. TEE Compile is an open-source project and currently supports projects written in Rust and Go, but it can be extended to work with additional programming languages. But before we get to talk about TEE Compile in-depth, we should set up our Nitro Enclave!

Setting up the Nitro Enclave

For this tutorial, we use an AWS r6g.xlarge instance featuring an ARM-based Graviton2 processor. Make sure to configure your instance to support Nitro Enclaves by enabling the option in Configure Instance Details when you create the instance. Once our instance is up and running, we need to set up a couple more things. The first step is installing the Nitro CLI from GitHub following the instructions provided in this tutorial. Remember to configure the memory allowed for the guest enclave in /etc/nitro_enclaves/allocator.yaml! The memory requirements for TEE Compile depend on the build type, and we will require approximately 20GB later.
To build TEE Compile, visit https://go.dev/dl to download the latest version of Go. Select the suitable installer based on your AWS instance; for an arm-based instance like r6g.xlarge, we download the go1.23.0.linux-arm64.tar.gz archive, the most recent version available at present.
Update your .bashrc file:

Setting up TEE Compile

With our Nitro Enclave VM configured, let’s head to GitHub to obtain and build TEE Compile:
Executing the above commands will produce the tee-compile executable, and copy it in the home directory. This executable will produce an attestable build later.
Next on the agenda is preparing the enclave image that produces attestable builds. Below is a Dockerfile suitable for creating enclave images capable of producing attestable builds for projects written in Rust. The container will install and set up the required dependencies together with Rust, copy the previously built tee-compile executable in the /workspace directory, and open a vsock to facilitate communication between the host and the enclave (e.g., the vsock will be used to transfer the source code used to generate an attestable build).
After creating and updating the Dockerfile in the tee-compile directory, we use it to create the Docker image named ata-build-reth. Lastly, we proceed to build the Enclave Image File (.eif) that will generate attestable builds from Rust code:
Our setup is now configured to create attestable builds for Rust repositories. In case you missed it, we’re gearing up to compile Reth!

Producing an Attestable Build for Reth

With all preparations in place, we can walk through the steps required to create an attestable build for Reth, an up-and-coming full-node Rust implementation of the Ethereum Execution Layer. Ensuring the authenticity of the Execution Layer build is essential to uphold Ethereum’s integrity and trustworthiness. Compromises of the execution layer could potentially lead to financial losses and disruptions within the ecosystem. An attestable build for Reth could provide node operators with the assurance they need regarding its integrity!
Let’s outline the steps required to produce the attestable build. To start, clone the repository:
Next, we specify how TEE Compile should build this project: in an editor of your choice, add the following lines to a file called build.json:
The build.json file instructs TEE Compile how to build the project:
  • "language": "rust" - specifies the language used (Rust in our case)
  • "cmd": "cargo build --release" - the command used to build the project
  • "vendor": "cargo fetch" - how to download the dependencies
  • "files": ["target/release/reth"] - the build artifacts copied from the enclave (in our case, the executable)
It’s time to run TEE Compile to generate the attestable build. If you followed the instructions in the previous section, the TEE Compile executable and the enclave image should be in the home directory. Executing the following command will generate the build:
Once the build generation process is complete, three files will be copied from the enclave to the host:
  1. reth.report - this binary file represents the attestation document endorsed by AWS, confirming that the entity compiling the source code is genuinely a Nitro Enclave. The report contains measurements performed by the enclave for its platform configuration registers (PCRs), including PCR0 - the hash of the enclave image file. Further details on attestation and its significance can be explored here
  2. reth.tar - contains the build artifacts, i.e. executable
  3. reth.txt - this represents the attestable build report, containing the hashes of the inputs and outputs associated with the source code and executable respectively
Here is what occurs behind the scenes as TEE Compile generates the attestable build:
  1. The project dependencies are fetched. To minimize the attack surface, Nitro Enclaves are restricted computing environments without direct Internet access. Therefore, TEE Compile first downloads the necessary project dependencies on the host system; these dependencies will later be provisioned to the enclave.
  2. A Nitro Enclave is instantiated based on the Docker image, after which the source code and its dependencies are copied inside the enclave.
  3. The project is built inside the enclave, and the relevant hashes of both the input (source code) and the output (executable) are generated.
  4. The build artifacts and the attestation report are copied from the enclave back to the host system.
  5. The enclave is destroyed.

How do we ensure the code and its dependencies are not tampered with before being transferred into the enclave?

Despite the enclave’s guaranteed integrity, the host is untrusted. How can we prevent the host from tampering with the project’s source code or dependencies before copying them into the enclave? The answer is we can’t, but any modification will be detectable:
  1. Any modification to the code alters its hash. As a result, the value of the input_hash in reth.txt will differ from the expected value, signaling to anyone verifying the report that the build does not correspond to the intended source code.
  2. Any modification to a dependency will cause the compilation process to fail. Files like Cargo.lock (Rust) and go.sum (Golang) provide precise information about the project dependencies, enabling the build system to validate their consistency. Failure to match the hashes of the supplied dependencies with those in the respective Cargo.lock or go.sum files will result in compilation errors. To prevent compilation errors, these files must also be altered, resulting in modifications to the source code which in turn lead to a different input hash as described in 1.

Integrating TEE Compile with your CI/CD Pipeline

Ideally, each time our source code is updated and a new build version is released, an attestable build will be triggered to produce a tamper-proof executable. This feature can be seamlessly enabled by integrating TEE Compile with GitHub Actions. Here are the steps to follow to set up this process:
  • Define the workflow: A sample workflow that triggers an attestable build for every new release.
Add the following content:
  • Configure the GitHub Runner: Go to Settings → Actions → Runners → New self-hosted runner
Choose the appropriate operating system and architecture, and follow the instructions. If you’ve been following us, select Linux for Runner image, and ARM64 for Architecture.
  • Lastly, create a release for your project and wait for the attestable build to be created!

Final Thoughts

The SolarWinds breach served as a stark reminder of how incorporating third-party dependencies can introduce vulnerabilities. With attestable builds generated by TEE Compile, we can reinforce trustworthiness, and transparency in the software development pipeline. By adopting these practices, developers can ensure their software remains secure, trustworthy, and resilient against emerging threats.
 

Comments