Running Nginx in an Anjuna Confidential Container

In this section, you will run a simple confidential container in a secure enclave with the Anjuna CLI. Starting from the Docker container for Nginx, you will configure and run an Anjuna Confidential Container, verify it is using AMD SEV, and inspect its measured boot values.

The following basic steps are needed to start an Anjuna Confidential Container:

  1. Identify a Docker image that contains an application

  2. Build the Docker image into a CSP compatible disk image for an Anjuna Confidential Container

  3. Upload the disk image to the CSP’s storage

  4. Start an Anjuna Confidential Container using the previously created disk image

Using the Anjuna CLI, you can automatically start existing containers in an Azure Confidential VM. This documentation refers to these as Anjuna Confidential Containers.

Depending on the application, these additional steps may be needed:

  • Configure the network and firewall rules for the Anjuna Confidential Container

  • Check the SEV attestation report and Measured Boot output to ensure that the Confidential VM has not been tampered with

In this section, you will learn about the basic usage of the Anjuna CLI to build and run an Anjuna Confidential Container.

Identify a Simple Docker Image

You will use the official Nginx Docker image to start a simple web server as an Anjuna Confidential Container.

To simplify the setup in this example, there is no TLS configuration. Instead, you will run Nginx as an HTTP server and learn how to configure the Anjuna Confidential Container to allow HTTP clients to connect to Nginx.

Build the Anjuna Confidential Container disk image

The Anjuna CLI works with unmodified Docker images. In this example, the Anjuna CLI will pull the Nginx Docker image from the public Docker Hub. If you are using a private Docker registry, use the docker login command to authenticate to your registry before running the following command.

  • Microsoft Azure

$ anjuna-azure-cli disk create --docker-uri=nginx:latest --disk-size 20G

This command will create a disk image file named disk.vhd in the current working directory.

The disk image contains a bootloader that starts a Linux kernel that boots directly into the Nginx application, as defined by the ENTRYPOINT and CMD parameters of the Docker container.

All the dependencies needed to run Nginx are included in the disk image, so when the Confidential VM starts, it will not need to download the Docker image.

Upload the disk image

In order to create an Anjuna Confidential Container, the disk image must be uploaded to the cloud provider first. This may require pre-existing cloud resources.

Disk upload prerequisites

  • Microsoft Azure

This section requires being authenticated to your Microsoft Azure subscription with the Azure CLI (az). See Install and authenticate to your cloud provider’s CLI for reference.

The following command displays your account and subscription, then sets an environment variable to the subscription for later use. If you have multiple subscriptions, you will need to select the correct one (use az account list to see your accounts).

$ az account show
$ export MY_AZURE_SUBSCRIPTION=$(az account show -o json | jq -r .id)

You will need to have the following resources ready before you issue the anjuna-azure-cli disk upload command. The anjuna-azure-cli command does not create the resources.

  • Resource Group: This command will create a resource group called myResourceGroup

    $ az group create --name myResourceGroup --location eastus
  • Storage Account: The command below creates a storage account named anjunaquickstart<SUFFIX> in the resource group myResourceGroup, with public access disabled to the blobs or containers in the account.

    $ export RANDOM_SUFFIX=$(cat /dev/urandom | LC_ALL=C tr -dc '[:lower:][:digit:]' | head -c 5)
    $ export STORAGE_ACCOUNT_NAME="anjunaquickstart${RANDOM_SUFFIX}"
    $ az storage account create \
                --resource-group myResourceGroup \
                --allow-blob-public-access false \
                --name ${STORAGE_ACCOUNT_NAME}
  • Storage Container: A storage container is similar to a directory and is used to organize data blobs. It is created inside a storage account. Hierarchically,

    resource group storage account storage container blob (disk)

    The command shown below creates a storage container called mystoragecontainer in the storage account from above:

    $ az storage container create \
                --name mystoragecontainer \
                --account-name ${STORAGE_ACCOUNT_NAME} \
                --resource-group myResourceGroup
  • Azure Compute (Shared Image) Gallery: Image galleries are used to organize and share OS images and applications.

    The command below will create a gallery called myGallery in the resource group called myResourceGroup.

    $ az sig create --resource-group myResourceGroup --gallery-name myGallery

    Refer to Creating a Compute Gallery for more details.

  • Image Definition: Image definitions are a logical grouping for versions of an image. Hierarchically,

    resource group image gallery image definition image (for the confidential container)

    The command below will create an image definition called myFirstDefinition in myGallery. This document explains the options used in the command.

    $ az sig image-definition create \
           --resource-group myResourceGroup \
           --gallery-name myGallery \
           --gallery-image-definition myFirstDefinition \
           --publisher Anjuna \
           --offer CVMGA \
           --os-type Linux \
           --sku AnjGALinux \
           --os-state specialized \
           --features SecurityType=ConfidentialVMSupported \
           --hyper-v-generation V2 \
           --architecture x64

    Anjuna requires the following settings. The other parameters for the definition can be configured as needed.

    Architecture: "x64"
    Features: {
        SecurityType: "ConfidentialVmSupported"
    }
    HyperVGeneration: "V2"
    OsState: "Specialized"
    OsType:  "Linux"

After you have created the necessary cloud resources, you can upload the disk image.

  • Microsoft Azure

This command uploads the local disk image to an Azure Storage Container and creates a shared image in a Compute Gallery. The shared image is saved as an Image Version of a pre-existing Image Definition.

$ anjuna-azure-cli disk upload \
  --disk disk.vhd \
  --image-name nginx-disk.vhd \
  --storage-account ${STORAGE_ACCOUNT_NAME} \
  --storage-container mystoragecontainer \
  --resource-group myResourceGroup \
  --image-gallery myGallery \
  --image-definition myFirstDefinition \
  --image-version 0.1.0 \
  --location eastus \
  --subscription-id ${MY_AZURE_SUBSCRIPTION}

Create a network with the proper firewall rules

You will need to update the Anjuna Confidential Container’s cloud network configuration to make it reachable through the internet from your management host.

  • Microsoft Azure

The example below shows one way to create network resources in order to be able to communicate using TCP over port 80. It also attaches a public IP address to the Network Interface called myNic.

$ # Replace these with your own values as needed
$ export RESOURCE_GROUP_NAME="myResourceGroup"
$ export LOCATION="eastus"
$ export VNET_NAME="myVnet"
$ export SUBNET_NAME="mySubnet"
$ export NSG_NAME="myNSG"
$ export PUBLIC_IP_NAME="myPublicIP"
$ export NIC_NAME="myNic"

$ # Create a Virtual Network
$ az network vnet create --resource-group ${RESOURCE_GROUP_NAME} --location ${LOCATION} --name ${VNET_NAME} --address-prefix 10.0.0.0/16

$ # Create a Subnet
$ az network vnet subnet create --resource-group ${RESOURCE_GROUP_NAME} --vnet-name ${VNET_NAME} --name ${SUBNET_NAME} --address-prefixes 10.0.0.0/24

$ # Create a Network Security Group
$ az network nsg create --resource-group ${RESOURCE_GROUP_NAME} --name ${NSG_NAME}

$ # Create a Security Rule allowing TCP traffic over port 80
$ az network nsg rule create --resource-group ${RESOURCE_GROUP_NAME} --nsg-name ${NSG_NAME} --name Allow-80 --protocol Tcp --direction Inbound --priority 1000 --source-address-prefix '*' --source-port-range '*' --destination-address-prefix '*' --destination-port-range 80 --access Allow

$ # Associate the Network Security Group with the Subnet
$ az network vnet subnet update --resource-group ${RESOURCE_GROUP_NAME} --vnet-name ${VNET_NAME} --name ${SUBNET_NAME} --network-security-group ${NSG_NAME}

$ # Create a Public IP address
$ az network public-ip create --resource-group ${RESOURCE_GROUP_NAME} --name ${PUBLIC_IP_NAME} --sku Standard --allocation-method Static

$ # Create a Network Interface with the Public IP address
$ az network nic create --resource-group ${RESOURCE_GROUP_NAME} --name ${NIC_NAME} --vnet-name ${VNET_NAME} --subnet ${SUBNET_NAME} --network-security-group ${NSG_NAME} --public-ip-address ${PUBLIC_IP_NAME}

Controlling log access

The Anjuna CLI for SEV supports cloud logging, which may need additional configuration.

  • Microsoft Azure

Currently, the Anjuna CLI for SEV on Azure supports logging to the Azure Serial Console.

The Anjuna CLI already handles the configuration required for the Anjuna Confidential Container to write logs to the Azure Serial Console. Additionally, when the --storage-account parameter is provided to the instance create command, the Azure boot diagnostics are written to that storage account.

Start the Anjuna Confidential Container

Run the following command to start the Anjuna Confidential Container on a new instance.

  • Microsoft Azure

The following command uses the cloud resources created in Disk upload prerequisites. You may need to change the parameter values if you chose different names.
$ export INSTANCE_NAME=anjuna-azure-nginx-instance
$ anjuna-azure-cli instance create \
  --name ${INSTANCE_NAME} \
  --location eastus \
  --image-gallery myGallery \
  --image-definition myFirstDefinition \
  --image-version 0.1.0 \
  --resource-group myResourceGroup \
  --storage-account ${STORAGE_ACCOUNT_NAME} \
  --nics myNic

Verify that the Nginx Confidential VM is running

Once the instance create command completes, your Anjuna Confidential Container will be running. You can run the following command to see its status:

  • Microsoft Azure

$ anjuna-azure-cli instance describe \
  --name ${INSTANCE_NAME} \
  --resource-group myResourceGroup

The command should produce output similar to this:

Name                         ResourceGroup       PowerState      PublicIps         Fqdns    Location    Zones
---------------------------  ------------------  --------------  ---------------  -------  ----------  -------
anjuna-azure-nginx-instance  myResourceGroup     VM Running      172.171.202.232           eastus

Verify that Nginx is running

Using the IP address of the Anjuna Confidential Container, make a request to Nginx:

  • Microsoft Azure

$ export IP_ADDRESS=$(anjuna-azure-cli instance describe \
    --resource-group myResourceGroup --name ${INSTANCE_NAME} \
    --json --query publicIps | grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | awk 'NR==1')
$ curl ${IP_ADDRESS}:80

You should see HTML output similar to the following:

<!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 Anjuna Confidential Container.

View application logs

Once the application is running, you can view its logs using the Anjuna CLI.

  • Microsoft Azure

You can tail the application logs with the following command:

$ anjuna-azure-cli instance log --tail \
  --name ${INSTANCE_NAME} \
  --resource-group myResourceGroup

Terminate the Anjuna Confidential Container

  • Microsoft Azure

To terminate the Azure Confidential VM, run the following command:

$ anjuna-azure-cli instance delete --name ${INSTANCE_NAME} --resource-group myResourceGroup