diff --git a/.gitignore b/.gitignore index 81d7197..3e438a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ c#/src/bin/ c#/src/obj/ node_modules/ -__pycache__/ \ No newline at end of file +__pycache__/ +*.pem +*.csr diff --git a/hashicorp/vault/example-apps/basic-secret/deployment.yaml b/hashicorp/vault/example-apps/basic-secret/deployment.yaml new file mode 100644 index 0000000..6afa3d8 --- /dev/null +++ b/hashicorp/vault/example-apps/basic-secret/deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: basic-secret + labels: + app: basic-secret +spec: + selector: + matchLabels: + app: basic-secret + replicas: 1 + template: + metadata: + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/tls-skip-verify: "true" + vault.hashicorp.com/agent-inject-secret-helloworld: "secret/basic-secret/helloworld" + vault.hashicorp.com/agent-inject-template-helloworld: | + {{- with secret "secret/basic-secret/helloworld" -}} + { + "username" : "{{ .Data.username }}", + "password" : "{{ .Data.password }}" + } + {{- end }} + vault.hashicorp.com/role: "basic-secret-role" + labels: + app: basic-secret + spec: + serviceAccountName: basic-secret + containers: + - name: app + image: jweissig/app:0.0.1 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: basic-secret + labels: + app: basic-secret \ No newline at end of file diff --git a/hashicorp/vault/example-apps/basic-secret/readme.md b/hashicorp/vault/example-apps/basic-secret/readme.md new file mode 100644 index 0000000..09bae17 --- /dev/null +++ b/hashicorp/vault/example-apps/basic-secret/readme.md @@ -0,0 +1,50 @@ +# Basic Secret Injection + + +In order for us to start using secrets in vault, we need to setup a policy. + + +``` +#Create a role for our app + +kubectl -n vault-example exec -it vault-example-0 sh + +vault write auth/kubernetes/role/basic-secret-role \ + bound_service_account_names=basic-secret \ + bound_service_account_namespaces=vault-example \ + policies=basic-secret-policy \ + ttl=1h +``` + +The above maps our Kubernetes service account, used by our pod, to a policy. +Now lets create the policy to map our service account to a bunch of secrets + + +``` +kubectl -n vault-example exec -it vault-example-0 sh +cat < /home/vault/app-policy.hcl +path "secret/basic-secret/*" { + capabilities = ["read"] +} +EOF +vault policy write basic-secret-policy /home/vault/app-policy.hcl +exit +``` + +Now our service account for our pod can access all secrets under `secret/basic-secret/*` +Lets create some secrets. + + +``` +kubectl -n vault-example exec -it vault-example-0 sh +vault secrets enable -path=secret/ kv +vault kv put secret/basic-secret/helloworld username=dbuser password=sUp3rS3cUr3P@ssw0rd +exit +``` + +Lets deploy our app and see if it works: + +``` +kubectl -n vault-example apply -f ./hashicorp/vault/example-apps/basic-secret/deployment.yaml +kubectl -n vault-example get pods +``` \ No newline at end of file diff --git a/hashicorp/vault/example-apps/dynamic-postgresql/deployment.yaml b/hashicorp/vault/example-apps/dynamic-postgresql/deployment.yaml new file mode 100644 index 0000000..d57d3d4 --- /dev/null +++ b/hashicorp/vault/example-apps/dynamic-postgresql/deployment.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dynamic-postgres + labels: + app: dynamic-postgres +spec: + selector: + matchLabels: + app: dynamic-postgres + replicas: 1 + template: + metadata: + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/tls-skip-verify: "true" + vault.hashicorp.com/agent-inject-secret-sql-role: "database/creds/sql-role" + vault.hashicorp.com/agent-inject-template-sql-role: | + { + {{- with secret "database/creds/sql-role" -}} + "db_connection": "host=postgres.postgress port=5432 user={{ .Data.username }} password={{ .Data.password }} dbname=postgresdb sslmode=disable" + {{- end }} + } + vault.hashicorp.com/role: "sql-role" + labels: + app: dynamic-postgres + spec: + serviceAccountName: dynamic-postgres + containers: + - name: app + image: jweissig/app:0.0.1 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dynamic-postgres + labels: + app: dynamic-postgres \ No newline at end of file diff --git a/hashicorp/vault/example-apps/dynamic-postgresql/pgadmin.yaml b/hashicorp/vault/example-apps/dynamic-postgresql/pgadmin.yaml new file mode 100644 index 0000000..208158d --- /dev/null +++ b/hashicorp/vault/example-apps/dynamic-postgresql/pgadmin.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: pgadmin-config + labels: + app: pgadmin +data: + PGADMIN_DEFAULT_EMAIL: admin@admin.com + PGADMIN_DEFAULT_PASSWORD: admin123 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pgadmin +spec: + selector: + matchLabels: + app: pgadmin + replicas: 1 + template: + metadata: + labels: + app: pgadmin + spec: + containers: + - name: pgadmin + image: dpage/pgadmin4 + imagePullPolicy: "IfNotPresent" + ports: + - containerPort: 80 + envFrom: + - configMapRef: + name: pgadmin-config \ No newline at end of file diff --git a/hashicorp/vault/example-apps/dynamic-postgresql/postgres.yaml b/hashicorp/vault/example-apps/dynamic-postgresql/postgres.yaml new file mode 100644 index 0000000..4010743 --- /dev/null +++ b/hashicorp/vault/example-apps/dynamic-postgresql/postgres.yaml @@ -0,0 +1,49 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: postgres-config + labels: + app: postgres +data: + POSTGRES_DB: postgresdb + POSTGRES_USER: postgresadmin + POSTGRES_PASSWORD: admin123 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres +spec: + selector: + matchLabels: + app: postgres + replicas: 1 + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:10.4 + imagePullPolicy: "IfNotPresent" + ports: + - containerPort: 5432 + envFrom: + - configMapRef: + name: postgres-config +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + labels: + app: postgres +spec: + selector: + app: postgres + ports: + - protocol: TCP + name: http + port: 5432 + targetPort: 5432 \ No newline at end of file diff --git a/hashicorp/vault/example-apps/dynamic-postgresql/readme.md b/hashicorp/vault/example-apps/dynamic-postgresql/readme.md new file mode 100644 index 0000000..b96546e --- /dev/null +++ b/hashicorp/vault/example-apps/dynamic-postgresql/readme.md @@ -0,0 +1,80 @@ +# Dynamic Secret Creation [PostgreSQL] + +Deploy our test database + + +``` +kubectl create ns postgres +kubectl -n postgres apply -f ./hashicorp/vault/example-apps/dynamic-postgresql/postgres.yaml +kubectl -n postgres apply -f ./hashicorp/vault/example-apps/dynamic-postgresql/pgadmin.yaml +kubectl -n postgres get pods + +kubectl -n postgres exec -it bash +psql --username=postgresadmin postgresdb +``` + +``` +Enable the database engine + +``` +kubectl -n vault-example exec -it vault-example-0 vault login +kubectl -n vault-example exec -it vault-example-0 vault secrets enable database +``` + +## Configure DB Credential creation + +``` +kubectl -n vault-example exec -it vault-example-0 sh + +vault write database/config/postgresdb \ + plugin_name=postgresql-database-plugin \ + allowed_roles="sql-role" \ + connection_url="postgresql://{{username}}:{{password}}@postgres.postgres:5432/postgresdb?sslmode=disable" \ + username="postgresadmin" \ + password="admin123" + + vault write database/roles/sql-role \ + db_name=postgresdb \ + creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \ + GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \ + default_ttl="1h" \ + max_ttl="24h" + +#test +vault read database/creds/sql-role + +``` + +## Example Application + +Create a policy to control access to secrets + +``` +kubectl -n vault-example exec -it vault-example-0 sh + +cat < /home/vault/postgres-app-policy.hcl +path "database/creds/sql-role" { + capabilities = ["read"] +} +EOF + +vault policy write postgres-app-policy /home/vault/postgres-app-policy.hcl + +``` + + +Bind our role to a service account for our application + + +``` +kubectl -n vault-example exec -it vault-example-0 sh + +vault write auth/kubernetes/role/sql-role \ + bound_service_account_names=dynamic-postgres \ + bound_service_account_namespaces=vault-example \ + policies=postgres-app-policy \ + ttl=1h + +``` + +kubectl -n vault-example apply -f .\hashicorp\vault\example-apps\dynamic-postgresql\deployment.yaml \ No newline at end of file diff --git a/hashicorp/vault/injector/injector-clusterrole.yaml b/hashicorp/vault/injector/injector-clusterrole.yaml new file mode 100644 index 0000000..1f58f4f --- /dev/null +++ b/hashicorp/vault/injector/injector-clusterrole.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: vault-example-agent-injector-clusterrole + labels: + app.kubernetes.io/name: vault-example-agent-injector +rules: +- apiGroups: ["admissionregistration.k8s.io"] + resources: ["mutatingwebhookconfigurations"] + verbs: + - "get" + - "list" + - "watch" + - "patch" diff --git a/hashicorp/vault/injector/injector-clusterrolebinding.yaml b/hashicorp/vault/injector/injector-clusterrolebinding.yaml new file mode 100644 index 0000000..4e883f1 --- /dev/null +++ b/hashicorp/vault/injector/injector-clusterrolebinding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: vault-example-agent-injector-binding + labels: + app.kubernetes.io/name: vault-example-agent-injector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: vault-example-agent-injector-clusterrole +subjects: +- kind: ServiceAccount + name: vault-example-agent-injector + namespace: vault-example diff --git a/hashicorp/vault/injector/injector-deployment.yaml b/hashicorp/vault/injector/injector-deployment.yaml new file mode 100644 index 0000000..f7769fb --- /dev/null +++ b/hashicorp/vault/injector/injector-deployment.yaml @@ -0,0 +1,64 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vault-example-agent-injector + labels: + app.kubernetes.io/name: vault-example-agent-injector + component: webhook +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: vault-example-agent-injector + component: webhook + template: + metadata: + labels: + app.kubernetes.io/name: vault-example-agent-injector + component: webhook + spec: + serviceAccountName: vault-example-agent-injector + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + containers: + - name: sidecar-injector + image: "hashicorp/vault-k8s:0.1.2" + imagePullPolicy: IfNotPresent + env: + - name: AGENT_INJECT_LISTEN + value: ":8080" + - name: AGENT_INJECT_LOG_LEVEL + value: "info" + - name: AGENT_INJECT_VAULT_ADDR + value: https://vault-example.vault-example.svc:8200 + - name: AGENT_INJECT_VAULT_IMAGE + value: "vault:1.3.1" + - name: AGENT_INJECT_TLS_AUTO + value: vault-example-agent-injector-cfg + - name: AGENT_INJECT_TLS_AUTO_HOSTS + value: vault-example-agent-injector-svc,vault-example-agent-injector-svc.vault-example,vault-example-agent-injector-svc.vault-example.svc + args: + - agent-inject + - 2>&1 + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTPS + failureThreshold: 2 + initialDelaySeconds: 1 + periodSeconds: 2 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTPS + failureThreshold: 2 + initialDelaySeconds: 2 + periodSeconds: 2 + successThreshold: 1 + timeoutSeconds: 5 diff --git a/hashicorp/vault/injector/injector-mutating-webhook.yaml b/hashicorp/vault/injector/injector-mutating-webhook.yaml new file mode 100644 index 0000000..7a455b1 --- /dev/null +++ b/hashicorp/vault/injector/injector-mutating-webhook.yaml @@ -0,0 +1,20 @@ +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: vault-example-agent-injector-cfg + labels: + app.kubernetes.io/name: vault-example-agent-injector +webhooks: + - name: vault.hashicorp.com + clientConfig: + service: + name: vault-example-agent-injector-svc + namespace: vault-example + path: "/mutate" + caBundle: "" + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] + namespaceSelector: \ No newline at end of file diff --git a/hashicorp/vault/injector/injector-service.yaml b/hashicorp/vault/injector/injector-service.yaml new file mode 100644 index 0000000..2a4430b --- /dev/null +++ b/hashicorp/vault/injector/injector-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: vault-example-agent-injector-svc + labels: + app.kubernetes.io/name: vault-example-agent-injector +spec: + ports: + - port: 443 + targetPort: 8080 + selector: + app.kubernetes.io/name: vault-example-agent-injector + component: webhook diff --git a/hashicorp/vault/injector/injector-serviceaccount.yaml b/hashicorp/vault/injector/injector-serviceaccount.yaml new file mode 100644 index 0000000..fdb55b1 --- /dev/null +++ b/hashicorp/vault/injector/injector-serviceaccount.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: vault-example-agent-injector + labels: + app.kubernetes.io/name: vault-example-agent-injector \ No newline at end of file diff --git a/hashicorp/vault/readme.md b/hashicorp/vault/readme.md new file mode 100644 index 0000000..bee642d --- /dev/null +++ b/hashicorp/vault/readme.md @@ -0,0 +1,115 @@ +# Hashicorp Vault Guide + +# Vault + +For this tutorial, I use Kuberentes 1.17 +It's critical because we'll need certain [admission controllers](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) enabled. + +To get 1.17 for Linux\Windows, just use `kind` since you can create a 1.17 with admissions all setup. + +``` +#Windows +kind create cluster --name vault --image kindest/node:v1.17.0@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62 + +#Linux +kind create cluster --name vault --kubeconfig ~/.kube/kind-vault --image kindest/node:v1.17.0@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62 +``` + +## TLS End to End Encryption + +VIDEO: +See steps in `hashicorp/vault/tls/ssl_generate_self_signed.txt` +You'll need to generate TLS certs (or bring your own) +Create base64 strings from the files, place it in the `server-tls-secret.yaml` and apply it. +Remember not to check-in your TLS to GIT :) + +## Deployment + +``` +kubectl create ns vault-example +kubectl -n vault-example apply -f ./hashicorp/vault/server/ +kubectl -n vault-example get pods +``` + +## Storage + +``` +kubectl -n vault-example get pvc +``` +ensure vault-claim is bound, if not, `kubectl -n vault-example describe pvc vault-claim` +ensure correct storage class is used for your cluster. +if you need to change the storage class, deleve the pvc , edit YAML and re-apply + +## Initialising Vault + +``` +kubectl -n vault-example exec -it vault-example-0 vault operator init +#unseal 3 times +kubectl -n vault-example exec -it vault-example-0 vault operator unseal +kubectl -n vault-example get pods +``` + +## Depploy the Injector + +VIDEO: + +Injector allows pods to automatically get secrets from the vault. + +``` +kubectl -n vault-example apply -f ./hashicorp/vault/injector/ +kubectl -n vault-example get pods +``` + +## Injector Kubernetes Auth Policy + +For the injector to be authorised to access vault, we need to enable K8s auth + +``` +kubectl -n vault-example exec -it vault-example-0 vault login +kubectl -n vault-example exec -it vault-example-0 vault auth enable kubernetes + + +kubectl -n vault-example exec -it vault-example-0 sh +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 +exit + +kubectl -n vault-example get pods + +``` + +# Summary + +So we have a vault, an injector, TLS end to end, stateful storage. +The injector can now inject secrets for pods from the vault. + +Now we are ready to use the platform for different types of secrets: + +## Secret Injection Guides + +I've broken this down into basic guides to avoid this document from becoming too large. + +### Basic Secrets + +Objective: +---------- +* Let's create a basic secret in vault manually +* Application consumes the secret automatically + +[Try it](./example-apps/basic-secret/readme.md) + +### Dynamic Secrets: Postgres + +Objective: +---------- +* We have a Postgres Database +* Let's delegate Vault to manage life cycles of our database credentials +* Deploy an app, that automatically gets it's credentials from vault + +[Try it](./example-apps/basic-secret/readme.md) + + + + diff --git a/hashicorp/vault/server/server-clusterrolebinding.yaml b/hashicorp/vault/server/server-clusterrolebinding.yaml new file mode 100644 index 0000000..66434a4 --- /dev/null +++ b/hashicorp/vault/server/server-clusterrolebinding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: vault-example-server-binding + labels: + app.kubernetes.io/name: vault-example +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: vault-example + namespace: vault-example diff --git a/hashicorp/vault/server/server-config-configmap.yaml b/hashicorp/vault/server/server-config-configmap.yaml new file mode 100644 index 0000000..4cd3f68 --- /dev/null +++ b/hashicorp/vault/server/server-config-configmap.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: vault-example-config + labels: + app.kubernetes.io/name: vault-example +data: + extraconfig-from-values.hcl: |- + disable_mlock = true + ui = true + listener "tcp" { + address = "0.0.0.0:8200" + tls_cert_file = "/vault/ssl/vault-example.pem" + tls_key_file = "/vault/ssl/vault-example-key.pem" + tls_min_version = "tls12" + cluster_address = "[::]:8201" + } + storage "file" { + path = "/vault/data" + } diff --git a/hashicorp/vault/server/server-disruptionbudget.yaml b/hashicorp/vault/server/server-disruptionbudget.yaml new file mode 100644 index 0000000..7cd2067 --- /dev/null +++ b/hashicorp/vault/server/server-disruptionbudget.yaml @@ -0,0 +1,22 @@ +# {{ template "vault.mode" . }} +# {{- if and (and (eq (.Values.global.enabled | toString) "true") (eq .mode "ha")) (eq (.Values.server.ha.disruptionBudget.enabled | toString) "true") -}} +# # PodDisruptionBudget to prevent degrading the server cluster through +# # voluntary cluster changes. +# apiVersion: policy/v1beta1 +# kind: PodDisruptionBudget +# metadata: +# name: {{ template "vault.fullname" . }} +# namespace: {{ .Release.Namespace }} +# labels: +# helm.sh/chart: {{ include "vault.chart" . }} +# app.kubernetes.io/name: {{ include "vault.name" . }} +# app.kubernetes.io/instance: {{ .Release.Name }} +# app.kubernetes.io/managed-by: {{ .Release.Service }} +# spec: +# maxUnavailable: {{ template "vault.pdb.maxUnavailable" . }} +# selector: +# matchLabels: +# app.kubernetes.io/name: {{ include "vault.name" . }} +# app.kubernetes.io/instance: {{ .Release.Name }} +# component: server +# {{- end -}} diff --git a/hashicorp/vault/server/server-ingress.yaml b/hashicorp/vault/server/server-ingress.yaml new file mode 100644 index 0000000..5b8d7f8 --- /dev/null +++ b/hashicorp/vault/server/server-ingress.yaml @@ -0,0 +1,44 @@ +# {{- if .Values.server.ingress.enabled -}} +# {{- $serviceName := include "vault.fullname" . -}} +# {{- $servicePort := .Values.server.service.port -}} +# apiVersion: extensions/v1beta1 +# kind: Ingress +# metadata: +# name: {{ template "vault.fullname" . }} +# namespace: {{ .Release.Namespace }} +# labels: +# helm.sh/chart: {{ include "vault.chart" . }} +# app.kubernetes.io/name: {{ include "vault.name" . }} +# app.kubernetes.io/instance: {{ .Release.Name }} +# app.kubernetes.io/managed-by: {{ .Release.Service }} +# {{- with .Values.server.ingress.labels }} +# {{- toYaml . | nindent 4 }} +# {{- end }} +# {{- with .Values.server.ingress.annotations }} +# annotations: +# {{- toYaml . | nindent 4 }} +# {{- end }} +# spec: +# {{- if .Values.server.ingress.tls }} +# tls: +# {{- range .Values.server.ingress.tls }} +# - hosts: +# {{- range .hosts }} +# - {{ . | quote }} +# {{- end }} +# secretName: {{ .secretName }} +# {{- end }} +# {{- end }} +# rules: +# {{- range .Values.server.ingress.hosts }} +# - host: {{ .host | quote }} +# http: +# paths: +# {{- range .paths }} +# - path: {{ . }} +# backend: +# serviceName: {{ $serviceName }} +# servicePort: {{ $servicePort }} +# {{- end }} +# {{- end }} +# {{- end }} diff --git a/hashicorp/vault/server/server-pv.yaml b/hashicorp/vault/server/server-pv.yaml new file mode 100644 index 0000000..b93235e --- /dev/null +++ b/hashicorp/vault/server/server-pv.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: vault + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data" \ No newline at end of file diff --git a/hashicorp/vault/server/server-pvc.yaml b/hashicorp/vault/server/server-pvc.yaml new file mode 100644 index 0000000..55b51fe --- /dev/null +++ b/hashicorp/vault/server/server-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: vault-claim +spec: + storageClassName: standard + #storageClassName: hostpath + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/hashicorp/vault/server/server-service.yaml b/hashicorp/vault/server/server-service.yaml new file mode 100644 index 0000000..e329d0f --- /dev/null +++ b/hashicorp/vault/server/server-service.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Service +metadata: + name: vault-example + labels: + app.kubernetes.io/name: vault-example + annotations: + # This must be set in addition to publishNotReadyAddresses due + # to an open issue where it may not work: + # https://github.com/kubernetes/kubernetes/issues/58662 + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" +spec: + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + - name: internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault-example + component: server diff --git a/hashicorp/vault/server/server-serviceaccount.yaml b/hashicorp/vault/server/server-serviceaccount.yaml new file mode 100644 index 0000000..efdfb01 --- /dev/null +++ b/hashicorp/vault/server/server-serviceaccount.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: vault-example + labels: + app.kubernetes.io/name: vault-example \ No newline at end of file diff --git a/hashicorp/vault/server/server-statefulset.yaml b/hashicorp/vault/server/server-statefulset.yaml new file mode 100644 index 0000000..d9bdba0 --- /dev/null +++ b/hashicorp/vault/server/server-statefulset.yaml @@ -0,0 +1,132 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: vault-example + labels: + app.kubernetes.io/name: vault-example +spec: + serviceName: vault-example + podManagementPolicy: Parallel + replicas: 1 + updateStrategy: + type: "OnDelete" + selector: + matchLabels: + app.kubernetes.io/name: vault-example + component: server + template: + metadata: + labels: + app.kubernetes.io/name: vault-example + component: server + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: vault-example + component: server + topologyKey: kubernetes.io/hostname + terminationGracePeriodSeconds: 10 + serviceAccountName: vault-example + shareProcessNamespace: true + securityContext: + fsGroup: 1000 + volumes: + - name: config + configMap: + name: vault-example-config + - name: data + persistentVolumeClaim: + claimName: vault-claim + - name: tls-secret + secret: + secretName: vault-example-tls-secret + initContainers: + - name: setupperms + image: alpine:latest + command: ['sh', '-c', 'echo The app is running! && chown 100 /vault/data && ls -l /vault/'] + volumeMounts: + - name: data + mountPath: /vault/data + containers: + - name: vault + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + capabilities: + add: ["IPC_LOCK"] + image: vault:1.3.1 + imagePullPolicy: IfNotPresent + command: + - "/bin/sh" + - "-ec" + args: + - | + sed -E "s/HOST_IP/${HOST_IP?}/g" /vault/config/extraconfig-from-values.hcl > /tmp/storageconfig.hcl; + sed -Ei "s/POD_IP/${POD_IP?}/g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_ADDR + value: "https://127.0.0.1:8200" + - name: VAULT_API_ADDR + value: "https://$(POD_IP):8200" + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: VAULT_CACERT + value: "/vault/ssl/ca.pem" + volumeMounts: + - name: tls-secret + mountPath: /vault/ssl + - name: config + mountPath: /vault/config + - name: data + mountPath: /vault/data + ports: + - containerPort: 8200 + name: http + - containerPort: 8201 + name: internal + - containerPort: 8202 + name: replication + readinessProbe: + httpGet: + path: "/v1/sys/health?standbyok=true" + port: 8200 + scheme: HTTPS + livenessProbe: + httpGet: + path: "/v1/sys/health?standbyok=true" + port: 8200 + scheme: HTTPS + initialDelaySeconds: 60 + periodSeconds: 3 + successThreshold: 1 + timeoutSeconds: 5 + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep 5 && kill -SIGTERM $(pidof vault)", + ] + \ No newline at end of file diff --git a/hashicorp/vault/server/server-tls-secret.yaml b/hashicorp/vault/server/server-tls-secret.yaml new file mode 100644 index 0000000..6e7997a --- /dev/null +++ b/hashicorp/vault/server/server-tls-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: vault-example-tls-secret +type: Opaque +data: + vault-example.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVUVENDQXpXZ0F3SUJBZ0lVUkJaRmlSVXZnMTRJMFF6NkU0N3RJKzQ0R21Nd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1VqRUxNQWtHQTFVRUJoTUNRVlV4RURBT0JnTlZCQWdUQjBWNFlXMXdiR1V4RWpBUUJnTlZCQWNUQ1UxbApiR0p2ZFhKdVpURVFNQTRHQTFVRUNoTUhSWGhoYlhCc1pURUxNQWtHQTFVRUN4TUNRMEV3SGhjTk1qQXdNekF5Ck1qSTBOakF3V2hjTk1qRXdNekF5TWpJME5qQXdXakJ4TVFzd0NRWURWUVFHRXdKQlZURVJNQThHQTFVRUNCTUkKVm1samRHOXlhV0V4RWpBUUJnTlZCQWNUQ1UxbGJHSnZkWEp1WlRFVE1CRUdBMVVFQ2hNS1MzVmlaWEp1WlhSbApjekVPTUF3R0ExVUVDeE1GVm1GMWJIUXhGakFVQmdOVkJBTVREWFpoZFd4MExXVjRZVzF3YkdVd2dnRWlNQTBHCkNTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDa2JHVHNzSEQ1N3FKVXVIRElsZGpsZHM5QytOWEoKZXpFekNkbEQwdFlsZkVtQU56eURFdnZqbTJielI1MnlCd1VxRGZkeElMdE0xYlQzVFZjYWdSWkVNWkJOVnBtWgpxMUh6aXV1ZkhJMnNVN1pBcmxFbnNyV25oZEdrb0lPd0FyUGl6NXZFVDVJYXh1bXQzUlc3b2hEaE9QdnF0M3ZsCmtBTW9LZXJvMm9QdWp0K0hoQ3ppVDNkMGdjNUlGZWZVbkNiTDBQUmpselBXeFdzQ1ByWDJpYmJaZzkzb2IvNFgKOHlTRVVmZlk4eC9aaytWZDRqZXc3TnhzYllZdGFhUkwzU0M2My9FNERpMkgxSWVtQkRFOGNBNGZiVGpEZGVqKwp5WGcwR1BvYTlWUkVsUlJjMEU1RUQ0eG9IRFFoTFFCeFJqWHJoT1Z1Mmp2cXlvR25OSHQ4MDViQkFnTUJBQUdqCmdmc3dnZmd3RGdZRFZSMFBBUUgvQkFRREFnV2dNQjBHQTFVZEpRUVdNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUYKQlFjREFqQU1CZ05WSFJNQkFmOEVBakFBTUIwR0ExVWREZ1FXQkJSeGg5U3JXTzhESlBkMjFqR091OW04d0Q1RQpSVEFmQmdOVkhTTUVHREFXZ0JUdDhnaDErS3pRZTc2N3I0cldaazhUU0FEc0REQjVCZ05WSFJFRWNqQndnZzEyCllYVnNkQzFsZUdGdGNHeGxnaTEyWVhWc2RDMWxlR0Z0Y0d4bExuWmhkV3gwTFdWNFlXMXdiR1V1YzNaakxtTnMKZFhOMFpYSXViRzlqWVd5Q0gzWmhkV3gwTFdWNFlXMXdiR1V1ZG1GMWJIUXRaWGhoYlhCc1pTNXpkbU9DQ1d4dgpZMkZzYUc5emRJY0Vmd0FBQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQWJ1V3VIbkJORnVKbCttSDlwVndhCnhDcGd4NHNPVDdZeFhJS2RXZ1QrREpPanlKYjFFMkt2ZTJLdVoxZ0tiazZua3pMRzlGZXlDbUFzNmdPTW95cmkKZm1uQ0QzR2FDaFV1OTNlVEtudlgxNVl5UDdoYldNaHVqT3c3Tmo2U0VpQkxTL08yanhHRkwzb0p6cWFRQTFtSwp3bjNsWGY0QVkyUTFFSWxuQ0JvU3lRTFRZS0lISUlNbk5GaWJjRG5jY0lUbG1IdktKd2hVTFk2REJ0WGR0QXFYCmgvam1OSVF4THZNSzFwL0JnRzZrai9XNlBkdXNoZjdLaWgvWU9La3N2WlJWZlhnRjdNeUZQR2NESXNLVk05bHUKRUdRQnBYcDJFa0NjNElXMmM5MGlPaU9wRlZqb0ZvSDFOVHE0TGM5RFk0WDNNa0FSck5RVitoa016M2M5bitWbwpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + vault-example-key.pem: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcEd4azdMQncrZTZpVkxod3lKWFk1WGJQUXZqVnlYc3hNd25aUTlMV0pYeEpnRGM4Cmd4TDc0NXRtODBlZHNnY0ZLZzMzY1NDN1ROVzA5MDFYR29FV1JER1FUVmFabWF0Ujg0cnJueHlOckZPMlFLNVIKSjdLMXA0WFJwS0NEc0FLejRzK2J4RStTR3NicHJkMFZ1NklRNFRqNzZyZDc1WkFES0NucTZOcUQ3bzdmaDRRcwo0azkzZElIT1NCWG4xSndteTlEMFk1Y3oxc1ZyQWo2MTlvbTIyWVBkNkcvK0YvTWtoRkgzMlBNZjJaUGxYZUkzCnNPemNiRzJHTFdta1M5MGd1dC94T0E0dGg5U0hwZ1F4UEhBT0gyMDR3M1hvL3NsNE5CajZHdlZVUkpVVVhOQk8KUkErTWFCdzBJUzBBY1VZMTY0VGxidG83NnNxQnB6UjdmTk9Xd1FJREFRQUJBb0lCQUJtY1NKd1ZYNE9PaC9wcQpRQ2IrTUNxTnR2clhoM1U2bXc4NEdYOVc5OFFlOTlQZ3hxd2o2TmdxL0g0b1NZZlJVQnljMEUzdXF6M3NpNk41CmlIZTRZNTk3bU41eS9yblExWkw1c0htNEdOa2VzT1NpUWtITXREN0R1VVBMUExmTnMvZEFIeU1Vd2MwcDdud0cKVTd4R0locnlwVXFLQ2VKWDdDWDFZWUdqaDZsQWwrYWR0YmFCaG1MSkRaek53WmZTcnlneVVPSnVjQXpOZCtxRwpxUXBmU1Y0c0Z6WGdQL3F3REtHcGg0anFYRnBFWkNCQzgyNE9rdUVQL0k2TVBrK3M5N2xjQXlmaWVGamRwT2E3CnVHUm1sV3BUNG9QZXNFTmF0YkNid1dsR25jamJtL1VxSldVRndJdjRFYlhlQ1JOVlRBM1VOSjlWMlRQVHFIRVUKb2FScFo0RUNnWUVBemhvWUR6R0c0S2xDdjAvclE4MDN5dHJwVzNqWEpOWlBNa1ZpcFNEUVV3MFp5SWpDeVdQWAo1WEhtaG1uc0I5YkpYbFpjK1pRTTBtalBYUGFOZXgzek1NSkp0L1VIM0Roc0FZL3dUOW0zQk1VbjhNeU4rWCt3CjFvUUhOMmZTSFhQNUx6bSs5YWQwUGpIRkVLOGlFQ2J1a1FmQkUwOGc0QjkyU3liN3F6VVM3RGNDZ1lFQXpEc2YKK0VTcUsvUEpleWs4Vmd1eTFISVNUY054d0htejVYcEpoTVYzRnRKTDJyM0l2dHloNGxiaExsenNlcHB5U3JsLwo3d3BlQW1aQklUblpia3dnVW9WVlR0VHdwT05TSEF1aEpNc1RFbzhBdnZGQ0hUQXMzbVQvcDBuT3hoTmgvbVdGCjJLSHgxakcxNUlBWHppWkZNNmhmY2lLcjV6QWZWMGpzR3hhRnlNY0NnWUJoWmUxMnlLWC81NTFXZ2JNaHdJcWMKUCtYRng0Nk1wd2FZTURnVTV6UHIrNlh5b2NiRG0zNTh2TjMvS1hGVXB3bFVucVdqZ0hhcXZNTTZJSDN0NzlKcwplWFNURGFYZ1NYMnBJMWVpdExXTCtJd09mT3lmT3R2Y0ZGckFzVHlYbEtYdXpuQlM4UWE3R1pRU2RXRTRsdDFwCkJtd1U3dkVQV1c3eXh3SnAvMVBvOHdLQmdGTVMwY1JKR2ZkYTZOL2lQQnE4RTNmN0ZwcnZIend0eFJGWkZzS2QKRi8zK2VNQjNaa3JNc0VUREZrR25wc0dRUldGRDUxZ3luVjdZZlRHb3VGcjNPRWFZMmNTQk5ZbTh0YytXbzJ1MQo5d0lialRBZUxzaDBxaXVrWmFHRWtrbGI1UVo0QVdQSEsvbjJxb2hSMmwwT2tDT2RINFhydUlVSHZCZmpIN2M5CkNCcE5Bb0dBYzdBUHh2UGRMemJlQkhmYTEvcFhkYm1VQ3hFd25YMEwwYWdSOUxpQk8wWmJmUHhwWFpKV2wrVDMKOXFMQWFwMFFQMFhpQU9RS3g2M1ZWcks1dDZRdzJUTlhSOWcrRnZDS1lwVkt0aHVvbU9yeUpTQ2pOTFFlVTBpTApKa1hVd3lPK0lhampVNVdCSk1lWSt1cTlFaW9jaUdNRU1ONDBWQUZhdzhTUnhaVlA4SDA9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== + ca.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURtRENDQW9DZ0F3SUJBZ0lVY0c2TVdaVDJka3Rla24vRk5XZUxQdG16SWp3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1VqRUxNQWtHQTFVRUJoTUNRVlV4RURBT0JnTlZCQWdUQjBWNFlXMXdiR1V4RWpBUUJnTlZCQWNUQ1UxbApiR0p2ZFhKdVpURVFNQTRHQTFVRUNoTUhSWGhoYlhCc1pURUxNQWtHQTFVRUN4TUNRMEV3SGhjTk1qQXdNekF5Ck1qSTBOakF3V2hjTk1qVXdNekF4TWpJME5qQXdXakJTTVFzd0NRWURWUVFHRXdKQlZURVFNQTRHQTFVRUNCTUgKUlhoaGJYQnNaVEVTTUJBR0ExVUVCeE1KVFdWc1ltOTFjbTVsTVJBd0RnWURWUVFLRXdkRmVHRnRjR3hsTVFzdwpDUVlEVlFRTEV3SkRRVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFLMm94UkFhCmF6MUs5VGVQdmxiL24rQUxOUCtET2h2eHorSHdxUXo0b0llSEV5NkEwUGZVcnVSRCtMcktrbTA4VVJyeXBKcjYKZXI3QWJKVmFqcXNNYXUxazQ3NTJkdFhzek9pcnlPUlpZeVFSTWF1M2MrK1VycnZHWi9nd2dSWGNleEhZRHlIYgptUDFTQjdiUXAzK2cvQ053OUVmL0N0dXFkMi8ydVc1clY4VFZYZldzMVNILyszT1VVY1ZzZ1E1RWYvbnVpOWN0CjhCT3JRUW5kQndjYWxYenlSV1A3MEswTXhleDU2M3UwU1pBTndYbWRLN21RcXVNREV6VEtwU0ZveC9RRTdIMDQKd3V3SHp1MFRiLzIwYlk2eWlzMVVRajJMSU02SDNHMU55TThNK2V2ZUJzaGxGckVQMzRRSWtzZldpVFl5RUdudwppT3N0MzBvekM1TkhkdDBDQXdFQUFhTm1NR1F3RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJCk1BWUJBZjhDQVFJd0hRWURWUjBPQkJZRUZPM3lDSFg0ck5CN3ZydXZpdFptVHhOSUFPd01NQjhHQTFVZEl3UVkKTUJhQUZPM3lDSFg0ck5CN3ZydXZpdFptVHhOSUFPd01NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUFjalE2YgpNRWZkWUQzd3FUUVhXY1ovYjFHTEFXWHNsTFU5VDYyNVJUUDV1MjFyc1FnaGJHWi9XcUgwT0JLa2FzVjc3c1JTCm9rRUJuVzd1UmpJbFBlS1pXS0tibHZ2ZGhyU2RDRmpraUVVYTBLdHkwNWtQTWt6bnhTdENQMzg3S2dQWWhYa0wKZGVlVkpwWFFURzZkNGx5blBUNDZmbmFvSjRoU052Y1RZdHUweXFBT3NqdEhuM2NMeW1GbS96L1U4cEZONWtTVApocUdSanhWbldQN09scW5hQ0txSjJuZDFwUUlMcWp5bFNScURrYzI0WXhHcVRPM3cyRndPS2VPbmhPelRpWk0yCk83WFVGMmE0cnNHQUdEejNQMEVENlJJZFpaWWRwQWwyN2VrZTJsSXFHTHBBcGJGMHJjUVg0aEdIUkZqaUt3WTgKU2F6YWM2cm8zRThIN3h3SwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== diff --git a/hashicorp/vault/server/ui-service.yaml b/hashicorp/vault/server/ui-service.yaml new file mode 100644 index 0000000..4d28847 --- /dev/null +++ b/hashicorp/vault/server/ui-service.yaml @@ -0,0 +1,20 @@ +# Headless service for Vault server DNS entries. This service should only +# point to Vault servers. For access to an agent, one should assume that +# the agent is installed locally on the node and the NODE_IP should be used. +# If the node can't run a Vault agent, then this service can be used to +# communicate directly to a server agent. +apiVersion: v1 +kind: Service +metadata: + name: vault-example-ui + labels: + app.kubernetes.io/name: vault-example-ui +spec: + selector: + app.kubernetes.io/name: vault-example + component: server + publishNotReadyAddresses: true + ports: + - name: http + port: 8080 + targetPort: 8200 \ No newline at end of file diff --git a/hashicorp/vault/tls/ca-config.json b/hashicorp/vault/tls/ca-config.json new file mode 100644 index 0000000..2d4b37a --- /dev/null +++ b/hashicorp/vault/tls/ca-config.json @@ -0,0 +1,13 @@ +{ + "signing": { + "default": { + "expiry": "8760h" + }, + "profiles": { + "default": { + "usages": ["signing", "key encipherment", "server auth", "client auth"], + "expiry": "8760h" + } + } + } +} diff --git a/hashicorp/vault/tls/ca-csr.json b/hashicorp/vault/tls/ca-csr.json new file mode 100644 index 0000000..1c863ca --- /dev/null +++ b/hashicorp/vault/tls/ca-csr.json @@ -0,0 +1,18 @@ +{ + "hosts": [ + "cluster.local" + ], + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "AU", + "L": "Melbourne", + "O": "Example", + "OU": "CA", + "ST": "Example" + } + ] +} diff --git a/hashicorp/vault/tls/ssl_generate_self_signed.txt b/hashicorp/vault/tls/ssl_generate_self_signed.txt new file mode 100644 index 0000000..ae8d476 --- /dev/null +++ b/hashicorp/vault/tls/ssl_generate_self_signed.txt @@ -0,0 +1,46 @@ + + +cd ./hashicorp/vault/tls/ + +docker run -it --rm -v ${PWD}:/work -w /work debian:buster bash +apt-get update && apt-get install -y curl && +curl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -o /usr/local/bin/cfssl && \ +curl https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -o /usr/local/bin/cfssljson && \ +chmod +x /usr/local/bin/cfssl && \ +chmod +x /usr/local/bin/cfssljson + +#generate certificate +cfssl gencert -initca ca-csr.json | cfssljson -bare ca +cfssl gencert \ + -ca=ca.pem \ + -ca-key=ca-key.pem \ + -config=ca-config.json \ + -hostname="vault-example,vault-example.vault-example.svc.cluster.local,vault-example.vault-example.svc,localhost,127.0.0.1" \ + -profile=default \ + vault-csr.json | cfssljson -bare vault-example + +#get values to make a secret +cat ca.pem | base64 | tr -d '\n' +cat vault-example.pem | base64 | tr -d '\n' +cat vault-example-key.pem | base64 | tr -d '\n' + +#linux - make the secret automatically +cat < ./server-tls-secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: vault-example-tls-secret +type: Opaque +data: + vault-example.pem: $(cat vault-example.pem | base64 | tr -d '\n') + vault-example-key.pem: $(cat vault-example-key.pem | base64 | tr -d '\n') + ca.pem: $(cat ca.pem | base64 | tr -d '\n') +EOF + + +#TEST +vault operator init #grab keys +vault operator unseal #unseal 3 times + +vault login +vault kv put cubbyhole/hello foo=world diff --git a/hashicorp/vault/tls/vault-csr.json b/hashicorp/vault/tls/vault-csr.json new file mode 100644 index 0000000..75e8677 --- /dev/null +++ b/hashicorp/vault/tls/vault-csr.json @@ -0,0 +1,16 @@ +{ + "CN": "vault-example", + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "AU", + "L": "Melbourne", + "O": "Kubernetes", + "OU": "Vault", + "ST": "Victoria" + } + ] +}