Skip to main content
Metoro’s access control model can be managed declaratively with Kubernetes custom resources under the rbac.metoro.io/v1alpha1 API group. This lets you keep access control in version control next to the workloads it protects, and delegate it with the Kubernetes RBAC you already have: a team with write access to a namespace can manage Metoro access for that namespace, and nothing else. On on-prem installs, group creation is centralized in the Metoro Hub cluster. Apply MetoroGroup resources to the hub cluster only; the MetoroGroup CRD is not installed in monitored clusters. Apply telemetry and resource rule resources to the monitored cluster whose access they describe.
KindScopeLives inGrantsShort names
MetoroGroupClusterOn-prem hub clusterNothing — declares a groupmgroup
MetoroNamespaceTelemetryRuleNamespacedAny monitored clusterTelemetry from its namespacemntelrule
MetoroClusterTelemetryRuleClusterAny monitored clusterTelemetry from the whole clustermctelrule
MetoroNamespaceResourceRuleNamespacedAny monitored clusterKubernetes-managed alerts/dashboards/webhooks from its namespacemnresrule
MetoroClusterResourceRuleClusterAny monitored clusterKubernetes-managed alerts/dashboards/webhooks from the whole clustermcresrule
If you know Kubernetes RBAC: a Metoro rule resource is a Role and a RoleBinding in one object. Each rule creates a managed Metoro role holding its permissions and binds that role to the groups listed in spec.groups. There is no separate binding resource.
Kubernetes RBAC is the trust boundary. Anyone who can write these custom resources in a namespace or cluster controls Metoro access for that scope — restrict write access to them accordingly.

Prerequisites

  • The four rule CRDs are installed by the Metoro exporter chart on every monitored cluster, so you can use them on both Metoro cloud and on-prem.
  • The MetoroGroup CRD is installed by the on-prem hub chart only. It is expected to be absent from monitored clusters.
  • After you apply or edit one of these resources, the exporter takes about a minute to discover it and send it to the hub, where it is reconciled automatically. To apply a change without waiting for the next automatic reconcile, wait that minute and then click Sync rules in Settings → Users & Groups — clicking it before the change reaches the hub has no effect.
All resources created this way appear in the Metoro UI with a Managed via K8s label and are read-only there — Kubernetes is the source of truth, so edit or delete the custom resource instead.

MetoroGroup

MetoroGroup is available on on-prem installs only, where it is applied to and synced from the hub cluster. It is intentionally not installed in monitored clusters, so group creation stays centralized. On Metoro cloud, create groups in the UI under Settings → Users & Groups → Groups instead.
MetoroGroup declares a Metoro group. It is cluster-scoped, has no spec, and the group name is simply the object’s metadata.name:
apiVersion: rbac.metoro.io/v1alpha1
kind: MetoroGroup
metadata:
  name: sre-team
Apply this to the hub cluster and a group named sre-team appears in the Groups tab, tagged Managed via K8s. The group itself is read-only in the UI, but you still add users to it normally — via Assign Groups in the UI or identity provider group sync. If a group with the same name already exists from another source (created in the UI or by IdP sync), the MetoroGroup is skipped rather than taking the existing group over. Groups tab showing groups tagged Managed via K8s alongside UI-managed and built-in groups Remember that a group grants nothing by itself — access comes from the rule resources below.

Walkthrough

This walkthrough gives the sre-team group a realistic access policy in the prod cluster, step by step: full telemetry for the kube-system namespace, metrics and error/warning logs across the rest of the cluster, read access to every webhook, and permission to author dashboards in kube-system. Metoro identifies clusters by their environment name, so prod below means the environment the rule is applied in.

Step 1: Create the group

On-prem, apply the MetoroGroup manifest above to the hub cluster. On cloud, create sre-team in Settings → Users & Groups → Groups. Then add the team’s users to the group.

Step 2: Grant telemetry access for a namespace

Apply a MetoroNamespaceTelemetryRule in the kube-system namespace of the prod cluster:
apiVersion: rbac.metoro.io/v1alpha1
kind: MetoroNamespaceTelemetryRule
metadata:
  name: kube-system-all-signals
  namespace: kube-system
spec:
  groups:
    - sre-team
  rules:
    - signals:
        - logs
        - metrics
        - traces
        - profiles
        - kubernetesResources
Namespaced rules are automatically scoped to where they live: this rule grants the five signals only for the kube-system namespace in the prod environment. You never need to (and cannot) widen a namespaced rule beyond its own namespace — that is the point of the namespaced variant.

Step 3: Grant metrics and problem logs across the cluster

Some access should span the whole cluster rather than one namespace. Use the cluster-scoped variant, which is scoped to the environment but not to any namespace. This rule grants two independent things: every metric everywhere, and error and warning logs everywhere.
apiVersion: rbac.metoro.io/v1alpha1
kind: MetoroClusterTelemetryRule
metadata:
  name: cluster-metrics-and-problem-logs
spec:
  groups:
    - sre-team
  rules:
    - signals:
        - metrics
    - signals:
        - logs
      match:
        matchExpressions:
          - key: log.severity
            operator: In
            values:
              - error
              - warn
The first rule has no match, so it grants every metric in the environment. The second narrows logs to those whose log.severity is error or warn. Expressions inside one matchExpressions list must all be true (ANDed); separate rules entries — and separate rule resources — are independent grants (ORed). Combined with Step 2, the team now sees all telemetry inside kube-system and, everywhere else, metrics plus error/warning logs. log.severity is a user-defined value (commonly error, warn, info, debug), so its casing can vary between workloads. If your logs are inconsistent, use the RegexIn operator with a case-insensitive pattern such as (?i)^err instead of In.

Step 4: See every webhook across the cluster

Resource rules grant permissions on Metoro resources that are themselves declared as Kubernetes resourcesMetoroAlert, MetoroDashboard, and MetoroWebhook objects. They do not grant access to resources created in the UI. Use the cluster-scoped variant to cover the whole environment. This grants read-only access to every Kubernetes-managed webhook:
apiVersion: rbac.metoro.io/v1alpha1
kind: MetoroClusterResourceRule
metadata:
  name: all-webhooks-read
spec:
  groups:
    - sre-team
  rules:
    - resources:
        - webhooks
      verbs:
        - read
With no path, this grants read on /webhooks/kubernetes-managed/prod/* — every Kubernetes-managed webhook in the prod environment.

Step 5: Let the team author dashboards in kube-system

The namespaced variant scopes resource permissions to its own namespace. This gives the team full control over dashboards declared in kube-system:
apiVersion: rbac.metoro.io/v1alpha1
kind: MetoroNamespaceResourceRule
metadata:
  name: kube-system-dashboards
  namespace: kube-system
spec:
  groups:
    - sre-team
  rules:
    - resources:
        - dashboards
      verbs:
        - create
        - read
        - update
        - delete
This grants all four verbs on /dashboards/kubernetes-managed/prod/kube-system/*. create is what lets the team add new MetoroDashboard resources in the namespace; update and delete let them change and remove them.

Step 6: What you see in the UI

Once the exporter has sent your changes to the hub (about a minute) and they have been reconciled:
  • The sre-team group appears in the Groups tab, tagged Managed via K8s, with its four bound roles listed — one per rule resource.
Groups tab filtered to sre-team, showing its four Kubernetes-managed roles
  • Each rule resource creates one managed role, named after the rule — for example, the Step 2 rule produces kube-system-all-signals-role. Opening the Roles tab shows the permissions Metoro derived from each, including the environment and namespace scoping it added automatically (note how the telemetry role scopes traces by server.namespace and Kubernetes resources by Namespace, while other signals use namespace).
  • The roles and their permissions carry a Managed via K8s label and are read-only. To change them, edit the custom resource in Kubernetes, then use Sync rules for an immediate refresh.
Roles tab showing the managed roles' derived telemetry and resource permissions

Step 7: Author a dashboard in kube-system

The resource rule from Step 5 lets sre-team create MetoroDashboard objects in kube-system. Here is a node CPU and memory dashboard the team might apply — note the namespace: kube-system, which is what brings it under the Step 5 grant:
apiVersion: observability.metoro.io/v1alpha1
kind: MetoroDashboard
metadata:
  name: node-cpu-memory
  namespace: kube-system
spec:
  title: Node CPU & Memory
  settings:
    defaultTimeRange: "1h"
    layoutVersion: 2
  content:
    title: Node CPU & Memory
    widgets:
      # ---- CPU ----
      - type: chart
        position:
          type: absolute
          absolute: { x: 0, "y": 0, w: 12, h: 12 }
        chart:
          title: Node CPU usage (cores)
          description: CPU cores in use per node (excludes idle).
          chartType: line
          expression:
            queryMode: metoroql
            metoroQLQueries:
              - query: sum(rate(node_resources_cpu_usage_seconds_total{mode!="idle"})) by (kubernetes.io/hostname)
                bucketSize: 60
                displaySettings:
                  label: "{{kubernetes.io/hostname}}"
                  unitOverride: cores
                  lineSettings: { lineType: solid, lineColour: "#2DD881" }
      - type: stat
        position:
          type: absolute
          absolute: { x: 12, "y": 0, w: 6, h: 12 }
        stat:
          title: Cluster CPU usage (cores)
          expression:
            queryMode: metoroql
            metoroQLQuery:
              query: sum(rate(node_resources_cpu_usage_seconds_total{mode!="idle"}))
              bucketSize: 60
            reduceAggregation: avg
          displaySettings:
            globalSettings: { valuePrecision: 2, unitOverride: cores }
      - type: stat
        position:
          type: absolute
          absolute: { x: 18, "y": 0, w: 6, h: 12 }
        stat:
          title: Cluster CPU capacity (cores)
          expression:
            queryMode: metoroql
            metoroQLQuery:
              query: sum(node_resources_cpu_logical_cores)
              bucketSize: 60
            reduceAggregation: avg
          displaySettings:
            globalSettings: { valuePrecision: 0, unitOverride: cores }
      # ---- Memory ----
      - type: chart
        position:
          type: absolute
          absolute: { x: 0, "y": 12, w: 12, h: 12 }
        chart:
          title: Node memory used (bytes)
          description: Physical memory in use per node (total minus available).
          chartType: line
          expression:
            queryMode: metoroql
            metoroQLQueries:
              - query: sum(node_resources_memory_total_bytes) by (kubernetes.io/hostname) - sum(node_resources_memory_available_bytes) by (kubernetes.io/hostname)
                bucketSize: 60
                displaySettings:
                  label: "{{kubernetes.io/hostname}}"
                  unitOverride: "By"
                  lineSettings: { lineType: solid, lineColour: "#4299E1", areaOpacity: 0.2 }
      - type: stat
        position:
          type: absolute
          absolute: { x: 12, "y": 12, w: 6, h: 12 }
        stat:
          title: Cluster memory used
          expression:
            queryMode: metoroql
            metoroQLQuery:
              query: sum(node_resources_memory_total_bytes) - sum(node_resources_memory_available_bytes)
              bucketSize: 60
            reduceAggregation: avg
          displaySettings:
            globalSettings: { valuePrecision: 0, unitOverride: "By" }
      - type: stat
        position:
          type: absolute
          absolute: { x: 18, "y": 12, w: 6, h: 12 }
        stat:
          title: Cluster memory capacity
          expression:
            queryMode: metoroql
            metoroQLQuery:
              query: sum(node_resources_memory_total_bytes)
              bucketSize: 60
            reduceAggregation: avg
          displaySettings:
            globalSettings: { valuePrecision: 0, unitOverride: "By" }

Reference

Telemetry rules

MetoroNamespaceTelemetryRule (namespaced) and MetoroClusterTelemetryRule (cluster-scoped) share the same spec:
FieldTypeRequiredDescription
spec.groups[]stringyesGroup names this rule grants access to. 1–256 entries.
spec.rules[]objectyesIndependent allow-grants; effective access is their union. 1–256 entries.
rules[].signals[]stringyesAny of logs, metrics, traces, profiles, kubernetesResources.
rules[].matchobjectnoOptional attribute selector narrowing the inherited scope.
match.matchExpressions[]objectyes, within match1–64 expressions, ANDed together.
matchExpressions[].keystringyesTelemetry attribute key, e.g. service.name, severity, metric.name, status.code, or a custom attribute.
matchExpressions[].operatorstringyesIn, NotIn, RegexIn, Exists, DoesNotExist.
matchExpressions[].values[]stringconditionalRequired for In/NotIn/RegexIn; must be omitted for Exists/DoesNotExist. Up to 256 values.
Implicit scoping added by Metoro (you don’t write these expressions yourself):
SignalNamespaced rule scoped byCluster rule scoped by
logs, metrics, profilesenvironment + namespaceenvironment
tracesenvironment + server.namespaceenvironment
kubernetesResourcesEnvironment + NamespaceEnvironment
Custom attribute keys are supported for logs, metrics, traces, and profiles and are case-sensitive. For example, team and Team are different keys. Custom attributes are matched against the signal’s attribute map: logs and metrics use Attributes, profiles use ResourceAttributes, and traces match either SpanAttributes or ResourceAttributes. kubernetesResources only supports its named resource fields such as Environment, Namespace, Kind, and ResourceName; labels, annotations, and YAML paths are not generic custom telemetry attributes.

Resource rules

MetoroNamespaceResourceRule (namespaced) and MetoroClusterResourceRule (cluster-scoped) share the same spec:
FieldTypeRequiredDescription
spec.groups[]stringyesGroup names this rule grants access to. 1–256 entries.
spec.rules[]objectyesIndependent allow-grants; effective access is their union. 1–256 entries.
rules[].resources[]stringyesAny of alerts, dashboards, webhooks.
rules[].verbs[]stringyesAny of create, read, update, delete.
rules[].pathstringnoRelative subfolder narrowing the grant. No leading /, no wildcards, no ...
Each rule grants its verbs on the permission path:
/<resource>/kubernetes-managed/<environment>[/<namespace>][/<path>]/*
where <namespace> is present only for namespaced rules.

kubectl tips

All five kinds are in the metoro and metoro-rbac categories, so you can list every Metoro RBAC object at once:
kubectl get metoro-rbac -A
Short names work as usual, e.g. kubectl get mntelrules -n kube-system, and the printer columns show each rule’s groups and grants at a glance.

How rules combine

The same evaluation model as the rest of Metoro RBAC applies:
  • All grants are allow-only and additive — the union of every rule, across every resource, in every cluster.
  • Within one matchExpressions list, expressions are ANDed.
  • Across rules entries and across separate rule resources, grants are ORed.
  • Telemetry is deny-by-default: a group with no telemetry grants sees no telemetry.

Gotchas

  • Create the group first. A rule that names a group that doesn’t exist yet is skipped for that group until the group is created — the rest of the rule still applies.
  • Built-in groups are off limits. Rules cannot bind roles to Metoro’s built-in default groups; bind to groups you created.
  • values and operators must agree. In, NotIn, and RegexIn require a non-empty values list; Exists and DoesNotExist require values to be omitted. The CRD schema rejects mismatches at apply time.
  • path is relative. No leading /, no wildcards, no .. — the environment (and namespace) prefix is always added by Metoro.
  • Resource rules only reach Kubernetes-managed resources. They grant access under /…/kubernetes-managed/…, i.e. to MetoroAlert/MetoroDashboard/MetoroWebhook objects — never to alerts, dashboards, or webhooks created in the UI.
  • Everything managed from Kubernetes is read-only in the UI. Edit the custom resource instead. Allow about a minute for the exporter to send your change to the hub; once it has arrived, Sync rules applies it immediately rather than waiting for the next automatic reconcile.