Skip to content

Commit

Permalink
feat(vai): add vault agent injector (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
FalcoSuessgott authored Oct 27, 2023
1 parent 0adc132 commit 68e2ed3
Show file tree
Hide file tree
Showing 16 changed files with 341 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ new-lab: ## creates a new lab directory
touch $(name)/output/.gitkeep
mkdir -p $(name)/templates
mkdir -p $(name)/files
echo "$(name)" > docs/$(name).md
echo "$(name)" > docs/$(name).md
143 changes: 143 additions & 0 deletions docs/vai.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Vault Agent Injector

## Requirements
For this lab youre going to need `kubectl`, `helm` and `jq` installed.

Also in your `terraform.tfvars`:
```
# terraform.tfvars
minikube = {
enabled = true
vault_agent_injector = true
}
```

You then can bootstrap the cluster using `make bootstrap`


## Overview
The following resources will be created:

1. The Vault Agent Injector Helm Chart is going to be installed in the `vai` Namespace.
2. A Kubernetes Auth Role `vai` bound to the `vai` Namespace & Service Account
3. KVv2 Secrets under `vai/secrets` containing 2 Example Secrets
4. A policy (`vai`) that allows reading `/vai/secrets` Secrets
5. A Demo App `kuard` is deployed wiht annotations that trigger the Vault Agent Injector to inject the secrets

## Walkthrough
The Vault Agenjt Injector (vai) is going to be installed in the `vai` namespace using the [Helm Chart](https://github.com/hashicorp/vault-helm).

```bash
$> helm list -n vai
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
vai vai 1 2023-10-05 16:32:06.04091193 +0200 CEST deployed external-secrets-0.9.5 v0.9.5
```

Additionally, a Vault Kubernetes Auth Role bounded to the Namespace and the vai Service Account has been created:

```bash
# https://localhost/ui/vault/access/minikube-cluster/item/role/vai
$> vault read auth/minikube-cluster/role/vai
Key Value
--- -----
alias_name_source serviceaccount_uid
bound_service_account_names [default] # valid SA names
bound_service_account_namespaces [vai] # valid namespaces
token_bound_cidrs []
token_explicit_max_ttl 0s
token_max_ttl 0s
token_no_default_policy false
token_num_uses 0
token_period 0s
token_policies [vai] # attached policies
token_ttl 1h
token_type default
```

Also KVv2 Secrets under `/vai/secrets/` have been created:

```bash
# https://localhost/ui/vault/secrets/vai/kv/secrets/details?version=1
$> vault kv get vai/secrets
== Secret Path ==
vai/data/secrets

======= Metadata =======
Key Value
--- -----
created_time 2023-10-05T11:58:36.987982616Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1

====== Data ======
Key Value
--- -----
password P@ssw0rd
username Admin
```

A corresponding policy `vai` that allows reading the vai secrets has also been crated:

```bash
# https://localhost/ui/vault/policy/acl/vai
$> vault policy read vai
path "vai/" {
capabilities = ["read", "list"]
}

path "vai/*" {
capabilities = ["read", "list"]
}
```

A Demo App with annotations telling VAI to inject secrets:

```bash
$> cat k8s-vault-agent-injector/files/kuard.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kuard
namespace: vai
spec:
selector:
matchLabels:
app: kuard
replicas: 1
template:
metadata:
annotations:
# https://developer.hashicorp.com/vault/docs/platform/k8s/injector/annotations
vault.hashicorp.com/auth-path: "auth/minikube-cluster"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "vai"
vault.hashicorp.com/tls-secret: ca-cert
vault.hashicorp.com/ca-cert: /vault/tls/ca.crt
vault.hashicorp.com/agent-inject-secret-secrets.txt: 'vai/data/secrets'
vault.hashicorp.com/agent-inject-template-secrets.txt: |
{{- with secret "vai/data/secrets" -}}
{
"username": "{{ .Data.data.username }}",
"password": "{{ .Data.data.password }}"
}
{{- end }}
labels:
app: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
imagePullPolicy: Always
name: kuard
ports:
- containerPort: 8080
```

Finally, the Secret containing the KVv2 Secrets from `/vai/secrets/` are injected into the FS of the Container:

```bash
$> kubectl exec -n vai -it $(kubectl get pods -l=app=kuard -n vai --no-headers -o custom-columns=":metadata.name") -- cat /vault/secrets/secrets.txt
username=Admin
password=P@ssw0rd
```
Empty file removed k8s-minikube/.gitkeep
Empty file.
36 changes: 36 additions & 0 deletions k8s-vault-agent-injector/files/kuard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: kuard
namespace: vai
spec:
selector:
matchLabels:
app: kuard
replicas: 1
template:
metadata:
annotations:
# https://developer.hashicorp.com/vault/docs/platform/k8s/injector/annotations
vault.hashicorp.com/auth-path: "auth/minikube-cluster"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "vai"
vault.hashicorp.com/tls-secret: ca-cert
vault.hashicorp.com/ca-cert: /vault/tls/ca.crt
vault.hashicorp.com/agent-inject-secret-secrets.txt: 'vai/data/secrets'
vault.hashicorp.com/agent-inject-template-secrets.txt: |
{{- with secret "vai/data/secrets" -}}
{
"username": "{{ .Data.data.username }}",
"password": "{{ .Data.data.password }}"
}
{{- end }}
labels:
app: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
imagePullPolicy: Always
name: kuard
ports:
- containerPort: 8080
7 changes: 7 additions & 0 deletions k8s-vault-agent-injector/files/values.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://github.com/hashicorp/vault-helm/blob/main/values.yaml
server:
enabled: false

injector:
enabled: true
externalVaultAddr: "https://host.minikube.internal"
7 changes: 7 additions & 0 deletions k8s-vault-agent-injector/files/vault-policy.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
path "vai/" {
capabilities = ["read", "list"]
}

path "vai/*" {
capabilities = ["read", "list"]
}
5 changes: 5 additions & 0 deletions k8s-vault-agent-injector/terraform/kuard.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "kubectl_manifest" "kuard" {
yaml_body = file("${path.module}/../files/kuard.yml")

depends_on = [helm_release.vai]
}
35 changes: 35 additions & 0 deletions k8s-vault-agent-injector/terraform/vai.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Install VAI via Helm
resource "helm_release" "vai" {
name = "vai"
repository = "https://helm.releases.hashicorp.com"
chart = "vault"
namespace = "vai"
create_namespace = true

values = [file("${path.module}/../files/values.yml")]
}

resource "kubernetes_secret" "ca_cert" {
metadata {
name = "ca-cert"
namespace = helm_release.vai.namespace
}

data = {
"ca.crt" = var.ca_cert
}
}

# Create a SA Secret for default SA
resource "kubernetes_secret" "sa_secret" {
metadata {
annotations = {
"kubernetes.io/service-account.name" = "default"
}
namespace = helm_release.vai.name
generate_name = "${helm_release.vai.name}-${helm_release.vai.chart}-token-"
}

type = "kubernetes.io/service-account-token"
wait_for_service_account_token = true
}
11 changes: 11 additions & 0 deletions k8s-vault-agent-injector/terraform/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "secrets" {
type = map(string)
default = {
username = "Admin"
password = "P@ssw0rd"
}
}

variable "ca_cert" {
type = string
}
28 changes: 28 additions & 0 deletions k8s-vault-agent-injector/terraform/vault.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
resource "vault_mount" "vai" {
path = "vai"
type = "kv"
options = { version = "2" }
description = "Secrets read by Vault Secrets Operator"
}

resource "vault_kv_secret_v2" "vai" {
mount = vault_mount.vai.path
name = "secrets"
delete_all_versions = true
data_json = jsonencode(var.secrets)
}

resource "vault_policy" "vai" {
name = "vai"

policy = file("${path.module}/../files/vault-policy.hcl")
}

resource "vault_kubernetes_auth_backend_role" "vai" {
backend = "minikube-cluster"
role_name = helm_release.vai.name
bound_service_account_names = ["default"]
bound_service_account_namespaces = [helm_release.vai.namespace]
token_ttl = 3600
token_policies = [vault_policy.vai.name]
}
26 changes: 26 additions & 0 deletions k8s-vault-agent-injector/terraform/version.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
terraform {
required_version = ">= 1.6.0"

required_providers {
vault = {
source = "hashicorp/vault"
version = "3.20.1"
}
local = {
source = "hashicorp/local"
version = "2.4.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.23.0"
}
helm = {
source = "hashicorp/helm"
version = "2.11.0"
}
kubectl = {
source = "gavinbunney/kubectl"
version = "1.14.0"
}
}
}
12 changes: 10 additions & 2 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,16 @@ module "esm" {
depends_on = [module.vault_k8s]
}

# Deploy Vault Secrets Operator
module "vai" {
count = var.minikube.enabled && var.minikube.vault_agent_injector ? 1 : 0

source = "./k8s-vault-agent-injector/terraform"

ca_cert = module.tls.ca.cert

depends_on = [module.vault_k8s]
}

module "vso" {
count = var.minikube.enabled && var.minikube.vault_secrets_operator ? 1 : 0

Expand All @@ -75,7 +84,6 @@ module "vso" {
depends_on = [module.vault_k8s]
}

# Deploy Cert Manager
module "cm" {
count = var.minikube.enabled && var.minikube.cert_manager ? 1 : 0

Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ nav:
- Minikube: minikube.md
- External Secrets Manager: esm.md
- Vault Secrets Operator: vso.md
- Vault Agent Injector: vai.md
- Cert Manager: cm.md

markdown_extensions:
Expand Down
3 changes: 3 additions & 0 deletions terraform.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ minikube = {

# enable cert manager
cert_manager = true

# enable vault agent injector
vault_agent_injector = true
}
27 changes: 27 additions & 0 deletions tests/e2e.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,30 @@ run "kuard_verifies_using_ca_cert" {
error_message = "Kuard Demo App is cannot be verified using the CA Cert (Want: 200, Got:${data.http.request.status_code})."
}
}

# 11. setup vault agent injector
run "setup_vai" {
plan_options {
target = [
module.vai
]
}
}

# 12. check if vai injected secrets into pod
run "vai_secret_is_injected" {
command = plan

module {
source = "./tests/external_cmd"
}

variables {
command = "kubectl exec -n vai -it $(kubectl get pods -l=app=kuard -n vai --no-headers -o custom-columns=\":metadata.name\") -- cat /vault/secrets/secrets.txt"
}

assert {
condition = length(data.shell_script.command.output) == 2
error_message = "VAI Secret is not injected into Pod"
}
}
1 change: 1 addition & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ variable "minikube" {
enabled = optional(bool, true)
external_secrets_manager = optional(bool, true)
vault_secrets_operator = optional(bool, true)
vault_agent_injector = optional(bool, true)
cert_manager = optional(bool, true)
})
}

0 comments on commit 68e2ed3

Please sign in to comment.