Running Nginx in Nitro

Nginx is a good example of an application that would benefit from running in a Nitro Enclave. In a production environment, it needs to have access to sensitive data (the TLS certificate), and it can serve web applications that connect to other services. Being able to completely isolate and protect the Nginx application from malicious users (even those who might have elevated privileges to the parent instance) is very valuable.

In this example, we simplify the setup and ignore the TLS configuration, and instead focus on running Nginx as a HTTP server to learn how to configure the Nitro Enclave to allow HTTP clients to connect to Nginx, even though it is running in the Nitro Enclave.

The steps to run Nginx in an enclave are similar to the ones in the previous section, except that there a couple of new steps (indicated in bold):

  • identify a Docker image that contains an application,

  • convert the Docker image to an Enclave Image File (EIF),

  • configure the network interfaces on the parent instance

  • start the Anjuna Nitro Network proxy

  • start a Nitro Enclave using the previously create Enclave Image File.

For Nginx to be able to communicate with clients outside the enclave, the anjuna-nitro-userspace-netd-parent is used to forward the network traffic to/from the enclave through the only data-exchange interface available in Nitro, which is the VSOCK interface. Without this deamon, the enclave would have no way to send/receive data from clients that are not in the enclave.

On this virtual interface, the enclave is assigned with the same IP address as the Parent VM.

For the Parent VM to communicate with the enclave, on the parent host, the command localhost:80 would connect to the application listening on port 80 inside the enclave.

In this example, we will just start an Nginx server in the enclave, configured to listen on port 80 (HTTP), and we will verify that the Nginx server is running by sending a curl command from the parent instance.

Let’s get started!

Identify a Simple Docker Image

We will use the Official Nginx docker image on Dockerhub: https://hub.docker.com/_/nginx

Build an enclave image file (EIF)

Since the Docker image is already built and published in Dockerhub, we can just convert it into a Enclave Image File. The following command will automatically pull the Nginx Docker image from Dockerhub and convert it into an EIF.

$ anjuna-nitro-cli build-enclave --docker-uri nginx:latest --output-file nginx.eif

Start the Anjuna Nitro Network proxy

Run the following command:

/opt/anjuna/nitro/bin/anjuna-nitro-userspace-netd-parent --expose 80 --daemonize

This command is responsible for moving the network packets between the enclave and the parent instance, and must be running for the enclave to communicate with the outside world.

This command needs to be executed only once, unless the system is rebooted, or there is a need to expose a different set of ports, and in the latter case, you need to kill the current running process and run it again with the new set of ports.

Start the Nitro Enclave

$ anjuna-nitro-cli run-enclave --cpu-count 2 --memory 1024 --eif-path nginx.eif --debug-mode

Verify that Nginx is running

$ curl localhost:80

You should see the following output:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Congratulations! Nginx is running and is accessible from outside the Nitro Enclave.

Exposing the Nitro Enclave to external hosts

In the previous section, you started a Nitro Enclave running Nginx and connected to it from the parent instance, using the IP address localhost.

In a typical scenario, Nginx should be reachable from other hosts:

  • If the Nitro EC2 instance is on a AWS VPC, other hosts on this VPC might try to connect to Nginx.

  • This Nitro EC2 instance might be reachable from the internet (your web browser for example).

This is only an example for going through this tutorial. In most cases, you should never expose the port 80 to serve HTTP content, and you should use TLS on port 443. In that case, the command would become:

$ /opt/anjuna/nitro/bin/anjuna-nitro-userspace-netd-parent --expose 443 --daemonize

If your application exposes multiple ports (for example port 80 and port 8080), you can use the --expose parameter multiple times.

After you exposed port 80, you can terminate and restart the Nitro Enclave running Nginx, and it will become reachable through the host’s external IP address.

$ anjuna-nitro-cli terminate-enclave --all
$ anjuna-nitro-cli run-enclave --cpu-count 2 --memory 1024 --eif-path nginx.eif --debug-mode

Run the following command on another host (replace <nitro-instance-ip> with the actual IP address of your Nitro instance):

$ curl <nitro-instance-ip>:80
You might need to update the security rules (inbound rules) to allow such traffic to be accepted by the Nitro host.