A more complex example

In this section, you will see how to configure NGINX, a popular HTTP server, to run in a secure enclave managed by the Anjuna SGX Runtime. Using the Anjuna SGX Runtime, you can ensure that sensitive data used by the NGINX server are protected by strong encryption.

This section assumes that you are using Ubuntu 18.04, and that OpenSSL, NGINX, and the Anjuna SGX Runtime are installed and configured.

To ensure that OpenSSL is installed, execute the following command:

$ sudo apt install openssl

For instructions on how to install NGINX, see the NGINX home page. For information about installing the Anjuna SGX Runtime, see Getting the Anjuna SGX Runtime.

Stop NGINX

The installation of NGINX automatically starts the server process. In this section you will instead start the process manually from a secure enclave managed by the Anjuna SGX Runtime. To avoid conflicts between the manual process and the one started automatically by the installer, you must first stop the automatically-configured server.

First confirm that NGINX is properly installed by executing the following command:

$ nginx -v

If NGINX is installed, then the command prints the installed version. If not, then it prints an error message.

Next, to ensure that the automatically-configured server is not running, execute the following command:

$ sudo service nginx stop

Set up NGINX

Next you will configure NGINX to support TLS. TLS enables clients to connect securely to NGINX without fear of eavesdroppers stealing their credentials or other sensitive data. After you configure NGINX to support TLS, you will then configure the Anjuna SGX Runtime to run NGINX inside a secure enclave so that even insiders with privileged access to the server cannot steal its secrets.

Create a working directory

Create a directory for your work using the following command:

$ mkdir -p nginx-demo/{certs,html,run,log}
$ cd nginx-demo

Generate a self-signed certificate

Normally you would configure NGINX for TLS using some enterprise PKI infrastructure or, in a small deployment, using an automated certificate service like Let’s Encrypt. For the sake of simplicity, you will instead generate a self-signed certificate. Note that this approach is not suitable for production deployments, but it differs from the recommended approach only in the details of obtaining a certificate. Once you have the certificate, the remaining configuration steps are the same as a recommended configuration.

Generate certificates to be used with NGINX:

$ openssl req -new -days 720 -newkey rsa:2048 -nodes -keyout certs/server.key -out certs/server.csr -subj "/C=US/ST=California/L=Palo Alto/O=Anjuna/OU=tutorial/CN=nginx.local.test"
$ openssl x509 -in certs/server.csr -out certs/server.crt -req -signkey certs/server.key -days 720

Configure DNS for testing

Modify your host’s /etc/hosts file to provide a network route to the Common Name that you used to generate the certificates:

$ echo "127.0.0.1 nginx.local.test" | sudo tee -a /etc/hosts

Create the NGINX configuration

NGINX takes its configuration from a file. Create a file in the working directory named nginx.conf. Populate it with the following text:

pid run/nginx.pid;
daemon off;
worker_processes 1;
master_process off;
events {
    worker_connections 768;
}
error_log $NGINX_DEMO_DIRECTORY_ABS_PATH/log/error.log;
http {
    server {
        listen 9443 ssl;
        root $NGINX_DEMO_DIRECTORY_ABS_PATH/html;
        ssl_certificate $NGINX_DEMO_DIRECTORY_ABS_PATH/certs/server.crt;
        ssl_certificate_key $NGINX_DEMO_DIRECTORY_ABS_PATH/certs/server.key;
        ssl_protocols TLSv1.2 TLSv1.3;
    }
    ##
    # Basic Settings
    ##
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    ##
    # SSL Settings
    ##
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;
    ##
    # Logging Settings
    ##
    access_log $NGINX_DEMO_DIRECTORY_ABS_PATH/log/access.log;
    error_log $NGINX_DEMO_DIRECTORY_ABS_PATH/log/error.log;
    ##
    # Gzip Settings
    ##
    gzip on;
}

Replace the $NGINX_DEMO_DIRECTORY_ABS_PATH with the absolute path to your nginx-demo working directory. You can run this envsubst command:

$ export NGINX_DEMO_DIRECTORY_ABS_PATH=$PWD
$ envsubst '$NGINX_DEMO_DIRECTORY_ABS_PATH' < nginx.conf > nginx.conf.tmp
$ mv nginx.conf.tmp nginx.conf

Create a home page

Now create a home page for your NGINX server to serve.

Create a file at html/index.html and populate it with the following text:

<html>
    <head>
        <title>Secured Page</title>
    </head>
    <body>
        <h1>Welcome to this Secured Nginx Page!</h1>
        <p>This page is secured using the Anjuna Runtime</p>
    </body>
</html>

Create the Anjuna SGX Runtime manifest

Just as you did in the simpler example in Configuring a simple application, you will use anjuna-sgxrun to generate a manifest template file, and then edit it to configure the secure enclave to run NGINX.

First, use anjuna-sgxrun to create a manifest template:

$ anjuna-sgxrun --setup nginx

This command will create a file named nginx.manifest.template.yaml in your working directory.

Next, edit the manifest template to tell the Anjuna SGX Runtime to automatically encrypt the home page and the server key that you created with OpenSSL. Edit nginx.manifest.template.yaml and add the following entries:

keys:
- id: input_encryption_key
  source: enclave_generated
encrypted_files:
- path: $NGINX_DEMO_DIRECTORY_ABS_PATH/html/index.html
  key: input_encryption_key
- path: $NGINX_DEMO_DIRECTORY_ABS_PATH/certs/server.key
  key: input_encryption_key

Replace the text $NGINX_DEMO_DIRECTORY_ABS_PATH with the absolute path to your nginx-demo working directory using the following envsubst command:

$ export NGINX_DEMO_DIRECTORY_ABS_PATH=$PWD
$ envsubst '$NGINX_DEMO_DIRECTORY_ABS_PATH' < nginx.manifest.template.yaml > nginx.manifest.template.yaml.tmp
$ mv nginx.manifest.template.yaml.tmp nginx.manifest.template.yaml

Encrypt sensitive files

Next you will use anjuna-sgxrun to encrypt the files that you added to the manifest template so that only the NGINX process can read them, and only when it runs in its secure enclave.

Use the following command to create the provisioning key that you will use to encrypt sensitive files:

$ anjuna-sgxrun --provision nginx

anjuna-sgxrun creates the files provision/nginx.pubkey and provision/nginx.quote.bin. You can use provision/nginx.pubkey to encrypt files so that only the protected NGINX process can read them. You can use provision/nginx.quote.bin to verify whether a key belongs to the NGINX secure enclave.

If the manifest template changes, this will invalidate the previous provisioning key and quote, which causes errors like Failed to load provisioning key. After changing the manifest template, you should re-run anjuna-sgxrun --provision and all commands that used the previous provisioning key and quote.

Use anjuna-check-attestation now to verify that provision/nginx.pubkey in fact belongs to the NGINX enclave:

$ anjuna-check-attestation --quote-file provision/nginx.quote.bin provision/nginx.pubkey

Next, use anjuna-encrypt to encrypt the home page and the server key:

$ anjuna-encrypt --public-key provision/nginx.pubkey ./html/index.html -o ./html/index.html
$ anjuna-encrypt --public-key provision/nginx.pubkey ./certs/server.key -o ./certs/server.key

You can confirm that the home page and the server key are encrypted using the hexdump tool. For example:

$ hexdump -n 100 -C certs/server.key

A file encrypted by the Anjuna SGX Runtime starts with the characters ANJUNAFS.

Run NGINX

Now you can use anjuna-sgxrun to run the NGINX server with the configuration you created in nginx.conf:

$ anjuna-sgxrun /usr/sbin/nginx -p ./ -c nginx.conf

The Anjuna SGX Runtime reads the secure enclave configuration from the manifest that you created. Because the manifest lists the server key and the home page as encrypted files, the secure enclave will automatically decrypt them when reading them.

In the output log, you may see the following log message: nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied). This is an informational log message which refers to a hard-coded file path, even though you configured a different error_log in nginx.conf.

The server is still running and you should continue. In NGINX versions v1.19.5 and higher, you can add the flag -e log/error.log to suppress the log message.

Connect to NGINX

In a new terminal, use curl to connect to the running NGINX server. Make sure that your curl command uses the right pathname for the server certificate that you obtained in the section Generate a self-signed certificate, above.

$ curl --cacert nginx-demo/certs/server.crt https://nginx.local.test:9443/index.html

curl requests the home page from the NGINX server over an HTTPS connection using TLS. The NGINX server fetches index.html, relying on the Anjuna SGX Runtime to automatically decrypt it inside the secure enclave as it is read. NGINX then returns the home page data over the secure TLS connection to curl.

The home page never exists in clear text on the server’s disk or in its memory. The automatic decryption takes place entirely within the secure enclave, and so it remains encrypted to any process other than the protected NGINX server, even to privileged processes running on the host.