AWS — Nitro Enclaves what's popping?
What is Nitro and why you should care about their enclaves?
Introduction to Nitro
AWS Nitro System
In short AWS Nitro is the underlying platform for the next generation of EC2 instances.
With claims of having:
- Faster innovation
- Enhanced security
- Better performance and price
These claims are great for us. Who doesn’t love an increase in these areas!
Enough about Nitro here’s how Nitro Enclaves come into play. ⬇️
Use case overview
Recently I have been working with a public sector organisation that requires maximum security as you can imagine. Enclaves are a great fit for this project as they allow you to create an isolated VM (Virtual Machine) that has its own Kernel, CPU, and memory.
You can only talk to said enclave using a local channel in the form of a vSocket. Meaning if an attacker managed to get on to the host machine they wouldn’t be able to touch the enclave.
Key benefits of an enclave
- Cryptographic attestation
- Flexible
- Additional isolation and security
Enclaves are great for processing sensitive data as they are fully isolated from the parent. They can also integrate with AWS KMS (Key Management Service). Meaning only attested enclaves are allowed to decrypt with the KMS key. Providing them extra layers of security.
Example Implementation
Implementation overview
In this example, I will show you how to set up an enclave and how to talk to it via the ec2 host using a client-server model. The client will connect to the server running in the enclave. By using the enclaves CID
(Context Identifier) once connected it will send the string hi from client
into the enclave through the vSocket which will then be returned back to the client as HI FROM CLIENT
.
Server / Enclave setup guide
- Once connected to the ec2 first setup the
nitro-cli
using this guide here. - To setup the enclave copy over to the ec2 the below Dockerfile, run.sh and server.py files into a folder called ‘server’.
Note: For this example application I am using python so we are using the python:3.9.1-slim
base image.
Dockerfile
FROM python:3.9.1-slim WORKDIR /app COPY server.py ./ COPY run.sh ./ RUN chmod +x run.sh CMD ["/app/run.sh"]
Save this as Dockerfile
.
Run script
#!/bin/sh # Run the app python3 /app/server.py
Save this as run.sh
.
Server Python code
import socketimport datetimedef server(): print('Hello from enclave server!') port = 5000 s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) cid = socket.VMADDR_CID_ANY s.bind((cid, port)) s.listen() client_socket, address = s.accept() print("Connection from: " + str(address)) while True: data = client_socket.recv(1024).decode('utf-8') if not data: break print('From online user: ' + data + ' | At: ' + str(datetime.datetime.now())) # Send msg back to client but uppercase. data = data.upper() client_socket.send(data.encode('utf-8')) client_socket.close()if __name__ == '__main__': server()
Save this as server.py
.
- Now to build the enclave you use:
sudo nitro-cli build-enclave --docker-path . --output-file server.eif
- To run the built enclave image use:
sudo nitro-cli run-enclave --cpu-count 2 --memory 512 --eif-path server.eif --debug-mode
- To view the read-only console of the enclave use:
ENCLAVE_ID=$(nitro-cli describe-enclaves | jq -r .[0].EnclaveID)nitro-cli console --enclave-id $ENCLAVE_ID
Client setup guide
- Now for the client. I suggest opening another ssh connection to your ec2 so you can clearly see both outputs from the server and client easily.
- Copy over the below file on to the host.
Client python code
import socketimport timeimport sysdef client(): port = 5000 connected = False s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) cid = int(sys.argv[1]) message = 'hi from client' while not connected: try: s.connect((cid, port)) connected = True print('Connected to server... ') # send msg via socket s.send(message.encode('utf-8')) # get back msg from server data = s.recv(1024).decode('utf-8') print('Received from server: ' + data) except ConnectionError: print('Server not available yet, trying again in 2 seconds...') time.sleep(2)if __name__ == '__main__': client()
Save this as client.py
.
CID=$(nitro-cli describe-enclaves | jq -r .[0].EnclaveCID) python3 ./client.py $CID
You have successfully talked to the enclave over vSocket! Let your imagination take you to the next level on what you can do with this!
Conclusion
I only scratched the surface here at what enclaves are capable of but im excited about what the future holds for them in regards to code security!
Bonus
Enclave tips
Heres some issues I found during my time using nitro enclaves, since they are so new I will aim to update this over time:
echo "vm.nr_hugepages=1536" | sudo tee /etc/sysctl.d/99-nitro.conf; sudo sysctl -p /etc/sysctl.d/99-nitro.conf
Originally published at https://elliotmorris.dev.