Primitives

Cages

Evervault Cages are the easiest way to build, deploy and scale Secure Enclaves.

Evervault Cages allow developers to easily deploy Docker containers in a Secure Enclave, powered by AWS Nitro Enclaves. Cages offer easy deployment, invocation and attestation of Secure Enclaves without the engineering overhead.

Cages Trial

How long is the trial program?

Starting from the day you deploy your first Cage, you will have full access to the Evervault Platform (free plan) and Cages for 14 days, so you can try building and deploying Cages. After that period, you’ll have the option to sign up for monthly pricing to continue testing.

If you’re unsure how long you have — you can always check the countdown timer in your Evervault Dashboard to see how much time is left in the allotment. If you need to adjust the timing or have any questions regarding the trial, feel free to reach out to our team.

How many Cages can I create?

During the 14 day trial period — A team can run one Cage. In order to manage our costs, Cages will run as a single instance which may cause downtime for deployments, however this is only for your trial period.

Following initial trial — If you upgrade to a paid plan and start using Cages in production, we will provide multiple instances with high availability and zero downtime deployments.

What happens during the trial?

Try it out! Start here if you’re not sure where to get started.

We would love to know more about the use cases you are building for, any errors or bugs you encounter, and any feedback you have.

What happens after the trial?

At the end of the 14-day period, we’ll notify you that your trial access has ended, and any active Cages will be deactivated.

Once deactivated, you will be able to access your Cage in your free account, but no instances will be running. If at any time you’d like to continue using Cages, you can easily upgrade to a paid plan and we will restart your Cage instances.

Upgrading to the paid plan will give you access to all of the Evervault Pro features and allow you to run multiple Cage instances with high availability and zero downtime deployments.

Other questions?

If anything comes up during the trial, please send us an email at support@evervault.com, and we’ll get back to you as soon as possible.

Getting Started

Get up and running with Cages.

Install the Cages CLI

You can install the Cages CLI by running the following command in your terminal:

hopper:~$
curl https://cage-build-assets.evervault.com/cli/install -sL | sh

The Cages CLI is currently separate to the main Evervault CLI.

Install Docker

The Cages CLI needs to perform Docker builds using the Docker CLI. Docker must be installed on your system.

Clone Hello Cage repository

To create your first Cage, you’ll need a server and a Dockerfile.

If you don’t have one already, then you can use our Hello, Cage! repository (a simple Node.js Express server) as a starting point. You can clone it by running the following command:

hopper:~$
git clone https://github.com/evervault/hello-cage

Authenticating with the Cages CLI

The Cages CLI currently only supports API Key authentication. To setup a session in your terminal, you will first need to create an App API Key in the Evervault Dashboard.

Once you have your API Key you can create the session by running:

hopper:~$
export EV_API_KEY=<API_KEY>

Initialize your Cage

Now we can initialize your Docker server as a Cage. This will reserve the Cage name within your Evervault App, so you can deploy your service.

For our first Cage, we’ll enable some features which help us see what’s happening inside the Secure Enclave, namely: debug mode and network egress. To do that, we need to run the following command:

hopper:~$
ev-cage init -f ./Dockerfile \
--name hello-cage \
--debug --egress \
--healthcheck /health

Configure your Cage

This command will generate a cage.toml file in your current directory containing the configuration for your Cage. It should look something like this:

1
name = "hello-cage"
2
uuid = "<YOUR_CAGE_ID>"
3
app_uuid = "<APP_ID>"
4
team_uuid = "<TEAM_ID>"
5
debug = true
6
dockerfile = "Dockerfile"
7
healthcheck = "/health"
8
9
[egress]
10
enabled = true
11
ports = ["443"]
12
13
[signing]
14
certPath = "~/.ev/cages/cert.pem"
15
keyPath = "~/.ev/cages/key.pem"

You can edit this .toml file whenever you want to reconfigure your Cage.

Build your Cage

Now that we have our cage.toml, we can build our Cage. The build command will convert the service’s Dockerfile into a Cage-compatible Enclave Image File (.eif). A .eif file is a binary image that is used to initialize an AWS Nitro Enclave, and all attestation measures are based on the .eif binary.

Because our cage.toml is in the current directory, we can run:

hopper:~$
ev-cage build --output .

The first build will be slow (approximately 2 minutes) as the CLI has to build several Docker images. Each subsequent build should be faster once the images have been cached.

Once the Cage has finished building, the Cage PCRs will be written to the cage.toml file. This will help with tracking changes in attestation measures within source control.

Our cage.toml should have a section that looks something like this at the end of it:

1
[attestation]
2
HashAlgorithm = "Sha384 { ... }"
3
PCR0 = "8576aa759528d6dc82b6a35504edf491bcf245266acb5745f7f15801e15988a5abbc8c637af3edeb96efcbe8e8a433a1"
4
PCR1 = "bcdf05fefccaa8e55bf2c8d6dee9e79bbff31e34bf28a99aa19e6b29c37ee80b214a414b7607236edf26fcb78654e63f"
5
PCR2 = "4ffe3d8b0211341c9eac73abccfcfed63f694a4a84b7758e70d1941d0ac6c0a7091c7860aa1ff2e4d39bbdd2b220608f"
6
PCR8 = "9f357c7861268d124143701d30fbd0401f4f2854db7698851c51a08bc719abe9cc89645324d24cdbac1f216b482d6ad8"

These four PCR values are the attestation measurements that we will use to trust the remote server (the Cage) before we share any sensitive data with it. The four measurements reflect various aspects of the image you are running in the enclave.

PCRMeasuresExplanation
PCR0Enclave Image FileA measure of the Image that will be run within the Cage.
PCR1Linux Kernel and BootstrapA measure of the kernel and boot ramfs data.
PCR2User ApplicationA measure of the User Application without the boot ramfs
PCR8Signing CertificateA measure of the certificate used to sign the Enclave Image File.

You can read more about attestation for Cages here.

The build command will also create two files: enclave.eif and ev-user.Dockerfile. The enclave.eif file is the image that the Cage will run in the AWS Nitro Enclave, and the ev-user.Dockerfile is the Dockerfile that was used to generate it.

Deploy your Cage

To deploy your Cage using an existing .eif run:

hopper:~$
ev-cage deploy --eif-path $EIF_PATH

You can build a fresh .eif for a deployment by omitting the --eif-path argument. This means you can skip the ev-cage build step and deploy in one command.

The CLI will track your Cage deployment. Once the deployment has stabilized, you should see a log which includes the domain for your Cage:

hopper:~$
Cage deployed successfully. Your Cage is now available at https://<cage_name>.<app_id>.cages.evervault.com

Invoke your Cage

You can now call your Cage over the internet! If you deployed the Evervault Hello, Cage! template, you can use the following cURL command to try it out:

hopper:~$
curl https://<cage_name>.<app_uuid>.cages.evervault.com/hello -H "API-Key: <API_KEY>" -k

You should now see an echo response from your Cage:

1
{ "response": "Hello! I'm writing to you from within an enclave" }

The Evervault Node and Python SDK includes support for attesting all connections to your Cages automatically.


Reference

cage.toml

Cages allows configuration to be embedded within the source code, so it is easily attestable and can't be tampered with. Simply include a cage.toml file in the root of your repository and Evervault will automatically include it at build time.

The cage.toml can be used to configure rules such as network egress and debug mode. It also includes dynamic attestation measures so each time your Cage is built, the PCR measures can be verified by comparing the result of the attestation with the measures included in Evervault's TLS attestation.

The format of your cage.toml is as follows:

1
# The name of your Cage
2
name = "hello-cage"
3
4
# The ID of your Cage
5
uuid = "<YOUR_CAGE_ID>"
6
7
# The ID of the Evervault App which is hosting the Cage
8
app_uuid = "<APP_ID>"
9
10
# The ID of the Evervault Team which owns the App
11
team_uuid = "<TEAM_ID>"
12
13
# Whether `debug` mode should be enabled.
14
# Logs are only available in `debug` mode.
15
debug = true
16
17
# Whether api key auth is enabled for the Cage
18
api_key_auth = true
19
20
# Whether transaction logging is enabled for the Cage.
21
# Note: Requires TLS Termination to be enabled
22
trx_logging = true
23
24
# Define a list of headers to ignore during obfuscation of transaction logs.
25
# Supports absolute matches and suffix wildcards.
26
trusted_headers = ["X-Error-*", "X-Trace-Id"]
27
28
# Turn off TLS Termination with certs signed by Evervault Root CA.
29
# Note: you will have to handle TLS termination within your process.
30
disable_tls_termination = true
31
32
# The path to your Cage's `Dockerfile`
33
dockerfile = "Dockerfile"
34
35
# An endpoint exposed by your service to track its health within the Cage.
36
# This endpoint will receive a `GET` request every second.
37
# The healthcheck's user agent will be: "Evervault-Healthcheck-Agent"
38
healthcheck = "/health"
39
40
# Whether or not to enable outbound network requests from your Cage
41
# If enabled, the networking layer will be bundled at build time
42
[egress]
43
enabled = true
44
destinations = ["*.evervault.com"]
45
ports = ["443"]
46
47
# The amount of replicas you would like to run for your Cage.
48
# Note: If only running one replica, there will be downtime
49
# between deployments. (Default 2)
50
[scaling]
51
desired_replicas=2
52
53
# The path to the signing key and certificate for your Cage
54
[signing]
55
certPath = "~/.ev/cages/cert.pem"
56
keyPath = "~/.ev/cages/key.pem"

TLS Termination

TLS termination is enabled by default on your Cage. When enabled, any requests into your Cage will terminate TLS within the enclave using a cert signed by the Evervault Root CA. With TLS termination enabled, you will have access to features like automatic field decryption, authentication using your Evervault API Key, and transaction logging. You can disable TLS termination by setting disable_tls_termination=true in your cage.toml configuration file. If you disable TLS termination, you will need to handle it within your process.

Cages provide a trusted cert that is provisioned upon startup. This cert is signed by a Trusted CA, making it compatible with most browsers. However, this cert does not include the attestation document, preventing attestation on the trusted domain at this time. Our SDKs are currently being updated with a new attestation flow to handle attestation on trusted certs.

To connect to your cage with the trusted cert use the domain *.cage.evervault.com (note cage, not cages).

Automatic Decryption

When TLS termination is enabled on your Cage, any Evervault-encrypted strings will be decrypted automatically before being sent to your process. The fields can then be treated as plaintext within your Cage.

Encryption API

Evervault exposes a simple encryption API within Cages which allows you to encrypt and decrypt Evervault-encrypted strings. It also allows you to manually retrieve an AWS Nitro Enclaves Attestation Document if you wish to implement your own attestation protocol.

Encrypt data

To encrypt data, you can make a request to /encrypt.

POST/encrypt
200Ok
{ "foo": "bar" }
Response
{ "foo": "ev:encrypted_string..." }

Decrypt

To decrypt data you can make another request to /decrypt.

POST/decrypt
200Ok
{ "foo": "ev:encrypted_string..." }
Response
{ "foo": "bar" }

Attestation Document

To fetch the attestation document for the cage, you can make a request to /attestation-doc

POST/attestation-doc
200Ok
{ "challenge": "abc", "nonce": "123" }
Response
Attestation Document as Base64-encoded binary blob

Default port

By default, traffic within the Cage will be forwarded to port 8008 on loopback. This can be configured by supplying an EXPOSE directive in your Dockerfile.

Note that several ports are reserved within the Cage.

Reserved Ports

Certain ports are reserved within Cages for internal services. In certain cases, these ports are only reserved when a feature is enabled. The reserved ports are as follows:

PortReasonCorresponding Feature
9999Used by the Encryption API
53Used to proxy egress DNS callsegress — can be disabled in the cage.toml
443Used to proxy egress TCP trafficegress — can be disabled in the cage.toml

Environment Variables

Environment Variables can be configured using the UI or CLI. Secrets can be encrypted on creation and will be made available to your Cage environment in plaintext on startup. When you create or modify an Environment Variable for your cage, it will only take effect once you redeploy the cage. You can restart your Cage's current deployment to redeploy your Cage without affecting it's PCRs.

Add a secret value

hopper:~$
ev-cage env add --key ACME_API_KEY --value secretvalue --secret

Add a non secret value

hopper:~$
ev-cage env add --key REGION --value europe

HTTP Transaction Logging

When TLS Termination is enabled on your cage, you can enable HTTP Transaction logging which will propagate request logs to the Evervault Activity Screen within the dashboard. These logs contain HTTP Status Codes, Methods, Timestamps etc. This can be configured within the cage toml by setting trx_logging = false (Default is true).

A unique ID is also appended to each request. This ID can be found in the response headers under x-evervault-cage-ctx. You can then search for this ID in the Activity Screen.

By default, your Cage's transaction logs will obfuscate any non-standard HTTP headers. You can specify a list of headers to pass through using the trusted_headers key in the cage.toml. This can be used to surface error codes or similar diagnostic information from your Cage. For example:

1
trusted_headers = ["X-Error-*", "X-Trace-Id"]

Note: certain headers cannot be trusted to avoid accidentally leaking credentials. This currently includes: api-key, authorization, and proxy-authorization.

Debug Mode

If you want to test your Docker Image in a Cage and see the output, you can run your Cage in debug mode. Transaction Logs will be emitted and viewable through the Evervault Dashboard. Raw logs from your cage can be viewed using the ev-cage logs command in the CLI.

When running a Cage in debug mode, the attestation measure returned will consist entirely of zeroes. This means that a Cage in debug mode is not attestable.

Egress

By default there is no networking out of a Cage. If you wish to make requests out of the Cage, turn on egress by including --egress on Cage init or update the cage.toml. The default egress port is on port 443 to support typical HTTPS traffic, if you are making requests to hosts on different ports you will need to specify them in the cage.toml or with --egress-ports 443,465,5432

You can also configure the allowed domains that requests out of the Cage should be allowed to with --egress-destinations. The list will be enforced in both the data plane and the control plane, requests to domains that are not explicity allowed will fail at the beginning of the TLS handshake. You can specify exact domains or wildcards to allow all subdomains, for example cages.evervault.com or *.evervault.com. The default behaviour is to allow requests to any domain but we recommend restricting them in production environments.

Egress will only work out of the enclave if the connection uses TLS.

Authentication

Cages expect an Evervault API key in an api-key request header by default. This can be disabled if you wish to implement your own form of authentication to the Cage.

Healthchecks

By default, Cages will perform a shallow healthcheck. The communication between the host instance and the Enclave will be tested, and the in Enclave environment will be validated to ensure that the Cage initialization completed successfully.

This healthcheck can be extended to include a GET to the service running in the Cage. Every second, the Evervault-Healthcheck-Agent will send a GET request to your process. Your process will be regarded as healthy if it responds with a successful status code (i.e. 2XX).


Cages CLI Reference

Manage Cages from your terminal.

Build

The build command mirrors the docker build command but produces an Enclave Image File (EIF) as output instead of a Docker image. The build command requires a cage.toml to be available when creating an Enclave Image File. This can be generated using the ev-cage init command.

hopper:~$
ev-cage build [OPTIONS] [CONTEXT_PATH]
Args
CONTEXT_PATH

Path to use for Docker context, defaults to the current directory.

Options
-c, --configDefaults to ./cage.toml

Path to the cage.toml file.

-f, --fileDefaults to ./Dockerfile

Path to the Dockerfile to use for the Cage.

-o, --output

Path to directory to save the processed Dockerfile and EIF.

--private-key

Private key to be used when signing the EIF.

--signing-cert

Certificate corresponding to the private key.

--build-arg

Build time arguments to provide to Docker.

--from-existing

Build from enclave.Dockerfile for build reproducibility

--reproducible

Build enclave with reproducible measures

New Certificate

Create a new Cage signing certificate and private key.

hopper:~$
ev-cage cert new [OPTIONS]
Options
-o, --output

Path to the directory where the credentials will be saved. Defaults to the current directory.

--subj

Defining the certificate’s distinguished name e.g. /CN=EV/C=IE/ST=LEI/L=DUB/O=Evervault/OU=Eng. If not given a generic Cage subject is given.

Upload Certificate Metadata

Upload a signing certificate's metadata to the Evervault API. This cert can then be used specified for locking deployments.

hopper:~$
ev-cage cert upload [OPTIONS]
Options
-p, --cert_path

Path to directory where the signing cert is. Defaults to the path specified in ./cage.toml

-c --config

Path to cage.toml config file. Default: ./cage.toml

Lock Certificates

Interactive prompt to lock Cage deployment to specific signing certificates. A Cage deployment will fail if the signing certificate used is not in specified locked certs. If no certificates are locked to a Cage, it can be deployed with any certificate.

hopper:~$
ev-cage cert lock [OPTIONS]
Options
-c --config

Path to cage.toml config file. Default: ./cage.toml

Delete

Delete the Cage defined in a given cage.toml.

hopper:~$
ev-cage delete [OPTIONS]
Options
-c, --configDefault to ./cage.toml

Path to the cage.toml config file.

--cage-uuid

Uuid of the Cage to delete

--background

Perform the Cage deletion in the background

Deploy

Deploy the Cage defined in your cage.toml file. By default, the deploy command will ignore any prebuilt EIFs and begin a fresh Cage build. You can prevent this by providing a path to an existing EIF using the --eif-path option.

hopper:~$
ev-cage deploy [OPTIONS] [CONTEXT_PATH]
Args
CONTEXT_PATH

Path to use for the Docker context. Defaults to the current directory.

Options
-c, --configDefault: ./cage.toml

Path to cage.toml config file

--eif-path

Path to EIF for the Cage. Will skip building if EIF is provided.

-f, --fileDefault: ./Dockerfile

Path to Dockerfile for Cage. Will override any Dockerfile specified in the cage.toml file

--private-key

Private key used to sign the Enclave Image File.

--quiet

Disable verbose output

--signing-cert

Certificate used to sign the Enclave Image File.

--build-arg

Build time arguments to provide to Docker

--from-existing

Build from enclave.Dockerfile for build reproducibility

--reproducible

Build enclave with reproducible measures

--healthcheck

The endpoint to use for healthchecks. See healthchecks for more information.

Restart

Restart the Cage defined in your cage.toml file. This will redeploy the Cage with the same image as your current Deployment, but with the latest environment variables and secrets. This won't affect your Cage's PCRs

hopper:~$
ev-cage restart [OPTIONS]
Options
-c, --configDefault to ./cage.toml

Path to the cage.toml config file.

--cage-uuid

Uuid of the Cage to restart

--background

Perform the Cage restart in the background

Describe

Get the PCRs of an existing EIF.

hopper:~$
ev-cage describe [OPTIONS] [EIF_PATH]
Args
EIF_PATHDefault: ./enclave.eif

Path to the EIF to describe.

Options
-h, --help

Print help information

--json

Toggle JSON output for stdout

-v, --verbose

Toggle verbose output

Init

Create a Cage and initialize a cage.toml in the current directory.

hopper:~$
ev-cage init [OPTIONS] --name <CAGE_NAME>
Options
--debug

Debug setting for the Cage. When debug is enabled, you can access logs from within the Enclave.

--egress

Flag to enable network egress from your Cage, default egress port is 443

--egress-ports

Comma separated list of ports to allow egress on

--egress-destinations

Comma separated list of domains to allow egress to, default is all (*)

-f, --file

Dockerfile to build the cage

-generate-signing

Flag to enable cert generation during initialization. This will use the default certificate.

-h, --help

Print help information

--json

Toggle JSON output for stdout

--name

Name of Cage to deploy

-o, --outputDefault: ./

Directory to write the cage.toml to. Defaults to the current directory.

--private-key

Path to the signing key to use for the Cage.

--signing-cert

Path to the signing cert to use for the Cage

-v, --verbose

Toggle verbose output

--disable-api-key-auth

Turn off API key authentication in the Cage.

List Cages

List your Cages

hopper:~$
ev-cage list cages [OPTIONS]
Options
-h, --help

Print help information

--json

Toggle JSON output for stdout

-v, --verbose

Toggle verbose output

List Deployments

List the deployments for your Cage.

hopper:~$
ev-cage list deployments [OPTIONS] --cage-uuid <CAGE_ID>
Options
--cage-uuid

The Cage ID to get deployments for.

-h, --help

Print help information.

--json

Toggle JSON output for stdout.

-v, --verbose

Toggle verbose output.

Logs

View the logs for your Cage. Only available for Cages in debug mode.

hopper:~$
ev-cage logs [Options] --cage-uuid <CAGE_ID>
Options
--cage-uuid

The Cage ID to get deployments for.

-c, --config

The local Cage config.

-h, --help

Print help information.

Add Environment Variable

Add environment variable to be used in a Cage.

hopper:~$
ev-cage env add [OPTIONS] --key <ENV_KEY> --value <SECRET_VALUE>
Flags
--keyRequired

The name of the environment variable.

--valueRequired

The value for the environment variable.

Options
-h, --help

Print help information.

--secret

Encrypt the environment variable.

--curve

Curve to use for encryption. If none is provided the default is secp256r1.

  • secp256r1 (alias: nist)
  • secp256k1 (alias: koblitz)

Delete Environment Variable

Permanently delete a environment variable from the Cage environment.

hopper:~$
ev-cage env delete --key <ENV_KEY>
Flags
--keyRequired

The name of the environment variable.

Get Environment Variables

Get the environment variables in json format.

hopper:~$
ev-cage env get

Encrypt String

Encrypt a string that can be decrypted in your Cage.

hopper:~$
ev-cage encrypt [OPTIONS] <VALUE>
Flags
--valueRequired

The value to encrypt.

Options
-h, --help

Print help information.

--curve

Curve to use for encryption. If none is provided the default is secp256r1.

  • secp256r1 (alias: nist)
  • secp256k1 (alias: koblitz)

FAQ

How does attestation work with Cages?

Evervault Cages abstract away the complexity of implementing and verifying attestation with your Secure Enclaves. Cages embed the attestation flow in the TLS handshake, which is performed every time you invoke a Cage.

TLS Attestation

When you connect to a Cage, your client performs a standard TLS handshake with the enclave. Evervault automatically handles all load balancing between enclaves at the network layer.

Each Cage provisions its own self-signed TLS certificate, with the AWS Nitro Attestation Document embedded within the certificate.

When a client connects to the enclave, it can verify the attestation before completing the TLS handshake. By default, the Evervault server-side SDKs include functionality to verify your Cage's attestation out-of-the-box.

How large can a Cage be?

The current limit for Cages is 16 vCPUs and 64 gigabytes of RAM.

Enclaves use a RAM-based filesystem. This means that the memory allocated to your Cage is used for both storage and RAM.

Cages and Evervault Encryption

Evervault Cages allow you to process data encrypted using Evervault Encryption. By default, TLS termination is handled automatically within your Cage, and encrypted data is automatically decrypted before it is passed to your application.

Can I run a Cage on my own infrastructure?

Evervault Cages are managed and run by Evervault on our infrastructure. A major advantage of using Cages is that the burden of hosting and scaling all of the infrastructure necessary to run Secure Enclaves is handled by Evervault.

Evervault running your Cage doesn't weaken the security guarantees provided by Secure Enclaves, thanks to attestation. You are still provided with the attestation measure at build-time and can verify that these haven't been tampered with — all within the TLS handshake.

Can I run multiple instances of a Cage?

Yes! Cages are deployed on two separate EC2 instances split between two Availability Zones in a region for greater resiliency. The number of instances running, and the regions they run in will soon be made configurable.

Can I restart my Cage?

Cages can be restarted from either the "Versions" tab in the Cages Dashboard or by using the CLI.

Why does my Cage perform decrypt requests on start-up?

Cages order a trusted TLS certificate on their first deployment. The private key is encrypted in the Cage, and backed-up so it can be loaded in on every subsequent start-up. As a result, when each instance of the Cage launches, they perform a single decrypt request to load in their private key in plaintext.


Best Practices Building with Cages

Cages are designed to be flexible to fit your development process. Here are a few best practices to set you up for success.

Writing the Dockerfile

Since Cages are built using Docker, a good place to start is making sure that you are formatting your Dockerfile correctly.

First you need to use the FROM command which will create a layer from the Docker image you wish to use. It’s best to use one of the container images available on Docker Hub to ensure you are using a trusted source. You can also opt for using the slim version of the container which can help reduce the image size, however the slim versions may not contain all of the common packages in the default tag.

1
FROM node:16-alpine3.16-slim

Next you will need to copy any files from your local source to the filesystem of the container. It’s best to use absolute paths in the Dockerfile. When your Docker container is converted to an Enclave, your commands might not be run in the directory you expect them to be, so it is safer to reference your files as absolute paths.

1
COPY ./index.js /index.js
2
COPY ./package.json /package.json
3
COPY ./package-lock.json /package-lock.json

Make sure that you are exposing the non-reserved port you want to listen on since that is where traffic within the Cage will be forwarded. The Cages documentation uses PORT 8008 as an example, but this can be any available port that you define.

1
EXPOSE 8008

Here are some examples of what your Dockerfile might look like.

Node.js

1
FROM node:16-alpine3.16
2
3
COPY ./index.js /index.js
4
COPY ./package.json /package.json
5
COPY ./package-lock.json /package-lock.json
6
7
EXPOSE 8008
8
9
RUN npm i
10
11
ENTRYPOINT ["node", "/index.js"]

Java

For Java and other compiled languages, you may need to use a multi-stage build that involves first creating a build stage and then a package stage.

1
# Build stage
2
FROM maven:3.5-jdk-8 AS build
3
4
COPY src /usr/src/app/src
5
COPY pom.xml /usr/src/app
6
7
RUN mvn -f /usr/src/app/pom.xml clean package
8
9
# Package stage
10
FROM gcr.io/distroless/java
11
12
COPY --from=build /usr/src/app/target/helloworld-1.0.0-SNAPSHOT.jar /usr/app/helloworld-1.0.0-SNAPSHOT.jar
13
14
EXPOSE 8080
15
16
ENTRYPOINT ["java","-jar","/usr/app/helloworld-1.0.0-SNAPSHOT.jar"]

Ruby

1
FROM ruby:3.0
2
3
RUN bundle config --global frozen 1
4
5
COPY ./app.rb /app.rb
6
COPY ./Gemfile /Gemfile
7
COPY ./Gemfile.lock /Gemfile.lock
8
9
EXPOSE 8008
10
11
RUN bundle install
12
13
COPY . .
14
15
ENTRYPOINT ["ruby", "app.rb"]

Docker will then build images by reading the instructions from your Dockerfile. The default instance size supports up to 2GB Docker images.

You can check the size of your Docker image using the docker inspect command

1
docker inspect [OPTIONS] NAME|ID [NAME|ID...]

Run Your Server Locally with Docker

One helpful step you can take is to try containerizing your application before deploying the container inside a Cage. If it can run with Docker, it should be able to run in a Cage, and it will help you debug errors that may exist from the Docker build before you attempt to run it in a Cage.

Once you have your app ready including the Dockerfile, navigate to the directory containing the Dockerfile, then run the following command:

1
docker build -t your-image .

Next, you can start the container using docker run along with the name of your image and the container port that you exposed in the Dockerfile mapped to the host port.

1
docker run -p 8080:8080 your-image

Now if you go to the host port http://localhost:8080 you should see your app running. You can also check the Docker Desktop app or CLI to confirm that the container is running.

Package Installation

Some of the instructions in your Dockerfile will install required packages. You can use whichever package manager makes sense for your application.

In some cases, you may want to use a manifest file that will contain the required packages and their versions, like a package.json for Node or requirements.txt for Python.

For Python specifically, it can also be helpful to use a virtual environment. You can run the virtual environment within the container and then install the packages within the environment. This will also help with minimizing image size.

Here is an example:

1
FROM python:3.8-slim
2
3
RUN python3 -m venv /opt/venv
4
COPY requirements.txt requirements.txt
5
RUN /opt/venv/bin/pip install -r requirements.txt
6
7
COPY app.py /app.py
8
9
EXPOSE 8008
10
11
ENTRYPOINT ["/venv/bin/python", "/app.py"]

Using the Encrypt and Decrypt Endpoints

By design, data that is encrypted with Evervault that passes into the Cage will automatically decrypt. There is no further action required to decrypt and start using the data.

You also can utilize /encrypt and /decrypt endpoints that are exposed on a mini app listening on port 9999 within the enclave. These are not available outside of the enclave, so if you have publicly available endpoints within your app that use the same naming, there should not be any conflict.

These endpoints are available for any Cage, but you will need to define them within your application. Here is an example of how you might implement it in Node.

1
app.all("/encrypt", async (req, res) => {
2
try {
3
const result = await axios.post('http://127.0.0.1:9999/encrypt', req.body);
4
res.send({ ...result.data });
5
} catch (err) {
6
console.log("Could not encrypt body", err);
7
res.status(500).send({msg: "Error from within the cage!"})
8
}
9
});

To encrypt data, you can make a request to /encrypt and pass in any data that you wish to encrypt as JSON.

1
curl -X POST \
2
-H "API-Key: <API_KEY>" \
3
-H 'Content-Type: application/json' \
4
-d '{ "foo": "bar" }' \
5
https://<cage_name>.<app_uuid>.cages.evervault.com/encrypt -k

The purpose of the decryption API is to offer flexibility with applications built inside the enclave. Examples could be if you need to upload a list of large files of encrypted data to be decrypted on start up of the enclave or a process that pulls in encrypted data from an external source.

Another point to note is that when TLS termination is turned off in the data plane, it’s not possible for the request to be scanned for encrypted strings. This is another instance where the Decrypt API could be used.

Here is an example of how to implement the Decrypt API.

1
app.all("/decrypt", async (req, res) => {
2
try {
3
const result = await axios.post('http://127.0.0.1:9999/encrypt', req.body);
4
5
res.send({ ...result.data });
6
} catch (err) {
7
console.log("Could not decrypt body", err);
8
res.status(500).send({msg: "Error from within the cage!"})
9
}
10
});

To decrypt data you can make another request to /decrypt and pass in the encrypted string as JSON.

1
curl -X POST \
2
-H "API-Key: <API_KEY>" \
3
-H 'Content-Type: application/json' \
4
-d '{ "foo": "bar" }' \
5
https://<cage_name>.<app_uuid>.cages.evervault.com/decrypt -k

Should you need to make outbound network calls from within an enclave you can make egress network calls. You will want to disable this at build time in order to prevent unauthorized data from flowing in or out of the Cage. While these options can be helpful for development, you will want to think about which ones should be disabled in production to reduce the attack surface.

Checking Attestation

The Cage Attestation measures are embedded in the TLS extensions within the TLS handshake. Evervault SDKs can be used to validate the PCRs returned in the handshake. You may use the Python or Node.js SDKs which have a function that will allow your application to attest all TLS connections to Evervault Cages.

Node.js

1
const encrypted = await evervault.encrypt("Your Data");
2
3
await evervaultClient.enableCagesBeta(
4
{ 'my-cage': [
5
{ pcr8: 'PCR8FULLHASH' },
6
{ pcr0: 'PCR0FULLHASH' },
7
]});
8
9
const response = await axios.post(
10
'https://my-cage.my-app.cages.evervault.com',
11
encrypted
12
);

This takes in a set of expected PCRs and validates they are returned in the TLS handshake of requests sent to the Cage. You can pass in a single PCR value as an object or multiple PCR values as an array of objects.

Python

1
attested_session = evervault.cage_requests_session({
2
'my-cage': [{ 'pcr_8': 'PCR8FULLHASH' }, { 'pcr_0': 'PCR0FULLHASH' }]
3
})
4
5
attested_session.post(
6
'https://my-cage.my-app.cages.evervault.com',
7
data=sensitive_payload
8
)

This is the same behavior in Python. You can pass in a single PCR value as a dictionary or multiple PCR values as a list of dictionaries.

Another way is to use openssl using this command:

1
openssl s_client -connect ${cage-name}.${cage-app}.cages.evervault.com:443 | openssl x509 -noout -text | grep DNS:

You can then validate it locally using the ev-cage attest command in the CLI. This will validate that the PCRs in the cage.toml file match the PCRs returned in the handshake.

To get the attestation doc itself, you can expose the available endpoint. When running your Cage in debug mode, every value in the attestation document returned will be 0, so if you need to check the attestation measures do not use the --debug flag when initializing the Cage.

1
app.all("/attestation-doc", async (req, res) => {
2
try {
3
const result = await axios.post('http://127.0.0.1:9999/attestation-doc', req.body);
4
res.send({ ...result.data });
5
} catch (err) {
6
console.log("Could not retrieve doc.", err);
7
res.status(500).send({msg: "Error from within the cage!"})
8
}
9
});

Then make a request to the Cage using the below command.

1
curl -X POST \
2
-H "API-Key: <API_KEY>" \
3
https://<cage_name>.<app_uuid>.cages.evervault.com/attestation-doc -k

Utilizing Logging

To turn logging off or on, you will need to define trx_logging = true or trx_logging = false in your cage.toml. You can also leave it undefined and it will be turned on because the default state is set to true.

From there you should be able to see all of your calls to the Cage in the Cages Dashboard under Activity.

Screenshot of the Cages Activity Log

You can also find a unique ID from the request and can use that ID to search for a specific call in case you have many calls to look through.

Screenshot of the Cages Activity Log with ID

IP Addresses

There are two ways to see the original IP on the connection.

When using TLS Termination, the IP will be included in the HTTP X-Forwarded-For header.

If you have TLS Termination disabled, you can receive the client IP via proxy protocol by setting the FORWARD_PROXY_PROTOCOL environment variable in your Cage.