Merge Phase-2 no-secret rollout prep into main
This commit is contained in:
16 files changed
+278
-53
No files matched your search
@@ -91,6 +91,67 @@ primary play runs, so a limit that excludes the primary yields
|
|||||||
self-contained. `ansible-playbook --syntax-check` OK on all playbooks; role lint clean under the CI
|
self-contained. `ansible-playbook --syntax-check` OK on all playbooks; role lint clean under the CI
|
||||||
gate (`make -C ci gate` PASS). No cluster change; the running cluster is untouched.
|
gate (`make -C ci gate` PASS). No cluster change; the running cluster is untouched.
|
||||||
|
|
||||||
|
## Phase 2 un-suspend PREP — no-secret rollout (author-first, 2026-08-16)
|
||||||
|
|
||||||
|
Operator directive: "Phase 2 un-suspend prep … verify cilium parity + fill REPLACE-MEs for the
|
||||||
|
no-secret components … author-first. Does NOT un-suspend/deploy yet." **Nothing was deployed;** the
|
||||||
|
running cluster is untouched (still 0 Flux Kustomizations / 0 HelmReleases — only controllers + the
|
||||||
|
GitRepository).
|
||||||
|
|
||||||
|
### Findings from three parallel readiness audits (Longhorn / monitoring / Kyverno)
|
||||||
|
- **Cilium parity VERIFIED (safety-critical):** every `[parity]` field in `platform/cilium/helmrelease.yaml`
|
||||||
|
equals the live CNI — `k8sServiceHost=10.73.0.11` (rendered by the chart as the pod env
|
||||||
|
`KUBERNETES_SERVICE_HOST`, **not** a configmap key — the empty `k8s-service-host` cm value was a red
|
||||||
|
herring), kube-proxy-replacement, gateway-api, egress+bpf masquerade, hubble, l2-announcements,
|
||||||
|
cgroup, v1.16.5. **Flux adoption is a no-op reconcile.**
|
||||||
|
- **Topology:** "suspended" = Option A (**no root Kustomization was ever created**), and the single base
|
||||||
|
`platform` Kustomization renders **all 11 components at once** — so a naive un-suspend would drag in
|
||||||
|
the secret-blocked components (cert-manager/externaldns/velero/keycloak/oauth2-proxy/image-automation)
|
||||||
|
and never go healthy (`apps` dependsOn platform, wait:true).
|
||||||
|
- **Longhorn:** manifests clean; node prereqs (open-iscsi, /var/lib/longhorn mount, iscsid) are in
|
||||||
|
cloud-init IaC; the REPLACE-ME bulk-NFS SC is isolated/harmless. Ready.
|
||||||
|
- **Monitoring:** right-sized (~1.25 GiB) but blocked by (a) needing the `longhorn` SC first and (b) two
|
||||||
|
**by-name secrets** (`grafana-admin-credentials`, `alertmanager-smtp`) that don't exist — the missing
|
||||||
|
mounts (not the REPLACE-ME text) are what wedge Grafana/Alertmanager.
|
||||||
|
- **Kyverno:** zero policy violations today (policies scope to `app-namespace=true`, none exist), but the
|
||||||
|
fail-closed webhook + 3 HA replicas + Enforce are unsafe/oversized for a cold cluster.
|
||||||
|
|
||||||
|
### DECISION D-P2A — per-component Flux Kustomizations for the dev no-secret rollout (operator-approved)
|
||||||
|
Chose **Option A (per-component Kustomizations)**. Authored `clusters/dev/rollout/` = 4 Flux
|
||||||
|
Kustomizations (`kyverno` → `cilium` → `longhorn` → `monitoring`, monitoring `dependsOn: longhorn`),
|
||||||
|
and the dev overlay now `$patch: delete`s the monolithic base `platform`/`apps` and adds `./rollout`.
|
||||||
|
The no-secret tier carries **no SOPS content**, so the missing `sops-age` key does not block it
|
||||||
|
(decryption dropped from these Kustomizations). Secret-blocked components are simply **not created**
|
||||||
|
until their hand-offs land.
|
||||||
|
- **cilium** uses `prune: false` (adopt the out-of-band CNI; never own-delete it).
|
||||||
|
- **R-ENV-02 divergence — REVIEW FLAG (architect):** the dev overlay now *adds resources* and diverges
|
||||||
|
its reconciliation graph from prod (base doc says "dev adds NO resources … graph reused verbatim").
|
||||||
|
This is a **transitional bootstrap scaffold**, consistent with the already-inlined dev bootstrap
|
||||||
|
patches (SOPS/URL). **Convergence path is coded in:** delete `clusters/dev/rollout/` + the two
|
||||||
|
`$patch: delete`s to re-adopt the unified base graph once all hand-offs arrive. **prod is untouched**
|
||||||
|
(still builds the monolith; verified). Flag for architect ratification.
|
||||||
|
|
||||||
|
### Component hardening (env-gated via cluster-vars so prod keeps the strict posture)
|
||||||
|
- New base knobs (strict/prod defaults) + dev overrides: `KYVERNO_VALIDATION_ACTION` (Enforce→**Audit**),
|
||||||
|
`KYVERNO_WEBHOOK_FAILURE_POLICY` (Fail→**Ignore**), `KYVERNO_ADMISSION_REPLICAS` (3→**1**),
|
||||||
|
`MONITORING_STORAGE_CLASS` (longhorn), `GRAFANA_ADMIN_SECRET` (grafana-admin-credentials→**""** in dev
|
||||||
|
→ chart-default password, dev-only/NAT-isolated).
|
||||||
|
- Kyverno webhook also gains an explicit `namespaceSelector` excluding kube-system/flux-system/kyverno/
|
||||||
|
longhorn-system/monitoring (belt-and-suspenders vs a fail-closed brick).
|
||||||
|
- Monitoring Alertmanager: the `alertmanager-smtp` mount + smtp_*/email_configs are **deferred** (commented
|
||||||
|
with verbatim re-enable markers) so Alertmanager starts with NULL receivers — alerts fire/route/show in
|
||||||
|
UI+Grafana, just no email until the Phase-4 SMTP hand-off.
|
||||||
|
- Validated: `kubectl kustomize` clean for clusters/dev (exactly the 4 Kustomizations, monolith removed),
|
||||||
|
clusters/prod (monolith intact), and all 4 components; envsubst→YAML-parse of kyverno+monitoring valid;
|
||||||
|
`make -C ci gate` PASS.
|
||||||
|
|
||||||
|
### Still required BEFORE an actual deploy (not part of this author-first prep)
|
||||||
|
1. **Push current `main` to the host Gitea** — Flux source is pinned at `6c3f9ed` (pre-dates these merges).
|
||||||
|
2. **Runtime node check:** `systemctl is-active iscsid` + `mountpoint /var/lib/longhorn` on all 3 nodes.
|
||||||
|
3. **Cilium adoption mechanism:** confirm helm-controller can adopt the Ansible-installed release (was it
|
||||||
|
`helm`-installed with a release secret, or CLI/kubectl?) — verify before creating the cilium Kustomization.
|
||||||
|
4. Create the dev root Kustomization (the actual un-suspend) — deliberately NOT done here.
|
||||||
|
|
||||||
## KNOWN ISSUES to fix before Phase-2 un-suspend (logged, not yet fixed)
|
## KNOWN ISSUES to fix before Phase-2 un-suspend (logged, not yet fixed)
|
||||||
| # | Issue | Impact | Fix |
|
| # | Issue | Impact | Fix |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|||||||
@@ -27,6 +27,15 @@ data:
|
|||||||
MONITORING_PROMETHEUS_RETENTION: "7d" # dev short | prod long
|
MONITORING_PROMETHEUS_RETENTION: "7d" # dev short | prod long
|
||||||
MONITORING_PROMETHEUS_PVC_SIZE: "10Gi"
|
MONITORING_PROMETHEUS_PVC_SIZE: "10Gi"
|
||||||
MONITORING_LOKI_PVC_SIZE: "10Gi"
|
MONITORING_LOKI_PVC_SIZE: "10Gi"
|
||||||
|
MONITORING_STORAGE_CLASS: "longhorn" # PVC tier for Prometheus/Loki (dev may override)
|
||||||
|
GRAFANA_ADMIN_SECRET: "grafana-admin-credentials" # by-name SOPS secret (prod); dev="" → chart default
|
||||||
|
|
||||||
|
# --- admission enforcement (Kyverno, ADR-0011). Base defaults are the STRICT/prod posture;
|
||||||
|
# the dev overlay softens them for bootstrap (Audit / Ignore / 1) so a cold cluster can
|
||||||
|
# come up before policies are proven and admission is HA. ---
|
||||||
|
KYVERNO_VALIDATION_ACTION: "Enforce" # dev: Audit (observe before enforcing)
|
||||||
|
KYVERNO_WEBHOOK_FAILURE_POLICY: "Fail" # dev: Ignore (fail-open during bootstrap)
|
||||||
|
KYVERNO_ADMISSION_REPLICAS: "3" # dev: 1 (no HA needed to iterate)
|
||||||
|
|
||||||
# --- identity / auth (ADR-0014) ---
|
# --- identity / auth (ADR-0014) ---
|
||||||
KEYCLOAK_REPLICAS: "1" # dev 1 | prod 2 (HA)
|
KEYCLOAK_REPLICAS: "1" # dev 1 | prod 2 (HA)
|
||||||
|
|||||||
@@ -2,39 +2,38 @@
|
|||||||
# clusters/dev — the ACTIVE build target overlay.
|
# clusters/dev — the ACTIVE build target overlay.
|
||||||
#
|
#
|
||||||
# `flux bootstrap ... --path=clusters/dev` points the dev cluster's Flux here.
|
# `flux bootstrap ... --path=clusters/dev` points the dev cluster's Flux here.
|
||||||
# This overlay adds NO resources of its own. Its ONLY job is to patch the shared cluster-vars
|
# TRANSITIONAL (Phase-2 bootstrap): this overlay reuses the base source + cluster-vars, but
|
||||||
# ConfigMap with dev-scoped values (R-ENV-02). The base reconciliation graph is reused verbatim.
|
# REPLACES the monolithic base `platform`/`apps` Kustomizations with a per-component NO-SECRET
|
||||||
|
# rollout (./rollout) — so dev brings up cilium/longhorn/monitoring/kyverno in order WITHOUT
|
||||||
|
# dragging in the secret-blocked components. It patches cluster-vars with dev values (R-ENV-02)
|
||||||
|
# and softens Kyverno for bootstrap. CONVERGENCE: delete ./rollout + the two $patch:delete
|
||||||
|
# below to return to the unified base graph once all operator hand-offs land. See DECISIONS-LOG.
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- ../base
|
- ../base
|
||||||
|
- rollout
|
||||||
patches:
|
patches:
|
||||||
# --- SOPS decryption + bootstrap source (PM-inlined from bootstrap/flux/patches/, 2026-08-16) ---
|
# --- Drop the monolithic base platform/apps Kustomizations. They reconcile ALL 11 components
|
||||||
# Inlined (not cross-dir file refs) to stay within kustomize's default load restrictions.
|
# as one unit (incl. secret-blocked cert-manager/externaldns/velero/keycloak/oauth2-proxy/
|
||||||
# Realises the commented decryption TODOs in clusters/base and sets the real source URL.
|
# image-automation/gateway/kubevirt); ./rollout replaces them with the per-component
|
||||||
# The `sops-age` Secret is created out-of-band (bootstrap/sops/install-age-key.sh) — name only.
|
# no-secret set for dev bootstrap. Remove these two deletes to re-adopt the unified graph.
|
||||||
|
# (No decryption patch is needed here: the rollout components carry no SOPS content, so
|
||||||
|
# the `sops-age` Secret is not required for the dev bootstrap tier.) ---
|
||||||
- patch: |-
|
- patch: |-
|
||||||
|
$patch: delete
|
||||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
metadata:
|
metadata:
|
||||||
name: platform
|
name: platform
|
||||||
namespace: flux-system
|
namespace: flux-system
|
||||||
spec:
|
|
||||||
decryption:
|
|
||||||
provider: sops
|
|
||||||
secretRef:
|
|
||||||
name: sops-age
|
|
||||||
- patch: |-
|
- patch: |-
|
||||||
|
$patch: delete
|
||||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
metadata:
|
metadata:
|
||||||
name: apps
|
name: apps
|
||||||
namespace: flux-system
|
namespace: flux-system
|
||||||
spec:
|
|
||||||
decryption:
|
|
||||||
provider: sops
|
|
||||||
secretRef:
|
|
||||||
name: sops-age
|
|
||||||
- patch: |-
|
- patch: |-
|
||||||
apiVersion: source.toolkit.fluxcd.io/v1
|
apiVersion: source.toolkit.fluxcd.io/v1
|
||||||
kind: GitRepository
|
kind: GitRepository
|
||||||
@@ -84,3 +83,18 @@ patches:
|
|||||||
- op: replace
|
- op: replace
|
||||||
path: /data/VELERO_RETENTION
|
path: /data/VELERO_RETENTION
|
||||||
value: "72h"
|
value: "72h"
|
||||||
|
# dev-softened Kyverno for cold-start (base defaults Enforce/Fail/3 = prod posture).
|
||||||
|
- op: replace
|
||||||
|
path: /data/KYVERNO_VALIDATION_ACTION
|
||||||
|
value: "Audit"
|
||||||
|
- op: replace
|
||||||
|
path: /data/KYVERNO_WEBHOOK_FAILURE_POLICY
|
||||||
|
value: "Ignore"
|
||||||
|
- op: replace
|
||||||
|
path: /data/KYVERNO_ADMISSION_REPLICAS
|
||||||
|
value: "1"
|
||||||
|
# dev: no Grafana admin Secret yet → empty makes the chart use its default admin password
|
||||||
|
# (dev-only, NAT-isolated). Prod keeps the by-name SOPS secret from the base default.
|
||||||
|
- op: replace
|
||||||
|
path: /data/GRAFANA_ADMIN_SECRET
|
||||||
|
value: ""
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
# Cilium — ADOPT the running Ansible-installed CNI. Parity between platform/cilium and the
|
||||||
|
# live config was verified (k8sServiceHost/port via pod env, kube-proxy-replacement, gateway-api,
|
||||||
|
# egress+bpf masquerade, hubble, l2, cgroup, v1.16.5) → this reconcile is a NO-OP.
|
||||||
|
# prune: false — Cilium was bootstrapped out-of-band; adopt its config, never own-delete the CNI.
|
||||||
|
# No decryption (no SOPS content). No dependsOn (it is the datapath everything else rides on).
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: cilium
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 30m
|
||||||
|
retryInterval: 2m
|
||||||
|
timeout: 10m
|
||||||
|
prune: false
|
||||||
|
wait: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
path: ./platform/cilium
|
||||||
|
postBuild:
|
||||||
|
substituteFrom:
|
||||||
|
- kind: ConfigMap
|
||||||
|
name: cluster-vars
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
# Phase-2 NO-SECRET incremental rollout — DEV bootstrap scaffold.
|
||||||
|
#
|
||||||
|
# Per-component Flux Kustomizations for the components that need NO operator secret:
|
||||||
|
# kyverno (admission, dev-softened) · cilium (adopt running CNI) · longhorn (storage)
|
||||||
|
# · monitoring (dependsOn longhorn).
|
||||||
|
# Secret-blocked components (cert-manager, externaldns, gateway, velero, keycloak,
|
||||||
|
# oauth2-proxy, image-automation, kubevirt) are intentionally NOT created until their
|
||||||
|
# operator hand-offs arrive (DECISIONS-LOG A2 / hand-off table).
|
||||||
|
#
|
||||||
|
# TRANSITIONAL: this is the dev bootstrap graph. Once every hand-off lands, dev drops
|
||||||
|
# ./rollout and re-adopts the unified base monolith (clusters/base/platform.yaml) so dev
|
||||||
|
# and prod share one reconciliation graph again (R-ENV-02 convergence). See DECISIONS-LOG.
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- kyverno.yaml # first: admission present (Audit/Ignore in dev — non-blocking) before workloads
|
||||||
|
- cilium.yaml # adopt the running CNI (parity verified — no-op reconcile)
|
||||||
|
- longhorn.yaml # storage; monitoring depends on its StorageClass
|
||||||
|
- monitoring.yaml # dependsOn longhorn
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
# Kyverno — in-cluster admission enforcer. DEV-SOFTENED via cluster-vars:
|
||||||
|
# KYVERNO_VALIDATION_ACTION=Audit, KYVERNO_WEBHOOK_FAILURE_POLICY=Ignore,
|
||||||
|
# KYVERNO_ADMISSION_REPLICAS=1 (base defaults Enforce/Fail/3 for prod).
|
||||||
|
# Non-blocking on today's cluster (policies match only `app-namespace=true` namespaces,
|
||||||
|
# none exist yet). No SOPS content. Listed first so admission is present before workloads.
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: kyverno
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 1h
|
||||||
|
retryInterval: 2m
|
||||||
|
timeout: 10m
|
||||||
|
prune: true
|
||||||
|
wait: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
path: ./platform/kyverno
|
||||||
|
postBuild:
|
||||||
|
substituteFrom:
|
||||||
|
- kind: ConfigMap
|
||||||
|
name: cluster-vars
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
# Longhorn — block storage / CSI (default `longhorn` StorageClass). No SOPS content.
|
||||||
|
# No dependsOn. The bulk-NFS StorageClass in this component carries REPLACE-ME (OQ-2) but is
|
||||||
|
# an isolated, unreferenced object — it does NOT block Longhorn's HelmRelease reconcile.
|
||||||
|
# PRE-DEPLOY CHECK (runtime, out of git): confirm `iscsid` active + /var/lib/longhorn mounted
|
||||||
|
# on all 3 nodes (cloud-init provides both; verify after the T-1.5 rebuild).
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: longhorn
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 10m
|
||||||
|
retryInterval: 2m
|
||||||
|
timeout: 10m
|
||||||
|
prune: true
|
||||||
|
wait: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
path: ./platform/longhorn
|
||||||
|
postBuild:
|
||||||
|
substituteFrom:
|
||||||
|
- kind: ConfigMap
|
||||||
|
name: cluster-vars
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
# Monitoring — kube-prometheus-stack + Loki + Alloy. Prometheus/Loki PVCs bind the
|
||||||
|
# `longhorn` StorageClass, so this dependsOn longhorn (Flux waits for it Ready first).
|
||||||
|
# Grafana admin password (chart default in dev) and Alertmanager SMTP (deferred to the
|
||||||
|
# Phase-4 hand-off) are handled WITHOUT an operator secret in dev → NO decryption block.
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: monitoring
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 10m
|
||||||
|
retryInterval: 2m
|
||||||
|
timeout: 15m
|
||||||
|
prune: true
|
||||||
|
wait: true
|
||||||
|
dependsOn:
|
||||||
|
- name: longhorn
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
path: ./platform/monitoring
|
||||||
|
postBuild:
|
||||||
|
substituteFrom:
|
||||||
|
- kind: ConfigMap
|
||||||
|
name: cluster-vars
|
||||||
@@ -34,18 +34,32 @@ spec:
|
|||||||
remediation:
|
remediation:
|
||||||
retries: 3
|
retries: 3
|
||||||
values:
|
values:
|
||||||
# HA admission controller so a single pod restart cannot open an enforcement gap.
|
# Admission controller replicas — cluster-vars knob (prod 3 / dev 1). HA so a single pod
|
||||||
|
# restart cannot open an enforcement gap; dev drops to 1 to save RAM during bootstrap.
|
||||||
admissionController:
|
admissionController:
|
||||||
replicas: 3
|
replicas: ${KYVERNO_ADMISSION_REPLICAS}
|
||||||
backgroundController:
|
backgroundController:
|
||||||
enabled: true # needed for generate (default-deny baseline) + sync
|
enabled: true # needed for generate (default-deny baseline) + sync
|
||||||
reportsController:
|
reportsController:
|
||||||
enabled: true
|
enabled: true
|
||||||
cleanupController:
|
cleanupController:
|
||||||
enabled: true
|
enabled: true
|
||||||
# Fail-CLOSED: if the webhook is unreachable, reject rather than silently admit
|
# Webhook failure policy — cluster-vars knob (prod Fail / dev Ignore). Fail is fail-CLOSED
|
||||||
# non-conforming resources (coordination-protocol.md #8). Tune per-resource in
|
# (reject rather than silently admit when the webhook is unreachable, coordination-protocol.md
|
||||||
# the policies' webhook config if a bootstrap deadlock appears.
|
# #8); dev uses Ignore (fail-open) so a cold/unhealthy Kyverno cannot brick admission during
|
||||||
|
# bootstrap. The namespaceSelector ALSO excludes system + platform namespaces at the webhook
|
||||||
|
# layer — belt-and-suspenders: policies already scope to app-namespace=true, but this
|
||||||
|
# guarantees kube-system/flux-system/CNI/storage/monitoring admission never routes to Kyverno.
|
||||||
config:
|
config:
|
||||||
webhooks:
|
webhooks:
|
||||||
- failurePolicy: Fail
|
- failurePolicy: ${KYVERNO_WEBHOOK_FAILURE_POLICY}
|
||||||
|
namespaceSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- key: kubernetes.io/metadata.name
|
||||||
|
operator: NotIn
|
||||||
|
values:
|
||||||
|
- kube-system
|
||||||
|
- flux-system
|
||||||
|
- kyverno
|
||||||
|
- longhorn-system
|
||||||
|
- monitoring
|
||||||
@@ -24,7 +24,7 @@ metadata:
|
|||||||
homelab.keithjackson.dev/requirements: R-SEC-01
|
homelab.keithjackson.dev/requirements: R-SEC-01
|
||||||
homelab.keithjackson.dev/scope: admission-only
|
homelab.keithjackson.dev/scope: admission-only
|
||||||
spec:
|
spec:
|
||||||
validationFailureAction: Enforce
|
validationFailureAction: ${KYVERNO_VALIDATION_ACTION}
|
||||||
background: false
|
background: false
|
||||||
rules:
|
rules:
|
||||||
- name: secrets-must-be-flux-managed
|
- name: secrets-must-be-flux-managed
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ metadata:
|
|||||||
Pods in application namespaces must declare
|
Pods in application namespaces must declare
|
||||||
homelab.keithjackson.dev/auth-mode = oidc | forward-auth | native | none.
|
homelab.keithjackson.dev/auth-mode = oidc | forward-auth | native | none.
|
||||||
spec:
|
spec:
|
||||||
validationFailureAction: Enforce
|
validationFailureAction: ${KYVERNO_VALIDATION_ACTION}
|
||||||
background: true
|
background: true
|
||||||
rules:
|
rules:
|
||||||
- name: require-auth-mode
|
- name: require-auth-mode
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ metadata:
|
|||||||
Pods in application namespaces must declare
|
Pods in application namespaces must declare
|
||||||
homelab.keithjackson.dev/egress-class = internet | vpn.
|
homelab.keithjackson.dev/egress-class = internet | vpn.
|
||||||
spec:
|
spec:
|
||||||
validationFailureAction: Enforce
|
validationFailureAction: ${KYVERNO_VALIDATION_ACTION}
|
||||||
background: true
|
background: true
|
||||||
rules:
|
rules:
|
||||||
- name: require-egress-class
|
- name: require-egress-class
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ metadata:
|
|||||||
Application-namespace pods must not use hostNetwork/hostPID/hostIPC (which
|
Application-namespace pods must not use hostNetwork/hostPID/hostIPC (which
|
||||||
bypass default-deny) or run privileged containers.
|
bypass default-deny) or run privileged containers.
|
||||||
spec:
|
spec:
|
||||||
validationFailureAction: Enforce
|
validationFailureAction: ${KYVERNO_VALIDATION_ACTION}
|
||||||
background: true
|
background: true
|
||||||
rules:
|
rules:
|
||||||
- name: disallow-host-namespaces
|
- name: disallow-host-namespaces
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ metadata:
|
|||||||
to include both Ingress and Egress. Rejects a baseline that silently leaves
|
to include both Ingress and Egress. Rejects a baseline that silently leaves
|
||||||
a direction open.
|
a direction open.
|
||||||
spec:
|
spec:
|
||||||
validationFailureAction: Enforce
|
validationFailureAction: ${KYVERNO_VALIDATION_ACTION}
|
||||||
background: true
|
background: true
|
||||||
rules:
|
rules:
|
||||||
- name: baseline-must-deny-both-directions
|
- name: baseline-must-deny-both-directions
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ spec:
|
|||||||
storageSpec:
|
storageSpec:
|
||||||
volumeClaimTemplate:
|
volumeClaimTemplate:
|
||||||
spec:
|
spec:
|
||||||
storageClassName: longhorn # replicated tier (ADR-0003)
|
storageClassName: ${MONITORING_STORAGE_CLASS} # replicated tier (ADR-0003); cluster-vars knob
|
||||||
accessModes: ["ReadWriteOnce"]
|
accessModes: ["ReadWriteOnce"]
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
@@ -80,22 +80,24 @@ spec:
|
|||||||
memory: 32Mi
|
memory: 32Mi
|
||||||
limits:
|
limits:
|
||||||
memory: 96Mi
|
memory: 96Mi
|
||||||
# SMTP credentials are an OPERATOR HAND-OFF: a SOPS-encrypted Secret, referenced BY NAME
|
# SMTP is an OPERATOR HAND-OFF DEFERRED to Phase 4 — no creds yet, and the by-name
|
||||||
# only (docs/07). Mounted at /etc/alertmanager/secrets/<name>/ so the config below can read
|
# `alertmanager-smtp` Secret does NOT exist. Mounting a missing Secret wedges Alertmanager
|
||||||
# the password from a file (never inline plaintext).
|
# in ContainerCreating, so the mount is DISABLED until the hand-off. Re-enable verbatim
|
||||||
secrets:
|
# (uncomment) when the SOPS `alertmanager-smtp` Secret is delivered. TODO(Phase-4 SMTP).
|
||||||
- alertmanager-smtp
|
# secrets:
|
||||||
# Routing/receivers. Non-secret SMTP endpoint fields (smarthost/from/to/username) are
|
# - alertmanager-smtp
|
||||||
# operator-supplied placeholders (REPLACE-ME, same convention as velero); the PASSWORD is
|
# Routing stays live; RECEIVERS ARE NULL until the SMTP hand-off (alerts still fire, route,
|
||||||
# read from the mounted SOPS secret via *_file. Both severities email; subject/route differ.
|
# and show in the Alertmanager UI + Grafana — they just aren't emailed). The smtp_* globals
|
||||||
|
# and email_configs are retained below, commented, so re-enabling is a pure uncomment.
|
||||||
config:
|
config:
|
||||||
global:
|
global:
|
||||||
resolve_timeout: 5m
|
resolve_timeout: 5m
|
||||||
smtp_smarthost: "REPLACE-ME:587" # operator hand-off (e.g. smtp.gmail.com:587)
|
# TODO(Phase-4 SMTP hand-off): uncomment together with the alertmanager-smtp Secret mount.
|
||||||
smtp_from: "REPLACE-ME" # operator hand-off (From: address)
|
# smtp_smarthost: "smtp.example.com:587"
|
||||||
smtp_auth_username: "REPLACE-ME" # operator hand-off (SMTP login)
|
# smtp_from: "[email protected]"
|
||||||
smtp_auth_password_file: /etc/alertmanager/secrets/alertmanager-smtp/smtp-password
|
# smtp_auth_username: "<smtp-login>"
|
||||||
smtp_require_tls: true
|
# smtp_auth_password_file: /etc/alertmanager/secrets/alertmanager-smtp/smtp-password
|
||||||
|
# smtp_require_tls: true
|
||||||
route:
|
route:
|
||||||
group_by: ["alertname", "namespace"]
|
group_by: ["alertname", "namespace"]
|
||||||
group_wait: 30s
|
group_wait: 30s
|
||||||
@@ -109,25 +111,29 @@ spec:
|
|||||||
group_wait: 10s
|
group_wait: 10s
|
||||||
repeat_interval: 1h
|
repeat_interval: 1h
|
||||||
receivers:
|
receivers:
|
||||||
|
# NULL receivers until Phase-4 SMTP. email_configs retained (commented) for verbatim re-enable.
|
||||||
- name: email-warning
|
- name: email-warning
|
||||||
email_configs:
|
# email_configs:
|
||||||
- to: "REPLACE-ME" # operator hand-off (alert inbox)
|
# - to: "<alert-inbox>"
|
||||||
send_resolved: true
|
# send_resolved: true
|
||||||
headers:
|
# headers:
|
||||||
subject: '[WARNING] {{ .CommonLabels.alertname }} ({{ .CommonLabels.namespace }})'
|
# subject: '[WARNING] {{ .CommonLabels.alertname }} ({{ .CommonLabels.namespace }})'
|
||||||
- name: email-critical
|
- name: email-critical
|
||||||
email_configs:
|
# email_configs:
|
||||||
- to: "REPLACE-ME" # operator hand-off (alert inbox)
|
# - to: "<alert-inbox>"
|
||||||
send_resolved: true
|
# send_resolved: true
|
||||||
headers:
|
# headers:
|
||||||
subject: '[CRITICAL] {{ .CommonLabels.alertname }} ({{ .CommonLabels.namespace }})'
|
# subject: '[CRITICAL] {{ .CommonLabels.alertname }} ({{ .CommonLabels.namespace }})'
|
||||||
|
|
||||||
# --- Grafana: dashboards (R-OBS-03), surfaced in the C2 UI (docs/12) behind Keycloak SSO ---
|
# --- Grafana: dashboards (R-OBS-03), surfaced in the C2 UI (docs/12) behind Keycloak SSO ---
|
||||||
grafana:
|
grafana:
|
||||||
enabled: true
|
enabled: true
|
||||||
# Admin bootstrap + (later) OIDC client are SOPS secrets referenced BY NAME (WS-SEC/ADR-0014).
|
# Admin bootstrap + (later) OIDC client are SOPS secrets referenced BY NAME (WS-SEC/ADR-0014).
|
||||||
|
# cluster-vars knob: prod = the SOPS `grafana-admin-credentials` Secret; dev = "" (empty),
|
||||||
|
# which makes the chart fall back to its default admin password (dev-only, NAT-isolated) so
|
||||||
|
# Grafana starts without an operator secret. TODO: supply a dev Secret if wider dev access.
|
||||||
admin:
|
admin:
|
||||||
existingSecret: grafana-admin-credentials
|
existingSecret: ${GRAFANA_ADMIN_SECRET}
|
||||||
userKey: admin-user
|
userKey: admin-user
|
||||||
passwordKey: admin-password
|
passwordKey: admin-password
|
||||||
# DEV TRIM: emptyDir (default). Dashboards are provisioned from ConfigMaps (sidecar), so no
|
# DEV TRIM: emptyDir (default). Dashboards are provisioned from ConfigMaps (sidecar), so no
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ spec:
|
|||||||
replicas: 1
|
replicas: 1
|
||||||
persistence:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
storageClass: longhorn # replicated tier (ADR-0003)
|
storageClass: ${MONITORING_STORAGE_CLASS} # replicated tier (ADR-0003); cluster-vars knob
|
||||||
size: ${MONITORING_LOKI_PVC_SIZE} # overlay knob (dev 5Gi / prod large)
|
size: ${MONITORING_LOKI_PVC_SIZE} # overlay knob (dev 5Gi / prod large)
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
|
|||||||
Reference in new issue
Block a user