Skip to content

Instantly share code, notes, and snippets.

@vdparikh
Last active April 30, 2023 22:04
Show Gist options
  • Save vdparikh/5ba1f34cb793c195921488326b74a6bc to your computer and use it in GitHub Desktop.
Save vdparikh/5ba1f34cb793c195921488326b74a6bc to your computer and use it in GitHub Desktop.
Nitro Enclave

What is Nitro Enclave

https://aws.amazon.com/ec2/nitro/nitro-enclaves/#:~:text=AWS%20Nitro%20Enclaves%20enables%20customers,within%20their%20Amazon%20EC2%20instances.

AWS Nitro Enclaves are isolated compute environments that provide additional security for highly sensitive data processing workloads. Nitro Enclaves are based on Nitro System, a combination of dedicated hardware and lightweight hypervisor, that isolates and protects resources like CPU, memory, and storage from the underlying infrastructure and other workloads.

Nitro Enclaves allow customers to securely process highly sensitive data without exposing the data to the underlying host, reducing the risk of data exfiltration and enabling compliance with strict security requirements.

Nitro Enclaves can run applications that require a secure and isolated environment, such as cryptographic key generation and storage, data processing in financial services, and code signing. Nitro Enclaves are integrated with AWS Key Management Service (KMS), AWS Secrets Manager, and other AWS services to help customers build secure and compliant applications.

Customers can use AWS Nitro Enclaves through the AWS Nitro Enclaves CLI, the Nitro Enclaves SDK, or with third-party software vendors who have integrated Nitro Enclaves into their products.

Steps to using Nitro Enclave for your application

  • Create a Nitro Enclave image: Before you can run an application in a Nitro Enclave, you first need to create a Nitro Enclave image. This involves building a custom Linux kernel with support for the Nitro Enclave, as well as any necessary drivers and software. Once you have built the kernel, you can use the Nitro CLI to create a Nitro Enclave image. You can either build the image from scratch, or start with an existing Linux distribution and customize it as needed.
  • Launch a Nitro Enclave instance: Once you have a Nitro Enclave image, you can launch a Nitro Enclave instance using the AWS Management Console, AWS CLI, or AWS SDK. When launching the instance, you'll need to specify the Nitro Enclave image ID, as well as the size and networking configuration of the instance. You can also specify the IAM role that the instance should assume, which determines the permissions that the instance has to AWS resources.
  • Configure the Nitro Enclave: Once the Nitro Enclave instance is running, you'll need to configure it to run your application. This may involve installing software dependencies, setting up network connections, and configuring security settings. You can do this by logging into the Nitro Enclave instance using SSH or the Nitro CLI, and then executing commands to configure the system.
  • Build and deploy your application: With the Nitro Enclave configured, you can now build and deploy your application. This may involve compiling your application code into an executable, setting up environment variables and configuration files, and configuring any necessary security settings. Once you have prepared your application, you can deploy it to the Nitro Enclave by copying it to the instance using SSH or the Nitro CLI.
  • Run your application: With your application deployed to the Nitro Enclave, you can now run it. This may involve executing the application from the command line, or configuring it to run as a service that starts automatically when the Nitro Enclave boots up. You can monitor the status of your application by viewing its logs or using system monitoring tools.
  • Clean up: When you are finished running your application in the Nitro Enclave, you should clean up any resources that you created. This may involve terminating the Nitro Enclave instance, deleting any security groups or IAM roles that you created, and deleting any data that was stored on the instance.

Using KMS with Nitro enclave

KMS within a Nitro Enclave, you can ensure that cryptographic keys remain protected from the underlying infrastructure, reducing the risk of exposure and enabling compliance with strict security requirements

# Create Instance for Nitro Enclave with support for GO and PKCS11
# Set the region and provider
provider "aws" {
region = "us-east-1"
}
# Define the Nitro Enclave instance
resource "aws_instance" "nitro_enclave_instance" {
ami = "ami-0ab6f49d8c2c6ab70" # Nitro Enclave AMI
instance_type = "c5n.xlarge" # Nitro Enclave instance size
key_name = "mykey" # SSH key for instance access
subnet_id = "subnet-12345678" # Subnet ID for instance placement
tags = {
Name = "nitro-enclave-instance"
}
}
# Define the script to install Golang and PKCS#11 dependencies
locals {
install_script = <<SCRIPT
#!/bin/bash
sudo yum update -y
sudo yum install -y epel-release
sudo yum install -y gcc make rpm-build git-core
sudo yum install -y golang pkcs11-helper-devel
export GOPATH=/opt/go
export PATH=$PATH:/opt/go/bin
SCRIPT
}
# Define the script to configure PKCS#11 for Golang
locals {
pkcs11_script = <<SCRIPT
#!/bin/bash
sudo mkdir -p /usr/local/lib/softhsm/lib
sudo ln -s /usr/lib64/pkcs11/pkcs11-helper.so /usr/local/lib/softhsm/lib/softhsm-pkcs11.so
export SOFTHSM_CONF=/etc/softhsm/softhsm.conf
export PKCS11_MODULE_PATH=/usr/lib64/softhsm/libsofthsm2.so
export PKCS11_PRIVATE_KEY="/path/to/private_key.pem"
export PKCS11_PUBLIC_KEY="/path/to/public_key.pem"
SCRIPT
}
# Define the user data to run the installation and PKCS#11 scripts
resource "aws_instance" "nitro_enclave_instance" {
user_data = <<-EOF
#!/bin/bash
${local.install_script}
${local.pkcs11_script}
EOF
}
# Create the AMI from the Nitro Enclave instance
resource "aws_ami_from_instance" "nitro_enclave_ami" {
name = "nitro-enclave-ami"
source_instance_id = aws_instance.nitro_enclave_instance.id
depends_on = [aws_instance.nitro_enclave_instance]
}
# Output the ID of the created AMI
output "nitro_enclave_ami_id" {
value = aws_ami_from_instance.nitro_enclave_ami.id
}
package main
import (
"fmt"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/service/kms/kmscrypto"
)
func main() {
// Set up an AWS session
sess := session.Must(session.NewSession())
// Create a KMS client
kmsClient := kms.New(sess)
// Get the ID of the customer master key (CMK) to use
keyID := os.Getenv("KMS_KEY_ID")
// Create a data key from the CMK
createKeyOutput, err := kmsClient.GenerateDataKey(&kms.GenerateDataKeyInput{
KeyId: aws.String(keyID),
KeySpec: aws.String(kms.DataKeySpecAes256),
})
if err != nil {
fmt.Printf("Error generating data key: %s", err)
return
}
// Use the data key to encrypt some plaintext
cipherText, err := kmscrypto.NewSymmetricCipher(createKeyOutput.Plaintext).Encrypt([]byte("Hello, Nitro Enclave!"))
if err != nil {
fmt.Printf("Error encrypting plaintext: %s", err)
return
}
// Use the data key to decrypt the ciphertext
plainText, err := kmscrypto.NewSymmetricCipher(createKeyOutput.Plaintext).Decrypt(cipherText)
if err != nil {
fmt.Printf("Error decrypting ciphertext: %s", err)
return
}
fmt.Printf("Plaintext: %s\n", plainText)
}
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from Nitro Enclave!")
})
http.ListenAndServe(":8080", nil)
}
# Creates a Nitro Enclave instance using the aws_instance resource and installs the Nitro Enclave CLI using the user_data argument. The metadata_options block is used to enable HTTP metadata requests, which are required for Nitro Enclave instances. The lifecycle block is used to ensure that the Nitro Enclave instance is created before the Nitro Enclave image.
# The code then creates a Nitro Enclave image using the aws_nitro_enclaves_image resource. The source_image_id argument is set to the ID of the Amazon Machine Image (AMI) for the Nitro Enclave instance. The efs_file_system_id argument is used to specify the ID of an Elastic File System (EFS) file system that will be attached to the Nitro Enclave image.
# Replace the values of the ami_id, subnet_id, and security_group_ids variables with your own values. You may also need to modify the user_data script to install any necessary dependencies for your application.
# Configure the AWS provider
provider "aws" {
region = "us-east-1"
}
# Define variables
variable "ami_id" {
description = "The ID of the Amazon Machine Image (AMI) for the Nitro Enclave"
}
variable "instance_type" {
description = "The instance type for the Nitro Enclave"
default = "c5n.large"
}
variable "subnet_id" {
description = "The ID of the subnet for the Nitro Enclave"
}
variable "security_group_ids" {
description = "The IDs of the security groups for the Nitro Enclave"
type = list(string)
}
# Create a Nitro Enclave image
resource "aws_instance" "nitro_enclave" {
ami = var.ami_id
instance_type = "c5.large"
subnet_id = var.subnet_id
security_group_ids = var.security_group_ids
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y aws-nitro-enclaves-cli
EOF
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
}
lifecycle {
create_before_destroy = true
}
}
# Create a Nitro Enclave image
resource "aws_nitro_enclaves_image" "nitro_enclave_image" {
name = "my-nitro-enclave-image"
enclave_size = 256
root_device_name = "/dev/nvme0n1"
source_image_id = aws_instance.nitro_enclave.ami
efs_file_system_id = "fs-12345678"
tags = {
Name = "my-nitro-enclave-image"
}
}
# Template launches a c5n.xlarge instance with a specified Amazon Machine Image (AMI) and network configuration. The instance also has a security group that allows inbound traffic on ports 80 and 443, which are required for Nitro Enclave communication.
# The instance's UserData is a Bash script that downloads and installs the Nitro CLI, and then uses the CLI to build a Nitro Enclave image. The resulting Nitro Enclave image is saved as nitro-enclave.img in the instance's root directory.
# Once the Nitro Enclave image has been generated, it can be used to launch Nitro Enclave instances as needed. Note that this example is just a basic template to give you an idea of the steps involved; you may need to customize it further depending on your specific use case.
Resources:
NitroEnclaveImage:
Type: 'AWS::EC2::Instance'
Properties:
InstanceType: c5n.xlarge
ImageId: ami-0d66fa498c7e23f03
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
GroupSet:
- !Ref SecurityGroup
SubnetId: !Ref Subnet
KeyName: !Ref KeyPairName
UserData:
Fn::Base64: !Sub |
#!/bin/bash
# Install prerequisites
yum install -y make gcc kernel-devel-$(uname -r)
# Download Nitro CLI
curl -s https://s3.amazonaws.com/aws-nitro-enclaves-cli/latest/nitro-cli-linux.tar.gz | tar xzf -
cd nitro-cli
# Build Nitro Enclave image
make && make image && mv *.img ../nitro-enclave.img
DependsOn:
- SecurityGroup
SecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: Nitro Enclave security group
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation template to spin off a Nitro Enclave'
Parameters:
NitroInstanceType:
Type: String
Default: 'c5n.large'
Description: 'The instance type to use for the Nitro Enclave'
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: 'The ID of the subnet to launch the Nitro Enclave in'
SecurityGroupId:
Type: AWS::EC2::SecurityGroup::Id
Description: 'The ID of the security group to associate with the Nitro Enclave'
NitroEnclaveImageId:
Type: AWS::EC2::Image::Id
Description: 'The ID of the Nitro Enclave image to use for the instance'
Resources:
NitroEnclaveInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref NitroInstanceType
SubnetId: !Ref SubnetId
SecurityGroupIds:
- !Ref SecurityGroupId
BlockDeviceMappings:
- DeviceName: /dev/nvme0n1
Ebs:
VolumeSize: 20
UserData:
Fn::Base64: !Sub |
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras install -y aws-nitro-enclaves-cli
sudo amazon-linux-extras install -y nitro-cli
sudo modprobe nitro_enclaves
nitro-cli build-enclave --memory 512 --cpu-count 1 --disable-vm-swap --debug-mode on
nitro-cli run-enclave --eif-path /opt/aws/nebuilder/data/enclave.eif
Outputs:
NitroEnclaveInstanceId:
Value: !Ref NitroEnclaveInstance
Description: 'The ID of the Nitro Enclave instance'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment