diff --git a/.gitignore b/.gitignore index a711443ff..26ed514be 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ test_inventory doc/source/_svg data/backups .vscode/ +.pre-commit-config.yaml diff --git a/kubernetes/helm_charts/local/vault/Chart.yaml b/kubernetes/helm_charts/local/vault/Chart.yaml new file mode 100644 index 000000000..b625dc496 --- /dev/null +++ b/kubernetes/helm_charts/local/vault/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: vault-additional-manifests +description: A custom Helm chart with additional Vault manifests (NetworkPolicy, etc.) +type: application +version: 1.0.0 +appVersion: "1.0.0" diff --git a/kubernetes/helm_charts/local/vault/templates/_helpers.tpl b/kubernetes/helm_charts/local/vault/templates/_helpers.tpl new file mode 100644 index 000000000..4535c5ea5 --- /dev/null +++ b/kubernetes/helm_charts/local/vault/templates/_helpers.tpl @@ -0,0 +1,21 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "vault-additional.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vault-additional.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "vault-additional.labels" -}} +helm.sh/chart: {{ include "vault-additional.chart" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} diff --git a/kubernetes/helm_charts/local/vault/templates/networkpolicy.yaml b/kubernetes/helm_charts/local/vault/templates/networkpolicy.yaml new file mode 100644 index 000000000..3baece8e9 --- /dev/null +++ b/kubernetes/helm_charts/local/vault/templates/networkpolicy.yaml @@ -0,0 +1,89 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: vault-network-policy + namespace: {{ .Release.Namespace }} + labels: + {{- include "vault-additional.labels" . | nindent 4 }} +spec: + podSelector: + {{- with .Values.networkPolicy.podSelector }} + {{- toYaml . | nindent 4 }} + {{- end }} + policyTypes: + - Ingress + - Egress + ingress: + # Allow Vault-to-Vault communication (raft/cluster) + - from: + - podSelector: + {{- with .Values.networkPolicy.podSelector }} + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - port: {{ .Values.networkPolicy.vaultPort }} + protocol: TCP + - port: {{ .Values.networkPolicy.clusterPort }} + protocol: TCP + # Allow from explicitly listed namespaces (with optional pod selector) + {{- range .Values.networkPolicy.allowedConsumers }} + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ .namespace }} + {{- if .podSelector }} + podSelector: + matchLabels: + {{- toYaml .podSelector | nindent 14 }} + {{- end }} + ports: + - port: {{ $.Values.networkPolicy.vaultPort }} + protocol: TCP + {{- end }} + {{- if .Values.networkPolicy.allowIngress.enabled }} + # Allow ingress controller (for remote cluster vault-agent access via LB) + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ .Values.networkPolicy.allowIngress.namespace }} + podSelector: + matchLabels: + {{- toYaml .Values.networkPolicy.allowIngress.podSelector | nindent 14 }} + ports: + - port: {{ .Values.networkPolicy.vaultPort }} + protocol: TCP + {{- end }} + egress: + # Vault to Vault (raft replication) + - to: + - podSelector: + {{- with .Values.networkPolicy.podSelector }} + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - port: {{ .Values.networkPolicy.vaultPort }} + protocol: TCP + - port: {{ .Values.networkPolicy.clusterPort }} + protocol: TCP + # DNS resolution + - to: + - namespaceSelector: {} + podSelector: + matchLabels: + k8s-app: coredns + ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + # Allow egress to Kubernetes API (for K8s auth) + - to: + - ipBlock: + cidr: 0.0.0.0/0 + ports: + - port: 443 + protocol: TCP + - port: 6443 + protocol: TCP +{{- end }} diff --git a/kubernetes/helm_charts/local/vault/templates/unsealer-rbac.yaml b/kubernetes/helm_charts/local/vault/templates/unsealer-rbac.yaml new file mode 100644 index 000000000..2f198d463 --- /dev/null +++ b/kubernetes/helm_charts/local/vault/templates/unsealer-rbac.yaml @@ -0,0 +1,31 @@ +{{- if .Values.unsealer.enabled }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-unsealer-role + namespace: {{ .Release.Namespace }} + labels: + {{- include "vault-additional.labels" . | nindent 4 }} +rules: +- apiGroups: [""] + resources: ["secrets"] + resourceNames: ["{{ .Values.unsealer.secretName }}"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ .Release.Name }}-unsealer-rolebinding + namespace: {{ .Release.Namespace }} + labels: + {{- include "vault-additional.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ .Release.Name }}-unsealer-role +subjects: +- kind: ServiceAccount + name: {{ .Values.unsealer.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/kubernetes/helm_charts/local/vault/values-prod.yaml b/kubernetes/helm_charts/local/vault/values-prod.yaml new file mode 100644 index 000000000..c4b4085a5 --- /dev/null +++ b/kubernetes/helm_charts/local/vault/values-prod.yaml @@ -0,0 +1,60 @@ +# Vault additional manifests configuration for otcinfra2 (production) +# This chart provides supplementary resources not available in upstream Vault Helm chart +# +# NetworkPolicy: deny-all by default, only explicitly listed consumers can reach Vault. +# Remote clusters (otcinfra1, otcci) access Vault via ingress-nginx. + +networkPolicy: + enabled: true + + # Pod selector for Vault pods + podSelector: + matchLabels: + app.kubernetes.io/name: vault + + # Explicit list of namespaces/pods allowed to connect to Vault on port 8200. + # Each entry creates a separate ingress rule. All unlisted namespaces are denied. + allowedConsumers: + # ArgoCD repo-server — runs argocd-vault-plugin for secret injection + - namespace: argocd + podSelector: + app.kubernetes.io/name: argocd-repo-server + + # Ingress controller — required for remote clusters to reach Vault via LB + # Remote consumers: + # otcinfra1: anubis, docsportal, swift-proxy, umami (auth/kubernetes_otcinfra1) + # otcci: zuul (auth/kubernetes_otcci) + allowIngress: + enabled: true + namespace: default + podSelector: + app.kubernetes.io/name: ingress-nginx + + # Vault API port + vaultPort: 8200 + + # Vault cluster port (for raft replication) + clusterPort: 8201 + +# Auto-unseal configuration +# The unsealer sidecar watches for sealed state and auto-unseals using K8s secret +# +# Required secret format (vault-unseal-keys in vault namespace): +# Create the secret with unseal keys from 'vault operator init': +# +# kubectl -n vault create secret generic vault-unseal-keys \ +# --from-literal=vault-root= \ +# --from-literal=vault-unseal-0= \ +# --from-literal=vault-unseal-1= \ +# --from-literal=vault-unseal-2= \ +# --from-literal=vault-unseal-3= \ +# --from-literal=vault-unseal-4= +# +# See: https://bank-vaults.dev/docs/unseal-keys/ + +# Unsealer RBAC - grants access to the vault-unseal-keys secret +unsealer: + enabled: true + secretName: vault-unseal-keys + # The upstream vault chart creates ServiceAccount named after the release + serviceAccountName: vault diff --git a/kubernetes/helm_charts/upstream/vault/Chart.yaml b/kubernetes/helm_charts/upstream/vault/Chart.yaml new file mode 100644 index 000000000..a287aa657 --- /dev/null +++ b/kubernetes/helm_charts/upstream/vault/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: vault +description: HashiCorp Vault for OTC preprod environment +version: 0.1.0 +dependencies: + - name: vault + version: "0.30.0" + repository: "https://helm.releases.hashicorp.com" diff --git a/kubernetes/helm_charts/upstream/vault/VAULT_KUBECTL_COMMANDS.md b/kubernetes/helm_charts/upstream/vault/VAULT_KUBECTL_COMMANDS.md new file mode 100644 index 000000000..66df8fc77 --- /dev/null +++ b/kubernetes/helm_charts/upstream/vault/VAULT_KUBECTL_COMMANDS.md @@ -0,0 +1,345 @@ +# Vault kubectl Commands Quick Reference + +## Environment Setup +```bash +VAULT_POD="vault-0" +VAULT_NS="vault" +VAULT_TOKEN="" +UNSEAL_KEY="" +``` + +## Basic Operations + +### Status & Health +```bash +# Check Vault status +kubectl exec -n $VAULT_NS $VAULT_POD -- vault status + +# Check if sealed +kubectl exec -n $VAULT_NS $VAULT_POD -- vault status | grep Sealed + +# Unseal Vault +kubectl exec -n $VAULT_NS $VAULT_POD -- vault operator unseal $UNSEAL_KEY + +# Login +kubectl exec -n $VAULT_NS $VAULT_POD -- vault login $VAULT_TOKEN +``` + +## Secrets Management (KV v2) + +### Create/Update Secrets +```bash +# Single secret +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv put secret/argocd/repositories/github \ + username="github-user" \ + password="github-token" + +# Multiple fields +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv put secret/argocd/clusters/otcinfra \ + server="https://kubernetes.default.svc.cluster.local" \ + certData="" \ + keyData="" + +# From file +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv put secret/argocd/config @config.json +``` + +### Read Secrets +```bash +# Read entire secret +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv get secret/argocd/repositories/github + +# Read specific field +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv get -field=username secret/argocd/repositories/github + +# JSON format +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv get -format=json secret/argocd/repositories/github +``` + +### List & Delete Secrets +```bash +# List secrets +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv list secret/argocd/repositories/ +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv list secret/argocd/clusters/ + +# Delete secret +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv delete secret/argocd/repositories/github + +# Destroy version (permanent) +kubectl exec -n $VAULT_NS $VAULT_POD -- vault kv destroy -versions=1 secret/argocd/repositories/github +``` + +## Policy Management + +### Create Policy +```bash +# From file +kubectl cp /tmp/argocd-policy.hcl $VAULT_NS/$VAULT_POD:/tmp/policy.hcl +kubectl exec -n $VAULT_NS $VAULT_POD -- vault policy write argocd-policy /tmp/policy.hcl + +# Inline policy +kubectl exec -n $VAULT_NS $VAULT_POD -- vault policy write my-policy - < +kubectl exec -n vault vault-1 -- vault operator unseal +``` diff --git a/kubernetes/helm_charts/upstream/vault/values-prod.yaml b/kubernetes/helm_charts/upstream/vault/values-prod.yaml new file mode 100644 index 000000000..9ae757efa --- /dev/null +++ b/kubernetes/helm_charts/upstream/vault/values-prod.yaml @@ -0,0 +1,232 @@ +# Vault configuration for OTC production environment (otcinfra2) +# Based on preprod configuration, adapted for production use +# Deployed on otcinfra2 cluster where ArgoCD resides +# Values are nested under vault: for the dependency chart + +vault: + global: + enabled: true + tlsDisable: true + + # Vault Agent Injector configuration + injector: + enabled: true + replicas: 1 + + resources: + requests: + memory: 256Mi + cpu: 250m + limits: + memory: 512Mi + cpu: 500m + + agentImage: + repository: "hashicorp/vault" + tag: "1.19.0" + + agentDefaults: + cpuLimit: "500m" + cpuRequest: "250m" + memLimit: "256Mi" + memRequest: "128Mi" + template: "map" + + # Vault Server configuration + server: + enabled: true + + image: + repository: "hashicorp/vault" + tag: "1.19.0" + pullPolicy: IfNotPresent + + resources: + requests: + memory: 1Gi + cpu: 500m + limits: + memory: 2Gi + cpu: 1000m + + # Ingress configuration for external access + ingress: + enabled: true + ingressClassName: "nginx" + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + kubernetes.io/tls-acme: "true" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + cert-manager.io/private-key-algorithm: RSA + cert-manager.io/private-key-size: "4096" + cert-manager.io/private-key-rotation-policy: Always + hosts: + - host: vault-k8s.eco.tsi-dev.otc-service.com + paths: + - / + pathType: Prefix + tls: + - secretName: vault-tls-cert + hosts: + - vault-k8s.eco.tsi-dev.otc-service.com + + # Override the default service to use the main vault service instead of vault-active + activeService: false + + # Service configuration + service: + enabled: true + type: ClusterIP + port: 8200 + targetPort: 8200 + annotations: {} + + # Enable Kubernetes auth method + authDelegator: + enabled: true + + # Security context + securityContext: + runAsNonRoot: true + runAsUser: 100 + runAsGroup: 1000 + fsGroup: 1000 + + # Init containers to download plugins + # Note: GitHub plugin (vault-plugin-secrets-github) can be added later + # if dynamic GitHub token generation is needed (paths: github, github_otcbot, github_zuul) + extraInitContainers: + - name: download-openstack-plugin + image: "curlimages/curl:latest" + command: [sh, -c] + args: + - >- + echo "Downloading OpenStack plugin..." && + cd /tmp && + curl -L "https://github.com/opentelekomcloud/vault-plugin-secrets-openstack/releases/download/v1.3.3/vault-plugin-secrets-openstack_1.3.3_linux_amd64.tar.gz" + -o vault-plugin-secrets-openstack.tar.gz && + tar -xzf vault-plugin-secrets-openstack.tar.gz && + chmod +x vault-plugin-secrets-openstack && + cp vault-plugin-secrets-openstack /usr/local/libexec/vault/ && + echo "OpenStack plugin installed successfully" + volumeMounts: + - name: plugins + mountPath: /usr/local/libexec/vault + + # Extra volumes for plugins + volumes: + - name: plugins + emptyDir: {} + + # Volume mounts for plugins + volumeMounts: + - mountPath: /usr/local/libexec/vault + name: plugins + readOnly: true + + # Data storage configuration + dataStorage: + enabled: true + size: 10Gi + storageClass: "csi-disk-retain" + accessMode: ReadWriteOnce + + # Audit storage configuration + auditStorage: + enabled: true + size: 5Gi + storageClass: "csi-disk-retain" + accessMode: ReadWriteOnce + + # High Availability configuration + ha: + enabled: true + replicas: 3 + + # Integrated Raft storage for HA + raft: + enabled: true + setNodeId: true + + config: |- + # Plugin directory + plugin_directory = "/usr/local/libexec/vault" + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + telemetry { + unauthenticated_metrics_access = "true" + } + } + + storage "raft" { + path = "/vault/data" + + retry_join { + leader_api_addr = "http://vault-0.vault-internal:8200" + } + retry_join { + leader_api_addr = "http://vault-1.vault-internal:8200" + } + retry_join { + leader_api_addr = "http://vault-2.vault-internal:8200" + } + } + + # API and cluster addresses + api_addr = "https://vault-k8s.eco.tsi-dev.otc-service.com" + cluster_addr = "http://POD_IP:8201" + + # Telemetry configuration for monitoring + telemetry { + prometheus_retention_time = "30s" + disable_hostname = true + } + + # Audit logging + audit "file" { + type = "file" + path = "/vault/audit/audit.log" + } + + # Standalone configuration (disabled in favor of HA) + standalone: + enabled: false + + # UI configuration + ui: + enabled: false + serviceType: "ClusterIP" + + # CSI Provider (for secret injection) + csi: + enabled: true + + image: + repository: "hashicorp/vault-csi-provider" + tag: "1.5.0" + pullPolicy: IfNotPresent + + resources: + requests: + memory: 256Mi + cpu: 250m + limits: + memory: 256Mi + cpu: 250m + + volumeMountPath: "/vault/secrets" + + # Prometheus metrics + serverTelemetry: + prometheusOperator: true + + # Additional environment variables + extraEnvironmentVars: + VAULT_ADDR: "https://vault-k8s.eco.tsi-dev.otc-service.com" + VAULT_API_ADDR: "https://vault-k8s.eco.tsi-dev.otc-service.com"