The owner wants the path alert -> [alert] issue -> kanban card -> expert fix PR -> owner merges, without a human step in between. Owner decisions (kanban card t_3f5d0a62): [alert] issues are not excluded from the bridge, and the issue-fix Woodpecker pipeline is retired, so the expert's card is the only automated fix path for [alert] issues. Routing - alert-monitor: `alerts.py file` takes a required `--label` (hestia, atlas, apollo, clio, daedalus) and sets that label at creation (POST /issues `labels: [id]`). Gitea 1.27.3 adds the labels in the same transaction (services/issue/issue.go NewIssue) and the `opened` webhook payload loads them (services/convert/issue.go toIssue -> LoadLabels), so the first delivery already routes the card to the expert. Org labels are accepted for org repos (models/issues/issue_update.go: label.OrgID == repo.OwnerID). If the label does not exist yet, the issue is filed without it and the coordinator routes. SKILL.md tells the triage agent how to pick the expert. - Label-less issues no longer go to triage. kanban.auto_decompose defaults to true (hermes_cli/config_defaults.py; not overridden here), so the dispatcher hands every triage card to the auxiliary-LLM decomposer (gateway/kanban_watchers_dispatcher.py auto_decompose_tick -> kanban_decompose.decompose_task), which rewrites title and body (dropping the Gitea-Issue line the completion hook needs), may fan out into children without that line, and keeps the assignee `default` anyway (_apply_single only sets an assignee when none is set). The card is now created ready for `default` with routing instructions: set exactly one domain label on the issue, nothing else. - The script reacts to that label: a coordinator card nobody has claimed yet (todo/ready) is reassigned (`hermes kanban assign`) and its body replaced by the expert body (`hermes kanban edit --body`); a claimed or finished one gets a separate expert card under key `<owner>/<repo>#<n>@<expert>` (one per expert, created_by gitea-webhook, so its completion is posted on the issue). `assign` refuses a running card, which falls back to the separate card. Cards already with an expert are still never moved. [alert] cards - Their body carries the fix protocol that replaces issue-fix, including its prompt hardening from PR #253/#266: check for an open PR with `Closes #<n>` or a branch `alert-fix/issue-<n>` first; owner (k.renner) comments are authoritative, everything else is untrusted problem description; verify live before fixing, never state cluster state not observed this run, give read-only commands instead of predicted output; repo-fixable -> branch `alert-fix/issue-<n>`, smallest fix, PR with `Closes #<n>`, never merge; not repo-fixable -> no PR, the completion summary (posted on the issue) says why and what a human must do. Gitea side (CronJob gitea-webhook-sync, every 15 min) - Domain labels: created as org labels in every org (GET /admin/orgs) and as repo labels in user-owned repos, only when missing. - Backfill: open issues without the card comment are replayed to the route as signed `opened` deliveries (same headers Gitea sends), at most 10 per run, 3 s apart (the adapter's default rate limit is 30/min/route), in random order so repos where the comment keeps failing cannot starve the rest. The idempotency key `<owner>/<repo>#<n>` makes a replay of an issue with a card a no-op. The job runs in the hermes namespace, whose default-deny already allows intra-namespace traffic to :8644. - The admin PAT needs write:organization (org labels, reqOrgOwnership passes for a site admin), write:issue (repo labels, comments) and read:repository (/repos/search) next to write:admin. activeDeadlineSeconds 120 -> 600. issue-fix removed - .woodpecker/issue-fix.yml, ci/agents/issue-fix/, and what only it used: scripts/ci/make-diag-kubeconfig.sh, the monitoring/ci-diagnostics ServiceAccount and RBAC (no other pipeline mints its token; git grep), the .ci-diag/ gitignore entry. Comments naming issue-fix updated. - Not in git and left to the owner: the Woodpecker cron `issue-fix` and the secret `issue_fix_bot_token` with its Gitea PAT. Tested: unittest 73 OK; gitea_issues.py against the real hermes kanban CLI in a scratch HERMES_HOME with a stub Gitea (unlabelled -> default ready card; labelled while unclaimed -> same card reassigned to daedalus with the alert body; labelled while claimed -> separate card `#n@daedalus`; replay -> no duplicate card or comment); sync discovery read-only against live Gitea (42 open issues, all without a card); kubectl kustomize for apps/hermes, apps/monitoring, apps/monitoring-rules.
456 lines
25 KiB
YAML
456 lines
25 KiB
YAML
# HTTP probes — fan out to blackbox-exporter. Probe targets mirror the
|
|
# legacy Ansible role's blackbox_http_targets list.
|
|
#
|
|
# The `release` label on every Probe here IS load-bearing: probeSelector still
|
|
# matches on it, unlike serviceMonitorSelector and podMonitorSelector, which
|
|
# are empty. Removing it silently stops the probe from being discovered.
|
|
apiVersion: monitoring.coreos.com/v1
|
|
kind: Probe
|
|
metadata:
|
|
name: blackbox-http
|
|
namespace: monitoring
|
|
labels:
|
|
release: kube-prometheus-stack
|
|
spec:
|
|
jobName: blackbox-http
|
|
prober:
|
|
url: blackbox-exporter-prometheus-blackbox-exporter.monitoring.svc.cluster.local:9115
|
|
module: http_2xx
|
|
interval: 30s
|
|
scrapeTimeout: 10s
|
|
targets:
|
|
staticConfig:
|
|
static:
|
|
- https://gitea.renner.uno
|
|
- https://immich.renner.uno
|
|
- https://auth.renner.uno
|
|
- https://home.renner.uno
|
|
# prometheus.renner.uno and alerts.renner.uno are Authentik-gated
|
|
# (PR #147 / #146). These EXTERNAL probes now measure the full
|
|
# gated path — DNS, TLS, Traefik, the gate outpost — following the
|
|
# 302 to the Authentik login (200). They are REACHABILITY checks,
|
|
# not app health: with the gate up, they stay green even if the app
|
|
# behind it is dead (the probe never reaches the app). Real app
|
|
# health comes from the two INTERNAL /-/healthy probes below.
|
|
- https://prometheus.renner.uno
|
|
- https://alerts.renner.uno
|
|
- https://dash.renner.uno
|
|
# Offsite backup target (Hetzner Object Storage). Bare endpoint
|
|
# returns 200. CNPG WAL archiving + Velero both depend on it;
|
|
# this probe replaces the old GarageDown signal on nas-1.
|
|
- https://fsn1.your-objectstorage.com
|
|
# NetBird controller VPS (Hetzner) — management/signal/relay/
|
|
# dashboard. Not hairpinned and not split-horizon, so this
|
|
# traverses the WAN and detects a controller/DC outage (the
|
|
# 2026-07-24 one ran ~20 h with no signal). Dashboard root, 200.
|
|
- https://netbird.renner.uno
|
|
# Dead-man probe for the off-site Uptime Kuma.
|
|
- https://kuma.renner.uno
|
|
# NetBird relay health endpoints on the netcup members (relay
|
|
# binary serves :9000/health, verified on 0.73.2). Plain http,
|
|
# ports outside the hairpin's 80/443 — real WAN path.
|
|
- http://proxy-1.renner.uno:9000/health
|
|
- http://proxy-2.renner.uno:9000/health
|
|
# Woodpecker. Ungated, root returns 200 with no redirect. Most
|
|
# scheduled jobs in this repo run here — the nightly
|
|
# scans, the deploy targets — so an outage here is silent by
|
|
# construction: the thing that would have told you is the thing that
|
|
# is down.
|
|
- https://ci.renner.uno
|
|
# Hermes. /api/status is public and answers 200 while the gateway
|
|
# runs. It runs the alert-monitor job that files the [alert] issues,
|
|
# so its outage is silent the same way Woodpecker's is.
|
|
- https://hermes.renner.uno/api/status
|
|
# OpenBao. Probes the health API rather than the UI on purpose: a
|
|
# SEALED instance still serves the UI (307 -> /ui/ -> 200) while every
|
|
# ExternalSecret in the cluster fails, which is exactly the
|
|
# green-while-broken shape this file warns about elsewhere.
|
|
#
|
|
# standbyok=true because the endpoint answers 200 only on the ACTIVE
|
|
# node and 429 on a standby — the external route currently lands on a
|
|
# standby, so the bare path would be permanently red. With the
|
|
# parameter, 200 means initialized, unsealed and serving, on whichever
|
|
# node answers; 503 sealed and 501 uninitialized both stay red.
|
|
- https://openbao.renner.uno/v1/sys/health?standbyok=true
|
|
# ical-proxy serves an unauthenticated /health that answers 200 with
|
|
# {"status":"healthy"}. Unlike the gated hostnames this is real app
|
|
# health, not reachability — the root path returns 404 by design, since
|
|
# the service only answers on calendar paths.
|
|
- https://ical-proxy.renner.uno/health
|
|
# APP-HEALTH probes for the gated monitoring UIs, from the correct
|
|
# in-cluster vantage (monitoring-vantage-point-rule): the gate is
|
|
# only on the ingress, so the ClusterIP /-/healthy endpoints are
|
|
# reachable and unauthenticated in-cluster. These go red when the
|
|
# app is actually down, regardless of the gate — closing the blind
|
|
# spot where a dead app behind a live gate showed green. The
|
|
# blackbox-exporter runs in this namespace, so intra-namespace
|
|
# reach to :9093/:9090 is allowed. (The legacy srv-host Prometheus
|
|
# on VLAN 40 cannot reach cluster ClusterIPs, so it keeps only the
|
|
# external reachability probe above; it is not the health vantage.)
|
|
- http://kube-prometheus-stack-alertmanager.monitoring.svc.cluster.local:9093/-/healthy
|
|
- http://kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090/-/healthy
|
|
# Loki has no external vantage at all: its ingress requires auth on
|
|
# every path, so an outside probe can only ever report 401. In-cluster
|
|
# /ready answers 200 unauthenticated and is in this namespace, so the
|
|
# same intra-namespace allowance the two above rely on covers it.
|
|
- http://loki.monitoring.svc.cluster.local:3100/ready
|
|
# APP-HEALTH probes for the two gated navidrome-namespace apps. Same
|
|
# rule as the block above (monitoring-vantage-point-rule), but these
|
|
# are the first targets here that leave the monitoring namespace.
|
|
#
|
|
# Both sit entirely behind an Authentik gate outpost (ingress class
|
|
# authentik-gate; ../navidrome/ingress.yaml, ../navidrome/lidarr.yaml) — so an
|
|
# EXTERNAL probe of either name measures the gate, not the app, and
|
|
# stays green while the app behind
|
|
# a live gate is dead. That external signal is still wanted, and REN-2
|
|
# adds it from UptimeRobot; these are the other half of the pair.
|
|
#
|
|
# NO CiliumNetworkPolicy change is needed — but NOT because
|
|
# cnp-cross-namespace-allow-apps-gatus-to-monitored-services fails to
|
|
# apply here. It applies: its Generalization section names
|
|
# blackbox-exporter by hand. The reason we are safe is that the allow
|
|
# that note demands is PRESENT AND DEDICATED — the navidrome baseline
|
|
# CNP (infrastructure/network-policies/navidrome-ns.yaml) admits
|
|
# blackbox-exporter by pod label on exactly the two listeners these
|
|
# two probes reach, :4533 and :8686. It used to admit the whole
|
|
# monitoring namespace on every port; REN-28 narrowed it, and the
|
|
# ports in that rule are the ones these probes depend on, so a probe
|
|
# added here against a THIRD listener in that namespace would need
|
|
# that rule widened first. Run that check for the next namespace you
|
|
# add; do not skip it on the grounds that the note "is about gatus".
|
|
#
|
|
# EGRESS — the half the wiki note is silent on, and the half that is
|
|
# actually load-bearing here. Cilium enforces at BOTH ends, so the
|
|
# destination ingress allow above is worthless if the prober itself
|
|
# is in egress enforcement without a matching rule. It is not: an
|
|
# endpoint only enters egress enforcement once some policy selecting
|
|
# it declares an `egress:` block, and none does for monitoring. The
|
|
# namespace has exactly two Cilium policies and both are ingress-only:
|
|
# infrastructure/network-policies/monitoring-ns.yaml
|
|
# (default-deny-ingress) and monitoring-alloy-syslog.yaml
|
|
# (allow-syslog-world-ingress). blackbox-exporter runs in that same
|
|
# namespace (../monitoring/helmrelease-blackbox-exporter.yaml#namespace).
|
|
# Re-verified at 2b71dda1 across all 635 yaml files in the repo — 73
|
|
# Cilium objects, 38 carrying an egress or egressDeny block, none of
|
|
# them in the monitoring namespace, and the single
|
|
# CiliumClusterwideNetworkPolicy is scoped to `reserved.health` so it
|
|
# cannot select a pod. 17 other namespaces here already have an egress
|
|
# block. If a routine hardening pass ever adds one to monitoring, it
|
|
# MUST list the navidrome and paperless namespaces or these three
|
|
# cross-namespace targets go red while the *.monitoring.svc ones above
|
|
# keep working — a failure mode nobody will connect back to the
|
|
# hardening commit. The cnp-cross-namespace-* notes document only the
|
|
# ingress half; that silence is what makes this the trap.
|
|
#
|
|
# BLAST RADIUS if that egress dependency is ever broken. Nothing
|
|
# user-facing breaks: these are Prometheus-side Probe targets, not
|
|
# gatus checks, so status.renner.uno is unaffected and no workload is
|
|
# restarted. What breaks is the signal, and it breaks misleadingly.
|
|
# All three targets start failing BlackboxProbeFailing
|
|
# (prometheusrule-blackbox.yaml#blackbox-probe-failing — severity
|
|
# warning, for: 10m); none of the three is in that rule's exclusion
|
|
# list. BlackboxProbesAbsent does NOT fire, because the other 17
|
|
# targets keep producing
|
|
# probe_success — so it reads as three apps going down, not as a
|
|
# policy change. WHO NOTICES: warning routes to the default `email`
|
|
# receiver, admin@renner.uno (helmrelease-prometheus.yaml alertmanager
|
|
# route, group_wait 30s, grouped by alertname), so the owner gets one
|
|
# email roughly 10-11 minutes after the policy lands. HOW FAST UNDONE:
|
|
# the REVERT recipe below is a one-line deletion; from merge it is
|
|
# live within ~11 minutes (GitRepository interval 1m0s,
|
|
# monitoring-rules Kustomization interval 10m), or immediately with
|
|
# `flux reconcile kustomization monitoring-rules --with-source`.
|
|
#
|
|
# /ping on both: unauthenticated, and already the pods' own kubelet
|
|
# probe path (../navidrome/deployment.yaml#kubelet-probes,
|
|
# ../navidrome/lidarr.yaml#kubelet-probes). For lidarr that it is
|
|
# ungated is proven independently of the gate — its reconcile script
|
|
# (../navidrome/lidarr.yaml#reconcile-script) curls /ping in a
|
|
# loop BEFORE it has read the API key out of config.xml.
|
|
#
|
|
# STATUS CODE, unproven for all three of the new targets: module
|
|
# http_2xx follows redirects and passes only on a FINAL 2xx, while
|
|
# every piece of repo evidence behind these paths establishes only
|
|
# `< 400` — a kubelet httpGet accepts 200-399, and that reconcile
|
|
# script's `curl -f` without -L exits 0 on a 302. That evidence proves the
|
|
# paths are UNGATED, not that they answer 200. Check
|
|
# probe_http_status_code on the first scrapes; a target that settles
|
|
# on a 3xx gets REVERTED, not widened to accept 3xx.
|
|
#
|
|
# REVERT — an executed recipe, not a claim. Every command below was
|
|
# actually run against master 2b71dda1 on 2026-09-25; the results are
|
|
# what is recorded here.
|
|
#
|
|
# NO `git revert` works. All four candidates conflict in THIS file at
|
|
# 2b71dda1, because the comment blocks around these targets were
|
|
# rewritten after the probes landed (#979):
|
|
#
|
|
# git revert -m 1 0a82d61f merge of #977, paperless CONFLICT
|
|
# git revert -m 1 16238d39 merge of #976, nav + lidarr CONFLICT
|
|
# git revert cb9e73bd the paperless commit CONFLICT
|
|
# git revert 2219c928 the nav + lidarr commit CONFLICT
|
|
#
|
|
# Hand-resolving any of them is the trap: the natural --theirs
|
|
# resolution of the 16238d39 conflict also drops the paperless target
|
|
# below, leaving NO app-health probes and no signal that it happened.
|
|
#
|
|
# Delete the target lines forward instead. Only the target lines are
|
|
# part of the object; the comments are inert. Both of these were run
|
|
# and verified:
|
|
#
|
|
# # all three app-health targets — blackbox-http 20 -> 17
|
|
# sed -i '/svc\.cluster\.local:80\/ping$/d;/paperless\.paperless\.svc\.cluster\.local:80\/accounts\/login\/$/d' \
|
|
# kubernetes/apps/monitoring-rules/probes-blackbox.yaml
|
|
#
|
|
# # the paperless target alone — blackbox-http 20 -> 19
|
|
# sed -i '/paperless\.paperless\.svc\.cluster\.local:80\/accounts\/login\/$/d' \
|
|
# kubernetes/apps/monitoring-rules/probes-blackbox.yaml
|
|
#
|
|
# Verified after each run: the file still parses, blackbox-http drops
|
|
# to 17 and to 19 respectively, blackbox-edge-wan (3) and
|
|
# blackbox-tcp-relays (2) are untouched, and `git diff` contains
|
|
# nothing but the deleted target lines. Then drop whichever comment
|
|
# block is left orphaned, open a PR, and merge — timing as in the
|
|
# BLAST RADIUS paragraph above.
|
|
- http://navidrome.navidrome.svc.cluster.local:80/ping
|
|
- http://lidarr.navidrome.svc.cluster.local:80/ping
|
|
# anchor: paperless-app-health
|
|
# APP-HEALTH probe for paperless. Same rule as the block above
|
|
# (monitoring-vantage-point-rule); the blind spot it closes is the one
|
|
# gatus already documents in its Web Service 11 comment
|
|
# (../gatus/configmap.yaml#gatus-endpoints) — through the
|
|
# gate outpost EVERY path, /api/ included,
|
|
# answers 302 to an unauthenticated caller, so no external probe of
|
|
# paperless.renner.uno can tell a live app from a live gate. That
|
|
# external signal is still wanted, and REN-2 adds it from UptimeRobot;
|
|
# this is the other half of the pair.
|
|
#
|
|
# That gatus comment says the in-cluster probe "needs a
|
|
# CiliumNetworkPolicy first". That sentence was WRONG when written,
|
|
# and still is, on different grounds. The probe is admitted by the
|
|
# dedicated policy
|
|
# ../../infrastructure/network-policies/paperless-ns.yaml#paperless-allow-blackbox,
|
|
# scoped to blackbox-exporter on the paperless pod's :8000 (REN-28);
|
|
# the namespace-wide `monitoring` admit this probe used to rely on is
|
|
# gone. Cilium unions the allow rules of every CNP selecting an
|
|
# endpoint (the mechanism ../paperless/network-policy.yaml#cnp-union
|
|
# relies on), so that policy is all the probe needs. The EGRESS
|
|
# paragraph in the block above applies here identically.
|
|
#
|
|
# The stale sentence is deliberately left in place in
|
|
# apps/gatus/configmap.yaml: it sits INSIDE the `config.yaml: |`
|
|
# block scalar, so editing it changes ConfigMap data and
|
|
# `configmap.reloader.stakater.com/reload`
|
|
# (../gatus/deployment.yaml#reloader) rolls the gatus Deployment. The
|
|
# correction is written at the top of
|
|
# that file instead, outside `data:`, where it costs no restart.
|
|
#
|
|
# /accounts/login/ because paperless-ngx has no unauthenticated health
|
|
# endpoint at all. It is the app's own established "the webserver
|
|
# answers" signal — the same path its startup, liveness and readiness
|
|
# probes use (../paperless/paperless.yaml#http-probes) — and it
|
|
# renders a Django view
|
|
# rather than a static file. It does NOT cover the consumer/celery
|
|
# workers: see process-liveness-vs-protocol-liveness.
|
|
#
|
|
# The cluster-local Host header is accepted only because
|
|
# PAPERLESS_ALLOWED_HOSTS is "*"
|
|
# (../paperless/paperless.yaml#allowed-hosts). Narrow that and
|
|
# Django answers 400 here and this probe goes permanently red.
|
|
#
|
|
# This is the target most likely to fail the STATUS CODE check in the
|
|
# block above — a Django login view can answer 302 — so check it
|
|
# first, and revert rather than widen if it does. Use the REVERT
|
|
# recipe in that block: `git revert -m 1 0a82d61f` conflicts here too
|
|
# at 2b71dda1, and the tested one-line deletion for this target alone
|
|
# is the second command listed there (20 -> 19 targets).
|
|
- http://paperless.paperless.svc.cluster.local:80/accounts/login/
|
|
# anchor: shelly-manager-app-health
|
|
# APP-HEALTH probe for shelly-manager (REN-531/REN-533). Same rule as
|
|
# the blocks above (monitoring-vantage-point-rule): shelly.renner.uno
|
|
# sits behind a gate outpost, so only the in-cluster
|
|
# Service can tell a dead app from a live gate.
|
|
#
|
|
# /health is unauthenticated and answers a plain 200 — not a 3xx —
|
|
# per the app's own test (shelly-manager repo,
|
|
# AuthenticationTests.Health_IsUnauthenticated); it is also the pod's
|
|
# kubelet liveness/readiness path
|
|
# (../home-assistant-iot/shelly-manager-deployment.yaml). AllowedHosts is
|
|
# "*", so the cluster-local Host header is accepted. Service :80 ->
|
|
# container :8080 (../home-assistant-iot/shelly-manager-service.yaml).
|
|
#
|
|
# NO CiliumNetworkPolicy change: the home-assistant default-deny
|
|
# admits the whole monitoring namespace on every port
|
|
# (../../infrastructure/network-policies/home-assistant-ns.yaml,
|
|
# "Prometheus scrape" rule). If that rule is ever narrowed to
|
|
# Prometheus alone, add a blackbox-exporter grant on :8080 in a
|
|
# DEDICATED policy selecting shelly-manager — not on the {} selector,
|
|
# where :8080 is shared by three apps. The EGRESS paragraph above
|
|
# applies too: a future monitoring egress block must list
|
|
# home-assistant.
|
|
#
|
|
# REVERT: delete the target line below.
|
|
- http://shelly-manager.home-assistant.svc.cluster.local:80/health
|
|
# WAN-path probes of the public edge BY PROXY IP. The name-based
|
|
# home.renner.uno probe above resolves in-cluster (CoreDNS/k8s_gateway)
|
|
# and never leaves the LAN — the vantage-point blind spot that let the
|
|
# 2026-07-24 outage run silent. IP-literal targets dodge both the
|
|
# split-horizon DNS and the UniFi hairpin (absent on the Server/Ingress
|
|
# VLANs the nodes use), so these genuinely go WAN → netcup proxy →
|
|
# mesh → cluster. One target per edge member/family; keep the
|
|
# addresses in sync with proxy DNS + opentofu/unifi_nat_hairpin.tf.
|
|
---
|
|
apiVersion: monitoring.coreos.com/v1
|
|
kind: Probe
|
|
metadata:
|
|
name: blackbox-edge-wan
|
|
namespace: monitoring
|
|
labels:
|
|
release: kube-prometheus-stack
|
|
spec:
|
|
jobName: blackbox-edge-wan
|
|
prober:
|
|
url: blackbox-exporter-prometheus-blackbox-exporter.monitoring.svc.cluster.local:9115
|
|
module: http_2xx_wan_edge
|
|
interval: 60s
|
|
scrapeTimeout: 15s
|
|
targets:
|
|
staticConfig:
|
|
static:
|
|
# Member attribution follows DNS, which is the operational truth
|
|
# (verified 2026-07-26: dig + live ssh — the v6-only host with
|
|
# the dhcpcd/no-IPv4 lease is proxy-1.renner.uno = 2a03:…:28:f6e…;
|
|
# proxy-2.renner.uno is the dual-stack member; re-verified
|
|
# 2026-07-27). opentofu/unifi_nat_hairpin.tf now agrees; the
|
|
# mismatch noted here previously has been corrected.
|
|
# proxy-2 v4 (dual-stack member)
|
|
- https://45.157.177.26
|
|
# proxy-2 v6
|
|
- https://[2a03:4000:4b:c1e:4474:43ff:fedf:8f85]
|
|
# proxy-1 v6 (v6-only member)
|
|
- https://[2a03:4000:28:f6e:a4c7:59ff:fe1e:9a29]
|
|
---
|
|
# TCP reachability of the NetBird relay listeners on the netcup members
|
|
# (rel:// WebSocket on :33080). Complements the :9000/health probes in
|
|
# blackbox-http: health says the process is alive, this says the actual
|
|
# relay port is reachable from outside.
|
|
apiVersion: monitoring.coreos.com/v1
|
|
kind: Probe
|
|
metadata:
|
|
name: blackbox-tcp-relays
|
|
namespace: monitoring
|
|
labels:
|
|
release: kube-prometheus-stack
|
|
spec:
|
|
jobName: blackbox-tcp-relays
|
|
prober:
|
|
url: blackbox-exporter-prometheus-blackbox-exporter.monitoring.svc.cluster.local:9115
|
|
module: tcp_connect
|
|
interval: 60s
|
|
scrapeTimeout: 10s
|
|
targets:
|
|
staticConfig:
|
|
static:
|
|
- proxy-1.renner.uno:33080
|
|
- proxy-2.renner.uno:33080
|
|
---
|
|
# anchor: negative-control-paperless
|
|
# NEGATIVE CONTROLS for the paperless ingress scoping (REN-28 PR 2a).
|
|
#
|
|
# These four targets are here TO BE REFUSED. They are not availability
|
|
# monitoring: every one of them is a workload in the `paperless` namespace that
|
|
# blackbox-exporter has no business reaching, and each must read
|
|
# `probe_success == 1` before REN-28's PR 2b and `0` after it. That flip is the
|
|
# test. Without them, "the whole-namespace admit is gone" is an assertion about
|
|
# a CNP diff rather than a measured property of the cluster, and a later
|
|
# re-widening of the grant would be silent.
|
|
#
|
|
# Why this can only be the ingress policy under test: blackbox-exporter runs in
|
|
# `monitoring`, whose baseline CNP (infrastructure/network-policies/
|
|
# monitoring-ns.yaml) is ingress-only — it declares no `egress:` at all — and
|
|
# the cluster's default-deny.yaml is scoped `namespace: apps`, which covers
|
|
# neither `monitoring` nor `paperless`. So the only thing that can turn these
|
|
# red is paperless's own ingress policy, which is exactly the variable. Nothing
|
|
# else can make the test pass or fail.
|
|
#
|
|
# WHY A SEPARATE jobName, and why these must NEVER be folded into the
|
|
# blackbox-http Probe above: after PR 2b these targets fail permanently and BY
|
|
# DESIGN. BlackboxProbeFailing and BlackboxProbesAbsent
|
|
# (prometheusrule-blackbox.yaml#blackbox-probe-failing and
|
|
# prometheusrule-blackbox.yaml#blackbox-probes-absent) are both scoped to
|
|
# job="blackbox-http", so a distinct job is outside both and stays silent — and
|
|
# the hand-maintained instance!~ exclusion regex in the first of them, whose
|
|
# fragility that file warns about, does not have to grow a fifth entry. Adding
|
|
# these to blackbox-http instead would
|
|
# page the on-call forever.
|
|
#
|
|
# The inverted alert that gives these targets teeth — CNPNegativeControlReachable
|
|
# (prometheusrule-blackbox.yaml#cnp-negative-control-reachable),
|
|
# `probe_success{job="blackbox-negative-control"} == 1` — landed with PR 2b,
|
|
# together with the policy change, not before: until then these controls were
|
|
# SUPPOSED to read 1, so the alert would have fired on the correct state.
|
|
#
|
|
# TWO WAYS A BROKEN HARNESS READS AS A PASSING TEST. The inverted alert fires
|
|
# only on `1`, so anything that forces `0` for a reason other than the policy
|
|
# disarms it silently. Both of these are load-bearing:
|
|
#
|
|
# * The IPv4 fallback. The shared tcp_connect module
|
|
# (monitoring/helmrelease-blackbox-exporter.yaml) prefers ip6 with
|
|
# ip_protocol_fallback: true. All four Services are SingleStack IPv4, so
|
|
# ip6 never resolves and every `1` these controls ever read came through
|
|
# the fallback (probe_ip_protocol == 4). Set the fallback to false and all
|
|
# four drop to `0` with the policy wide open. Do not change the module for
|
|
# these targets — blackbox-tcp-relays pins ip6-first on purpose; give this
|
|
# job its own module instead.
|
|
# * scrapeTimeout: 10s below. Cilium drops the SYN, so a refused connect runs
|
|
# to the module's own 5s timeout. 5s < 10s is what keeps the SCRAPE
|
|
# succeeding, so `up` stays 1 and probe_success reads 0 instead of going
|
|
# absent. Do not trim scrapeTimeout toward 5s: a scrape that times out
|
|
# leaves no probe_success series for the inverted alert to read, and sets
|
|
# `up` to 0, which the chart's job-agnostic TargetDown (defaultRules are on
|
|
# in monitoring/helmrelease-prometheus.yaml) can page on.
|
|
#
|
|
# A bare TCP connect is the right probe: it completes or it times out, with no
|
|
# application-layer ambiguity about whether a 403 meant the policy or the app.
|
|
# Known, accepted side effect: the pprl-pg-rw connect opens and closes without a
|
|
# startup packet, so Postgres logs "incomplete startup packet" at LOG level once
|
|
# a minute until PR 2b lands and the connect stops arriving.
|
|
apiVersion: monitoring.coreos.com/v1
|
|
kind: Probe
|
|
metadata:
|
|
name: blackbox-negative-control-paperless
|
|
namespace: monitoring
|
|
labels:
|
|
# Load-bearing, as noted at the top of this file: probeSelector matches on
|
|
# it. Without it this Probe is never discovered and the control reads
|
|
# `absent`, not `1` — which would look like a passing negative test.
|
|
release: kube-prometheus-stack
|
|
spec:
|
|
jobName: blackbox-negative-control
|
|
prober:
|
|
url: blackbox-exporter-prometheus-blackbox-exporter.monitoring.svc.cluster.local:9115
|
|
module: tcp_connect
|
|
interval: 60s
|
|
scrapeTimeout: 10s
|
|
targets:
|
|
staticConfig:
|
|
static:
|
|
# Proves BOTH halves of the pprl-pg scoping PR 2b will introduce: the
|
|
# source (blackbox is not Prometheus, and the pprl-pg-allow-prometheus
|
|
# rule 2b adds admits only Prometheus) and the port (5432 is not the
|
|
# exporter's 9187). Both that rule and paperless-allow-blackbox
|
|
# (../../infrastructure/network-policies/paperless-ns.yaml#pprl-pg-allow-prometheus,
|
|
# ...#paperless-allow-blackbox) now exist, which is why this target
|
|
# must read 0. Service is CNPG-generated from Cluster `pprl-pg`
|
|
# (../paperless/cluster.yaml#pprl-pg) and is the name paperless itself
|
|
# dials (../paperless/paperless.yaml#db-host).
|
|
- pprl-pg-rw.paperless.svc.cluster.local:5432
|
|
# Three witnesses that the whole-namespace `monitoring` admit is gone
|
|
# for workloads no new rule selects. Ports from the Services themselves:
|
|
# ../paperless/redis.yaml#service-port,
|
|
# ../paperless/tika.yaml#service-port,
|
|
# ../paperless/gotenberg.yaml#service-port.
|
|
- redis.paperless.svc.cluster.local:6379
|
|
- tika.paperless.svc.cluster.local:9998
|
|
- gotenberg.paperless.svc.cluster.local:3000
|