https://github.com/stuttgart-things/vault-base-setup
terraform module for base-setup configuration of hashicorp vault.
https://github.com/stuttgart-things/vault-base-setup
csi-secrets-store k8s-auth terraform vault vso
Last synced: 8 days ago
JSON representation
terraform module for base-setup configuration of hashicorp vault.
- Host: GitHub
- URL: https://github.com/stuttgart-things/vault-base-setup
- Owner: stuttgart-things
- License: apache-2.0
- Created: 2023-11-29T14:43:11.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2026-05-26T08:16:11.000Z (about 2 months ago)
- Last Synced: 2026-05-26T10:15:05.833Z (about 2 months ago)
- Topics: csi-secrets-store, k8s-auth, terraform, vault, vso
- Language: HCL
- Homepage:
- Size: 147 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# stuttgart-things/vault-base-setup
terraform module for base-setup configuration of hashicorp vault.
## EXAMPLE USAGE
KUBECONFIG USAGE EXAMPLES
### Option 1: Using File Path (Existing Method)
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.demo-infra.example.com"
cluster_name = "kind-dev2"
context = "kind-dev2"
skip_tls_verify = true
kubeconfig_path = "/home/sthings/.kube/kind-dev2"
csi_enabled = true
namespace_csi = "vault"
vso_enabled = true
namespace_vso = "vault"
k8s_auths = [
{
name = "dev"
namespace = "default"
token_policies = ["read-k8s"]
token_ttl = 3600
},
]
}
```
### Option 2: Using String Content (New Method)
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.demo-infra.example.com"
cluster_name = "kind-dev2"
context = "kind-dev2"
skip_tls_verify = true
kubeconfig_content = file("/home/sthings/.kube/kind-dev2")
csi_enabled = true
namespace_csi = "vault"
vso_enabled = true
namespace_vso = "vault"
k8s_auths = [
{
name = "dev"
namespace = "default"
token_policies = ["read-k8s"]
token_ttl = 3600
},
]
}
```
### Option 2b: Using an Environment Variable
```hcl
variable "kubeconfig_content_from_env" {
type = string
sensitive = true
}
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.demo-infra.example.com"
cluster_name = "kind-dev2"
context = "kind-dev2"
skip_tls_verify = true
kubeconfig_content = var.kubeconfig_content_from_env
csi_enabled = true
namespace_csi = "vault"
vso_enabled = true
namespace_vso = "vault"
k8s_auths = [
{
name = "dev"
namespace = "default"
token_policies = ["read-k8s"]
token_ttl = 3600
},
]
}
```
```bash
export TF_VAR_kubeconfig_content_from_env=$(cat /home/sthings/.kube/kind-dev2)
```
### Option 2c. Using a Heredoc (Inline String)
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.demo-infra.example.com"
cluster_name = "kind-dev2"
context = "kind-dev2"
skip_tls_verify = true
kubeconfig_content = <<-EOT
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTi...
server: https://127.0.0.1:6443
name: kind-dev2
contexts:
- context:
cluster: kind-dev2
user: kind-dev2
name: kind-dev2
current-context: kind-dev2
kind: Config
preferences: {}
users:
- name: kind-dev2
user:
client-certificate-data: LS0tLS1CRUdJTi...
client-key-data: LS0tLS1CRUdJTi...
EOT
csi_enabled = true
namespace_csi = "vault"
vso_enabled = true
namespace_vso = "vault"
k8s_auths = [
{
name = "dev"
namespace = "default"
token_policies = ["read-k8s"]
token_ttl = 3600
},
]
}
```
Important Notes:
* Exactly one of kubeconfig_path or kubeconfig_content must be provided
* kubeconfig_content is marked as sensitive to prevent accidental exposure in logs
* Using file() function reads the file at plan time, while kubeconfig_path reads it during data source execution
EXECUTE VAULT CONFIG (VAULT SERVER)
### MODULE CALL
```bash
cat > vault-base.tf <<'EOF'
module "vault-secrets-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
kubeconfig_path = "/home/sthings/.kube/demo-infra"
vault_addr = "https://vault.demo-infra.sthings-vsphere.labul.sva.de"
createDefaultAdminPolicy = true
csi_enabled = false
vso_enabled = false
enableApproleAuth = true
skip_tls_verify = true
approle_roles = [
{
name = "read-k8s"
token_policies = ["read-k8s"]
}
]
secret_engines = [
{
path = "apps"
name = "s3"
description = "minio app secrets"
data_json = < test-approle.yaml
---
- hosts: localhost
become: true
vars:
vault_approle_id: "INSERT-HERE"
vault_approle_secret: "INSERT-HERE" # pragma: allowlist secret
vault_url: "https://vault.demo-infra.example.com"
username: "{{ lookup('community.hashi_vault.hashi_vault', 'secret=apps/data/s3:accessKey validate_certs=false auth_method=approle role_id={{ vault_approle_id }} secret_id={{ vault_approle_secret }} url={{ vault_url }}') }}"
kubeconfig: "{{ lookup('community.hashi_vault.hashi_vault', 'secret=kubeconfigs/data/kind-dev2:kubeconfig validate_certs=false auth_method=approle role_id={{ vault_approle_id }} secret_id={{ vault_approle_secret }} url={{ vault_url }}') }}" # pragma: allowlist secret
tasks:
- name: Debug
debug:
var: username
- name: Debug
debug:
var: kubeconfig
EOF
ansible-playbook test-approle.yaml -vv
```
DEPLOY K8S AUTH ON CLUSTER
```hcl
cat > vault-k8s.tf <<'EOF'
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.demo-infra.example.com"
cluster_name = "kind-dev2"
context = "kind-dev2"
skip_tls_verify = true
kubeconfig_path = "/home/sthings/.kube/kind-dev2"
csi_enabled = true
namespace_csi = "vault"
vso_enabled = true
namespace_vso = "vault"
k8s_auths = [
{
name = "dev"
namespace = "default"
token_policies = ["read-k8s"]
token_ttl = 3600
},
]
}
EOF
```
```bash
# ONLY APPLY IF VSO IS ENABLED
kubectl apply -f https://raw.githubusercontent.com/hashicorp/vault-secrets-operator/main/chart/crds/secrets.hashicorp.com_vaultconnections.yaml
kubectl apply -f https://raw.githubusercontent.com/hashicorp/vault-secrets-operator/main/chart/crds/secrets.hashicorp.com_vaultauths.yaml
export VAULT_TOKEN=
terraform init --upgrade
terraform apply
```
```yaml
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: demo-infra
namespace: default
spec:
vaultAuthRef: dev # Reference to a VaultAuth CRD, e.g., token or approle
mount: kubeconfigs # The KV mount path in Vault
type: kv-v2 # KV engine version
path: demo-infra # Path under the mount
refreshAfter: 10s # How often to sync from Vault
destination:
create: true
name: demo-infra-kube # Name of the Kubernetes Secret to create
```
VAULT PKI CA WITH CERT-MANAGER
### Module Call
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.demo-infra.example.com"
cluster_name = "kind-dev2"
context = "kind-dev2"
skip_tls_verify = true
kubeconfig_path = "/home/sthings/.kube/kind-dev2"
csi_enabled = false
vso_enabled = false
# PKI CA configuration
pki_enabled = true
pki_path = "pki"
pki_common_name = "example.com"
pki_organization = "My Org"
pki_root_ttl = "87600h"
pki_roles = [
{
name = "example-dot-com"
allowed_domains = ["example.com"]
allow_subdomains = true
max_ttl = "720h"
}
]
k8s_auths = [
{
name = "dev"
namespace = "default"
token_policies = ["pki-issue"]
token_ttl = 3600
},
]
}
```
### cert-manager ClusterIssuer
```yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: vault-issuer
spec:
vault:
path: pki/sign/example-dot-com
server: https://vault.demo-infra.example.com
auth:
kubernetes:
role: dev
mountPath: /v1/auth/kind-dev2-dev
serviceAccountRef:
name: dev
```
### cert-manager Certificate
```yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-app-tls
namespace: default
spec:
secretName: my-app-tls
issuerRef:
name: vault-issuer
kind: ClusterIssuer
commonName: myapp.example.com
dnsNames:
- myapp.example.com
```
DEPLOY VAULT SERVER IN DEV MODE
Deploys a Vault server in dev mode — automatically initialized and unsealed with in-memory storage. No auto-unseal operator needed. Useful for development and testing.
> **Note:** Dev mode uses in-memory storage — all data is lost on pod restart. Do not use in production.
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.example.com"
cluster_name = "my-cluster"
context = "default"
skip_tls_verify = true
kubeconfig_path = "/home/sthings/.kube/my-cluster"
vault_enabled = true
vault_dev_mode = true
vault_dev_root_token = "root"
vault_injector_enabled = false
namespace_vault = "vault"
vault_storage_class = "openebs-hostpath"
csi_enabled = false
vso_enabled = false
}
```
DEPLOY VAULT SERVER (BITNAMI)
Deploys a Vault server using the Bitnami Helm chart. When `vault_autounseal_enabled` is set to `true`, the [vault-autounseal](https://github.com/pytoshka/vault-autounseal) chart is deployed alongside Vault to automatically initialize and unseal the server. Unseal keys and root token are stored as Kubernetes secrets (`vault-keys` and `vault-root-token`) in the Vault namespace.
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.example.com"
cluster_name = "my-cluster"
context = "default"
skip_tls_verify = true
kubeconfig_path = "/home/sthings/.kube/my-cluster"
vault_enabled = true
vault_injector_enabled = false
namespace_vault = "vault"
vault_storage_class = "openebs-hostpath"
vault_autounseal_enabled = true
csi_enabled = false
vso_enabled = false
}
```
Retrieve the root token after deployment:
```bash
kubectl get secret vault-root-token -n vault -o jsonpath='{.data.root_token}' | base64 -d
```
DEPLOY VAULT SERVER WITH INGRESS/TLS
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.demo-infra.example.com"
cluster_name = "my-cluster"
context = "default"
skip_tls_verify = true
kubeconfig_path = "/home/sthings/.kube/my-cluster"
vault_enabled = true
vault_injector_enabled = false
namespace_vault = "vault"
vault_storage_class = "openebs-zfs"
vault_autounseal_enabled = true
vault_ingress_enabled = true
vault_ingress_class = "nginx"
vault_ingress_hostname = "vault.demo-infra.example.com"
vault_ingress_issuer_name = "cluster-issuer-approle"
vault_ingress_issuer_kind = "ClusterIssuer"
csi_enabled = false
vso_enabled = false
}
```
DEPLOY VAULT SERVER WITH GATEWAY API
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.whatever.sthings-vsphere.labul.sva.de"
cluster_name = "my-cluster"
context = "default"
skip_tls_verify = true
kubeconfig_path = "/home/sthings/.kube/my-cluster"
vault_enabled = true
vault_injector_enabled = false
namespace_vault = "vault"
vault_storage_class = "openebs-hostpath"
vault_autounseal_enabled = true
vault_gateway_enabled = true
vault_gateway_hostname = "vault.whatever.sthings-vsphere.labul.sva.de"
vault_gateway_name = "whatever-gateway"
vault_gateway_namespace = "default"
vault_gateway_section = "https"
csi_enabled = false
vso_enabled = false
}
```
PKI CA BOOTSTRAP WORKFLOW
When Vault is the PKI CA but needs TLS on its own ingress, use the bootstrap workflow: deploy cert-manager with a self-signed CA first, then configure Vault PKI and create a Vault-backed ClusterIssuer.
**Two-phase apply required:** Phase 1 deploys cert-manager + bootstrap CA + Vault. Phase 2 configures PKI + Vault ClusterIssuer.
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.demo-infra.example.com"
cluster_name = "my-cluster"
context = "default"
skip_tls_verify = true
kubeconfig_path = "/home/sthings/.kube/my-cluster"
# Vault with auto-unseal + ingress
vault_enabled = true
vault_injector_enabled = false
namespace_vault = "vault"
vault_storage_class = "openebs-zfs"
vault_autounseal_enabled = true
vault_ingress_enabled = true
vault_ingress_class = "nginx"
vault_ingress_hostname = "vault.demo-infra.example.com"
# cert-manager + bootstrap CA (auto-wires ingress issuer)
certmanager_enabled = true
certmanager_bootstrap_enabled = true
# PKI + Vault ClusterIssuer (second apply)
pki_enabled = true
pki_common_name = "example.com"
pki_organization = "My Org"
pki_roles = [
{
name = "example-dot-com"
allowed_domains = ["example.com"]
allow_subdomains = true
max_ttl = "720h"
}
]
certmanager_vault_issuer_enabled = true
certmanager_vault_issuer_pki_role = "example-dot-com"
csi_enabled = false
vso_enabled = false
}
```
```bash
# Phase 1
terraform apply \
-target=helm_release.cert_manager \
-target=kubectl_manifest.bootstrap_ca_clusterissuer \
-target=helm_release.vault \
-target=helm_release.vault_autounseal
# Phase 2 (after Vault is unsealed)
export VAULT_TOKEN=$(kubectl get secret vault-root-token -n vault -o jsonpath='{.data.root_token}' | base64 -d)
terraform apply
```
VAULT CLUSTERISSUER (FLUX-MANAGED CERT-MANAGER / CROSS-CLUSTER)
Use the Vault ClusterIssuer without deploying cert-manager or the PKI engine through this module. This is useful when:
- cert-manager is managed by Flux (or another GitOps tool)
- The PKI engine already exists on a remote Vault instance
- You want to issue certificates on an edge cluster from a central Vault
### Same-cluster with Flux-managed cert-manager
When Vault runs in the same cluster and cert-manager is deployed externally (e.g. via Flux), use the internal cluster URL to avoid TLS/caBundle requirements:
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.example.com"
skip_tls_verify = true
kubeconfig_path = "/path/to/kubeconfig"
cluster_name = "my-cluster"
csi_enabled = false
vso_enabled = false
pki_enabled = true
pki_path = "pki"
pki_common_name = "example.com"
pki_organization = "My Org"
pki_roles = [
{
name = "example-dot-com"
allowed_domains = ["example.com"]
allow_subdomains = true
max_ttl = "720h"
}
]
certmanager_vault_issuer_enabled = true
certmanager_vault_issuer_pki_role = "example-dot-com"
certmanager_vault_issuer_server = "http://vault-server.vault.svc.cluster.local:8200"
}
```
### Cross-cluster (edge cluster → remote Vault)
When the PKI engine already exists on a remote Vault, set `pki_enabled = false` and provide the policy name and CA bundle explicitly:
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
vault_addr = "https://vault.k3s-infra.example.com"
skip_tls_verify = true
kubeconfig_path = "/path/to/edge-kubeconfig"
cluster_name = "edge-cluster"
csi_enabled = false
vso_enabled = false
pki_enabled = false
certmanager_vault_issuer_enabled = true
certmanager_vault_issuer_pki_role = "k3s-infra"
certmanager_vault_issuer_server = "https://vault.k3s-infra.example.com"
certmanager_vault_issuer_ca_bundle = var.vault_ca_bundle
certmanager_vault_issuer_policy_name = "pki-issue"
}
```
The `vault_ca_bundle` variable should contain the base64-encoded PEM of the CA that signed the Vault ingress TLS certificate.
### Variables Reference
| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `certmanager_vault_issuer_enabled` | bool | `false` | Create Vault-backed ClusterIssuer |
| `certmanager_vault_issuer_name` | string | `"vault-pki"` | ClusterIssuer name |
| `certmanager_vault_issuer_pki_role` | string | `""` | PKI role for certificate issuance |
| `certmanager_vault_issuer_server` | string | `""` | Vault URL (defaults to `vault_addr`) |
| `certmanager_vault_issuer_ca_bundle` | string | `""` | Base64-encoded CA bundle (falls back to bootstrap CA, then omitted) |
| `certmanager_vault_issuer_policy_name` | string | `""` | Vault policy name (defaults to `pki_policy_name`) |
| `certmanager_vault_issuer_namespace` | string | `"cert-manager"` | Namespace for the token secret |
| `certmanager_vault_token_ttl` | string | `"720h"` | Vault token TTL |
| `certmanager_vault_token_secret_name` | string | `"vault-pki-token"` | K8s secret name for the token |
CSI PROVIDER EXAMPLE APPLICATION
### SecretProviderClass
```yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: vault-lab-creds
namespace: default
spec:
provider: vault
parameters:
objects: |
- objectName: "lab"
secretPath: "env/data/labul"
secretKey: "lab"
roleName: dev
vaultAddress: https://vault.dev11.4sthings.tiab.ssc.sva.de
vaultKubernetesMountPath: utah-dev
vaultSkipTLSVerify: "true"
```
### Example Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app3
labels:
app: demo
spec:
selector:
matchLabels:
app: demo
replicas: 1
template:
metadata:
labels:
app: demo
spec:
serviceAccountName: dev
containers:
- name: app
image: nginx
volumeMounts:
- name: 'vault-user-creds'
mountPath: '/mnt/secrets-store'
readOnly: true
volumes:
- name: vault-user-creds
csi:
driver: 'secrets-store.csi.k8s.io'
readOnly: true
volumeAttributes:
secretProviderClass: 'vault-lab-creds'
```
CALL MODULE W/ VALUES
```hcl
module "vault-base-setup" {
source = "github.com/stuttgart-things/vault-base-setup"
createDefaultAdminPolicy = true
secret_engines = [
{
path = "cloud"
name = "vsphere"
description = "vsphere secrets",
data_json = <
EXECUTE TERRAFORM
```bash
export VAULT_ADDR=${VAULT_ADDR}
export VAULT_TOKEN=${VAULT_TOKEN}
terraform init
terraform validate
terraform plan
terraform apply
```
## Author Information
```bash
Xiaomin Lai, stuttgart-things 10/2023
Patrick Hermann, stuttgart-things 12/2023
```
## License
Licensed under the Apache License, Version 2.0 (the "License").
You may obtain a copy of the License at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an _"AS IS"_ basis, without WARRANTIES or conditions of any kind, either express or implied.
See the License for the specific language governing permissions and limitations under the License.