Files
platform-core/scripts/validate/test-render-observability-access.sh
T

1067 lines
42 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd -P)"
RENDERER="$ROOT/scripts/validate/render-observability-access.sh"
# The renderer is intentionally sourced: these tests exercise the same parser and
# manifest assertions used by executable rendering, without downloading charts or
# contacting a Kubernetes API server.
source "$RENDERER"
ASSERTIONS=0
WORK=''
PUBLISH_ROOTS=()
cleanup() {
local rc=$?
trap - EXIT HUP INT TERM
if [[ -n "$WORK" && "$WORK" == /tmp/platform-observability-access-test.* &&
-d "$WORK" && ! -L "$WORK" ]]; then
rm -rf -- "$WORK"
fi
local publish_root
for publish_root in "${PUBLISH_ROOTS[@]}"; do
if [[ "$publish_root" == /tmp/platform-observability-metrics.?????? &&
-d "$publish_root" && ! -L "$publish_root" ]]; then
rm -rf -- "$publish_root"
fi
done
exit "$rc"
}
trap cleanup EXIT HUP INT TERM
pass() {
ASSERTIONS=$((ASSERTIONS + 1))
printf 'PASS: %s\n' "$1"
}
expect_pass() {
local label="$1"
shift
if "$@" >/dev/null 2>&1; then
pass "$label"
else
printf 'FAIL: expected success: %s\n' "$label" >&2
exit 1
fi
}
expect_fail() {
local label="$1"
shift
if "$@" >/dev/null 2>&1; then
printf 'FAIL: expected rejection: %s\n' "$label" >&2
exit 1
fi
pass "$label"
}
expect_output() {
local label="$1" expected="$2"
shift 2
local actual
actual="$($@)" || {
printf 'FAIL: output command failed: %s\n' "$label" >&2
exit 1
}
if [[ "$actual" != "$expected" ]]; then
printf 'FAIL: unexpected output: %s\nexpected:\n%s\nactual:\n%s\n' \
"$label" "$expected" "$actual" >&2
exit 1
fi
pass "$label"
}
mutate_yaml() {
local source="$1" destination="$2" mutation="$3"
python3 - "$source" "$destination" "$mutation" <<'PY'
import sys
import yaml
class FixtureLoader(yaml.SafeLoader):
pass
FixtureLoader.add_constructor(
"tag:yaml.org,2002:value",
lambda loader, node: loader.construct_scalar(node),
)
source, destination, mutation = sys.argv[1:]
with open(source, "r", encoding="utf-8") as stream:
items = [item for item in yaml.load_all(stream, Loader=FixtureLoader) if item is not None]
def object_named(kind, name):
matches = [
item for item in items
if item.get("kind") == kind and (item.get("metadata") or {}).get("name") == name
]
if len(matches) != 1:
raise SystemExit(f"fixture object is not exact: {kind}/{name}")
return matches[0]
if mutation == "grafana-public-service":
object_named("Service", "grafana")["spec"]["type"] = "LoadBalancer"
elif mutation == "grafana-missing-class":
object_named("Ingress", "grafana")["spec"].pop("ingressClassName", None)
elif mutation == "grafana-main-token":
deployment = object_named("Deployment", "grafana")
main = next(c for c in deployment["spec"]["template"]["spec"]["containers"] if c["name"] == "grafana")
main.setdefault("volumeMounts", []).append({
"name": "dashboard-sidecar-api-access",
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"readOnly": True,
})
elif mutation == "grafana-cluster-role":
role = object_named("Role", "grafana-dashboard-sidecar")
role["kind"] = "ClusterRole"
role["metadata"].pop("namespace", None)
elif mutation == "oidc-viewer-fallback":
config = object_named("ConfigMap", "grafana")
config["data"]["grafana.ini"] = config["data"]["grafana.ini"].replace("|| null", "|| 'Viewer'")
elif mutation == "oidc-editor":
config = object_named("ConfigMap", "grafana")
config["data"]["grafana.ini"] = config["data"]["grafana.ini"].replace("&& 'Viewer'", "&& 'Editor'")
elif mutation == "oidc-grafana-admin":
config = object_named("ConfigMap", "grafana")
config["data"]["grafana.ini"] = config["data"]["grafana.ini"].replace(
"allow_assign_grafana_admin = false", "allow_assign_grafana_admin = true"
)
elif mutation == "oidc-group-missing":
config = object_named("ConfigMap", "grafana")
config["data"]["grafana.ini"] = config["data"]["grafana.ini"].replace(
"/platform-observability-viewers", "/not-authorized"
)
elif mutation == "blackbox-arbitrary-target":
probe = object_named("Probe", "platform-public-edge")
probe["spec"]["targets"]["staticConfig"]["static"].append("https://example.invalid/")
elif mutation == "blackbox-public-service":
object_named("Service", "blackbox-exporter")["spec"]["type"] = "NodePort"
elif mutation == "missing-instance-label":
object_named("Probe", "platform-public-edge")["metadata"]["labels"].pop(
"observability.hyeonworks.com/instance", None
)
elif mutation == "outside-target":
items.append({
"apiVersion": "monitoring.coreos.com/v1",
"kind": "ServiceMonitor",
"metadata": {
"name": "rabbitmq",
"namespace": "messaging",
"labels": {"observability.hyeonworks.com/instance": "home"},
},
"spec": {"selector": {"matchLabels": {"app": "rabbitmq"}}, "endpoints": [{"port": "metrics"}]},
})
elif mutation == "forbidden-product":
items.append({
"apiVersion": "monitoring.coreos.com/v1",
"kind": "PrometheusRule",
"metadata": {
"name": "platform-jvm",
"namespace": "observability",
"labels": {"observability.hyeonworks.com/instance": "home"},
},
"spec": {"groups": [{"name": "jvm", "rules": [{"alert": "JVMHeapHigh", "expr": "vector(0)"}]}]},
})
elif mutation == "alertmanager-inline-secret":
items.append({
"apiVersion": "v1",
"kind": "Secret",
"metadata": {"name": "inline-fixture", "namespace": "observability"},
"type": "Opaque",
"stringData": {"password": "forbidden-fixture-value"},
})
elif mutation == "alertmanager-missing-title":
config = object_named("AlertmanagerConfig", "platform-alertmanager")
receiver = next(
receiver for receiver in config["spec"]["receivers"]
if receiver["name"] == "platform-slack"
)
receiver["slackConfigs"][0].pop("title")
elif mutation == "dns-public-address":
config = object_named("ConfigMap", "coredns-custom")
config["data"]["learn-hyeonworks.server"] = config["data"]["learn-hyeonworks.server"].replace(
"192.168.0.107 grafana.learn.hyeonworks.com",
"203.0.113.10 grafana.learn.hyeonworks.com",
)
elif mutation == "grafana-broad-policy":
policy = object_named("NetworkPolicy", "observability-allow-grafana-ingress")
policy["spec"] = {"podSelector": {}, "policyTypes": ["Ingress"], "ingress": [{}]}
elif mutation == "blackbox-broad-egress":
policy = object_named("NetworkPolicy", "observability-allow-blackbox-egress")
policy["spec"] = {"podSelector": {}, "policyTypes": ["Egress"], "egress": [{}]}
elif mutation == "targets-broad-egress":
policy = object_named("NetworkPolicy", "observability-allow-prometheus-platform-targets")
policy["spec"] = {"podSelector": {}, "policyTypes": ["Egress"], "egress": [{}]}
elif mutation == "grafana-extra-allow-all":
items.append({
"apiVersion": "networking.k8s.io/v1", "kind": "NetworkPolicy",
"metadata": {"name": "observability-extra-grafana-allow-all", "namespace": "observability"},
"spec": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/name": "grafana", "app.kubernetes.io/instance": "grafana",
}},
"policyTypes": ["Ingress", "Egress"], "ingress": [{}], "egress": [{}],
},
})
elif mutation == "blackbox-extra-allow-all":
items.append({
"apiVersion": "networking.k8s.io/v1", "kind": "NetworkPolicy",
"metadata": {"name": "observability-extra-blackbox-allow-all", "namespace": "observability"},
"spec": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/name": "prometheus-blackbox-exporter",
"app.kubernetes.io/instance": "blackbox-exporter",
}},
"policyTypes": ["Ingress", "Egress"], "ingress": [{}], "egress": [{}],
},
})
elif mutation == "targets-extra-allow-all":
items.append({
"apiVersion": "networking.k8s.io/v1", "kind": "NetworkPolicy",
"metadata": {"name": "observability-extra-prometheus-allow-all", "namespace": "observability"},
"spec": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/name": "prometheus",
"app.kubernetes.io/instance": "observability-core-kube-pr-prometheus",
}},
"policyTypes": ["Egress"], "egress": [{}],
},
})
elif mutation == "alertmanager-extra-allow-all":
items.append({
"apiVersion": "networking.k8s.io/v1", "kind": "NetworkPolicy",
"metadata": {"name": "observability-extra-alertmanager-allow-all", "namespace": "observability"},
"spec": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/name": "alertmanager",
"app.kubernetes.io/instance": "observability-core-kube-pr-alertmanager",
}},
"policyTypes": ["Egress"], "egress": [{}],
},
})
else:
raise SystemExit(f"unknown mutation: {mutation}")
with open(destination, "w", encoding="utf-8") as stream:
yaml.safe_dump_all(items, stream, explicit_start=True, sort_keys=False)
PY
}
write_inventory_phase() {
local root="$1" phase="$2" pool="$3" provenance="$4" metric="$5"
local directory="$root/$phase" hash
mkdir -m 0700 -- "$directory"
printf '%s\n' \
'{' \
' "schema": "platform-observability-metric-inventory/v1",' \
" \"phase\": \"$phase\"," \
' "captured_at_utc": "2026-08-12T00:00:00Z",' \
' "context": "default",' \
' "api_server": "https://127.0.0.1:6443",' \
' "prometheus": "observability/observability-core-kube-pr-prometheus",' \
' "preexisting_verify_only": ["grafana"],' \
' "targets": [' \
' {' \
" \"scrape_pool\": \"$pool\"," \
' "scrape_url": "http://127.0.0.1:9090/metrics",' \
' "health": "up",' \
' "last_error": "",' \
" \"provenance\": \"$provenance\"," \
" \"metrics\": [{\"name\": \"$metric\", \"label_names\": [\"__name__\", \"instance\", \"namespace\"]}]" \
' }' \
' ]' \
'}' >"$directory/inventory.json"
chmod 0600 "$directory/inventory.json"
hash="$(sha256sum "$directory/inventory.json" | awk '{print $1}')"
printf '%s inventory.json\n' "$hash" >"$directory/inventory.sha256"
chmod 0600 "$directory/inventory.sha256"
}
make_publish_root() {
local root
root="$(mktemp -d /tmp/platform-observability-metrics.XXXXXX)"
chmod 0700 "$root"
PUBLISH_ROOTS+=("$root")
write_inventory_phase "$root" target-initial \
serviceMonitor/observability/grafana/0 preexisting-verify-only up
write_inventory_phase "$root" post-substrate \
probe/observability/platform-public-edge substrate-owned probe_success
PUBLISH_ROOT=$root
}
publication_tree_fingerprint() {
local root=$1
python3 - "$root" <<'PY'
import hashlib
import os
import stat
import sys
root = sys.argv[1]
records = []
def record(path, relative):
metadata = os.lstat(path)
content_hash = "-"
if stat.S_ISREG(metadata.st_mode):
descriptor = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
try:
digest = hashlib.sha256()
while True:
chunk = os.read(descriptor, 1024 * 1024)
if not chunk:
break
digest.update(chunk)
content_hash = digest.hexdigest()
finally:
os.close(descriptor)
records.append("|".join((
relative,
str(metadata.st_dev),
str(metadata.st_ino),
oct(stat.S_IFMT(metadata.st_mode)),
oct(stat.S_IMODE(metadata.st_mode)),
str(metadata.st_uid),
str(metadata.st_gid),
str(metadata.st_nlink),
str(metadata.st_size),
str(metadata.st_mtime_ns),
str(metadata.st_ctime_ns),
content_hash,
)))
if stat.S_ISDIR(metadata.st_mode):
with os.scandir(path) as entries:
for entry in sorted(entries, key=lambda item: item.name):
child_relative = entry.name if relative == "." else f"{relative}/{entry.name}"
record(entry.path, child_relative)
record(root, ".")
print(hashlib.sha256("\0".join(records).encode()).hexdigest())
PY
}
make_inventory_only_clone() {
local source=$1 root phase name
root="$(mktemp -d /tmp/platform-observability-metrics.XXXXXX)"
chmod 0700 "$root"
PUBLISH_ROOTS+=("$root")
for phase in target-initial post-substrate; do
mkdir -m 0700 -- "$root/$phase"
for name in inventory.json inventory.sha256; do
cp --no-dereference --reflink=never -- \
"$source/$phase/$name" "$root/$phase/$name"
chmod 0600 "$root/$phase/$name"
done
done
PUBLISH_ROOT=$root
}
inventory_clone_matches_source() {
local source=$1 clone=$2 phase name uid
uid=$(id -u)
for phase in target-initial post-substrate; do
[[ -d "$source/$phase" && ! -L "$source/$phase" ]] || return 1
[[ -d "$clone/$phase" && ! -L "$clone/$phase" ]] || return 1
[[ "$(stat -c '%u|%a' -- "$source/$phase")" == "$uid|700" ]] || return 1
[[ "$(stat -c '%u|%a' -- "$clone/$phase")" == "$uid|700" ]] || return 1
for name in inventory.json inventory.sha256; do
[[ -f "$source/$phase/$name" && ! -L "$source/$phase/$name" ]] || return 1
[[ -f "$clone/$phase/$name" && ! -L "$clone/$phase/$name" ]] || return 1
[[ "$(stat -c '%u|%a|%h' -- "$source/$phase/$name")" == "$uid|600|1" ]] || return 1
[[ "$(stat -c '%u|%a|%h' -- "$clone/$phase/$name")" == "$uid|600|1" ]] || return 1
cmp -s "$source/$phase/$name" "$clone/$phase/$name" || return 1
done
done
}
published_complete_exact() {
local root=$1 source=$2 name uid
local expected_root expected_source
expected_root=$'alertmanager.yaml\nblackbox.yaml\ndashboards.yaml\ngrafana.yaml\npost-substrate\nprivate-dns.yaml\nrules.yaml\ntarget-initial\ntargets.yaml'
expected_source=$'alertmanager.yaml\nblackbox.yaml\ndashboards.yaml\ngrafana.yaml\nprivate-dns.yaml\nrules.yaml\ntargets.yaml'
[[ "$(find "$root" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort)" == "$expected_root" ]] || return 1
[[ "$(find "$source" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort)" == "$expected_source" ]] || return 1
uid=$(id -u)
for name in \
grafana.yaml blackbox.yaml targets.yaml dashboards.yaml rules.yaml alertmanager.yaml private-dns.yaml; do
[[ -f "$source/$name" && ! -L "$source/$name" ]] || return 1
[[ -f "$root/$name" && ! -L "$root/$name" ]] || return 1
[[ "$(stat -c '%u|%a|%h' -- "$source/$name")" == "$uid|600|1" ]] || return 1
[[ "$(stat -c '%u|%a|%h' -- "$root/$name")" == "$uid|600|1" ]] || return 1
cmp -s "$source/$name" "$root/$name" || return 1
done
}
published_rules_alerts_exact() {
local root=$1 source=$2 name
local expected=$'alertmanager.yaml\ndashboards.yaml\npost-substrate\nrules.yaml\ntarget-initial'
[[ "$(find "$root" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort)" == "$expected" ]] || return 1
for name in dashboards.yaml rules.yaml alertmanager.yaml; do
[[ -f "$root/$name" && ! -L "$root/$name" && "$(stat -c %a -- "$root/$name")" == 600 ]] || return 1
cmp -s "$source/$name" "$root/$name" || return 1
done
}
collision_left_no_partial_publish() {
local root=$1
[[ -f "$root/rules.yaml" && ! -e "$root/dashboards.yaml" && ! -e "$root/alertmanager.yaml" ]] || return 1
}
complete_collision_left_no_partial() {
local root=$1 uid expected
expected=$'post-substrate\nprivate-dns.yaml\ntarget-initial'
[[ "$(find "$root" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort)" == "$expected" ]] || return 1
[[ -f "$root/private-dns.yaml" && ! -L "$root/private-dns.yaml" ]] || return 1
uid=$(id -u)
[[ "$(stat -c '%u|%a|%h' -- "$root/private-dns.yaml")" == "$uid|600|1" ]] || return 1
[[ "$(<"$root/private-dns.yaml")" == 'preexisting complete collision' ]]
}
secure_render_context_creates_private_files() {
local directory=$1
(
access_secure_render_context
printf '%s\n' rendered >"$directory/rendered.yaml"
) || return 1
[[ "$(stat -c %a -- "$directory/rendered.yaml")" == 600 ]]
}
WORK="$(mktemp -d /tmp/platform-observability-access-test.XXXXXX)"
chmod 0700 "$WORK"
grafana="$WORK/grafana.yaml"
cat >"$grafana" <<'YAML'
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: grafana-dashboard-sidecar
namespace: observability
rules:
- apiGroups: [""]
resources: [configmaps]
verbs: [list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: grafana-dashboard-sidecar
namespace: observability
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: grafana-dashboard-sidecar
subjects:
- kind: ServiceAccount
name: grafana
namespace: observability
---
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana
namespace: observability
data:
grafana.ini: |
[auth]
login_maximum_lifetime_duration = 8h
login_maximum_inactive_lifetime_duration = 30m
[auth.anonymous]
enabled = false
[auth.generic_oauth]
enabled = true
scopes = openid profile email
groups_attribute_path = groups
allowed_groups = /platform-observability-admins /platform-observability-viewers
role_attribute_strict = true
allow_assign_grafana_admin = false
role_attribute_path = contains(groups[*], '/platform-observability-admins') && 'Admin' || contains(groups[*], '/platform-observability-viewers') && 'Viewer' || null
auth_url = https://id.learn.hyeonworks.com/realms/hyeonworks/protocol/openid-connect/auth
token_url = https://id.learn.hyeonworks.com/realms/hyeonworks/protocol/openid-connect/token
api_url = https://id.learn.hyeonworks.com/realms/hyeonworks/protocol/openid-connect/userinfo
signout_redirect_url = https://id.learn.hyeonworks.com/realms/hyeonworks/protocol/openid-connect/logout
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: observability
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
ports:
- name: service
port: 80
targetPort: grafana
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grafana
namespace: observability
spec:
ingressClassName: traefik
rules:
- host: grafana.learn.hyeonworks.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: grafana
port:
number: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: observability
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
spec:
automountServiceAccountToken: false
serviceAccountName: grafana
containers:
- name: grafana-sc-dashboard
image: quay.io/kiwigrid/k8s-sidecar:2.10.0@sha256:129877c81acf2bc8c3fa000e89a62e020eb89d41ceb94767c657aef5bb0cc0d3
volumeMounts:
- name: dashboard-sidecar-api-access
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
- name: grafana
image: docker.io/grafana/grafana:13.1.1@sha256:f33c692ba1a5ee15724cf6b22db65e9de39dde14d80f7d73a9546e3fc917270b
env:
- name: GF_SECURITY_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: grafana-admin
key: admin-password
- name: GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: grafana-keycloak-oidc
key: client-secret
volumes:
- name: dashboard-sidecar-api-access
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3600
path: token
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: grafana
namespace: observability
labels:
observability.hyeonworks.com/instance: home
spec:
selector:
matchLabels:
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
endpoints:
- port: service
path: /metrics
YAML
kubectl kustomize "$ROOT/services/observability/network-policies" >"$WORK/core-policies.yaml"
blackbox="$WORK/blackbox.yaml"
cat >"$blackbox" <<'YAML'
apiVersion: v1
kind: Service
metadata:
name: blackbox-exporter
namespace: observability
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/instance: blackbox-exporter
ports:
- name: http
port: 9115
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: blackbox-exporter
namespace: observability
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/instance: blackbox-exporter
spec:
automountServiceAccountToken: false
containers:
- name: blackbox-exporter
image: quay.io/prometheus/blackbox-exporter:v0.28.0@sha256:43027b43fb785b7c5adc53bd3b5dbc1a258270a2e8aff24f477b45c4e38dac68
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: blackbox-exporter
namespace: observability
labels:
observability.hyeonworks.com/instance: home
spec:
selector:
matchLabels:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/instance: blackbox-exporter
endpoints:
- port: http
path: /metrics
---
apiVersion: monitoring.coreos.com/v1
kind: Probe
metadata:
name: platform-public-edge
namespace: observability
labels:
observability.hyeonworks.com/instance: home
spec:
jobName: blackbox-public-edge
module: http_2xx
prober:
url: blackbox-exporter.observability.svc.cluster.local:9115
scheme: http
path: /probe
proxyFromEnvironment: false
targets:
staticConfig:
labels:
observability.hyeonworks.com/probe-group: public-edge
static:
- https://git.learn.hyeonworks.com/api/healthz
- https://id.learn.hyeonworks.com/realms/hyeonworks/.well-known/openid-configuration
---
apiVersion: monitoring.coreos.com/v1
kind: Probe
metadata:
name: platform-private-edge
namespace: observability
labels:
observability.hyeonworks.com/instance: home
spec:
jobName: blackbox-private-edge
module: http_private_edge_403
prober:
url: blackbox-exporter.observability.svc.cluster.local:9115
scheme: http
path: /probe
proxyFromEnvironment: false
targets:
staticConfig:
labels:
observability.hyeonworks.com/probe-group: private-edge
static:
- https://grafana.learn.hyeonworks.com/
- https://storage-admin.learn.hyeonworks.com/
- https://db-admin.learn.hyeonworks.com/
---
apiVersion: monitoring.coreos.com/v1
kind: Probe
metadata:
name: platform-private-internal
namespace: observability
labels:
observability.hyeonworks.com/instance: home
spec:
jobName: blackbox-private-internal
module: http_private_internal_200
prober:
url: blackbox-exporter.observability.svc.cluster.local:9115
scheme: http
path: /probe
proxyFromEnvironment: false
targets:
staticConfig:
labels:
observability.hyeonworks.com/probe-group: private-internal
static:
- http://grafana.observability.svc.cluster.local/api/health
- http://pgadmin.platform-admin.svc.cluster.local/misc/ping
- http://minio-aistor-console.object-storage.svc.cluster.local:9090/
relabelingConfigs:
- sourceLabels: [__param_target]
regex: http://grafana[.]observability[.]svc[.]cluster[.]local/api/health
targetLabel: __param_hostname
replacement: grafana.learn.hyeonworks.com
- sourceLabels: [__param_target]
regex: http://pgadmin[.]platform-admin[.]svc[.]cluster[.]local/misc/ping
targetLabel: __param_hostname
replacement: db-admin.learn.hyeonworks.com
- sourceLabels: [__param_target]
regex: http://minio-aistor-console[.]object-storage[.]svc[.]cluster[.]local:9090/
targetLabel: __param_hostname
replacement: storage-admin.learn.hyeonworks.com
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: observability-allow-prometheus-to-blackbox
namespace: observability
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/instance: blackbox-exporter
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
app.kubernetes.io/instance: observability-core-kube-pr-prometheus
ports:
- protocol: TCP
port: 9115
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: observability-allow-blackbox-egress
namespace: observability
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/instance: blackbox-exporter
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- {protocol: UDP, port: 53}
- {protocol: TCP, port: 53}
- to:
- ipBlock: {cidr: 192.168.0.107/32}
ports:
- {protocol: TCP, port: 443}
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
ports:
- {protocol: TCP, port: 3000}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: platform-admin
podSelector:
matchLabels:
app.kubernetes.io/name: pgadmin4
app.kubernetes.io/instance: pgadmin
ports:
- {protocol: TCP, port: 5050}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: object-storage
podSelector:
matchLabels:
aistor.min.io/objectStore: minio-aistor
ports:
- {protocol: TCP, port: 9090}
YAML
kubectl kustomize "$ROOT/services/observability/targets" >"$WORK/targets.yaml"
kubectl kustomize "$ROOT/services/observability/dashboards/platform" >"$WORK/dashboards.yaml"
kubectl kustomize "$ROOT/services/observability/rules/platform" >"$WORK/rules.yaml"
cat >"$WORK/alertmanager-base.yaml" <<'YAML'
apiVersion: monitoring.coreos.com/v1
kind: Alertmanager
metadata:
name: observability-core-kube-pr-alertmanager
namespace: observability
spec:
alertmanagerConfiguration:
name: platform-alertmanager
YAML
kubectl kustomize "$ROOT/services/observability/alerting" >"$WORK/alerting.yaml"
python3 - "$WORK/alertmanager-base.yaml" "$WORK/alerting.yaml" "$WORK/alertmanager.yaml" <<'PY'
import sys
import yaml
class FixtureLoader(yaml.SafeLoader):
pass
FixtureLoader.add_constructor(
"tag:yaml.org,2002:value",
lambda loader, node: loader.construct_scalar(node),
)
items = []
for path in sys.argv[1:3]:
with open(path, "r", encoding="utf-8") as stream:
items.extend(item for item in yaml.load_all(stream, Loader=FixtureLoader) if item is not None)
with open(sys.argv[3], "w", encoding="utf-8") as stream:
yaml.safe_dump_all(items, stream, explicit_start=True, sort_keys=False)
PY
kubectl kustomize "$ROOT/infrastructure/networking/private-dns/kubernetes" >"$WORK/private-dns.yaml"
kubectl kustomize "$ROOT/infrastructure/networking/traefik/overlays/trust" >"$WORK/traefik-trust.yaml"
kubectl kustomize "$ROOT/infrastructure/networking/traefik/overlays/observe" >"$WORK/traefik-observe.yaml"
expected_complete=$'grafana.yaml\nblackbox.yaml\ntargets.yaml\ndashboards.yaml\nrules.yaml\nalertmanager.yaml\nprivate-dns.yaml'
expect_output 'complete mode has the exact seven access artifacts' "$expected_complete" access_output_names complete
expect_output 'grafana mode has one artifact' 'grafana.yaml' access_output_names grafana
expect_output 'blackbox mode has one artifact' 'blackbox.yaml' access_output_names blackbox
expect_output 'targets mode has one artifact' 'targets.yaml' access_output_names targets
expect_output 'rules-alerts mode has three artifacts' $'dashboards.yaml\nrules.yaml\nalertmanager.yaml' access_output_names rules-alerts
expect_fail 'unknown component is rejected' access_output_names arbitrary
expect_pass 'Grafana private access contract' assert_access_grafana_contract "$grafana" "$WORK/core-policies.yaml"
expect_pass 'Grafana and sidecar images are digest pinned' assert_pinned_images "$grafana"
mutate_yaml "$WORK/core-policies.yaml" "$WORK/grafana-broad-policy.yaml" grafana-broad-policy
expect_fail 'broad Grafana ingress NetworkPolicy is rejected' \
assert_access_grafana_contract "$grafana" "$WORK/grafana-broad-policy.yaml"
mutate_yaml "$WORK/core-policies.yaml" "$WORK/grafana-extra-policy.yaml" grafana-extra-allow-all
expect_fail 'an extra Grafana allow-all NetworkPolicy is rejected' \
assert_access_grafana_contract "$grafana" "$WORK/grafana-extra-policy.yaml"
mutate_yaml "$grafana" "$WORK/grafana-public-service.yaml" grafana-public-service
expect_fail 'Grafana public Service is rejected' assert_access_grafana_contract "$WORK/grafana-public-service.yaml" "$WORK/core-policies.yaml"
mutate_yaml "$grafana" "$WORK/grafana-missing-class.yaml" grafana-missing-class
expect_fail 'Grafana missing ingressClass is rejected' assert_access_grafana_contract "$WORK/grafana-missing-class.yaml" "$WORK/core-policies.yaml"
mutate_yaml "$grafana" "$WORK/grafana-main-token.yaml" grafana-main-token
expect_fail 'Grafana main container token mount is rejected' assert_access_grafana_contract "$WORK/grafana-main-token.yaml" "$WORK/core-policies.yaml"
mutate_yaml "$grafana" "$WORK/grafana-cluster-role.yaml" grafana-cluster-role
expect_fail 'cluster-wide dashboard ConfigMap RBAC is rejected' assert_access_grafana_contract "$WORK/grafana-cluster-role.yaml" "$WORK/core-policies.yaml"
for mutation in oidc-viewer-fallback oidc-editor oidc-grafana-admin oidc-group-missing; do
mutate_yaml "$grafana" "$WORK/$mutation.yaml" "$mutation"
expect_fail "Grafana OIDC regression $mutation is rejected" \
assert_access_grafana_contract "$WORK/$mutation.yaml" "$WORK/core-policies.yaml"
done
sed 's/@sha256:[0-9a-f]\{64\}//g' "$grafana" >"$WORK/grafana-tag-only.yaml"
expect_fail 'tag-only Grafana and sidecar images are rejected' assert_pinned_images "$WORK/grafana-tag-only.yaml"
expect_pass 'Blackbox fixed target and network contract' assert_access_blackbox_contract "$blackbox"
expect_pass 'Blackbox image is digest pinned' assert_pinned_images "$blackbox"
mutate_yaml "$blackbox" "$WORK/blackbox-broad-egress.yaml" blackbox-broad-egress
expect_fail 'broad Blackbox egress NetworkPolicy is rejected' \
assert_access_blackbox_contract "$WORK/blackbox-broad-egress.yaml"
mutate_yaml "$blackbox" "$WORK/blackbox-extra-policy.yaml" blackbox-extra-allow-all
expect_fail 'an extra Blackbox allow-all NetworkPolicy is rejected' \
assert_access_blackbox_contract "$WORK/blackbox-extra-policy.yaml"
mutate_yaml "$blackbox" "$WORK/blackbox-arbitrary.yaml" blackbox-arbitrary-target
expect_fail 'arbitrary Blackbox target is rejected' assert_access_blackbox_contract "$WORK/blackbox-arbitrary.yaml"
mutate_yaml "$blackbox" "$WORK/blackbox-public.yaml" blackbox-public-service
expect_fail 'public Blackbox Service is rejected' assert_access_blackbox_contract "$WORK/blackbox-public.yaml"
sed 's/@sha256:[0-9a-f]\{64\}//g' "$blackbox" >"$WORK/blackbox-tag-only.yaml"
expect_fail 'tag-only Blackbox image is rejected' assert_pinned_images "$WORK/blackbox-tag-only.yaml"
expect_pass 'target object set and target scope contract' assert_access_targets_contract "$WORK/targets.yaml"
mutate_yaml "$WORK/targets.yaml" "$WORK/targets-broad-egress.yaml" targets-broad-egress
expect_fail 'broad Prometheus target egress NetworkPolicy is rejected' \
assert_access_targets_contract "$WORK/targets-broad-egress.yaml"
mutate_yaml "$WORK/targets.yaml" "$WORK/targets-extra-policy.yaml" targets-extra-allow-all
expect_fail 'an extra Prometheus allow-all NetworkPolicy is rejected' \
assert_access_targets_contract "$WORK/targets-extra-policy.yaml"
expect_pass 'dashboard/rule/alertmanager contract' assert_access_rules_alerts_contract \
"$WORK/dashboards.yaml" "$WORK/rules.yaml" "$WORK/alertmanager.yaml"
alertmanager_source_before="$(publication_tree_fingerprint "$WORK/alertmanager.yaml")"
mutate_yaml "$WORK/alertmanager.yaml" "$WORK/alertmanager-missing-title.yaml" alertmanager-missing-title
expect_fail 'Alertmanager Slack config without title is rejected' \
assert_access_rules_alerts_contract \
"$WORK/dashboards.yaml" "$WORK/rules.yaml" "$WORK/alertmanager-missing-title.yaml"
expect_output 'rejected Alertmanager handoff preserves its canonical source exactly' \
"$alertmanager_source_before" publication_tree_fingerprint "$WORK/alertmanager.yaml"
mutate_yaml "$WORK/alertmanager.yaml" "$WORK/alertmanager-extra-policy.yaml" alertmanager-extra-allow-all
expect_fail 'an extra Alertmanager allow-all NetworkPolicy is rejected' \
assert_access_rules_alerts_contract \
"$WORK/dashboards.yaml" "$WORK/rules.yaml" "$WORK/alertmanager-extra-policy.yaml"
expect_pass 'all monitor/rule instance labels are scoped' assert_access_scope_contract \
"$blackbox" "$WORK/targets.yaml" "$WORK/rules.yaml"
mutate_yaml "$blackbox" "$WORK/missing-instance.yaml" missing-instance-label
expect_fail 'missing Probe instance label is rejected' assert_access_scope_contract "$WORK/missing-instance.yaml"
mutate_yaml "$blackbox" "$WORK/outside-target.yaml" outside-target
expect_fail 'out-of-scope platform target is rejected' assert_access_scope_contract "$WORK/outside-target.yaml"
mutate_yaml "$WORK/rules.yaml" "$WORK/forbidden-product.yaml" forbidden-product
expect_fail 'Spring/JVM/Kafka/batch/backup scope is rejected' assert_access_scope_contract "$WORK/forbidden-product.yaml"
mutate_yaml "$WORK/alertmanager.yaml" "$WORK/inline-secret.yaml" alertmanager-inline-secret
expect_fail 'inline credential Secret is rejected' assert_no_credentials "$WORK/inline-secret.yaml"
expect_pass 'private DNS is exact and private-only' assert_access_private_dns_contract \
"$WORK/private-dns.yaml" \
"$ROOT/infrastructure/networking/private-dns/host/dnsmasq-lan.conf" \
"$ROOT/infrastructure/networking/private-dns/host/dnsmasq-tailscale.conf"
mutate_yaml "$WORK/private-dns.yaml" "$WORK/private-dns-public.yaml" dns-public-address
expect_fail 'public Grafana DNS address assumption is rejected' assert_access_private_dns_contract \
"$WORK/private-dns-public.yaml" \
"$ROOT/infrastructure/networking/private-dns/host/dnsmasq-lan.conf" \
"$ROOT/infrastructure/networking/private-dns/host/dnsmasq-tailscale.conf"
expect_pass 'Traefik trust overlay render is accepted' assert_access_traefik_trust_contract "$WORK/traefik-trust.yaml"
expect_fail 'Traefik observe overlay render is rejected' assert_access_traefik_trust_contract "$WORK/traefik-observe.yaml"
make_publish_root
inventory_root=$PUBLISH_ROOT
expect_pass 'both verified inventory phases are accepted' validate_access_inventory_root "$inventory_root"
make_publish_root
inventory_unsafe_root=$PUBLISH_ROOT
chmod 0777 "$inventory_unsafe_root"
expect_fail 'world-writable inventory root is rejected' validate_access_inventory_root "$inventory_unsafe_root"
make_publish_root
inventory_unsafe_phase=$PUBLISH_ROOT
chmod 0755 "$inventory_unsafe_phase/post-substrate"
expect_fail 'group-readable inventory phase is rejected' validate_access_inventory_root "$inventory_unsafe_phase"
make_publish_root
inventory_unsafe_file=$PUBLISH_ROOT
chmod 0644 "$inventory_unsafe_file/post-substrate/inventory.json"
expect_fail 'group-readable inventory file is rejected' validate_access_inventory_root "$inventory_unsafe_file"
make_publish_root
inventory_hardlink_file=$PUBLISH_ROOT
mv -T "$inventory_hardlink_file/post-substrate/inventory.json" "$WORK/shared-inventory.json"
ln "$WORK/shared-inventory.json" "$inventory_hardlink_file/post-substrate/inventory.json"
expect_fail 'hard-linked inventory file is rejected' validate_access_inventory_root "$inventory_hardlink_file"
make_publish_root
inventory_bad_checksum=$PUBLISH_ROOT
printf '%064d inventory.json\n' 0 >"$inventory_bad_checksum/post-substrate/inventory.sha256"
chmod 0600 "$inventory_bad_checksum/post-substrate/inventory.sha256"
expect_fail 'inventory checksum drift is rejected' validate_access_inventory_root "$inventory_bad_checksum"
make_publish_root
inventory_missing_phase=$PUBLISH_ROOT
mv -T "$inventory_missing_phase/post-substrate" "$WORK/inventory-missing-phase-saved"
expect_fail 'missing post-substrate phase is rejected' validate_access_inventory_root "$inventory_missing_phase"
expect_pass 'rules-alerts component requires inventories' access_component_requires_inventory rules-alerts
expect_pass 'complete component requires inventories' access_component_requires_inventory complete
expect_fail 'grafana component does not accept inventory as a substitute for its source' access_component_requires_inventory grafana
expect_pass 'render context creates private files by default' \
secure_render_context_creates_private_files "$WORK"
publish_source="$WORK/publish-source"
mkdir -m 0700 -- "$publish_source"
printf '%s\n' 'kind: ConfigMap' >"$publish_source/dashboards.yaml"
printf '%s\n' 'kind: PrometheusRule' >"$publish_source/rules.yaml"
printf '%s\n' 'kind: AlertmanagerConfig' >"$publish_source/alertmanager.yaml"
chmod 0600 "$publish_source"/*.yaml
make_publish_root
publish_success_root=$PUBLISH_ROOT
expect_pass 'rules-alerts publication is complete and exact' \
publish_access_outputs "$publish_success_root" "$publish_source" rules-alerts
expect_pass 'rules-alerts publication preserves exact bytes and modes' \
published_rules_alerts_exact "$publish_success_root" "$publish_source"
complete_publish_source="$WORK/complete-publish-source"
mkdir -m 0700 -- "$complete_publish_source"
for complete_name in \
grafana.yaml blackbox.yaml targets.yaml dashboards.yaml rules.yaml alertmanager.yaml private-dns.yaml; do
cp --no-dereference --reflink=never -- \
"$WORK/$complete_name" "$complete_publish_source/$complete_name"
done
chmod 0600 "$complete_publish_source"/*.yaml
published_root_before="$(publication_tree_fingerprint "$publish_success_root")"
complete_source_before="$(publication_tree_fingerprint "$complete_publish_source")"
expect_fail 'a rules-alerts-published root rejects later complete publication' \
publish_access_outputs "$publish_success_root" "$complete_publish_source" complete
expect_output 'rejected complete publication preserves the published root exactly' \
"$published_root_before" publication_tree_fingerprint "$publish_success_root"
expect_output 'rejected complete publication preserves all complete sources exactly' \
"$complete_source_before" publication_tree_fingerprint "$complete_publish_source"
make_inventory_only_clone "$publish_success_root"
complete_publish_root=$PUBLISH_ROOT
target_inventory_before="$(publication_tree_fingerprint "$complete_publish_root/target-initial")"
post_inventory_before="$(publication_tree_fingerprint "$complete_publish_root/post-substrate")"
expect_pass 'the inventory-only clone exactly copies the four published inventory files' \
inventory_clone_matches_source "$publish_success_root" "$complete_publish_root"
expect_pass 'an inventory-only clone passes inventory validation' \
validate_access_inventory_root "$complete_publish_root"
expect_pass 'an inventory-only clone accepts complete publication' \
publish_access_outputs "$complete_publish_root" "$complete_publish_source" complete
expect_pass 'complete publication has the exact nine-entry topology and seven secure YAML files' \
published_complete_exact "$complete_publish_root" "$complete_publish_source"
expect_output 'complete publication preserves target-initial inventory exactly' \
"$target_inventory_before" publication_tree_fingerprint "$complete_publish_root/target-initial"
expect_output 'complete publication preserves post-substrate inventory exactly' \
"$post_inventory_before" publication_tree_fingerprint "$complete_publish_root/post-substrate"
expect_output 'complete publication preserves the rules-alerts source root exactly' \
"$published_root_before" publication_tree_fingerprint "$publish_success_root"
expect_output 'complete publication preserves all complete sources exactly' \
"$complete_source_before" publication_tree_fingerprint "$complete_publish_source"
make_inventory_only_clone "$publish_success_root"
complete_collision_root=$PUBLISH_ROOT
printf '%s\n' 'preexisting complete collision' >"$complete_collision_root/private-dns.yaml"
chmod 0600 "$complete_collision_root/private-dns.yaml"
complete_collision_before="$(publication_tree_fingerprint "$complete_collision_root")"
expect_fail 'a last-name collision rejects complete publication before any output is added' \
publish_access_outputs "$complete_collision_root" "$complete_publish_source" complete
expect_pass 'a complete last-name collision leaves no partial publication' \
complete_collision_left_no_partial "$complete_collision_root"
expect_output 'a rejected complete collision preserves its destination tree exactly' \
"$complete_collision_before" publication_tree_fingerprint "$complete_collision_root"
expect_output 'a rejected complete collision preserves the rules-alerts source root exactly' \
"$published_root_before" publication_tree_fingerprint "$publish_success_root"
expect_output 'a rejected complete collision preserves all complete sources exactly' \
"$complete_source_before" publication_tree_fingerprint "$complete_publish_source"
make_publish_root
publish_collision_root=$PUBLISH_ROOT
printf '%s\n' 'preexisting' >"$publish_collision_root/rules.yaml"
chmod 0600 "$publish_collision_root/rules.yaml"
expect_fail 'a later-name collision rejects the whole publication' \
publish_access_outputs "$publish_collision_root" "$publish_source" rules-alerts
expect_pass 'a later-name collision leaves no partial publication' \
collision_left_no_partial_publish "$publish_collision_root"
printf 'Assertions: %d\n' "$ASSERTIONS"
printf 'OBSERVABILITY ACCESS RENDER ASSERTION TEST PASS\n'