93358505fc
Security: - Drop ALL capabilities, enforce non-root, RuntimeDefault seccomp on neuron-mcp, neuron-rest, neuron-marketing pods - Add startup probes (150s window for JVM) so liveness doesn't fire early - Replace docker-sock hostPath with BuildKit rootless TCP endpoint (moby/buildkit:v0.19.0-rootless) — removes node root access from CI - Document full ESO AppRole migration path in cluster-secret-store.yaml Autoscaling & availability: - HPAs on mcp (1–6), rest (1–4), marketing (2–8) at 65–70% CPU - PodDisruptionBudgets (minAvailable: 1) on all three services - NetworkPolicy: default-deny-all in neuron-prod, explicit allow rules for Traefik ingress, intra-namespace, and egress to DNS/platform/vault Observability: - ServiceMonitors for mcp, rest, marketing (cross-namespace enabled in kube-prometheus-stack with serviceMonitorSelectorNilUsesHelmValues:false) - PrometheusRules: high error rate, high latency, crash loops, replica shortage, Postgres down/connections, backup failure, backup staleness Chart version pinning: - kube-prometheus-stack, loki, tempo, redis, alloy, postgres — all pinned to major-version ranges to block silent breaking upgrades Backup hardening: - restic:latest → restic:0.17.3 (deterministic image) - Weekly backup-verify CronJob: restores latest snapshot and validates SQL dump structure (≥5 CREATE TABLE, pg_dump header check) ArgoCD: - neuron-prod AppProject: scopes deploys to neuron-prod + platform ns, blacklists ClusterRole/ClusterRoleBinding/Namespace creation, automated sync window 2–6am UTC, manual always allowed
70 lines
2.8 KiB
YAML
70 lines
2.8 KiB
YAML
---
|
|
# ClusterSecretStore — connects ESO to Vault
|
|
#
|
|
# CURRENT STATE: token auth (root token). This works but the root token
|
|
# has no expiry and full Vault access — it's a single credential that
|
|
# compromises everything if leaked.
|
|
#
|
|
# MIGRATION TO APPROLE (do this before GCP migration):
|
|
# ─────────────────────────────────────────────────────
|
|
# 1. Enable AppRole on Vault (if not already):
|
|
# vault auth enable approle
|
|
#
|
|
# 2. Create a policy scoped to only the paths ESO reads:
|
|
# vault policy write eso-read - <<EOF
|
|
# path "secret/data/neuron-technologies/*" { capabilities = ["read"] }
|
|
# path "secret/data/r2" { capabilities = ["read"] }
|
|
# path "secret/data/slack" { capabilities = ["read"] }
|
|
# path "secret/data/legion-db" { capabilities = ["read"] }
|
|
# EOF
|
|
#
|
|
# 3. Create the AppRole:
|
|
# vault write auth/approle/role/eso \
|
|
# token_policies="eso-read" \
|
|
# token_ttl=1h \
|
|
# token_max_ttl=4h \
|
|
# secret_id_ttl=0 # no expiry for the secretId
|
|
#
|
|
# 4. Get the roleId and secretId:
|
|
# vault read auth/approle/role/eso/role-id → ROLE_ID
|
|
# vault write -f auth/approle/role/eso/secret-id → SECRET_ID
|
|
#
|
|
# 5. Create the k8s secret (one-time bootstrap, same as the root token was):
|
|
# kubectl create secret generic vault-approle \
|
|
# --namespace external-secrets \
|
|
# --from-literal=roleId=<ROLE_ID> \
|
|
# --from-literal=secretId=<SECRET_ID>
|
|
#
|
|
# 6. Flip this file to the appRole stanza below, commit, push.
|
|
# ESO will hot-reload when the manifest changes.
|
|
#
|
|
# 7. Delete the old vault-token secret:
|
|
# kubectl delete secret vault-token -n external-secrets
|
|
# ─────────────────────────────────────────────────────
|
|
|
|
apiVersion: external-secrets.io/v1beta1
|
|
kind: ClusterSecretStore
|
|
metadata:
|
|
name: vault
|
|
spec:
|
|
provider:
|
|
vault:
|
|
server: "https://vault.neuralplatform.ai"
|
|
path: "secret"
|
|
version: "v2"
|
|
auth:
|
|
# ── Current: root token auth ──────────────────────────────────────────
|
|
# TODO: replace with appRole stanza once AppRole is bootstrapped (see above).
|
|
tokenSecretRef:
|
|
name: vault-token
|
|
namespace: external-secrets
|
|
key: token
|
|
# ── Target: AppRole auth (uncomment after bootstrap) ──────────────────
|
|
# appRole:
|
|
# path: "approle"
|
|
# roleId: "eso"
|
|
# secretRef:
|
|
# name: vault-approle
|
|
# namespace: external-secrets
|
|
# key: secretId
|