Skip to content

VMUserJWT: add defaultVMAccessClaim to map vmauth default_vm_access_claim (v1.147.0+) #2375

Description

@cesium147

Problem

Starting with vmauth v1.147.0, JWT users are only processed when the token contains a vm_access claim or the user config defines default_vm_access_claim:

// app/vmauth/main.go (v1.147.0)
if tkn.HasVMAccessClaim() || ui.JWT.DefaultVMAccessClaim != nil {
    processUserRequest(w, r, ui, tkn)
    return true
}

If neither is present, a valid JWT that matches match_claims and passes OIDC verification still falls through to unauthorized_user / 401 Unauthorized. Metrics show vmauth_user_requests_total{username="jwt"}=0 and vmauth_http_request_errors_total{reason="invalid_auth_token"} increasing.

This is documented in vmauth v1.147.0:

For setups that use JWT claim matching only (e.g. sub + custom claims) without vm_access in the IdP token and without URL templating placeholders, the intended workaround is an empty default claim:

users:
  - jwt:
      default_vm_access_claim: {}
      match_claims:
        sub: admin
        claims: some-role
      oidc:
        issuer: https://idp.example.com
    url_map:
      - src_paths: ["/prometheus/.*"]
        url_prefix: ["http://vmselect:8481"]

Gap in the operator

VMUser.spec.jwt (v0.72.0 and v0.73.0) exposes:

  • matchClaims
  • oidc
  • publicKeys / publicKeyRefs
  • skipVerify

There is no CRD field for default_vm_access_claim, so operator-managed VMAuth configs cannot set it. Users must either:

  1. Patch vmauth config manually via externalConfig (loses operator-managed VMUser sync), or
  2. Add vm_access: {} in the IdP (not always possible).

Use case

Grafana with oauthPassThru: true forwarding OIDC access tokens to vmauth. Tokens contain sub, claims[], iss, etc., but no vm_access. VMUser is configured with jwt.oidc + jwt.matchClaims and targetRefs to VMCluster/vmselect (no JWT placeholders in URLs).

Request

Add support for defaultVMAccessClaim (or default_vm_access_claim in generated yaml) under VMUser.spec.jwt, mapping to vmauth’s jwt.default_vm_access_claim.

Suggested API shape (open to discussion):

apiVersion: operator.victoriametrics.com/v1beta1
kind: VMUser
metadata:
  name: example
spec:
  jwt:
    defaultVMAccessClaim: {}
    oidc:
      issuer: https://idp.example.com
    matchClaims:
      sub: admin
      claims: admin-role
  targetRefs:
    - crd:
        kind: VMCluster/vmselect
        name: metrics
        namespace: monitoring
      paths: ["/prometheus/.*"]

Optional fields inside defaultVMAccessClaim should mirror vmauth’s VMAccessClaim (metrics/logs account/project IDs, extra filters/labels) for templating use cases.

Environment

  • VictoriaMetrics operator: v0.72.0 / v0.73.0
  • vmauth: v1.147.0
  • Auth: OIDC (jwt.oidc.issuer) + jwt.matchClaims
  • No vm_access claim in JWT from IdP

Expected behavior

Operator generates vmauth config with default_vm_access_claim when set on VMUser, enabling claim-match routing without requiring vm_access in the token.

Actual behavior

Generated config has jwt.match_claims + jwt.oidc only; valid JWTs are rejected with 401.


Metadata

Metadata

Labels

enhancementNew feature or requestwaiting for releaseThe change was merged to upstream, but wasn't released yet.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions