Merge pull request 'feat(network-policies): scope paperless database/monitoring ingress per workload (REN-28 PR 2b)' (#1044) from atlas/ren-148-paperless-ingress-scoping into master
This commit was merged in pull request #1044.
This commit is contained in:
@@ -44,14 +44,15 @@ metadata:
|
||||
# production restart for a comment. This header is outside `data:`, so it is
|
||||
# inert; the served config is byte-identical.
|
||||
#
|
||||
# * ../../infrastructure/network-policies/paperless-ns.yaml#monitoring-admit
|
||||
# ALREADY admits the whole monitoring namespace on every port, and
|
||||
# monitoring is where
|
||||
# blackbox-exporter runs. Cilium unions the allow rules of every CNP
|
||||
# selecting an endpoint, so no extra policy is required.
|
||||
# * The in-cluster paperless probe therefore exists, added with no change
|
||||
# to the paperless namespace:
|
||||
# monitoring-rules/probes-blackbox.yaml, target
|
||||
# * When written, the paperless baseline admitted the whole monitoring
|
||||
# namespace on every port, and monitoring is where blackbox-exporter runs.
|
||||
# That admit is gone (REN-28); the probe is now admitted by the dedicated
|
||||
# ../../infrastructure/network-policies/paperless-ns.yaml#paperless-allow-blackbox,
|
||||
# scoped to blackbox-exporter on the paperless pod's :8000. Cilium unions
|
||||
# the allow rules of every CNP selecting an endpoint, so nothing else is
|
||||
# required.
|
||||
# * The in-cluster paperless probe therefore exists:
|
||||
# ../monitoring-rules/probes-blackbox.yaml#paperless-app-health, target
|
||||
# http://paperless.paperless.svc.cluster.local:80/accounts/login/
|
||||
# * The claim does NOT generalise to gatus itself. Gatus runs in `apps`,
|
||||
# which is a different source namespace; a gatus-sourced in-cluster check
|
||||
|
||||
@@ -222,16 +222,16 @@ spec:
|
||||
# this is the other half of the pair.
|
||||
#
|
||||
# That gatus comment says the in-cluster probe "needs a
|
||||
# CiliumNetworkPolicy first". That sentence is simply WRONG, not
|
||||
# merely inapplicable:
|
||||
# ../../infrastructure/network-policies/paperless-ns.yaml#monitoring-admit
|
||||
# already admits the whole monitoring namespace, which is where
|
||||
# blackbox-exporter runs,
|
||||
# and Cilium unions the allow rules of every CNP selecting an
|
||||
# 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). The EGRESS paragraph in the block above applies here
|
||||
# identically. Nothing in the paperless namespace is edited by this
|
||||
# change.
|
||||
# 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: |`
|
||||
@@ -324,12 +324,13 @@ spec:
|
||||
- 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` today and `0` once REN-28's PR 2b lands. That flip is the
|
||||
# `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.
|
||||
@@ -353,10 +354,31 @@ spec:
|
||||
# these to blackbox-http instead would
|
||||
# page the on-call forever.
|
||||
#
|
||||
# The inverted alert that gives these targets teeth — CNPNegativeControlReachable,
|
||||
# `probe_success{job="blackbox-negative-control"} == 1` — ships in PR 2b, NOT
|
||||
# here. Between 2a and 2b these controls are SUPPOSED to read 1, so the alert
|
||||
# would fire on the correct state. Do not add it before the policy change.
|
||||
# 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.
|
||||
@@ -386,9 +408,10 @@ spec:
|
||||
# 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). Neither that rule nor paperless-allow-blackbox
|
||||
# exists yet at this commit — that is the point, and why this target
|
||||
# must read 1 now. Service is CNPG-generated from Cluster `pprl-pg`
|
||||
# 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
|
||||
|
||||
@@ -21,11 +21,14 @@
|
||||
# live rule set on 2026-09-26. This is deliberate: a monitor that pages for an
|
||||
# expected failure is worse than no monitor.
|
||||
#
|
||||
# NO POLICY CHANGE IS NEEDED TO MAKE THESE WORK TODAY. Ingress: the paperless
|
||||
# baseline still admits the whole `monitoring` namespace on every port
|
||||
# (../../infrastructure/network-policies/paperless-ns.yaml#monitoring-admit) —
|
||||
# that admission is
|
||||
# exactly what REN-28 removes. Egress: the `monitoring` namespace is in no
|
||||
# NO POLICY CHANGE WAS NEEDED TO MAKE THESE WORK — SUPERSEDED BY REN-28 PR 2b.
|
||||
# Ingress: until PR 2b the paperless baseline admitted the whole `monitoring`
|
||||
# namespace on every port; that admission is gone, and blackbox-exporter now
|
||||
# reaches only the paperless pod on :8000, via
|
||||
# ../../infrastructure/network-policies/paperless-ns.yaml#paperless-allow-blackbox.
|
||||
# Every target below is therefore expected to read 0 from PR 2b's reconcile on:
|
||||
# that is the "after" reading, and once it is taken this file is deleted
|
||||
# (REN-159). Egress: the `monitoring` namespace is in no
|
||||
# egress enforcement at all; its only two Cilium policies, monitoring-ns.yaml
|
||||
# and monitoring-alloy-syslog.yaml, are ingress-only. Same dependency the three
|
||||
# cross-namespace targets in probes-blackbox.yaml already rest on.
|
||||
|
||||
@@ -59,3 +59,43 @@ spec:
|
||||
they are all blind until it returns.
|
||||
labels:
|
||||
severity: critical
|
||||
# The inverted alert for the paperless negative controls (REN-28 §5.6).
|
||||
# A separate group so nothing above can grow a matcher for it by accident.
|
||||
- name: blackbox-negative-control.rules
|
||||
rules:
|
||||
# The four targets in probes-blackbox.yaml#negative-control-paperless are
|
||||
# paperless-namespace workloads that blackbox-exporter must NOT reach
|
||||
# since REN-28 PR 2b scoped the paperless ingress to per-workload
|
||||
# policies. `0` is healthy here and `1` is the failure: a control that
|
||||
# connects means the grant was re-widened — a namespace-wide
|
||||
# `monitoring` admit back on the paperless baseline, or a per-workload
|
||||
# policy whose selector grew — and blackbox, a pod with no business
|
||||
# there, can open TCP into the namespace whose paperless pod trusts
|
||||
# X-Authentik-Username from anything that connects.
|
||||
#
|
||||
# Scoped to job="blackbox-negative-control" ONLY. Never
|
||||
# blackbox-ren28-negative (temporary, deleted on REN-159) and never
|
||||
# blackbox-http, whose targets are meant to read 1.
|
||||
#
|
||||
# This rule reads 0 as silence, so anything that forces 0 for another
|
||||
# reason disarms it without a sound. The two known ways are commented
|
||||
# on the Probe: the IPv4 fallback of the shared tcp_connect module, and
|
||||
# scrapeTimeout staying above the module's 5s timeout.
|
||||
#
|
||||
# for: 15m rides out a short policy churn during a Cilium restart or a
|
||||
# CNP re-apply; a real re-widening is permanent and fires regardless.
|
||||
# anchor: cnp-negative-control-reachable
|
||||
- alert: CNPNegativeControlReachable
|
||||
expr: probe_success{job="blackbox-negative-control"} == 1
|
||||
for: 15m
|
||||
annotations:
|
||||
summary: "Negative control {{ $labels.instance }} is reachable from blackbox-exporter"
|
||||
description: >-
|
||||
blackbox-exporter in `monitoring` completed a TCP connect to
|
||||
{{ $labels.instance }}, which the paperless ingress policy must
|
||||
refuse (REN-28). The paperless ingress grant has been re-widened.
|
||||
Check infrastructure/network-policies/paperless-ns.yaml for a
|
||||
namespace-wide `monitoring` admit on default-deny-ingress, or a
|
||||
per-workload policy whose selector now matches this workload.
|
||||
labels:
|
||||
severity: warning
|
||||
|
||||
@@ -5,23 +5,25 @@
|
||||
# any pod that can talk directly to paperless's HTTP listener can claim any
|
||||
# username.
|
||||
#
|
||||
# WHAT IS ENFORCED IS WIDER THAN THIS RULE. default-deny-ingress in
|
||||
# WHAT IS ENFORCED IS WIDER THAN THIS RULE, though no longer by much.
|
||||
# default-deny-ingress in
|
||||
# kubernetes/infrastructure/network-policies/paperless-ns.yaml uses
|
||||
# `endpointSelector: {}`, so it selects this pod as well, and Cilium unions
|
||||
# the allow rules of every CNP selecting an endpoint (the same mechanism
|
||||
# described below). The effective ingress set is this outpost PLUS, on
|
||||
# every port:
|
||||
# - the paperless namespace
|
||||
# `endpointSelector: {}`, so it selects this pod as well, and Cilium unions the
|
||||
# allow rules of every CNP selecting an endpoint (the same mechanism described
|
||||
# below). The effective ingress set for THIS pod is the paperless-gate outpost
|
||||
# below, plus:
|
||||
# - the paperless namespace, every port
|
||||
# ../../infrastructure/network-policies/paperless-ns.yaml#paperless-admit
|
||||
# - the database namespace
|
||||
# ../../infrastructure/network-policies/paperless-ns.yaml#database-admit
|
||||
# - the monitoring namespace
|
||||
# ../../infrastructure/network-policies/paperless-ns.yaml#monitoring-admit
|
||||
# - host / remote-node
|
||||
# - host / remote-node, every port
|
||||
# ../../infrastructure/network-policies/paperless-ns.yaml#host-admit
|
||||
# So any pod in database or monitoring can reach this HTTP listener
|
||||
# directly and set X-Authentik-Username to any value. Do not read this
|
||||
# rule as the whole boundary. Narrowing is tracked in REN-28.
|
||||
# — the kubelet sources the HTTP probes from the host netns, so this one
|
||||
# can never be removed while the pod has HTTP probes.
|
||||
# - blackbox-exporter on :8000
|
||||
# ../../infrastructure/network-policies/paperless-ns.yaml#paperless-allow-blackbox
|
||||
# The `database` and whole-`monitoring` admits that used to be in this list
|
||||
# were removed in REN-28. Any pod in the paperless namespace, and anything on a
|
||||
# node, can still reach this listener and set X-Authentik-Username to any
|
||||
# value. Do not read the gate rule as the whole boundary.
|
||||
#
|
||||
# anchor: cnp-union
|
||||
# This is a CiliumNetworkPolicy (not a standard NetworkPolicy) so it
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
# Baseline ingress/egress policy for the `paperless` namespace.
|
||||
#
|
||||
# CAUTION when adding rules here: this policy has `endpointSelector: {}`, so
|
||||
# every rule below applies to EVERY pod in the namespace — nine workloads, one
|
||||
# of which (paperless) grants identity from the X-Authentik-Username header and
|
||||
# will believe anything that can open a TCP connection to it. A `toPorts` block
|
||||
# narrows the PORT, not the destination workload; the destination is governed
|
||||
# solely by the enclosing policy's endpointSelector. Anything meant for one
|
||||
# workload belongs in its own policy with its own selector, as
|
||||
# pprl-pg-allow-cnpg-operator, pprl-pg-allow-prometheus and
|
||||
# paperless-allow-blackbox below do.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
@@ -20,23 +31,14 @@ spec:
|
||||
# kubernetes/apps/paperless/network-policy.yaml for the
|
||||
# gate-specific ingress rule.
|
||||
#
|
||||
# Note: the database and monitoring allows below DO broaden the trust
|
||||
# boundary in the way this note warns about, because endpointSelector
|
||||
# is {} and therefore covers the paperless pod too. They are kept
|
||||
# because both are load-bearing — CNPG reaches the postgres cluster
|
||||
# from `database`, and the blackbox probe of
|
||||
# http://paperless.paperless.svc.cluster.local:80/accounts/login/
|
||||
# (kubernetes/apps/monitoring-rules/probes-blackbox.yaml#paperless-app-health)
|
||||
# runs from `monitoring`.
|
||||
# Scoping them to specific pods and ports is tracked in REN-28.
|
||||
# anchor: database-admit
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": database
|
||||
# anchor: monitoring-admit
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": monitoring
|
||||
# `database` and `monitoring` are intentionally NOT in this list either,
|
||||
# and for the same reason: on endpointSelector:{} they reached all nine
|
||||
# workloads on every port, including paperless's HTTP listener. Both are
|
||||
# load-bearing, so both are preserved as per-workload policies at the
|
||||
# bottom of this file (REN-28):
|
||||
# pprl-pg-allow-cnpg-operator database/cloudnative-pg -> pprl-pg :8000
|
||||
# pprl-pg-allow-prometheus monitoring/prometheus -> pprl-pg :9187
|
||||
# paperless-allow-blackbox monitoring/blackbox -> paperless :8000
|
||||
# anchor: host-admit
|
||||
- fromEntities:
|
||||
- host
|
||||
@@ -172,3 +174,101 @@ spec:
|
||||
protocol: UDP
|
||||
- port: "21027"
|
||||
protocol: UDP
|
||||
---
|
||||
# anchor: pprl-pg-allow-cnpg-operator
|
||||
# The CNPG operator polls each instance's status endpoint over HTTPS
|
||||
# (https://<pod-ip>:8000/pg/status). Without it the Cluster reports "Instance
|
||||
# Status Extraction Error" while Postgres serves normally, and the operator
|
||||
# loses failover and switchover decisions (#433).
|
||||
#
|
||||
# Shape and port lifted from wp-pg-allow-cnpg-operator (woodpecker-ns.yaml),
|
||||
# which runs in production against a wp-pg that registers the same barman-cloud
|
||||
# WAL archiver as pprl-pg (cluster.yaml plugins[].isWALArchiver) and takes daily
|
||||
# plugin-method base backups, with no `database` rule on its baseline at all.
|
||||
#
|
||||
# barman-cloud needs no ingress here: the archiver is a container INSIDE this
|
||||
# instance pod — see the debug recipe in
|
||||
# monitoring-rules/prometheusrule-cnpg-backup.yaml
|
||||
# (`kubectl logs <pod> -c plugin-barman-cloud`) — and this namespace's egress
|
||||
# does not permit `database` at all, so every flow on that path is
|
||||
# database-initiated. The three mariadb-operator pods in `database` have no
|
||||
# MariaDB resource in this namespace to act on.
|
||||
#
|
||||
# No ICMP rule here, deliberately: the baseline in this file already allows all
|
||||
# standard ICMP from any cluster pod on endpointSelector:{}, and admits
|
||||
# host/remote-node L3-only, which wildcards every protocol from those entities.
|
||||
# Cilium unions allow rules, so attaching toPorts cannot subtract either grant.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: pprl-pg-allow-cnpg-operator
|
||||
namespace: paperless
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
cnpg.io/cluster: pprl-pg
|
||||
ingress:
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": database
|
||||
app.kubernetes.io/name: cloudnative-pg
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8000"
|
||||
protocol: TCP
|
||||
---
|
||||
# anchor: pprl-pg-allow-prometheus
|
||||
# Prometheus -> the pprl-pg CNPG built-in exporter. PodMonitor cnpg-pprl-pg
|
||||
# (monitoring-rules/cnpg-podmonitors.yaml) selects cnpg.io/cluster: pprl-pg and
|
||||
# is live and up on both instances. Dropping this silently blinds
|
||||
# monitoring-rules/prometheusrule-cnpg-backup.yaml.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: pprl-pg-allow-prometheus
|
||||
namespace: paperless
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
cnpg.io/cluster: pprl-pg
|
||||
ingress:
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": monitoring
|
||||
app.kubernetes.io/name: prometheus
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "9187"
|
||||
protocol: TCP
|
||||
---
|
||||
# anchor: paperless-allow-blackbox
|
||||
# blackbox-exporter -> the paperless web listener only.
|
||||
#
|
||||
# NOTE: Cilium matches the POD port after service translation. The probe in
|
||||
# kubernetes/apps/monitoring-rules/probes-blackbox.yaml#paperless-app-health
|
||||
# targets :80 on the Service, which resolves to containerPort 8000
|
||||
# (paperless.yaml). `port: "80"` here would read clean in review and break the
|
||||
# probe.
|
||||
#
|
||||
# Scoped to the paperless pod so this does NOT also open pprl-pg's
|
||||
# instance-manager API, which serves on :8000 as well — the hazard refused in
|
||||
# woodpecker-ns.yaml for woodpecker-ci. Measured safe besides: pprl-pg's pods
|
||||
# carry app.kubernetes.io/name: postgresql, not paperless.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: paperless-allow-blackbox
|
||||
namespace: paperless
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: paperless
|
||||
ingress:
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": monitoring
|
||||
app.kubernetes.io/name: prometheus-blackbox-exporter
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8000"
|
||||
protocol: TCP
|
||||
|
||||
Reference in New Issue
Block a user