feat(vault): add vault
This commit is contained in:
137
.gitignore
vendored
137
.gitignore
vendored
@@ -1,3 +1,140 @@
|
|||||||
|
### Generated by gibo (https://github.com/simonwhitaker/gibo)
|
||||||
|
### https://raw.github.com/github/gitignore/6eeebe6f49678aacd8311ce079842c971b3ebe96/Node.gitignore
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
.env.secrets
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# Our rules
|
||||||
/.env.local*
|
/.env.local*
|
||||||
/custom.just
|
/custom.just
|
||||||
/custom/
|
/custom/
|
||||||
|
|||||||
4
justfile
4
justfile
@@ -1,5 +1,7 @@
|
|||||||
set dotenv-filename := ".env.local"
|
set dotenv-filename := ".env.local"
|
||||||
|
|
||||||
|
export PATH := "./node_modules/.bin:" + env_var('PATH')
|
||||||
|
|
||||||
[private]
|
[private]
|
||||||
default:
|
default:
|
||||||
@just --list --unsorted --list-submodules
|
@just --list --unsorted --list-submodules
|
||||||
@@ -7,5 +9,7 @@ default:
|
|||||||
mod env
|
mod env
|
||||||
mod k8s
|
mod k8s
|
||||||
mod longhorn
|
mod longhorn
|
||||||
|
mod utils
|
||||||
|
mod vault
|
||||||
|
|
||||||
import? "custom.just"
|
import? "custom.just"
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ helm = "3.17.4"
|
|||||||
just = "1.42.4"
|
just = "1.42.4"
|
||||||
k3sup = "0.13.10"
|
k3sup = "0.13.10"
|
||||||
kubelogin = "1.34.0"
|
kubelogin = "1.34.0"
|
||||||
|
node = "22.18.0"
|
||||||
vault = "1.20.2"
|
vault = "1.20.2"
|
||||||
|
|||||||
3592
package-lock.json
generated
Normal file
3592
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
package.json
Normal file
22
package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "buun-stack",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Buun stack: Kubernetes home lab",
|
||||||
|
"author": "Buun ch.",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@dotenvx/dotenvx": "^1.31.3",
|
||||||
|
"@keycloak/keycloak-admin-client": "^26.0.6",
|
||||||
|
"tiny-invariant": "^1.3.3",
|
||||||
|
"tsx": "^4.19.2",
|
||||||
|
"zx": "^8.2.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@fsouza/prettierd": "^0.25.4",
|
||||||
|
"@types/node": "^22.10.1",
|
||||||
|
"eslint": "^9.16.0",
|
||||||
|
"eslint-config-unjs": "^0.4.2",
|
||||||
|
"prettier": "^3.4.1",
|
||||||
|
"typescript": "^5.7.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
vault/.gitignore
vendored
Normal file
1
vault/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
vault-values.yaml
|
||||||
8
vault/auth-token-secret.yaml
Normal file
8
vault/auth-token-secret.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: vault-auth-token
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/service-account.name: vault-auth
|
||||||
|
type: kubernetes.io/service-account-token
|
||||||
|
|
||||||
181
vault/justfile
Normal file
181
vault/justfile
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
set fallback := true
|
||||||
|
|
||||||
|
export VAULT_NAMESPACE := env("VAULT_NAMESPACE", "vault")
|
||||||
|
export VAULT_CHART_VERSION := env("VAULT_CHART_VERSION", "0.29.1")
|
||||||
|
export VAULT_HOST := env("VAULT_HOST", "")
|
||||||
|
export VAULT_ADDR := "https://" + VAULT_HOST
|
||||||
|
SECRET_PATH := "secret"
|
||||||
|
|
||||||
|
[private]
|
||||||
|
default:
|
||||||
|
@just --list --unsorted --list-submodules
|
||||||
|
|
||||||
|
# Add Helm repository
|
||||||
|
add-helm-repo:
|
||||||
|
helm repo add hashicorp https://helm.releases.hashicorp.com
|
||||||
|
helm repo update
|
||||||
|
|
||||||
|
# Remove Helm repository
|
||||||
|
remove-helm-repo:
|
||||||
|
helm repo remove hashicorp
|
||||||
|
|
||||||
|
# Create Keycloak namespace
|
||||||
|
create-namespace:
|
||||||
|
@kubectl get namespace ${VAULT_NAMESPACE} &>/dev/null || \
|
||||||
|
kubectl create namespace ${VAULT_NAMESPACE}
|
||||||
|
|
||||||
|
# Delete Keycloak namespace
|
||||||
|
delete-namespace:
|
||||||
|
@kubectl delete namespace ${VAULT_NAMESPACE} --ignore-not-found
|
||||||
|
|
||||||
|
# Install Vault
|
||||||
|
install: check-env
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
just create-namespace
|
||||||
|
just add-helm-repo
|
||||||
|
gomplate -f vault-values.gomplate.yaml -o vault-values.yaml
|
||||||
|
helm upgrade --cleanup-on-fail --install vault hashicorp/vault \
|
||||||
|
--version ${VAULT_CHART_VERSION} -n ${VAULT_NAMESPACE} --wait -f vault-values.yaml
|
||||||
|
|
||||||
|
# Wait for the primary vault pod to complete init containers and be ready to accept commands
|
||||||
|
kubectl wait pod --for=condition=PodReadyToStartContainers \
|
||||||
|
-n ${VAULT_NAMESPACE} vault-0 --timeout=5m
|
||||||
|
|
||||||
|
init_output=$(kubectl exec -n ${VAULT_NAMESPACE} vault-0 -- \
|
||||||
|
vault operator init -key-shares=1 -key-threshold=1 -format=json || true)
|
||||||
|
|
||||||
|
root_token=""
|
||||||
|
if echo "${init_output}" | grep -q "Vault is already initialized"; then
|
||||||
|
echo "Vault is already initialized"
|
||||||
|
while [ -z "${root_token}" ]; do
|
||||||
|
root_token=$(gum input --prompt="Vault root token: " --password --width=100)
|
||||||
|
done
|
||||||
|
else
|
||||||
|
unseal_key=$(echo "${init_output}" | jq -r '.unseal_keys_b64[0]')
|
||||||
|
root_token=$(echo "${init_output}" | jq -r '.root_token')
|
||||||
|
kubectl exec -n ${VAULT_NAMESPACE} vault-0 -- \
|
||||||
|
vault operator unseal "${unseal_key}"
|
||||||
|
echo "Vault initialized and unsealed successfully"
|
||||||
|
echo "Root Token: ${root_token}"
|
||||||
|
echo "Unseal Key: ${unseal_key}"
|
||||||
|
echo "Please save these credentials securely!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wait for all vault instances to pass readiness checks and be ready to serve requests
|
||||||
|
kubectl wait pod --for=condition=ready -n ${VAULT_NAMESPACE} \
|
||||||
|
-l app.kubernetes.io/name=vault --timeout=5m
|
||||||
|
|
||||||
|
just configure-kubernetes-auth "${root_token}"
|
||||||
|
just create-secrets-engine {{ SECRET_PATH }} "${root_token}"
|
||||||
|
|
||||||
|
# Uninstall Vault
|
||||||
|
uninstall delete-ns='false':
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
helm uninstall vault -n ${VAULT_NAMESPACE} --ignore-not-found --wait
|
||||||
|
just delete-namespace
|
||||||
|
|
||||||
|
# Create admin token
|
||||||
|
create-admin-token root_token='': check-env
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
export VAULT_TOKEN="{{ root_token }}"
|
||||||
|
while [ -z "${VAULT_TOKEN}" ]; do
|
||||||
|
VAULT_TOKEN=$(gum input --prompt="Vault root token: " --password --width=100)
|
||||||
|
done
|
||||||
|
vault policy write admin - <<EOF
|
||||||
|
path "sys/auth" {
|
||||||
|
capabilities = ["read", "list", "sudo"]
|
||||||
|
}
|
||||||
|
path "sys/auth/*" {
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
||||||
|
}
|
||||||
|
path "secret/*" {
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list"]
|
||||||
|
}
|
||||||
|
path "auth/*" {
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list"]
|
||||||
|
}
|
||||||
|
path "sys/policy/*" {
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list"]
|
||||||
|
}
|
||||||
|
path "sys/policies/acl/*" {
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list"]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
vault token create -policy=admin
|
||||||
|
|
||||||
|
# Create secrets engine
|
||||||
|
create-secrets-engine path root_token='':
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
export VAULT_TOKEN="{{ root_token }}"
|
||||||
|
while [ -z "${VAULT_TOKEN}" ]; do
|
||||||
|
VAULT_TOKEN=$(gum input --prompt="Vault root token: " --password --width=100)
|
||||||
|
done
|
||||||
|
# Use kubectl exec during initial setup to avoid DNS dependency
|
||||||
|
kubectl exec -n ${VAULT_NAMESPACE} vault-0 -- sh <<EOF
|
||||||
|
export VAULT_TOKEN='${VAULT_TOKEN}'
|
||||||
|
vault secrets enable -path='{{ path }}' kv-v2
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Configure Kubernetes authentication for Vault
|
||||||
|
configure-kubernetes-auth root_token='':
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
export VAULT_TOKEN="{{ root_token }}"
|
||||||
|
while [ -z "${VAULT_TOKEN}" ]; do
|
||||||
|
VAULT_TOKEN=$(gum input --prompt="Vault root token: " --password --width=100)
|
||||||
|
done
|
||||||
|
|
||||||
|
gomplate -f ./serviceaccount.gomplate.yaml | kubectl apply -n "${VAULT_NAMESPACE}" -f -
|
||||||
|
gomplate -f ./rolebinding.gomplate.yaml | kubectl apply -n "${VAULT_NAMESPACE}" -f -
|
||||||
|
kubectl apply -n "${VAULT_NAMESPACE}" -f ./auth-token-secret.yaml
|
||||||
|
|
||||||
|
SA_SECRET="vault-auth-token"
|
||||||
|
SA_JWT=$(kubectl get secret -n ${VAULT_NAMESPACE} ${SA_SECRET} -o jsonpath='{.data.token}' | \
|
||||||
|
base64 --decode)
|
||||||
|
SA_CA=$(kubectl get secret -n ${VAULT_NAMESPACE} ${SA_SECRET} -o jsonpath='{.data.ca\.crt}' | \
|
||||||
|
base64 --decode)
|
||||||
|
|
||||||
|
# Use kubectl exec to run vault commands directly in the pod during initial setup
|
||||||
|
kubectl exec -n ${VAULT_NAMESPACE} vault-0 -- sh <<EOF
|
||||||
|
export VAULT_TOKEN='${VAULT_TOKEN}'
|
||||||
|
vault auth list -format=json | jq -e '.["kubernetes/"]' >/dev/null 2>&1 || \
|
||||||
|
vault auth enable kubernetes
|
||||||
|
vault write auth/kubernetes/config \
|
||||||
|
token_reviewer_jwt='${SA_JWT}' \
|
||||||
|
kubernetes_host='https://kubernetes.default.svc' \
|
||||||
|
kubernetes_ca_cert='${SA_CA}'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Get key value
|
||||||
|
get path field: check-env
|
||||||
|
@vault kv get -mount=secret -field={{ field }} {{ path }}
|
||||||
|
|
||||||
|
# Put key value
|
||||||
|
put path *args: check-env
|
||||||
|
@vault kv put -mount=secret {{ path }} {{ args }}
|
||||||
|
|
||||||
|
# Delete key value
|
||||||
|
delete path: check-env
|
||||||
|
@vault kv delete -mount=secret {{ path }}
|
||||||
|
|
||||||
|
# Check if key exists
|
||||||
|
exist path: check-env
|
||||||
|
@vault kv get -mount=secret {{ path }} &>/dev/null
|
||||||
|
|
||||||
|
# Check the environment
|
||||||
|
[private]
|
||||||
|
check-env:
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -z "${VAULT_HOST}" ]; then
|
||||||
|
while [ -z "${VAULT_HOST}" ]; do
|
||||||
|
VAULT_HOST=$(
|
||||||
|
gum input --prompt="Vault host: " --width=100 --placeholder="vault.example.com"
|
||||||
|
)
|
||||||
|
done
|
||||||
|
just env::set VAULT_HOST "${VAULT_HOST}"
|
||||||
|
fi
|
||||||
12
vault/rolebinding.gomplate.yaml
Normal file
12
vault/rolebinding.gomplate.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: vault-auth-binding
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: system:auth-delegator
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: vault-auth
|
||||||
|
namespace: {{ .Env.VAULT_NAMESPACE }}
|
||||||
5
vault/serviceaccount.gomplate.yaml
Normal file
5
vault/serviceaccount.gomplate.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: vault-auth
|
||||||
|
namespace: {{ .Env.VAULT_NAMESPACE }}
|
||||||
17
vault/vault-values.gomplate.yaml
Normal file
17
vault/vault-values.gomplate.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
csi:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
server:
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: traefik
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
ingressClassName: traefik
|
||||||
|
hosts:
|
||||||
|
- host: {{ .Env.VAULT_HOST }}
|
||||||
|
paths:
|
||||||
|
- /
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- {{ .Env.VAULT_HOST }}
|
||||||
Reference in New Issue
Block a user