mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 12:17:13 +00:00
The readiness module http_ready rendered insecure_skip_verify from
TLS_MODE and lookup('ca_injected', application_id) (#851). The lookup is
true only when Prometheus itself has a domain, TLS and the self-signed
mode, so in a self-signed deployment where Prometheus runs in another
mode the exporter had no CA file and skipped verification. All targets
share the module, so a target with a publicly trusted certificate was
probed without verification too, and anyone on the probe's path could
answer for a failed application.
http_ready now always verifies. A second module, http_ready_private_ca,
skips verification, and only the targets that need it select it: those
in the self-signed mode while the CA is not mounted into Prometheus,
through the __param_module label, which overrides the job's module for
that target. The default self-signed deployment mounts the CA and keeps
every target on the verifying module.
Verified on a local self-signed Tor round, where the CA is mounted:
the rendered blackbox.yml holds http_ready with insecure_skip_verify:
false and http_ready_private_ca with true, and no target of the scrape
configuration carries the module label. All targets of that round are
http onion names, so the https path itself ran only in the unit tests:
tests/unit/python/roles/web-app-prometheus pass, among them a publicly
trusted target that keeps the verifying module while the CA is missing,
a self-signed target that switches to the private-CA module, and the
verifying module rendering insecure_skip_verify: false without the CA.
Against the templates of main the three new cases fail.
Closes #851
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
127 lines
4.3 KiB
Python
127 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from jinja2 import Environment, FileSystemLoader, StrictUndefined, select_autoescape
|
|
|
|
from utils.cache.files import PROJECT_ROOT
|
|
from utils.cache.yaml import load_yaml_str
|
|
|
|
TEMPLATE_DIR = PROJECT_ROOT / "roles/web-app-prometheus/templates/configuration"
|
|
ONION = "a" * 56 + ".onion"
|
|
|
|
DOMAINS = {
|
|
"web-app-prometheus": [f"prometheus.{ONION}"],
|
|
"web-app-nextcloud": {"nextcloud": f"next.cloud.{ONION}"},
|
|
"web-app-gitea": ["git.infinito.test"],
|
|
"web-app-matrix": ["matrix.infinito.test"],
|
|
"web-svc-cdn": ["cdn.infinito.test"],
|
|
"svc-db-postgres": ["postgres.infinito.test"],
|
|
}
|
|
TLS_MODES = {
|
|
"web-app-nextcloud": "off",
|
|
"web-app-gitea": "self_signed",
|
|
"web-app-matrix": "letsencrypt",
|
|
}
|
|
ROLES_WITH_THE_PROMETHEUS_SERVICE = [
|
|
"web-app-nextcloud",
|
|
"web-app-gitea",
|
|
"web-app-matrix",
|
|
]
|
|
|
|
|
|
def probed_groups(*, ca_injected: bool) -> dict[str, dict]:
|
|
def lookup(name, *terms, **_kwargs):
|
|
if name == "domains":
|
|
return DOMAINS
|
|
if name == "roles_with_service":
|
|
return [{"id": role} for role in ROLES_WITH_THE_PROMETHEUS_SERVICE]
|
|
if name == "ca_injected":
|
|
return ca_injected
|
|
if name == "tls":
|
|
term, want = terms
|
|
if want == "mode":
|
|
return TLS_MODES[term]
|
|
scheme = "http" if term.endswith(".onion") else "https"
|
|
return {
|
|
"url.base": f"{scheme}://{term}/",
|
|
"protocols.web": "http",
|
|
"enabled": False,
|
|
}[want]
|
|
if name == "domain":
|
|
return DOMAINS[terms[0]][0]
|
|
if name == "scrape_target":
|
|
return f"{terms[1]}:9000"
|
|
if name == "native_metrics_apps":
|
|
return []
|
|
raise AssertionError(f"the template calls an unexpected lookup: {name!r}")
|
|
|
|
env = Environment(
|
|
loader=FileSystemLoader(str(TEMPLATE_DIR)),
|
|
undefined=StrictUndefined,
|
|
autoescape=select_autoescape(),
|
|
)
|
|
env.filters["bool"] = bool
|
|
rendered = env.get_template("prometheus.yml.j2").render(
|
|
lookup=lookup,
|
|
application_id="web-app-prometheus",
|
|
CURRENT_PLAY_APPLICATIONS=list(DOMAINS),
|
|
DOMAIN_PRIMARY="infinito.test",
|
|
ALERT_RULES_CONFIG_DOCKER="/etc/prometheus/alert_rules.yml",
|
|
)
|
|
jobs = {job["job_name"]: job for job in load_yaml_str(rendered)["scrape_configs"]}
|
|
return {
|
|
group["labels"]["app"]: group
|
|
for group in jobs["blackbox-healthz"]["static_configs"]
|
|
}
|
|
|
|
|
|
def probed_targets() -> dict[str, list[str]]:
|
|
return {
|
|
app: group["targets"] for app, group in probed_groups(ca_injected=True).items()
|
|
}
|
|
|
|
|
|
class TestBlackboxTargets(unittest.TestCase):
|
|
def test_an_onion_app_is_probed_under_its_onion_name_over_http(self):
|
|
self.assertEqual(
|
|
probed_targets()["web-app-nextcloud"],
|
|
[f"http://next.cloud.{ONION}/healthz/ready"],
|
|
)
|
|
|
|
def test_a_clearnet_app_is_probed_under_its_own_scheme(self):
|
|
self.assertEqual(
|
|
probed_targets()["web-app-gitea"],
|
|
["https://git.infinito.test/healthz/ready"],
|
|
)
|
|
|
|
def test_only_the_roles_the_service_lookup_names_are_probed(self):
|
|
self.assertEqual(set(probed_targets()), set(ROLES_WITH_THE_PROMETHEUS_SERVICE))
|
|
|
|
def test_a_private_ca_target_skips_verification_while_the_ca_is_missing(self):
|
|
labels = probed_groups(ca_injected=False)["web-app-gitea"]["labels"]
|
|
self.assertEqual(labels["__param_module"], "http_ready_private_ca")
|
|
|
|
def test_a_publicly_trusted_target_is_verified_while_the_ca_is_missing(self):
|
|
labels = probed_groups(ca_injected=False)["web-app-matrix"]["labels"]
|
|
self.assertNotIn("__param_module", labels)
|
|
|
|
def test_a_plain_http_target_keeps_the_verifying_module(self):
|
|
labels = probed_groups(ca_injected=False)["web-app-nextcloud"]["labels"]
|
|
self.assertNotIn("__param_module", labels)
|
|
|
|
def test_every_target_is_verified_once_the_ca_is_mounted(self):
|
|
groups = probed_groups(ca_injected=True)
|
|
self.assertEqual(
|
|
[
|
|
app
|
|
for app, group in groups.items()
|
|
if "__param_module" in group["labels"]
|
|
],
|
|
[],
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|