Cerrar el auto-reinicio: fail-closed, permisos por nombre y fallo visible
Tres agujeros que encontro una review adversarial de Codex sobre el trabajo de ayer. Los tres verificados contra el fichero antes de tocar nada. 1. El webhook publico fallaba en ABIERTO. Con `optional: true` y un `if (secreto && ...)`, la ausencia del secreto equivalia a autorizacion: cualquier POST con heartbeat.status=0 y un monitor.name del mapa reiniciaba n8n, Gitea, ArgoCD, Vaultwarden o los blogs. El argumento para dejarlo asi era "que un despiste no te deje sin avisos", y era falso: el aviso de caida lo manda Kuma por su notificacion 1, directa, no por este webhook. Ahora el secreto es obligatorio (sin `optional`) y la puerta rechaza si falta. Secreto ausente da error ruidoso; cabecera que no casa, vacio. 2. El ClusterRole permitia patch sobre CUALQUIER deployment de 10 namespaces, y la SA va montada en el pod entero de n8n: cualquier workflow con un nodo Code heredaba eso. Ahora son Roles por namespace con resourceNames sobre los 12 workloads exactos del SERVICE_MAP. 3. El flujo daba exito aunque el PATCH fallara. `restartOk` se calculaba y se tiraba: `K8s API Check Status` construia un objeto nuevo sin el, y el IF final solo miraba `statusOk`. Un 403 sobre un servicio ya sano mandaba "reiniciado correctamente". Ahora hay rama de fallo tras el PATCH y el exito exige observedGeneration >= la generation que devolvio el PATCH. Se compara con >= porque selfHeal de ArgoCD revierte la anotacion y vuelve a subir la generation; con == daria un fallo falso. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+126
-25
@@ -1,4 +1,5 @@
|
||||
# Permisos para el workflow "Uptime Kuma -> K8s Auto-Restart" (2026-07-30).
|
||||
# Permisos para el workflow "Uptime Kuma -> K8s Auto-Restart" (2026-07-30,
|
||||
# acotado el 2026-07-31 tras una review adversarial de Codex).
|
||||
#
|
||||
# El nodo Code lee el token proyectado de la ServiceAccount y hace un PATCH
|
||||
# contra kubernetes.default.svc. Nunca habia existido ningun Role ni RoleBinding
|
||||
@@ -8,28 +9,34 @@
|
||||
#
|
||||
# SA propia y no la "default" del namespace: cualquier pod que se despliegue en
|
||||
# n8n sin pedir SA hereda la default, y estos permisos cruzan namespaces.
|
||||
#
|
||||
# ⚠️ Esta SA va montada en el pod ENTERO de n8n, no solo en este workflow: los
|
||||
# nodos Code leen el token de /var/run/secrets/..., asi que CUALQUIER workflow
|
||||
# con un nodo Code hereda lo que aqui se conceda. Por eso el permiso no es
|
||||
# "deployments del namespace" sino resourceNames con los 12 workloads EXACTOS
|
||||
# del SERVICE_MAP. Si se anade un servicio al mapa, hay que anadirlo aqui o
|
||||
# dara 403 -- deliberado: que el mapa y el permiso se muevan juntos.
|
||||
#
|
||||
# Antes habia un ClusterRole sin resourceNames + un RoleBinding por namespace.
|
||||
# Eso permitia reiniciar CUALQUIER deployment de esos 10 namespaces. Ahora son
|
||||
# Roles por namespace, cada uno con su lista cerrada.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: n8n
|
||||
---
|
||||
# get ademas de patch: tras el PATCH el workflow espera 60 s y RELEE el
|
||||
# deployment para comprobar readyReplicas -- sin get, ese chequeo posterior
|
||||
# devolvia "Cannot read properties of undefined (reading 'replicas')".
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: n8n
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
# statefulsets porque Gitea lo es (no un Deployment) y esta en el mapa.
|
||||
resources: ["deployments", "statefulsets"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["n8n"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
# Un RoleBinding por namespace y no un ClusterRoleBinding: el webhook que
|
||||
# dispara esto es PUBLICO y sin autenticar, asi que el alcance de lo que
|
||||
# puede reiniciar un desconocido debe ser exactamente el SERVICE_MAP.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
@@ -37,7 +44,7 @@ metadata:
|
||||
namespace: n8n
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
@@ -45,13 +52,24 @@ subjects:
|
||||
namespace: n8n
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: monitoring
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["kube-prometheus-stack-grafana", "uptime-kuma"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: monitoring
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
@@ -59,13 +77,24 @@ subjects:
|
||||
namespace: n8n
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: homarr
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["homarr"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: homarr
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
@@ -73,13 +102,24 @@ subjects:
|
||||
namespace: n8n
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: gitea
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["statefulsets"]
|
||||
resourceNames: ["gitea"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: gitea
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
@@ -87,13 +127,24 @@ subjects:
|
||||
namespace: n8n
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: authentik
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["authentik-server"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: authentik
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
@@ -101,13 +152,24 @@ subjects:
|
||||
namespace: n8n
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: vaultwarden
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["vaultwarden"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: vaultwarden
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
@@ -115,22 +177,41 @@ subjects:
|
||||
namespace: n8n
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: ollama
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["ollama"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: ollama
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: n8n-restarter
|
||||
namespace: n8n
|
||||
---
|
||||
# Los dos blogs (2026-07-30). Son lo unico de aqui que da la cara al publico, y
|
||||
# justo lo que peor se lleva con quince dias sin nadie mirando: si Ghost se
|
||||
# atasca, que al menos se intente un reinicio antes de que llegue el aviso.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: ghost-en
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["ghost-en"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
@@ -138,7 +219,7 @@ metadata:
|
||||
namespace: ghost-en
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
@@ -146,21 +227,41 @@ subjects:
|
||||
namespace: n8n
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: zona-exclusion
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["zona-exclusion"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: zona-exclusion
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: n8n-restarter
|
||||
namespace: n8n
|
||||
---
|
||||
# argocd el ultimo a proposito: reiniciar argocd-server o argocd-repo-server
|
||||
# es lo mas agresivo del mapa (deja el cluster sin reconciliar un rato).
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: n8n-restarter
|
||||
namespace: argocd
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
resourceNames: ["argocd-repo-server", "argocd-server"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
@@ -168,7 +269,7 @@ metadata:
|
||||
namespace: argocd
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
name: n8n-restarter
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
|
||||
Reference in New Issue
Block a user