feat: deploy n8n on the cluster
The runtime for the estate's autonomous agents. Postgres as a StatefulSet with its own claim, n8n behind a cert-manager certificate and the nginx ingress, and both secrets in Vault behind an AppRole scoped to n8n/* alone. The certificate is issued into the cluster rather than copied into the repo, so the private key never reaches git. The CA certificate is committed and mounted as NODE_EXTRA_CA_CERTS, so a workflow can reach keycloak and the domain gateways without anyone disabling verification to make it work. One replica and strategy: Recreate, both deliberate — n8n needs Redis and queue mode before a second scheduler is safe, and a ReadWriteOnce volume deadlocks a rolling update. N8N_ENCRYPTION_KEY is generated once and kept in Vault. Regenerating it does not re-encrypt the stored credentials, it orphans them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# One AppRole per estate, each with a policy that can read only its own path.
|
||||
# The n8n store cannot read `keycloak/*` and the keycloak store cannot read
|
||||
# `n8n/*` — so a compromised workload in one namespace does not become a way
|
||||
# into the other's secrets.
|
||||
#
|
||||
# Its Vault side is created by hand, once:
|
||||
#
|
||||
# vault policy write n8n-readonly - <<'HCL'
|
||||
# path "secrets/data/n8n/*" { capabilities = ["read"] }
|
||||
# path "secrets/metadata/n8n/*" { capabilities = ["list", "read"] }
|
||||
# HCL
|
||||
# vault write auth/approle/role/n8n-eso token_policies=n8n-readonly \
|
||||
# token_ttl=1h token_max_ttl=4h
|
||||
# kubectl -n external-secrets create secret generic vault-approle-n8n \
|
||||
# --from-literal=roleId=$(vault read -field=role_id auth/approle/role/n8n-eso/role-id) \
|
||||
# --from-literal=secretId=$(vault write -f -field=secret_id auth/approle/role/n8n-eso/secret-id)
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ClusterSecretStore
|
||||
metadata:
|
||||
name: vault-n8n
|
||||
spec:
|
||||
provider:
|
||||
vault:
|
||||
server: https://vault.sttlab.eu
|
||||
path: secrets
|
||||
version: v2
|
||||
auth:
|
||||
appRole:
|
||||
path: approle
|
||||
roleRef:
|
||||
name: vault-approle-n8n
|
||||
namespace: external-secrets
|
||||
key: roleId
|
||||
secretRef:
|
||||
name: vault-approle-n8n
|
||||
namespace: external-secrets
|
||||
key: secretId
|
||||
@@ -0,0 +1,22 @@
|
||||
# Two values, both of which must outlive any pod.
|
||||
#
|
||||
# N8N_ENCRYPTION_KEY encrypts the credentials n8n stores in its database. Lose
|
||||
# it and every stored credential becomes unreadable — the workflows survive, the
|
||||
# credentials do not. It is generated once and kept in Vault for that reason,
|
||||
# never regenerated on a redeploy.
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: n8n-secrets
|
||||
namespace: n8n
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: vault-n8n
|
||||
target:
|
||||
name: n8n-secrets
|
||||
creationPolicy: Owner
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: n8n/instance
|
||||
Reference in New Issue
Block a user