Security

Hardening the Operator

When we develop features for the operator, we assume a certain level of trust granted to users who will be able to create CRDs. This might not work in every situation though so this section documents common pitfalls, security implications you might not expect and how to harden the operator to allow secure deployments in every situation.

Per resource implications

The following parts outline capabilities of our custom resources. You should only grant access to these resources to users that already have the same powers in a specific namespace.

Grafana

The Grafana resources is a very powerful resource. When used to deploy a managed instance, it can override almost any field in the deployment and as such, run arbitrary workloads in the cluster. This includes mounting arbitrary secrets, configmaps and volumes from the same namespace.

GrafanaDatasource, GrafanaDashboards, GrafanaContactPoints, GrafanaManifest, GrafanaLibraryPanel, GrafanaContactPoint

These resources all allow users to modify them based on ConfigMaps and Secrets in the same namespace. Consequently, this means that if you have a Grafana instance in the same namespace as a GrafanaDashboard, a malicious user with edit permissions for GrafanaDashboards can use this power to extract the credential secret of the instance. See the hardening section on various approaches to prevent this if required.

Some resources (Data sources, Dashboards, Library Panels) also allow installing plugins in the targeted Grafana instance.

GrafanaFolder, GrafanaNotificationPolicy, GrafanaNotificationPolicyRoute, GrafanaNotificationTemplate, GrafanaSilence, GrafanaMuteTiming

Right now, these resources do not allow customization through secrets or config maps. However, with the upcoming patching support this will change and they’ll also have the capability to reference external data.

GrafanaServiceAccount

The GrafanaServiceAccount resource is a bit special. It dynamically creates secrets for configured tokens which might cause existing secrets to be overwritten.

Hardening

If you require a hardened setup and want to allow low-trust users to manage some of the before mentioned resources, here are some patterns you can use to enable this.

Isolate resources in namespaces

This mitigates leaking of the instance secret. Create the Grafana instance in one namespace (e.g. grafana-instance) and enable spec.allowCrossNamespaceImport. You can then create a new empty namespace (e.g. grafana-resources) which does not contain any sensitive secrets and have users create their resources in this namespace.

ValidatingAdmissionPolicy

To restrict usage of certain fields, you can make use of Validating Admission Policies . These allow you to validate resources before they reach the API server.

For example, you can prevent creation of resources that reference secrets with the following policy:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "prevent-secret-referencing"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: ["grafana.integreatly.org"]
        apiVersions: ["v1beta1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["grafanadashboards", "grafanalibrarypanels"]
  validations:
    - expression: |
        (
          !has(object.spec.envs) ||
          object.spec.envs.all(v,
            !has(v.valueFrom) || !has(v.valueFrom.secretKeyRef)
          )
        )
        &&
        (
          !has(object.spec.envFrom) ||
          object.spec.envFrom.all(v,!has(v.secretKeyRef))
        )
        &&
        (
          !has(object.spec.patch) ||
          !has(object.spec.patch.env) ||
          object.spec.patch.env.all(v,!has(v.valueFrom) || !has(v.valueFrom.secretKeyRef))
        )
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "prevent-secret-referencing-clusterwide"
spec:
  policyName: "prevent-secret-referencing"
  validationActions: [Deny]

You can also use this to limit modification of the deployment created by the Grafana resource.

Verification of container images

Grafana-operator container images are signed by github attestation . Executing the following command can be used to verify the signature of a container image:

This applies to grafana-operator version 5.19.1 and forward

Prerequisites

Get container digest

# Get the image digest using crane
crane digest --platform linux/amd64 ghcr.io/grafana/grafana-operator:v5.19.1

# Or using oras
oras resolve --platform linux/amd64 ghcr.io/grafana/grafana-operator:v5.19.1

Verify the grafana-operator image

gh attestation verify --owner grafana oci://ghcr.io/grafana/grafana-operator@<sha256>

For example

gh attestation verify --owner grafana oci://ghcr.io/grafana/grafana-operator@$(oras resolve --platform linux/amd64 ghcr.io/grafana/grafana-operator:v5.19.1)

Or if you prefer, you can use cosign.

cosign verify-attestation --certificate-identity-regexp 'https://github\.com/grafana/grafana-operator/\.github/workflows/.+'  --certificate-oidc-issuer https://token.actions.githubusercontent.com --new-bundle-format  --type=slsaprovenance1 ghcr.io/grafana/grafana-operator:@$(oras resolve --platform linux/amd64 ghcr.io/grafana/grafana-operator:v5.19.1) | jq -r '.payload | @base64d | fromjson'

Verify SBOM

As a part of our release cycle we also generate SBOMs. You can find them as artifacts in our github repositorie or in the public cosign instance.

Notice the platform specification in the commands. This is needed since the sbom is matching to the platform specific container image.

# Download the SBOM attestation using the digest (example with oras)
cosign download attestation --predicate-type https://spdx.dev/Document \
  ghcr.io/grafana/grafana-operator@$(oras resolve --platform linux/amd64 ghcr.io/grafana/grafana-operator:v5.19.1)
Last modified October 20, 2026: chore(go): bump to 1.27.3 (#3017) (8227aa9)