Bahriya Tersane

Documentation / policy-reporter

Policy Reporter

Installs Policy Reporter, an in-cluster view of the policy reports Kyverno writes: every policy’s results for the resources in the cluster, by namespace, policy and severity, including the results of the Compliance and Assurance Convoy’s policies.

The chart installs Policy Reporter’s own Helm chart, unchanged, under the name reporter, with settings for a cluster on your own premises:

  • every image from the Mamluk registry, pinned by digest;
  • the UI and the Kyverno plugin, which adds each policy’s detail to the UI;
  • nothing sent outside the cluster: every notification target, email reports and the Trivy plugin are refused;
  • the UI read through kubectl port-forward, and published only behind sign-in through your own identity provider;
  • NetworkPolicies that allow each Pod only what it uses;
  • every Pod meeting the restricted Pod Security Standard.

Prerequisites

  • Kubernetes 1.33 or later, and the Bahriya Tersane kyverno chart.
  • A namespace of its own, conventionally policy-reporter.
  • A pull secret for 1x.ax named 1x-ax in that namespace, created from an image token as described in the access documentation.

Installation

helm install policy-reporter tersane/policy-reporter \
  --namespace policy-reporter --create-namespace \
  --version <chart version>

Open the UI, read-only:

kubectl --namespace policy-reporter port-forward service/policy-reporter-ui 8080:8080

then http://localhost:8080/. Whoever may create pods/portforward in the namespace may read it.

What it shows, and what it does not

Policy Reporter reads the policy reports in both of Kyverno’s formats (wgpolicyk8s.io and openreports.io), so it shows the result of every Kyverno policy, the Convoy’s included.

It does not show trivy-operator’s results. trivy-operator does not write policy reports, and showing them would need a further component that converts them, and calls to services outside the cluster. The Compliance and Assurance Convoy’s report presents trivy-operator’s results with each failing object and how to fix it.

It is a view, not evidence. It keeps results in a SQLite file on a volume that lasts as long as its Pod, reads them again from the reports when it restarts, and shows what Kyverno reports now. The Convoy’s dated, signed report is the evidence for a regime.

Publishing the UI

The UI has no access control of its own. The chart therefore refuses an Ingress for it until people sign in through OpenID Connect:

  1. Create a client for this UI alone at your identity provider: a confidential client, with the redirect URI https://<host>/callback. Do not reuse the client that signs people in to the cluster itself (on a Bahriya Kubernetes Engine cluster, bahriya-kubernetes-login): it has settings of its own that this UI does not need, and a change made for one would break the other.
  2. Put its details in a Secret in the release’s namespace, with the keys discoveryUrl (the issuer’s /.well-known/openid-configuration), clientId and clientSecret.
  3. Set the values:

    reporter:
      ui:
        openIDConnect:
          enabled: true
          secretRef: policy-reporter-oidc
          callbackUrl: https://reports.example.com/callback
          groupClaim: groups
        ingress:
          enabled: true
          className: <your ingress class>
          hosts:
            - host: reports.example.com
              paths: [{path: /, pathType: Prefix}]
    networkPolicy:
      allowFrom:
        - namespaceSelector: {matchLabels: {kubernetes.io/metadata.name: <your ingress controller's namespace>}}
      identityProviderCIDRs: [192.0.2.10/32]
    

identityProviderCIDRs names the addresses the UI may reach to sign people in, on port 443; an identity provider inside the cluster is named by its Pods’ addresses. groupClaim names the token claim that lists a person’s groups, which Policy Reporter’s access settings can use.

reporter.ui.openIDConnect.pkce is off. Turn it on only if your client requires PKCE; the UI then sends a code challenge with each sign-in. The client secret is never a value, and the identity provider’s certificate is always verified: clientSecret and skipTLS are refused.

Network

Pod Reached by Reaches
Core the UI DNS, the Kubernetes API server
Kyverno plugin the UI, the core DNS, the Kubernetes API server
UI networkPolicy.allowFrom DNS, the Kubernetes API server (it reads its Secrets there when it starts), the core, the plugin; with sign-in, identityProviderCIDRs

The API server’s addresses are read from the kubernetes EndpointSlices in default when the chart is installed or upgraded. After adding a control-plane node, upgrade the chart, or the Pods cannot reach its API server. Where the chart cannot read them, for example when a GitOps tool renders it, give them in apiServer.cidrs.

What the chart refuses

Setting Why
Any reporter.target.* destination, channel or secret, reporter.target.crd, reporter.existingTargetConfig Each would send results outside the cluster
reporter.emailReports The same
reporter.plugin.trivy It calls outside APIs
reporter.ui.oauth Sign-in through a public provider; use OpenID Connect with your own
An Ingress or HTTPRoute for the UI without openIDConnect The UI has no access control of its own
An Ingress or HTTPRoute for the core, reporter.rest The core’s API is read by the UI inside the cluster

Parameters

Parameter Description Default
reporter.image, reporter.ui.image, reporter.plugin.kyverno.image The images, from 1x.ax by digest pinned
reporter.imagePullSecrets, reporter.ui.imagePullSecrets, reporter.plugin.kyverno.imagePullSecrets Pull secrets [{name: 1x-ax}]
reporter.ui.enabled The UI true
reporter.plugin.kyverno.enabled Each policy’s detail in the UI true
reporter.ui.openIDConnect.* Sign-in; see above off
reporter.ui.ingress.* Publishing the UI, with sign-in off
networkPolicy.allowFrom Peers that may reach the UI []
networkPolicy.identityProviderCIDRs The identity provider’s addresses, with sign-in []
apiServer.cidrs, apiServer.port The API server’s addresses, where the chart cannot read them [], 6443
reporter.* resources and security contexts Set for Pod Security restricted see values.yaml