One of the neat features of the Aqua Security solution is the ability to inject secrets into the environment of a running container, so that they never get written to disk. In this article I’m setting up an Aqua installation on Azure, using Kubernetes as the orchestrator and HashiCorp Vault as the secrets store, so that I can try this secret injection.
Quickstart template for Kubernetes
Azure have a whole suite of quickstart templates set up that help you get started with some common tasks. There is also a nice walkthrough of setting up Kubernetes on Azure that’s easy to follow.
I had some issues around not having sufficient privileges in Active Directory to create a service principal. It turned out that these are different from the privileges you have within the Azure subscription, so you may need addition permissions to be added under the Azure Active Directory section of the Azure portal.
The Azure walkthrough gets you set up with kubectl
in your local terminal so that you can do things like kubectl create -f some-file.yaml
to deploy the entities described in that file to your Kubernetes cluster in Azure. It also shows you how to use kubectl proxy
so that you can browse the UI for your Kubernetes cluster.
Once I had Kubernetes set up I installed Aqua on the cluster — as that’s only available to customers I’ll gloss over the details, suffice it to say it’s simply a matter of deploying Aqua components as services and a daemonset through the usual sort of Kubernetes YAML files.
Installing Vault on Kubernetes
I used the current default Docker image, 0.7.0, by deploying this YAML file:
Most of this file is pretty self-explanatory if you’ve ever deployed anything on Kubernetes, but a couple of things bear pointing out:
- Adding the IPC_LOCK capability enables mlock for the vault executable. This is used to stop memory being swapped to disk (which we want, so that our secrets don’t get written to disk).
- In dev mode, for convenience you can specify a root token you’ll use for Vault as an environment variable.
This sets up Vault in its default development mode. At some point I’d like to set up Vault properly so that it can store secrets in Azure storage, but that gets quite involved quite quickly — you start having to worry about TLS certificates, unsealing the Vault and so on. I’m going to set that aside for another day!
Vault status
There may be other ways, but the way I found to get CLI access to my Vault was to exec a shell in the Vault pod as follows (use kubectl get pods
to find the pod name first):
To make client requests we’ll need a token, and for the purposes of this non-production deployment let’s just use the Vault root token (the same one that’s defined in the Vault YAML file above).
We can now run vault commands here, for example vault mounts
to list the available mount backends for storing secrets.
Accessing Vault from Aqua
Setting up a new key store in Aqua is really easy, because Kubernetes and service discovery means I can just refer to my Vault service by name rather than needing to find the IP address. Of course in a real deployment this should be HTTPS rather than HTTP, but I’m just using Vault in dev mode with no TLS connections for now.
For this example I used the same root token for the Vault Connection Token. In production you’d want to generate another token for this purpose.
The Secret Backend should be one of the backend mounts where Vault stores secrets. I have used the default ‘secret’ generic backend for this example. If this were production we might want to set up an Azure storage backend.
Storing a secret in Aqua
I can now set up secrets within the Aqua console, and they’ll get stored in Vault.
Those same secrets can be seen using the Vault CLI:
Using an Aqua secret in a Kubernetes deployment
I’ve got a very simple web service demo that uses a PostgreSQL database to store some very simple data. I’m going to pass the database password in as an Aqua secret as an example.
First I defined a secret called aqua-password that holds the value of the password. This is a toy example so it is OK for me to tell you that the password is “hello” as you can see in the code that sets up the database. (Obviously you should never write a real password into code like that, by the way!)
Here’s the whole YAML file for the web service — the secret is passed as an environment variable in the very last line.
The web service works! And as I know it must be reading and writing to the database, it must have the correct password.
But what’s really nice is that from the perspective of everything but the process running my web service, the environment variable actually has the value “{aqua-password}” and not the secret value “hello”. You can see this in the Kubernetes UI.
This means the secret value is never written to disk, making it a whole lot safer than storing it directly in an environment variable (which get written to /proc/<id>/environ
on the host). If I had set up a storage backend for Vault it would be written there, but only in encrypted form.