v1alpha1

package
v5.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 13, 2023 License: MIT Imports: 16 Imported by: 0

README

vault

This package contains any CRDs and webhooks GoCardless use to interact with Vault. At present, the only thing we provide is a webhook to automatically configure Kubernetes pods with access to Vault secrets.

Summary

GoCardless use Vault to manage application environment variable secrets. We happen to build software in a variety of languages, and make use of process environment variables as a language agnostic method of injecting configuration material.

We needed a way to inject secrets from Vault into application environment variables without requiring developers to write code to speak directly to Vault. Besides the effort involved in writing that interaction, and the risk involved in doing it incorrectly, there's no guarantee each language would be well supported by the Vault ecosystem.

Instead, we've built a webhook that listens for pods with an annotations like:

secrets-injector.vault.crd.gocardless.com/configs: app:config/env.yaml

When our webhook sees this annotation, it tries configuring the app container to pull secrets from Vault, using the configuration from the config/env.yaml file within the container. It resolves these secrets and sets them as environment variables, finally running the original container process.

The webhook makes use of the theatre-secrets command to perform an authentication dance with Vault. Once we've acquired a Vault token, we translate our simple configuration file format into a Hashicorp [secrets][secrets] config file, then use secrets to perform the fetching and lease-management of the secret values.

Configuring Vault

For this authentication flow to work, we expect Vault to be configured with a Kubernetes authentication backend that points at the Kubernets API server. The authentication exchange works as follows:

  • Pods attempt a login with Vault using their service account token
  • Vault receives a service account token, and uses this token to perform a token review request against the API server configured on this auth backend. If the request succeeds, we know the token is valid, and we permit the login

The theatre-secrets acceptance tests verify this flow against a Vault server. If anything is unclear, look at the Prepare method for how we configure the test Vault server.

How does the webhook work

Once installed, the webhook will listen for containers with a specific annotation:

---
apiVersion: v1
kind: Pod
metadata:
  name: app
  annotations:
    "secrets-injector.vault.crd.gocardless.com/configs": "app"
    "envconsul-injector.vault.crd.gocardless.com/configs": "app"
spec:
  containers:
    - name: app
      command:
        - env

Currently we still support the now deprecated envconsul-injector annotation as well. The two should not be used together.

---
apiVersion: v1
kind: Pod
metadata:
  name: app
  annotations:
    "envconsul-injector.vault.crd.gocardless.com/configs": "app"
spec:
  containers:
    - name: app
      command:
        - env

We will modify this pod to do the following...

1. Install binaries

Add an init container that installs the theatre-secrets tool into a temporary installation path volume. We use a default storage medium. This installation volume will be mounted into any of the containers that are targeted by the secrets-injector.vault.crd.gocardless.com/configs annotation. In our example, this means the app container is the only target.

---
apiVersion: v1
kind: Pod
metadata:
  name: app
  annotations:
    "secrets-injector.vault.crd.gocardless.com/configs": "app"
spec:
  initContainers:
    - name: theatre-secrets-injector
      image: theatre:latest
      imagePullPolicy: IfNotPresent
      command:
        - theatre-secrets
        - install
        - --path
        - /var/run/theatre
      volumeMounts:
        - mountPath: /var/run/theatre
          name: theatre-secrets-install
  containers:
    - name: app
      command:
        - env
  volumes:
    - name: theatre-secrets-install
      emptyDir: {}
2. Add service account volume

Instead of relying on default Kubernetes service account volume mounts, we make use of the projected service account volume mounts. Unlike default tokens, these are managed by the kubelet and automatically rotated at regular intervals, and whenever the pod is destroyed.

We mount the emphemeral token at /var/run/secrets/kubernetes.io/vault. In terms of additional pod configuration, this means we add a projected volume and volume-mount to any targeted containers:

---
apiVersion: v1
kind: Pod
metadata:
  name: app
  annotations:
    "secrets-injector.vault.crd.gocardless.com/configs": "app"
spec:
  initContainers: ...
  containers:
    - name: app
      command:
        - env
      volumeMounts:
        - name: theatre-secrets-serviceaccount
          mountPath: /var/run/secrets/kubernetes.io/vault
  volumes:
    - name: theatre-secrets-install
      emptyDir: {}
    - name: theatre-secrets-serviceaccount
      projected:
        sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 900
3. Prepend theatre-secrets inject

The container must resolve secrets before we run the original command. We use theatre-secrets to perform the resolution, then exec the original container command. The application container has access to the theatre binaries via the init container, having installed them in the theatre-secrets-install volume:

---
apiVersion: v1
kind: Pod
metadata:
  name: app
  annotations:
    "secrets-injector.vault.crd.gocardless.com/configs": "app"
spec:
  initContainers: ...
  containers:
    - name: app
      command:
        - /var/run/theatre/theatre-secrets
      args:
        - exec
        - --vault-address=http://vault.vault.svc.cluster.local:8200
        - --vault-path-prefix=secret/data/kubernetes/project/namespace/app
        - --install-path=/var/run/theatre
        - --service-account-token-file=/var/run/secrets/kubernetes.io/vault/token
        - --
        - env
      volumeMounts:
        - name: theatre-secrets-install
          mountPath: /var/run/theatre
        - name: theatre-secrets-serviceaccount
          mountPath: /var/run/secrets/kubernetes.io/vault
  volumes:
    - name: theatre-secrets-install
      emptyDir: {}
    - name: theatre-secrets-serviceaccount
      projected:
        sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 900

Documentation

Index

Constants

View Source
const EnvconsulInjectorFQDN = "envconsul-injector.vault.crd.gocardless.com"
View Source
const SecretsInjectorFQDN = "secrets-injector.vault.crd.gocardless.com"

Variables

Functions

This section is empty.

Types

type SecretsInjector

type SecretsInjector struct {
	// contains filtered or unexported fields
}

func NewSecretsInjector

func NewSecretsInjector(c client.Client, logger logr.Logger, opts SecretsInjectorOptions, scheme *runtime.Scheme) *SecretsInjector

func (*SecretsInjector) Handle

func (i *SecretsInjector) Handle(ctx context.Context, req admission.Request) (resp admission.Response)

func (*SecretsInjector) InjectDecoder

func (e *SecretsInjector) InjectDecoder(d *admission.Decoder) error

type SecretsInjectorOptions

type SecretsInjectorOptions struct {
	Image                       string           // image of theatre to use when constructing pod
	InstallPath                 string           // location of vault installation directory
	NamespaceLabel              string           // namespace label that enables webhook to operate on
	VaultConfigMapKey           client.ObjectKey // reference to the vault config configMap
	ServiceAccountTokenFile     string           // mount path of our projected service account token
	ServiceAccountTokenExpiry   time.Duration    // Kubelet expiry for the service account token
	ServiceAccountTokenAudience string           // optional token audience
	Timeout                     time.Duration    // timeout to use when reading secrets from Vault
	Debug                       bool             // whether to enable debug mode for verbose loggging
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL