Member preview

Generating Kubernetes EncryptionConfig with Terraform

Enabling at-rest encryption for your Kubernetes Secrets is a fairly well-documented process:

https://www.twistlock.com/2017/08/02/kubernetes-secrets-encryption/

So, how you choose to manage the update to your `kube-apiserver` is up to you depending upon how you deploy your cluster (via systemd, or for example, if you deploy kube services in their own pods, etc.). However, in all implementations, it requires the use of an `EncryptionConfig` object which configures the encryption provider.

Automating the provider setup can be helpful in situations where you’re spinning up and down a lot of cluster, or are making regular updates to the cluster, and which to rotate your keys (which requires changing the secret in the config and then re-encrypting the secrets when the apiserver is restarted), so the last thing you probably want to be doing is running `head -c 32 /dev/urandom | base64 -i -` and then plugging in new values each time you do this (which would be annoying if you had to do it for multiple clusters on a regular basis).

Adding this step to your Terraform scripts can actually be handled by a resource type in Terraform, and an interpolation function to create a base64-encoded string that would actually get used in your Kubernetes resource definition.

Let’s assume you have handling (cloud-init, or other provisioning scripts) that manage the files once the server provisioning request has been sent to your provider, and focus on managing the template itself for the `/etc/kubernetes/secrets.conf` file passed to the apiserver in the ` — experimental-encryption-provider-config=` argument.

You could use a Terraform template:

https://www.terraform.io/docs/providers/template/d/file.html

which in our case, this might look like:

data "template_file" "secrets_conf" {
template = "${file("${path.module}/secretsconfig.tpl")}"
vars {
kube_secret = "WHATEVER}"
}
}

with a template like:

kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${kube_secret}
- identity: {}

So, rather than plugging in that value each time your manifest (or if you use a [module](https://www.terraform.io/docs/modules/index.html) to make this repeatable) is run, you can do something like this to create a resource from a `random_string` to the specification expected:

resource "random_string" "kube_secrets_secret" {
length = 32
special = false
}

and then update the template block to look like:

data "template_file" "secrets_conf" {
template = "${file("${path.module}/secretsconfig.tpl")}"
vars {
kube_secret = "${base64encode(random_string.kube_secrets_secret.result)}"
}
}

The `base64encode` function will return a base64-encoded string from the output of the `random_string.kube_secrets_secret` attribute `result`, which is the generated string that resource created and stored. From here, that template will populate the `kube_secret` with the base64-encoded string.