Files
home-network-k3s-impl/platform/vuln-scanner
jakman85andClaude Opus 5.5 d4bea8a78c feat(vuln): encrypted oauth2-proxy credential for the CVE page
Generated through pipes and encrypted to the repo's public age recipient; values never echoed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 09:43:12 -06:00
..

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):

  1. 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\""'
    
  2. Add to platform/keycloak/keycloak-realm-import.yaml, beside the crafty client:

    # 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
    
  3. Regenerate the derived URL file: python3 ci/render-keycloak-client-urls.py --write. Commit all three, gate, merge.

  4. 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 with kcadm or re-trigger the import). The client-secret reconciler then converges its secret and URLs.