Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a namespace named databases. KUBECONFIG is an environment variable, so the Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at config-hosteurope - a different cluster. Had the environment pointed at the other reachable cluster instead of an unauthorized one, make apps-pg-deploy would have applied RPF-WP-0019 connection limits, role timeouts and backup config to the wrong cluster and reported success. The Unauthorized error was the only thing that prevented it. Filename selection cannot protect against this: both kubeconfigs resolve to a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard pins identity instead, comparing the live kube-system namespace UID against RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell. Verified refusing on the wrong cluster, refusing when unreachable, and passing on railiance01. Not global: db-status legitimately targets the other cluster for gitea-db. RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our wiring was wrong. RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two unrelated problems: CCR-2026-0010 is an active lane missing its whole openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite has no way to express. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
1cbde550b5
commit
b1f973c2d5
4 changed files with 227 additions and 13 deletions
51
Makefile
51
Makefile
|
|
@ -1,9 +1,25 @@
|
|||
SHELL := /usr/bin/env bash
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
KUBECONFIG ?= $(firstword $(wildcard $(HOME)/.kube/config) $(HOME)/.kube/config-hosteurope)
|
||||
KUBECONFIG ?= $(firstword $(wildcard $(HOME)/.kube/config-railiance01) $(wildcard $(HOME)/.kube/config))
|
||||
KUBECTL_BIN ?= $(firstword $(shell command -v kubectl 2>/dev/null) $(wildcard $(HOME)/.local/bin/kubectl) kubectl)
|
||||
KUBECTL := $(KUBECTL_BIN) --kubeconfig=$(KUBECONFIG)
|
||||
|
||||
# Cluster identity pin (RPF-WP-0019).
|
||||
#
|
||||
# Two reachable clusters each carry a CNPG Cluster named `apps-pg` in a
|
||||
# namespace named `databases`. Selecting by kubeconfig FILENAME does not
|
||||
# protect against applying to the wrong one: KUBECONFIG is an environment
|
||||
# variable, `?=` does not override it, and both files resolve to a
|
||||
# 127.0.0.1 tunnel port. So the guard pins cluster IDENTITY instead —
|
||||
# the kube-system namespace UID, which is unique per cluster and stable
|
||||
# for its lifetime.
|
||||
#
|
||||
# railiance01 (k3s v1.35.1) carries platform-pg, forgejo-db and both
|
||||
# apps-pg consumers. The other cluster carries gitea-db and only one
|
||||
# apps-pg consumer; `db-status` legitimately targets it, which is why the
|
||||
# guard is applied per-target rather than globally.
|
||||
RAILIANCE01_CLUSTER_UID ?= a553c742-0115-43d4-99a4-a5ca56fe0786
|
||||
HELM := helm --kubeconfig=$(KUBECONFIG)
|
||||
NAMESPACE := platform
|
||||
|
||||
|
|
@ -82,10 +98,31 @@ consumption-preflight: ## Refuse a new order that exceeds a restricted entity's
|
|||
|
||||
##@ Shared apps-pg (S5 application databases)
|
||||
|
||||
cluster-id: ## Print the kube-system UID of the cluster KUBECONFIG currently selects
|
||||
@echo "kubeconfig: $(KUBECONFIG)"
|
||||
@echo "cluster-uid: $$($(KUBECTL) get ns kube-system -o jsonpath='{.metadata.uid}' 2>/dev/null || echo UNREACHABLE)"
|
||||
|
||||
railiance01-guard: ## Fail closed unless KUBECONFIG selects railiance01
|
||||
@uid=$$($(KUBECTL) get ns kube-system -o jsonpath='{.metadata.uid}' 2>/dev/null); \
|
||||
if [ -z "$$uid" ]; then \
|
||||
echo "railiance01-guard: cluster unreachable via $(KUBECONFIG)" >&2; \
|
||||
echo " hint: unset KUBECONFIG, or set it to ~/.kube/config-railiance01" >&2; \
|
||||
exit 2; \
|
||||
fi; \
|
||||
if [ "$$uid" != "$(RAILIANCE01_CLUSTER_UID)" ]; then \
|
||||
echo "railiance01-guard: REFUSING - wrong cluster" >&2; \
|
||||
echo " kubeconfig: $(KUBECONFIG)" >&2; \
|
||||
echo " expected: $(RAILIANCE01_CLUSTER_UID)" >&2; \
|
||||
echo " found: $$uid" >&2; \
|
||||
echo " both clusters carry apps-pg in namespace databases; applying here would hit the wrong one." >&2; \
|
||||
exit 2; \
|
||||
fi; \
|
||||
echo "railiance01-guard: ok ($$uid)"
|
||||
|
||||
apps-pg-verify-capacity: ## Verify cell ceiling, role limits and distinct backup prefixes
|
||||
python3 tools/verify_apps_pg_capacity.py helm/apps-pg-cluster.yaml helm/apps-pg-2-cluster.yaml
|
||||
|
||||
apps-pg-deploy: apps-pg-verify-capacity ## Apply shared apps-pg cnpg Cluster + NetworkPolicies
|
||||
apps-pg-deploy: railiance01-guard apps-pg-verify-capacity ## Apply shared apps-pg cnpg Cluster + NetworkPolicies
|
||||
@if [ -n "$(CONSUMING_ENTITY)" ]; then \
|
||||
$(MAKE) consumption-preflight CONSUMING_ENTITY='$(CONSUMING_ENTITY)' \
|
||||
ESTIMATE_EUR='$(ESTIMATE_EUR)' CONSUMPTION_CLASS=new-order; \
|
||||
|
|
@ -93,11 +130,11 @@ apps-pg-deploy: apps-pg-verify-capacity ## Apply shared apps-pg cnpg Cluster + N
|
|||
$(KUBECTL) apply -f helm/apps-pg-cluster.yaml
|
||||
$(KUBECTL) apply -f helm/apps-pg-networkpolicies.yaml
|
||||
|
||||
apps-pg-backup-deploy: ## Apply the daily backup after the governed S3 Secret exists
|
||||
apps-pg-backup-deploy: railiance01-guard ## Apply the daily backup after the governed S3 Secret exists
|
||||
@$(KUBECTL) get secret platform-pg-backup-s3 -n databases >/dev/null || { echo 'missing governed platform-pg-backup-s3 secret' >&2; exit 2; }
|
||||
$(KUBECTL) apply -f helm/apps-pg-backup.yaml
|
||||
|
||||
apps-pg-overflow-dry-run: apps-pg-verify-capacity ## Server-validate the unapplied overflow cell
|
||||
apps-pg-overflow-dry-run: railiance01-guard apps-pg-verify-capacity ## Server-validate the unapplied overflow cell
|
||||
@$(KUBECTL) get secret apps-pg-2-credentials -n databases >/dev/null || { echo 'missing distinct apps-pg-2-credentials secret' >&2; exit 2; }
|
||||
$(KUBECTL) apply --dry-run=server -f helm/apps-pg-2-cluster.yaml
|
||||
$(KUBECTL) apply --dry-run=server -f helm/apps-pg-2-networkpolicies.yaml
|
||||
|
|
@ -108,11 +145,11 @@ apps-pg-overflow-deploy: apps-pg-overflow-dry-run ## Provision only for an appro
|
|||
$(KUBECTL) apply -f helm/apps-pg-2-networkpolicies.yaml
|
||||
$(KUBECTL) apply -f helm/apps-pg-2-backup.yaml
|
||||
|
||||
apps-pg-status: ## Show apps-pg cnpg cluster health
|
||||
apps-pg-status: railiance01-guard ## Show apps-pg cnpg cluster health
|
||||
$(KUBECTL) cnpg status apps-pg -n databases 2>/dev/null || \
|
||||
$(KUBECTL) get cluster apps-pg -n databases -o wide
|
||||
|
||||
apps-pg-shell: ## Open psql shell on apps-pg primary as apps_admin / apps_meta
|
||||
apps-pg-shell: railiance01-guard ## Open psql shell on apps-pg primary as apps_admin / apps_meta
|
||||
$(KUBECTL) cnpg psql apps-pg -n databases -- -U apps_admin apps_meta 2>/dev/null || \
|
||||
$(KUBECTL) exec -it -n databases apps-pg-1 -- psql -U apps_admin apps_meta
|
||||
|
||||
|
|
@ -383,7 +420,7 @@ argocd-status: ## Show Railiance ArgoCD projects, root app, and registered repos
|
|||
|
||||
##@ Backup
|
||||
|
||||
RAILIANCE01_KUBECONFIG ?= $(HOME)/.kube/config-hosteurope
|
||||
RAILIANCE01_KUBECONFIG ?= $(HOME)/.kube/config-railiance01
|
||||
|
||||
backup: ## Backup platform services (PostgreSQL logical dump) — age-encrypted to Nextcloud
|
||||
@test -x tools/cmd/railiance-backup || { echo "tools/cmd/railiance-backup not installed; use forgejo-backup for Forgejo." >&2; exit 1; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue