Azure Container Instances (ACI) and Secrets - Using Secure Environment Variables

In this post I'm talking about how you can use something called secureValue, or secure environment variables, with your Azure Container Instance. Given the sensitive nature of some data you put into the variables, it is a good idea to understand different ways to protect some of that data - with secure environment variables you can hide the values from the UI and API calls to the ACI, and from the Azure Portal.

Background

Running your applications in the cloud has become increasingly popular. Migrating your .NET applications to .NET Core and hosting them as Docker images in the cloud is pretty trivial, even for the more complex types of applications - I've done this a lot, and today .NET Core is so mature that there's no obvious reason to use the full fledged .NET Framework for any of my use cases.

When running your containers in Azure Container Instances, you sometimes pull in sensitive data, like a connection string to an Azure Storage Account, or other information that you shouldn't risk disclosing to any third part.

There's multiple ways to protect sensitive data. The other posts in this series discuss a few of the other options. Right now, we're checking out secure environment variables.

Considerations for Security

Using secure environment variables is great. It helps us protect snooping eyes, screenshots, someone browsing the Azure portal, you name it. But it does not inherently mean greater security of the values themselves - they're still exposed inside the container, so should your container or code be compromised, the environment variables are clearly accessible from inside the containers. This is something to be aware of, so the word "Secure" means you cannot read it after you create it, outside of the container; however it's still possible from within, for obvious reasons.

Another thing to note, which I've seen in the field (yikes), is when someone is defining their yaml files, they put the secure environment values inside the yaml file, but the file remains on disk or (oh no) in a code repository.

Creating secure environment variables in ACI

Creating environment variables is common for a lot of different types of applications. Sometimes, however, you want them to be treated with a bit more respect than the plain-text visible environment variables. This is where Secure value comes into play.

There's a couple of ways to accomplish this. The two main options I've used is the Azure CLI directly, or by creating the definition in yaml and then issue the create command from the CLI.

Let's explore both ways, and I'll use the same image and code as I used in my previous post - so if you want the source code, check out that repository here on GitHub.

Option 1: Secret environment variable with a YAML definition

Here's a sample definition file. Mine is called aci-demo-with-secure-variables.yaml:

apiVersion: 2018-10-01
name: aci-demo-app-with-secure-environmentvariables
location: westeurope
type: Microsoft.ContainerInstance/containerGroups
properties:
  osType: Linux
  restartPolicy: Always
  containers:
  - name: aci-demo-app-with-secure-enviromentvariables
    properties:
      image: acrdemomagic.azurecr.io/aci-demo-app:latest
      resources:
        requests:
          cpu: 2.0
          memoryInGB: 2.0
      environmentVariables:
        - name: 'AzureStorageAccountConnectionString'
          secureValue: 'YOUR_CONNECTION_STRING'
  imageRegistryCredentials:
  - server: acrdemomagic.azurecr.io
    username: YOUR_ACR_USERNAME
    password: YOUR_ACR_PASSWORD

The difference between a non-sensitive and sensitive value in this case is just whether you specify "secureValue" or "value" in front of the data. Then the system knows how to deal with it. Magic.

Things to note:

  • acrdemomagic.azurecr.io/aci-demo-app:latest: this is my docker image in ACR.
  • environmentVariables.AzureStorageAccountConnectionString: this is the name of the variable that my C# app will read.
  • environmentVariables.secureValue: the value of the variable, which will later be hidden.
  • imageRegistryCredentials: I'm using a private Azure Container Registry, so we have to specify the credentials to access it when we spin up the new image.

Next, let's prove that it works by creating this container group and container instance by shooting off the following command:

az container create -g demo -f aci-demo-with-secure-variables.yaml

It works to deploy this, and we can see that the secure environment variable is attached. It is displayed as any other environment variable, but there's no value being displayed:

Secure environment variable attached, but not displaying the value. Success!

Before we verify that it works, let's try to do the same thing but from the Azure CLI instead. Next up, command line only.

Option 2: Secret environment variables with the Azure CLI

If you're not a declarative type of person, that's cool too. Have no fear, because the CLI is here!

In my previous post I mentioned how to create an ACI and specify an Environment Variable. Now, the only difference is that you specify it slightly differently on the last part.

You can replace the -e parameter with --secure-environment-variables, which would then look like this:

az container create 
  -n aci-demo-app-with-secure-environmentvariables 
  -g demos 
  --image "acrdemomagic.azurecr.io/aci-demo-app:latest" 
  --registry-username "acrdemomagic" 
  --registry-password "YOUR_ACR_PASSWORD" 
  --secure-environment-variables  AzureStorageAccountConnectionString="YOUR_STORAGE_CONNECTION_STRING"

Creating the ACI with this should yield the same result as in the previous section when doing it with YAML, and it should be treated as a normal Environment Variable inside the container.

Verifying that the Secure environment variables are accessible inside the containers

At this point we want to verify that the instance of our container exist, is running and that we cannot see the value of the secure environment variable.

Let's do it both with the Azure CLI and in the portal, depending on which approach you prefer.

Show ACI using Azure CLI

az container show -g demos -n aci-demo-app-with-secure-environmentvariables
      "environmentVariables": [
        {
          "name": "AzureStorageAccountConnectionString",
          "secureValue": null,
          "value": null
        }
      ],

We can clearly see that the secretValue is null, and the value is null. Great - we cannot snoop from the outside and see the connection string now.

Show ACI using Azure Portal

If you're a UI person and want to check it out from the Azure Portal, that's okay too. Here's where we can list the variables for our ACI:

Empty values for the Environment Variable, because it's stored as a secret value.

Because it's a secret value, it's not displayed in the UI either. Success.

Show the logs of the container to verify that it works

If our app is running correctly, it should produce some log messages in the Logs section of the Container Instance page in the Azure Portal.

My C# .NET Core app is already built to use the storage connection string environment variable, and that it is of type secureValue doesn't matter to the underlying container - variable as variable, it's accessible inside the container - so with C# we can grab it just like before with no code change: Environment.GetEnvironmentVariable("YourVariableName").

If you want to display the logs directly from the CLI, see my previous post where I explain how you can do that. Search for az container attach.

Summary

We've successfully looked two ways to set secure environment variables on our Azure Container Instance, and have walked through these things:

  • Define secure environment variables in your yaml files.
  • Define secure environment variables in your az container CLI commands.
  • Verify that we cannot read the secure environment variables.
  • Verify that our C# app play along and can keep utilizing the Environment.GetEnvironmentVariable() methods without any code changes.

Enjoy.