Generated through pipes and encrypted to the repo's public age recipient; values never echoed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
vuln-scanner — scheduled CVE visibility for what is running
Answers three questions with no manual step: what is running, what is it vulnerable to, and can the
answer be trusted. Contract: spec docs/17-vulnerability-visibility.md, ADR-0027 (R-VULN-01..09).
Hand-authored platform tooling (not a catalog service), reconciled by its own non-gating Flux tier
clusters/base/vuln-scanner.yaml. Operator runbook: runbooks/vuln-scanner.md.
| Piece | Where |
|---|---|
Scanner source (python3, stdlib only), mounted from a ConfigMap at /app |
src/ |
| Accepted risk (R-VULN-08) | acceptances.yaml |
| Pod, security context, sidecar | deployment.yaml |
| Read-only identity (R-VULN-09) | rbac.yaml |
| Network: Gateway→4180, CI→8081, Prometheus→9090; API, DNS, public 443 | networkpolicy.yaml |
| Aggregate metrics and the five alerts | servicemonitor.yaml, alerts.yaml, alerts.promtool-test.yaml |
The page is cve.<domain>, internal only, gated by the per-pod oauth2-proxy sidecar to platform-admins.
The route is disowned from public DNS and the tunnel 404s it; PiHole answers it on the LAN.
Operator step: add the Keycloak client (NOT done by the change that added this tree)
This change adds everything around the Keycloak client but not the client. Adding it needs a new key in
platform/keycloak/keycloak-realm-clients.sops.yaml, an encrypted file an agent must not decrypt; the
L0 check keycloak-client-secret-reconciler then requires the client and that key to appear in the
same change. Until this is done the page cannot complete a login (invalid_client, fail-closed); the
scanner, its metrics and its alerts are unaffected.
In one PR (the realm import does not re-run on its own for a NEW client):
-
Generate one value and set it in both encrypted files, through pipes so it is never echoed:
./infra/devcontainer/dev.sh run bash -c ' cs=$(python3 -c "import secrets; print(secrets.token_hex(32))") sops set platform/keycloak/keycloak-realm-clients.sops.yaml \ "[\"stringData\"][\"vuln-scanner-client-secret\"]" "\"$cs\"" sops set platform/vuln-scanner/vuln-scanner-oauth2-proxy-credentials.sops.yaml \ "[\"stringData\"][\"client-secret\"]" "\"$cs\""' -
Add to
platform/keycloak/keycloak-realm-import.yaml, beside thecraftyclient:# spec.placeholders VULN_SCANNER_CLIENT_SECRET: secret: name: keycloak-realm-clients key: vuln-scanner-client-secret # spec.realm.clients - clientId: vuln-scanner name: CVE report (oauth2-proxy) protocol: openid-connect enabled: true publicClient: false standardFlowEnabled: true directAccessGrantsEnabled: false secret: "$(env:VULN_SCANNER_CLIENT_SECRET)" redirectUris: - https://cve.${BASE_DOMAIN}/oauth2/callback webOrigins: - https://cve.${BASE_DOMAIN} defaultClientScopes: [email, profile, roles] protocolMappers: - name: groups protocol: openid-connect protocolMapper: oidc-group-membership-mapper config: "full.path": "false" "id.token.claim": "true" "access.token.claim": "true" "userinfo.token.claim": "true" "claim.name": groups -
Regenerate the derived URL file:
python3 ci/render-keycloak-client-urls.py --write. Commit all three, gate, merge. -
After merge, the client must also exist in the live realm (a completed realm import does not re-run, per
platform/keycloak/README.md, so create it once withkcadmor re-trigger the import). The client-secret reconciler then converges its secret and URLs.