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

826 lines
38 KiB
Bash

#!/usr/bin/env bash
# Static renderer and sourceable assertion library for the observability access
# layer. It intentionally reuses the pinned core renderer for Helm/cache and
# image/credential boundaries instead of maintaining a second downloader.
set -o pipefail
readonly ACCESS_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd -P)"
# shellcheck source=render-observability-core.sh
source "$ACCESS_ROOT/scripts/validate/render-observability-core.sh"
access_output_names() {
case "$1" in
grafana) printf '%s\n' grafana.yaml ;;
blackbox) printf '%s\n' blackbox.yaml ;;
targets) printf '%s\n' targets.yaml ;;
rules-alerts) printf '%s\n' dashboards.yaml rules.yaml alertmanager.yaml ;;
complete) printf '%s\n' grafana.yaml blackbox.yaml targets.yaml dashboards.yaml rules.yaml alertmanager.yaml private-dns.yaml ;;
*) return 1 ;;
esac
}
access_component_requires_inventory() {
[[ "$1" == rules-alerts || "$1" == complete ]]
}
access_secure_render_context() {
umask 077
}
_access_assert() {
local mode=$1
shift
python3 - "$mode" "$@" <<'PY'
import ipaddress
import json
import pathlib
import re
import sys
import yaml
class AccessSafeLoader(yaml.SafeLoader):
pass
AccessSafeLoader.add_constructor(
"tag:yaml.org,2002:value",
lambda loader, node: loader.construct_scalar(node),
)
mode = sys.argv[1]
paths = [pathlib.Path(value) for value in sys.argv[2:]]
def reject(message):
print(f"REJECT: {message}", file=sys.stderr)
raise SystemExit(1)
def load(path):
with path.open(encoding="utf-8") as stream:
return [item for item in yaml.load_all(stream, Loader=AccessSafeLoader) if item is not None]
def one(items, kind, name, namespace=None):
matches = [
item for item in items
if item.get("kind") == kind
and (item.get("metadata") or {}).get("name") == name
and (namespace is None or (item.get("metadata") or {}).get("namespace") == namespace)
]
if len(matches) != 1:
reject(f"expected one {kind}/{name}, found {len(matches)}")
return matches[0]
def labels(item):
return (item.get("metadata") or {}).get("labels") or {}
def exact_policy_specs(items, expected):
policies = {
(item.get("metadata") or {}).get("name"): item.get("spec") or {}
for item in items if item.get("kind") == "NetworkPolicy"
}
for name, spec in expected.items():
if policies.get(name) != spec:
reject(f"NetworkPolicy spec is not exact: {name}")
def selector_matches(selector, pod_labels):
selector = selector or {}
for key, value in (selector.get("matchLabels") or {}).items():
if pod_labels.get(key) != value:
return False
for expression in selector.get("matchExpressions") or []:
key = expression.get("key")
operator = expression.get("operator")
values = expression.get("values") or []
value = pod_labels.get(key)
if operator == "In" and value not in values:
return False
if operator == "NotIn" and value in values:
return False
if operator == "Exists" and value is None:
return False
if operator == "DoesNotExist" and value is not None:
return False
if operator not in {"In", "NotIn", "Exists", "DoesNotExist"}:
reject("NetworkPolicy selector operator is unsupported")
return True
def exact_selecting_policy_names(items, namespace, pod_labels, expected):
selected = {
(item.get("metadata") or {}).get("name")
for item in items
if item.get("kind") == "NetworkPolicy"
and (item.get("metadata") or {}).get("namespace") == namespace
and selector_matches((item.get("spec") or {}).get("podSelector") or {}, pod_labels)
}
if selected != set(expected):
reject("NetworkPolicy selecting set is not exact")
if mode == "grafana":
items, policies = load(paths[0]), load(paths[1])
service = one(items, "Service", "grafana", "observability")
if (service.get("spec") or {}).get("type") != "ClusterIP":
reject("Grafana Service is not ClusterIP")
ingress = one(items, "Ingress", "grafana", "observability")
spec = ingress.get("spec") or {}
rules = spec.get("rules") or []
if spec.get("ingressClassName") != "traefik" or spec.get("tls") not in (None, []):
reject("Grafana Ingress boundary is not exact")
if len(rules) != 1 or rules[0].get("host") != "grafana.learn.hyeonworks.com":
reject("Grafana host is not exact")
deployment = one(items, "Deployment", "grafana", "observability")
pod = (((deployment.get("spec") or {}).get("template") or {}).get("spec") or {})
if pod.get("automountServiceAccountToken") is not False:
reject("Grafana Pod token automount is not disabled")
containers = {container.get("name"): container for container in pod.get("containers") or []}
if set(containers) != {"grafana", "grafana-sc-dashboard"}:
reject("Grafana container set is not exact")
token_volume = "dashboard-sidecar-api-access"
main_mounts = {mount.get("name") for mount in containers["grafana"].get("volumeMounts") or []}
side_mounts = {mount.get("name") for mount in containers["grafana-sc-dashboard"].get("volumeMounts") or []}
if token_volume in main_mounts or token_volume not in side_mounts:
reject("dashboard API token mount is not sidecar-only")
volume = next((volume for volume in pod.get("volumes") or [] if volume.get("name") == token_volume), None)
sources = (((volume or {}).get("projected") or {}).get("sources") or [])
if len(sources) != 3 or not any("serviceAccountToken" in source for source in sources) \
or not any("configMap" in source for source in sources) \
or not any("downwardAPI" in source for source in sources):
reject("sidecar projected API volume is not exact")
one(items, "Role", "grafana-dashboard-sidecar", "observability")
if any(item.get("kind") == "ClusterRole" for item in items):
reject("cluster-wide Grafana RBAC is forbidden")
config = one(items, "ConfigMap", "grafana", "observability")
ini = ((config.get("data") or {}).get("grafana.ini") or "")
required = (
"allowed_groups = /platform-observability-admins /platform-observability-viewers",
"role_attribute_strict = true",
"allow_assign_grafana_admin = false",
"&& 'Admin' || contains(groups[*], '/platform-observability-viewers') && 'Viewer' || null",
)
if not all(token in ini for token in required) or "&& 'Editor'" in ini or "|| 'Viewer'" in ini:
reject("Grafana OIDC role mapping is not fail-closed")
exact_policy_specs(policies, {
"observability-default-deny": {
"podSelector": {}, "policyTypes": ["Ingress", "Egress"],
},
"observability-allow-grafana-ingress": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "grafana", "app.kubernetes.io/name": "grafana",
}},
"policyTypes": ["Ingress"],
"ingress": [
{"from": [{
"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "kube-system"}},
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "traefik-kube-system",
"app.kubernetes.io/name": "traefik",
}},
}], "ports": [{"protocol": "TCP", "port": 3000}]},
{"from": [{"podSelector": {"matchLabels": {
"app.kubernetes.io/name": "prometheus",
}}}], "ports": [{"protocol": "TCP", "port": 3000}]},
{"from": [{"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "blackbox-exporter",
"app.kubernetes.io/name": "prometheus-blackbox-exporter",
}}}], "ports": [{"protocol": "TCP", "port": 3000}]},
],
},
"observability-allow-grafana-datasources": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "grafana", "app.kubernetes.io/name": "grafana",
}},
"policyTypes": ["Egress"],
"egress": [
{"to": [{"podSelector": {"matchLabels": {"app.kubernetes.io/name": "prometheus"}}}],
"ports": [{"protocol": "TCP", "port": 9090}]},
{"to": [{"podSelector": {"matchLabels": {"app.kubernetes.io/name": "loki"}}}],
"ports": [{"protocol": "TCP", "port": 3100}]},
{"to": [{"podSelector": {"matchLabels": {"app.kubernetes.io/name": "tempo"}}}],
"ports": [{"protocol": "TCP", "port": 3200}]},
{"to": [{"ipBlock": {"cidr": "192.168.0.107/32"}}],
"ports": [{"protocol": "TCP", "port": 443}]},
],
},
"observability-allow-grafana-dashboard-api": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "grafana", "app.kubernetes.io/name": "grafana",
}},
"policyTypes": ["Egress"],
"egress": [
{"to": [{"ipBlock": {"cidr": "10.43.0.1/32"}}],
"ports": [{"protocol": "TCP", "port": 443}]},
{"to": [{"ipBlock": {"cidr": "192.168.0.107/32"}}],
"ports": [{"protocol": "TCP", "port": 6443}]},
],
},
})
exact_selecting_policy_names(policies, "observability", {
"app.kubernetes.io/instance": "grafana", "app.kubernetes.io/name": "grafana",
}, {
"observability-default-deny", "observability-allow-dns",
"observability-allow-grafana-ingress", "observability-allow-grafana-datasources",
"observability-allow-grafana-dashboard-api",
})
elif mode == "blackbox":
items = load(paths[0])
service = one(items, "Service", "blackbox-exporter", "observability")
if (service.get("spec") or {}).get("type") != "ClusterIP":
reject("Blackbox Service is public")
deployment = one(items, "Deployment", "blackbox-exporter", "observability")
pod = (((deployment.get("spec") or {}).get("template") or {}).get("spec") or {})
if pod.get("automountServiceAccountToken") is not False or len(pod.get("containers") or []) != 1:
reject("Blackbox Pod attack surface is not exact")
expected = {
"platform-public-edge": {
"https://git.learn.hyeonworks.com/api/healthz",
"https://id.learn.hyeonworks.com/realms/hyeonworks/.well-known/openid-configuration",
},
"platform-private-edge": {
"https://grafana.learn.hyeonworks.com/", "https://storage-admin.learn.hyeonworks.com/",
"https://db-admin.learn.hyeonworks.com/",
},
"platform-private-internal": {
"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/",
},
}
for name, targets in expected.items():
probe = one(items, "Probe", name, "observability")
actual = set((((probe.get("spec") or {}).get("targets") or {}).get("staticConfig") or {}).get("static") or [])
if actual != targets:
reject(f"Blackbox target set drifted: {name}")
exact_policy_specs(items, {
"observability-allow-prometheus-to-blackbox": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "blackbox-exporter",
"app.kubernetes.io/name": "prometheus-blackbox-exporter",
}},
"policyTypes": ["Ingress"],
"ingress": [{
"from": [{"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "observability-core-kube-pr-prometheus",
"app.kubernetes.io/name": "prometheus",
}}}],
"ports": [{"protocol": "TCP", "port": 9115}],
}],
},
"observability-allow-blackbox-egress": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "blackbox-exporter",
"app.kubernetes.io/name": "prometheus-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/instance": "grafana", "app.kubernetes.io/name": "grafana",
}}}], "ports": [{"protocol": "TCP", "port": 3000}]},
{"to": [{
"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "platform-admin"}},
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "pgadmin", "app.kubernetes.io/name": "pgadmin4",
}},
}], "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}]},
],
},
})
if {
(item.get("metadata") or {}).get("name")
for item in items if item.get("kind") == "NetworkPolicy"
} != {"observability-allow-prometheus-to-blackbox", "observability-allow-blackbox-egress"}:
reject("Blackbox NetworkPolicy name set is not exact")
elif mode == "targets":
items = load(paths[0])
monitors = [(item.get("kind"), (item.get("metadata") or {}).get("namespace"), (item.get("metadata") or {}).get("name"))
for item in items if item.get("kind") in {"ServiceMonitor", "PodMonitor"}]
if sorted(monitors) != sorted([
("PodMonitor", "platform-data", "platform-postgres"),
("ServiceMonitor", "object-storage", "aistor-bucket-usage"),
]):
reject("manual target monitor set is not exact")
exact_policy_specs(items, {
"traefik-preserve-ingress-and-allow-prometheus-metrics": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "traefik-kube-system", "app.kubernetes.io/name": "traefik",
}},
"policyTypes": ["Ingress"],
"ingress": [
{"ports": [
{"protocol": "TCP", "port": 8000}, {"protocol": "TCP", "port": 8443},
{"protocol": "TCP", "port": 8080},
]},
{"from": [{
"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "observability"}},
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "observability-core-kube-pr-prometheus",
"app.kubernetes.io/name": "prometheus",
}},
}], "ports": [{"protocol": "TCP", "port": 9100}]},
],
},
"observability-allow-prometheus-platform-targets": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "observability-core-kube-pr-prometheus",
"app.kubernetes.io/name": "prometheus",
}},
"policyTypes": ["Egress"],
"egress": [
{"to": [{
"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "gitea"}},
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "gitea", "app.kubernetes.io/name": "gitea",
}},
}], "ports": [{"protocol": "TCP", "port": 3000}]},
{"to": [{
"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "keycloak"}},
"podSelector": {"matchLabels": {
"app": "keycloak", "app.kubernetes.io/instance": "keycloak",
"app.kubernetes.io/managed-by": "keycloak-operator",
}},
}], "ports": [{"protocol": "TCP", "port": 9000}]},
{"to": [{
"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "platform-data"}},
"podSelector": {"matchLabels": {"cnpg.io/cluster": "platform-postgres"}},
}], "ports": [{"protocol": "TCP", "port": 9187}]},
{"to": [{
"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "object-storage"}},
"podSelector": {"matchLabels": {"aistor.min.io/objectStore": "minio-aistor"}},
}], "ports": [{"protocol": "TCP", "port": 9000}]},
{"to": [{
"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "kube-system"}},
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "traefik-kube-system", "app.kubernetes.io/name": "traefik",
}},
}], "ports": [{"protocol": "TCP", "port": 9100}]},
],
},
})
if {
(item.get("metadata") or {}).get("name")
for item in items if item.get("kind") == "NetworkPolicy"
} != {
"traefik-preserve-ingress-and-allow-prometheus-metrics",
"observability-allow-prometheus-platform-targets",
}:
reject("target NetworkPolicy name set is not exact")
elif mode == "rules-alerts":
dashboards, rules, alertmanager = map(load, paths)
dashboard_names = sorted((item.get("metadata") or {}).get("name") for item in dashboards)
expected_dashboards = sorted([
"grafana-dashboard-kubernetes-node", "grafana-dashboard-workload-health",
"grafana-dashboard-platform-services", "grafana-dashboard-observability-backends",
"grafana-dashboard-https-endpoints",
])
if dashboard_names != expected_dashboards or any(labels(item).get("grafana_dashboard") != "1" for item in dashboards):
reject("dashboard ConfigMap set is not exact")
rule_names = sorted((item.get("metadata") or {}).get("name") for item in rules)
if rule_names != sorted(["platform-aistor-storage-quota", "platform-certificate-probes", "platform-observability-core", "platform-verified-services"]):
reject("PrometheusRule set is not exact")
am = one(alertmanager, "Alertmanager", "observability-core-kube-pr-alertmanager", "observability")
if (((am.get("spec") or {}).get("alertmanagerConfiguration") or {}).get("name")) != "platform-alertmanager":
reject("global AlertmanagerConfig reference is absent")
config = one(alertmanager, "AlertmanagerConfig", "platform-alertmanager", "observability")
receivers = (config.get("spec") or {}).get("receivers") or []
slack_receiver = next((receiver for receiver in receivers if receiver.get("name") == "platform-slack"), {})
slack = ((slack_receiver.get("slackConfigs") or [{}])[0])
api_url = slack.get("apiURL") or {}
if api_url != {"name": "alertmanager-slack-webhook", "key": "url"} or slack.get("sendResolved") is not True:
reject("Slack Secret selector or resolved route is not exact")
one(alertmanager, "NetworkPolicy", "observability-allow-alertmanager-public-https", "observability")
exact_policy_specs(alertmanager, {
"observability-allow-alertmanager-public-https": {
"podSelector": {"matchLabels": {
"app.kubernetes.io/instance": "observability-core-kube-pr-alertmanager",
"app.kubernetes.io/name": "alertmanager",
}},
"policyTypes": ["Egress"],
"egress": [{
"to": [{"ipBlock": {
"cidr": "0.0.0.0/0",
"except": ["10.0.0.0/8", "100.64.0.0/10", "172.16.0.0/12", "192.168.0.0/16"],
}}],
"ports": [{"protocol": "TCP", "port": 443}],
}],
},
})
if {
(item.get("metadata") or {}).get("name")
for item in alertmanager if item.get("kind") == "NetworkPolicy"
} != {"observability-allow-alertmanager-public-https"}:
reject("Alertmanager NetworkPolicy name set is not exact")
elif mode == "scope":
items = []
for path in paths:
items.extend(load(path))
forbidden = re.compile(r"spring|jvm|kafka|batch|backup", re.I)
for item in items:
kind = item.get("kind")
name = (item.get("metadata") or {}).get("name") or ""
if kind in {"Probe", "ServiceMonitor", "PodMonitor", "PrometheusRule"}:
if labels(item).get("observability.hyeonworks.com/instance") != "home":
reject(f"missing monitor/rule instance label: {kind}/{name}")
semantic_names = [name]
if kind == "PrometheusRule":
for group in (item.get("spec") or {}).get("groups") or []:
semantic_names.append(group.get("name") or "")
for rule in group.get("rules") or []:
semantic_names.append(rule.get("alert") or rule.get("record") or "")
if any(forbidden.search(value) for value in semantic_names):
reject(f"forbidden product scope: {kind}/{name}")
if kind in {"ServiceMonitor", "PodMonitor"}:
namespace = (item.get("metadata") or {}).get("namespace")
allowed = {
("observability", "blackbox-exporter"),
("platform-data", "platform-postgres"),
("object-storage", "aistor-bucket-usage"),
}
if (namespace, name) not in allowed:
reject(f"out-of-scope access target: {namespace}/{name}")
elif mode == "private-dns":
items = load(paths[0])
config = one(items, "ConfigMap", "coredns-custom", "kube-system")
content = "\n".join((config.get("data") or {}).values())
lan = paths[1].read_text()
tail = paths[2].read_text()
hosts = ("git", "id", "storage-admin", "db-admin", "grafana")
for short in hosts:
host = f"{short}.learn.hyeonworks.com"
if f"192.168.0.107 {host}" not in content \
or f"address=/{host}/192.168.0.107" not in lan \
or f"address=/{host}/100.92.240.34" not in tail:
reject(f"private DNS mapping is not exact: {host}")
if re.search(r"(?:^|\s)(?!192\.168\.0\.107\b)\d+\.\d+\.\d+\.\d+\s+grafana\.learn", content):
reject("Grafana DNS assumes a non-private address")
elif mode == "traefik":
items = load(paths[0])
hcc = one(items, "HelmChartConfig", "traefik", "kube-system")
values = yaml.safe_load((hcc.get("spec") or {}).get("valuesContent") or "") or {}
web = ((((values.get("ports") or {}).get("web") or {}).get("forwardedHeaders") or {}))
if web.get("trustedIPs") != ["10.42.0.1/32"] or web.get("insecure") is True:
reject("Traefik trust overlay is not exact")
else:
reject(f"unknown assertion mode: {mode}")
PY
}
assert_access_grafana_contract() { _access_assert grafana "$@"; }
assert_access_blackbox_contract() { _access_assert blackbox "$@"; }
assert_access_targets_contract() { _access_assert targets "$@"; }
assert_access_rules_alerts_contract() {
(( $# == 3 )) || return 1
_access_assert rules-alerts "$@" || return 1
assert_alertmanager_routing_contract "$3"
}
assert_access_scope_contract() { _access_assert scope "$@"; }
assert_access_private_dns_contract() { _access_assert private-dns "$@"; }
assert_access_traefik_trust_contract() { _access_assert traefik "$@"; }
validate_access_inventory_root() {
local root=$1 phase directory expected actual filename extra entries current_uid
current_uid="$(id -u)" || return 1
[[ "$root" =~ ^/tmp/platform-observability-metrics\.[A-Za-z0-9]{6}$ ]] || return 1
[[ -d /tmp && ! -L /tmp && "$(readlink -f -- /tmp)" == /tmp &&
"$(stat -c %u:%a -- /tmp)" == 0:1777 ]] || return 1
[[ -d "$root" && ! -L "$root" && "$(readlink -f -- "$root")" == "$root" &&
"$(stat -c %u:%a -- "$root")" == "$current_uid:700" ]] || return 1
entries="$(find "$root" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort)" || return 1
[[ "$entries" == $'post-substrate\ntarget-initial' ]] || return 1
for phase in target-initial post-substrate; do
directory="$root/$phase"
[[ -d "$directory" && ! -L "$directory" && -f "$directory/inventory.json" &&
-f "$directory/inventory.sha256" && ! -L "$directory/inventory.json" &&
! -L "$directory/inventory.sha256" ]] || return 1
[[ "$(readlink -f -- "$directory")" == "$directory" &&
"$(stat -c %u:%a -- "$directory")" == "$current_uid:700" ]] || return 1
entries="$(find "$directory" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort)" || return 1
[[ "$entries" == $'inventory.json\ninventory.sha256' ]] || return 1
for filename in inventory.json inventory.sha256; do
[[ -f "$directory/$filename" && ! -L "$directory/$filename" &&
"$(stat -c %u:%a:%h -- "$directory/$filename")" == "$current_uid:600:1" ]] || return 1
done
read -r expected filename extra <"$directory/inventory.sha256"
[[ "$expected" =~ ^[0-9a-f]{64}$ && "$filename" == inventory.json && -z "${extra:-}" ]] || return 1
actual="$(sha256sum "$directory/inventory.json" | awk '{print $1}')"
[[ "$actual" == "$expected" ]] || return 1
jq -e --arg phase "$phase" '
.schema == "platform-observability-metric-inventory/v1" and
.phase == $phase and (.targets | type == "array" and length > 0) and
all(.targets[]; .health == "up" and .last_error == "" and (.metrics | type == "array" and length > 0))
' "$directory/inventory.json" >/dev/null || return 1
done
}
publish_access_outputs() {
local root=$1 work=$2 component=$3
local -a names=()
mapfile -t names < <(access_output_names "$component") || return 2
(( ${#names[@]} > 0 )) || return 2
python3 - "$root" "$work" "${names[@]}" <<'PY'
import hashlib
import os
import re
import stat
import sys
class Rejected(Exception):
pass
def reject(reason):
raise Rejected(reason)
def same_identity(metadata, expected):
return (metadata.st_dev, metadata.st_ino) == expected
def sha256_descriptor(descriptor):
digest = hashlib.sha256()
os.lseek(descriptor, 0, os.SEEK_SET)
while True:
chunk = os.read(descriptor, 1024 * 1024)
if not chunk:
break
digest.update(chunk)
os.lseek(descriptor, 0, os.SEEK_SET)
return digest.hexdigest()
root, work, *names = sys.argv[1:]
root_descriptor = None
work_descriptor = None
source_descriptors = {}
temporary_names = []
created = {}
try:
if os.path.dirname(root) != "/tmp" or not re.fullmatch(
r"platform-observability-metrics\.[A-Za-z0-9]{6}", os.path.basename(root)
):
reject("output root is not the expected direct /tmp handoff")
tmp_metadata = os.lstat("/tmp")
root_metadata = os.lstat(root)
if not stat.S_ISDIR(tmp_metadata.st_mode) or stat.S_ISLNK(tmp_metadata.st_mode):
reject("/tmp is not a physical directory")
if tmp_metadata.st_uid != 0 or stat.S_IMODE(tmp_metadata.st_mode) != 0o1777:
reject("/tmp owner or mode differs")
if os.path.realpath("/tmp") != "/tmp" or os.path.realpath(root) != root:
reject("output root lineage is not physical")
if not stat.S_ISDIR(root_metadata.st_mode) or stat.S_ISLNK(root_metadata.st_mode):
reject("output root is not a physical directory")
if root_metadata.st_uid != os.getuid() or stat.S_IMODE(root_metadata.st_mode) != 0o700:
reject("output root owner or mode differs")
root_identity = (root_metadata.st_dev, root_metadata.st_ino)
root_descriptor = os.open(root, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
if not same_identity(os.fstat(root_descriptor), root_identity):
reject("opened output root identity differs")
base_entries = {"target-initial", "post-substrate"}
if set(os.listdir(root_descriptor)) != base_entries:
reject("output root is not pristine before publication")
for phase in base_entries:
phase_metadata = os.stat(phase, dir_fd=root_descriptor, follow_symlinks=False)
if not stat.S_ISDIR(phase_metadata.st_mode) or phase_metadata.st_uid != os.getuid():
reject("inventory phase type or owner differs")
if stat.S_IMODE(phase_metadata.st_mode) != 0o700:
reject("inventory phase mode differs")
phase_descriptor = os.open(
phase, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=root_descriptor
)
try:
if set(os.listdir(phase_descriptor)) != {"inventory.json", "inventory.sha256"}:
reject("inventory phase entry set differs")
for filename in ("inventory.json", "inventory.sha256"):
metadata = os.stat(filename, dir_fd=phase_descriptor, follow_symlinks=False)
if not stat.S_ISREG(metadata.st_mode) or metadata.st_uid != os.getuid():
reject("inventory file type or owner differs")
if stat.S_IMODE(metadata.st_mode) != 0o600 or metadata.st_nlink != 1:
reject("inventory file mode or link count differs")
finally:
os.close(phase_descriptor)
if len(set(names)) != len(names) or any("/" in name or name in {".", ".."} for name in names):
reject("publication names are malformed")
if any(name in base_entries for name in names):
reject("publication name collides with inventory")
for name in names:
try:
os.stat(name, dir_fd=root_descriptor, follow_symlinks=False)
except FileNotFoundError:
continue
reject("output destination already exists")
work_metadata = os.lstat(work)
if not stat.S_ISDIR(work_metadata.st_mode) or stat.S_ISLNK(work_metadata.st_mode):
reject("verified render directory is not physical")
if work_metadata.st_uid != os.getuid() or stat.S_IMODE(work_metadata.st_mode) != 0o700:
reject("verified render directory owner or mode differs")
work_identity = (work_metadata.st_dev, work_metadata.st_ino)
work_descriptor = os.open(work, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
if not same_identity(os.fstat(work_descriptor), work_identity):
reject("opened render directory identity differs")
source_hashes = {}
for name in names:
descriptor = os.open(name, os.O_RDONLY | os.O_NOFOLLOW, dir_fd=work_descriptor)
source_descriptors[name] = descriptor
metadata = os.fstat(descriptor)
if not stat.S_ISREG(metadata.st_mode) or metadata.st_uid != os.getuid():
reject("verified output type or owner differs")
if stat.S_IMODE(metadata.st_mode) != 0o600 or metadata.st_nlink != 1:
reject("verified output mode or link count differs")
source_hashes[name] = sha256_descriptor(descriptor)
for position, name in enumerate(names):
temporary = f".access-handoff-{os.getpid()}-{position}"
target_descriptor = os.open(
temporary,
os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW,
0o600,
dir_fd=root_descriptor,
)
temporary_names.append(temporary)
try:
source_descriptor = source_descriptors[name]
os.lseek(source_descriptor, 0, os.SEEK_SET)
while True:
chunk = os.read(source_descriptor, 1024 * 1024)
if not chunk:
break
view = memoryview(chunk)
while view:
view = view[os.write(target_descriptor, view):]
os.fchmod(target_descriptor, 0o600)
os.fsync(target_descriptor)
staged_identity = (os.fstat(target_descriptor).st_dev, os.fstat(target_descriptor).st_ino)
finally:
os.close(target_descriptor)
os.link(
temporary,
name,
src_dir_fd=root_descriptor,
dst_dir_fd=root_descriptor,
follow_symlinks=False,
)
created[name] = staged_identity
os.unlink(temporary, dir_fd=root_descriptor)
temporary_names.remove(temporary)
os.fsync(root_descriptor)
if set(os.listdir(root_descriptor)) != base_entries | set(names):
reject("published output entry set differs")
for name in names:
metadata = os.stat(name, dir_fd=root_descriptor, follow_symlinks=False)
if not same_identity(metadata, created[name]) or not stat.S_ISREG(metadata.st_mode):
reject("published output identity or type differs")
if metadata.st_uid != os.getuid() or stat.S_IMODE(metadata.st_mode) != 0o600:
reject("published output owner or mode differs")
descriptor = os.open(name, os.O_RDONLY | os.O_NOFOLLOW, dir_fd=root_descriptor)
try:
if sha256_descriptor(descriptor) != source_hashes[name]:
reject("published output content differs")
finally:
os.close(descriptor)
if not same_identity(os.lstat(root), root_identity):
reject("output root identity changed during publication")
except BaseException as error:
if root_descriptor is not None:
for name, identity in reversed(tuple(created.items())):
try:
metadata = os.stat(name, dir_fd=root_descriptor, follow_symlinks=False)
if same_identity(metadata, identity):
os.unlink(name, dir_fd=root_descriptor)
except OSError:
pass
for name in reversed(temporary_names):
try:
os.unlink(name, dir_fd=root_descriptor)
except OSError:
pass
try:
os.fsync(root_descriptor)
except OSError:
pass
if isinstance(error, Rejected):
print(f"REJECT: access output publication failed: {error}", file=sys.stderr)
else:
print("REJECT: access output publication failed: filesystem operation failed", file=sys.stderr)
raise SystemExit(23)
finally:
for descriptor in source_descriptors.values():
try:
os.close(descriptor)
except OSError:
pass
if work_descriptor is not None:
os.close(work_descriptor)
if root_descriptor is not None:
os.close(root_descriptor)
PY
}
_access_usage() {
cat <<'USAGE'
Usage:
PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm \
bash scripts/validate/render-observability-access.sh \
--component grafana|blackbox|targets|rules-alerts|complete \
[--verified-output-dir /tmp/platform-observability-metrics.XXXXXX]
Static rendering only; no Kubernetes resource is applied.
USAGE
}
access_render_main() (
local component=complete output_root='' argument core_handoff work
access_secure_render_context
while (( $# > 0 )); do
argument=$1
case "$argument" in
--component) (( $# >= 2 )) || return 2; component=$2; shift 2 ;;
--verified-output-dir) (( $# >= 2 )) || return 2; output_root=$2; shift 2 ;;
-h|--help) _access_usage; return 0 ;;
*) _access_usage >&2; return 2 ;;
esac
done
access_output_names "$component" >/dev/null || return 2
if access_component_requires_inventory "$component"; then
[[ -n "$output_root" ]] || { printf 'REJECT: rules-alerts/complete requires --verified-output-dir\n' >&2; return 1; }
validate_access_inventory_root "$output_root" || { printf 'REJECT: verified inventory root is invalid\n' >&2; return 1; }
fi
work="$(mktemp -d /tmp/platform-observability-access-render.XXXXXX)"
chmod 0700 "$work"
core_handoff="$(mktemp -d /tmp/platform-observability-core-apply.XXXXXX)"
chmod 0700 "$core_handoff"
trap '[[ -n ${work:-} && $work == /tmp/platform-observability-access-render.* ]] && rm -rf -- "$work"; [[ -n ${core_handoff:-} && $core_handoff == /tmp/platform-observability-core-apply.* ]] && rm -rf -- "$core_handoff"' EXIT INT TERM
/usr/bin/bash "$ACCESS_ROOT/scripts/validate/render-observability-core.sh" \
--verified-output-dir "$core_handoff"
cp -- "$core_handoff/grafana.yaml" "$work/grafana.yaml"
cp -- "$core_handoff/blackbox.yaml" "$work/blackbox.yaml"
cp -- "$core_handoff/targets.yaml" "$work/targets.yaml"
cp -- "$core_handoff/dashboards.yaml" "$work/dashboards.yaml"
cp -- "$core_handoff/core-rules.yaml" "$work/rules.yaml"
kubectl kustomize "$ACCESS_ROOT/infrastructure/networking/private-dns/kubernetes" >"$work/private-dns.yaml"
python3 - "$core_handoff/kps.yaml" "$core_handoff/alerting.yaml" "$work/alertmanager.yaml" <<'PY'
import sys, yaml
class AccessExtractionSafeLoader(yaml.SafeLoader):
pass
AccessExtractionSafeLoader.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, encoding="utf-8") as stream:
items.extend(
item
for item in yaml.load_all(stream, Loader=AccessExtractionSafeLoader)
if item is not None
)
selected=[item for item in items if item.get("kind") in {"Alertmanager", "AlertmanagerConfig", "NetworkPolicy"}]
with open(sys.argv[3], "w", encoding="utf-8") as stream:
yaml.safe_dump_all(selected, stream, explicit_start=True, sort_keys=False)
PY
assert_access_grafana_contract "$work/grafana.yaml" "$core_handoff/core-policies.yaml"
assert_pinned_images "$work/grafana.yaml"
assert_access_blackbox_contract "$work/blackbox.yaml"
assert_pinned_images "$work/blackbox.yaml"
assert_access_targets_contract "$work/targets.yaml"
assert_access_rules_alerts_contract "$work/dashboards.yaml" "$work/rules.yaml" "$work/alertmanager.yaml"
assert_access_scope_contract "$work/blackbox.yaml" "$work/targets.yaml" "$work/rules.yaml"
assert_access_private_dns_contract "$work/private-dns.yaml" \
"$ACCESS_ROOT/infrastructure/networking/private-dns/host/dnsmasq-lan.conf" \
"$ACCESS_ROOT/infrastructure/networking/private-dns/host/dnsmasq-tailscale.conf"
assert_no_credentials "$work"/*.yaml
if [[ -n "$output_root" ]]; then
publish_access_outputs "$output_root" "$work" "$component"
fi
printf 'OBSERVABILITY ACCESS STATIC RENDER PASS\n'
)
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
set -Eeuo pipefail
access_render_main "$@"
fi