diff --git a/sezon-2/7-vault/README.md b/sezon-2/7-vault/README.md new file mode 100755 index 0000000..8c2538f --- /dev/null +++ b/sezon-2/7-vault/README.md @@ -0,0 +1,200 @@ +# Vault na Kubernetes + +Znajdziesz tutaj komendy do uruchomienia Vault. + +## 1. Instalacja Vault z Helm Chart + +```shell +CHARTDIR=/tmp/vault-helm + +git clone https://github.com/hashicorp/vault-helm $CHARTDIR + +DNSDOMAIN="$(minikube ip).nip.io" + +helm install vault -f vault-values.yaml \ + --set server.ingress.hosts[0].host=vault.${DNSDOMAIN} \ + $CHARTDIR + +echo http://vault.${DNSDOMAIN} + + +cat << EOF +################## + +# Komenda do zalogowania na poda z vaultem + +kubectl exec -ti vault-0 sh + +# Inicjalizacja + +vault operator init + +# NIE ZAPOMNIJ zapisać informacji które powyższa komenda zwróci! + +# Użyj 3 tokenów do "odpieczętowania" vaulta komendą + +vault operator unseal + +# Eksport zmiennych na twoim kliencie + +export VAULT_ADDR=http://vault.${DNSDOMAIN} +export VAULT_TOKEN= + +EOF +``` + +## 2. Konfiguracja Vaulta + +*Oparte głównie na wpisie z oficjalnego bloga https://www.hashicorp.com/blog/injecting-vault-secrets-into-kubernetes-pods-via-a-sidecar* + + +```shell +## Część Kubernetesowa - wykonaj z poda vaulta + +## Potrzebujesz też tokenu root: +## export VAULT_TOKEN= + +cat < /home/vault/app-policy.hcl +path "secret*" { + capabilities = ["read"] +} +EOF + +vault policy write app /home/vault/app-policy.hcl + +vault auth enable kubernetes + +vault write auth/kubernetes/config \ + token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ + kubernetes_host=https://${KUBERNETES_PORT_443_TCP_ADDR}:443 \ + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + + +# allow access from all namespaces with 'app' sa only +vault write auth/kubernetes/role/myapp \ + bound_service_account_names=app \ + bound_service_account_namespaces='*' \ + policies=app \ + ttl=1h + +# not enabled by default? +vault secrets enable -path=secret kv + +# create secret + +vault kv put secret/helloworld username=hellovault password=P@ssw0rd123 +``` + +## 3. Instalacja aplikacji + +```shell +kubectl apply -f krazy-cow.yaml +``` + +Weryfikacja - nie powinno być żadnych danych w `/vault/secrets`: + +```shell +kubectl iexec krazy-cow -- ls -l /vault/secrets +``` + +Dodanie anotacji agenta vaulta do aplikacji: + +```shell +kubectl patch deployment krazy-cow --patch "$(cat krazy-cow-patch.yaml)" +``` + +Ponowna weryfikacja + +```shell +kubectl iexec krazy-cow -- ls -l /vault/secrets +kubectl iexec krazy-cow -- cat /vault/secrets/helloworld +``` + +Zmiana szablonu dla wynikowego pliku: + +```shell +kubectl patch deployment krazy-cow --patch "$(cat krazy-cow-patch2.yaml)" +``` + +## 4. Vault i dynamiczne poświadczenia (credentials) do bazy danych + +Instalacja bazy + +```shell +helm install mydb \ + --set postgresqlPassword=secretpassword,postgresqlDatabase=mydatabase \ + --set service.type=NodePort,service.nodePort=32543 \ + stable/postgresql +``` + +Włączenie silnika obsługi baz danych na vault + +```shell +vault secrets enable database +``` + + +Konfiguracja połączenia do bazy z vaulta + +```shell +vault write database/config/postgresql \ + plugin_name=postgresql-database-plugin \ + allowed_roles=readonly \ + connection_url=postgresql://postgres:secretpassword@$(minikube ip):32543/postgres?sslmode=disable +``` + +Utworzenie szablonu roli, którą będzie tworzył vault na bazie danych + +```shell +cat << EOF > /tmp/vault-postgres.sql +CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; +GRANT SELECT ON ALL TABLES IN SCHEMA public TO "{{name}}"; +EOF + +vault write database/roles/readonly db_name=postgresql \ + creation_statements=@/tmp/vault-postgres.sql \ + default_ttl=30s max_ttl=60s +``` + +Utworzenie polityki zezwalającej na pobieranie danych logowania do bazy + +```shell +cat << EOF > /tmp/postgres-policy.hcl +path "database/creds/readonly" { + capabilities = [ "read" ] +} +EOF + +vault policy write dbapps /tmp/postgres-policy.hcl + + +vault write auth/kubernetes/role/myapp \ + bound_service_account_names=app \ + bound_service_account_namespaces='*' \ + policies=app,dbapps \ + ttl=1h +``` + +### Weryfikacja + +Pobranie tokena + +```shell +APP_TOKEN=$(vault token create -policy="dbapps" -field=token) +VAULT_TOKEN="$APP_TOKEN" vault read database/creds/readonly +``` + +Uruchomienie testowego kontenera z postgresql + +```shell +kubectl run -i --tty --rm debug --image=postgres:alpine --restart=Never -- sh +``` + +Dodanie aplikacji z konfiguracją + +```shell +kubectl apply -f krazy-cow.yaml +kubectl patch deployment krazy-cow --patch "$(cat krazy-cow-patch3.yaml)" +``` + +Dane do logowania powinny być w `/vault/secrets` wewnątrz poda. \ No newline at end of file diff --git a/sezon-2/7-vault/krazy-cow-patch.yaml b/sezon-2/7-vault/krazy-cow-patch.yaml new file mode 100644 index 0000000..8802a5f --- /dev/null +++ b/sezon-2/7-vault/krazy-cow-patch.yaml @@ -0,0 +1,7 @@ +spec: + template: + metadata: + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/agent-inject-secret-helloworld: "secret/helloworld" + vault.hashicorp.com/role: "myapp" diff --git a/sezon-2/7-vault/krazy-cow-patch2.yaml b/sezon-2/7-vault/krazy-cow-patch2.yaml new file mode 100644 index 0000000..0fbd51e --- /dev/null +++ b/sezon-2/7-vault/krazy-cow-patch2.yaml @@ -0,0 +1,11 @@ +spec: + template: + metadata: + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/agent-inject-secret-helloworld: "secret/helloworld" + vault.hashicorp.com/role: "myapp" + vault.hashicorp.com/agent-inject-template-helloworld: | + {{- with secret "secret/helloworld" -}} + {{ .Data.username }}:{{ .Data.password }} + {{- end }} diff --git a/sezon-2/7-vault/krazy-cow-patch3.yaml b/sezon-2/7-vault/krazy-cow-patch3.yaml new file mode 100644 index 0000000..965145c --- /dev/null +++ b/sezon-2/7-vault/krazy-cow-patch3.yaml @@ -0,0 +1,7 @@ +spec: + template: + metadata: + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/agent-inject-secret-dbcreds: "database/creds/readonly" + vault.hashicorp.com/role: "myapp" diff --git a/sezon-2/7-vault/krazy-cow.yaml b/sezon-2/7-vault/krazy-cow.yaml new file mode 100644 index 0000000..de36871 --- /dev/null +++ b/sezon-2/7-vault/krazy-cow.yaml @@ -0,0 +1,29 @@ +# app.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: krazy-cow + labels: + app: vault-agent-demo +spec: + selector: + matchLabels: + app: vault-agent-demo + replicas: 1 + template: + metadata: + annotations: + labels: + app: vault-agent-demo + spec: + serviceAccountName: app + containers: + - name: app + image: cloudowski/krazy-cow:0.4.1 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: app + labels: + app: vault-agent-demo diff --git a/sezon-2/7-vault/vault-values.yaml b/sezon-2/7-vault/vault-values.yaml new file mode 100644 index 0000000..26d5340 --- /dev/null +++ b/sezon-2/7-vault/vault-values.yaml @@ -0,0 +1,44 @@ +server: + ingress: + enabled: true + hosts: + - host: chart-example.local + paths: + - / + + authDelegator: + enabled: true + + dataStorage: + enabled: true + size: 1Gi + storageClass: null + + dev: + enabled: false + + # past a single replica. + standalone: + enabled: "true" + + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a PersistentVolumeClaim mounted at /vault/data + # and store data there. This is only used when using a Replica count of 1, and + # using a stateful set. This should be HCL. + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "file" { + path = "/vault/data" + } + +ui: + enabled: true + serviceType: "ClusterIP" + serviceNodePort: null + externalPort: 8200