KubeMQ
ConfigureReference

Security (Auth · TLS)

JWT and OIDC authentication, policy-based authorization, and TLS/mTLS.

KubeMQ secures the server with three independent layers: authentication (verify who is connecting, via JWT or OIDC), authorization (policy-based access control), and TLS/mTLS (transport encryption). Each setting is shown for both deployment targets — Docker single-node (config.yaml key · env var) and Kubernetes/Helm (spec.* path). A dash () in the Helm/CRD column means the setting is not available on that surface (it is config.yaml/env-only).

Version floor: the aligned spec.authentication.enable, spec.authentication.type, spec.authentication.signatureType, spec.authentication.key, and spec.authentication.oidc fields are present throughout the current GA chart line — kubemq-crds and kubemq-cluster 3.x (latest 3.2.0) with kubemq-controller 2.x (operator v2.3.0). Anything older than the 3.0.0 / 2.0.0 GA release predates this reference and will reject these fields; upgrade to the current line. On Docker the corresponding authentication.* keys are available regardless of chart version.

Authentication

Authentication is off by default and opt-in (enable: true). The type field is the mode selector: the literal value oidc delegates verification to an OIDC provider; any other value (including empty or jwt) selects JWT mode, which validates a signed token with a configured key and signature algorithm. There is no wire-connector opt-in/opt-out toggle here — authentication uses the plain enable flag on both surfaces.

SettingTypeDefaultValid valuesDocker (config.yaml key · env var)Helm/CRD pathNotes
Enableboolfalsetrue / falseauthentication.enable · AUTHENTICATION_ENABLEspec.authentication.enableOff by default. When false the server ignores every other auth field (authentication.go:100). Operator emits from the Enable pointer.
Type (mode selector)string""oidc, or empty ⇒ JWTauthentication.type · AUTHENTICATION_TYPEspec.authentication.typeOnly the exact value oidc triggers OIDC; empty or any other value ⇒ JWT (authentication.go:103).
JWT signature typestring""HS256·HS384·HS512·RS256·RS384·RS512·ES256·ES384·ES512authentication.jwtconfig.signaturetype · AUTHENTICATION_JWT_CONFIG_SIGNATURE_TYPEspec.authentication.signatureTypeRequired when JWT is enabled (authentication.go:19). The 9 algorithms come from pkg/authentication/jwt.go:10-20. Name divergence jwtconfig.signaturetypesignatureType. Operator stores it in a Secret.
JWT keystring""HMAC secret / PEM public keyauthentication.jwtconfig.key · AUTHENTICATION_JWT_CONFIG_KEYspec.authentication.keyVerification key, read verbatim. Either key or filePath is required (authentication.go:22). Name divergence jwtconfig.keykey. Operator stores it in a Secret.
JWT key filestring""file pathauthentication.jwtconfig.filepath · AUTHENTICATION_JWT_CONFIG_FILE_PATHAlternative to the inline key; validated as a filename (authentication.go:25). config.yaml/env-only — superseded by the inline key on the CRD (allowlist).
OIDC configstring (base64 JSON)""base64-encoded OIDC JSONauthentication.config · AUTHENTICATION_CONFIGspec.authentication.oidcDocker takes base64-encoded OIDC JSON, which the server base64-decodes (authentication.go:74). Helm takes a first-class oidc block that the operator encodes for you. Required when type: oidc (authentication.go:104).

OIDC block fields

These live inside the OIDC config: on Docker they are keys of the base64-encoded JSON in AUTHENTICATION_CONFIG; on Helm they are typed fields under spec.authentication.oidc. They have no individual env bindings — the whole block travels as the single AUTHENTICATION_CONFIG value (config-file/secret-only per field).

SettingTypeDefaultValid valuesDocker (config.yaml key · env var)Helm/CRD pathNotes
Issuerstring""issuer URL(part of authentication.config JSON) · —spec.authentication.oidc.issuerRequired (authentication.go:51).
Client IDstring""OAuth2 client id(part of authentication.config JSON) · —spec.authentication.oidc.clientIDRequired unless skipClientIDCheck is set (authentication.go:54).
Skip client-ID checkboolfalsetrue / false(part of authentication.config JSON) · —spec.authentication.oidc.skipClientIDCheckDisables audience validation.
Skip expiry checkboolfalsetrue / false(part of authentication.config JSON) · —spec.authentication.oidc.skipExpiryCheckInsecure — accepts expired tokens; logs a warning (authentication.go:64).
Skip issuer checkboolfalsetrue / false(part of authentication.config JSON) · —spec.authentication.oidc.skipIssuerCheckInsecure — accepts any issuer; logs a warning (authentication.go:67).
Insecure skip signature checkboolfalsetrue / false(part of authentication.config JSON) · —spec.authentication.oidc.insecureSkipSignatureCheckInsecure — token signatures are NOT verified; logs a warning (authentication.go:61).

OIDC hard rejection: a config that disables all four checks at once (insecureSkipSignatureCheck + skipExpiryCheck + skipIssuerCheck + skipClientIDCheck) is rejected — at least one check must remain enabled (authentication.go:58).

spec.authentication.oidc supersedes the legacy spec.configData OIDC block. A CR that sets both JWT fields (key / signatureType) and oidc is rejected — pick one authentication mode (k8s config/authentication.go:100). Note that spec.configData is a raw string carrying only an OIDC block (k8s config/config_data.go); it is not a generic config.yaml passthrough.

The rejection happens twice and is loud: at admission, by a CEL rule on the KubemqCluster CRD shipped in charts 3.2.0, and again at reconcile by the operator (v2.3.0), which raises a ReconcileError condition and a Warning event and leaves running pods untouched.

ReconcileError clears on its own once you fix the CR. From operator v2.3.0 the condition flips to False with reason LastReconcileCycleSucceeded on the next reconcile that completes — it is not deleted, so alerts keyed on the condition's presence keep working and the recovery carries its own transition time. On older operators the condition stayed True with the original stale message indefinitely; if you are reading a cluster that has never been through a v2.3.0 reconcile, compare observedGeneration against generation before believing it.

If you already have a cluster with both modes set, it is serving traffic unauthenticated right now. Before operator v2.3.0 that combination was accepted and produced a broker with no authentication at all — no AUTHENTICATION_* variable reached the pod, the pod was Running and ready, and the CR reported Deployed with no conditions. Nothing surfaced the problem.

Two things follow. Fix the CR before you upgrade — on operator v2.3.0 an affected cluster stops reconciling until it is fixed (running pods keep serving; new changes stop being applied). And treat the window as an exposure: the broker was reachable without credentials for as long as that CR was live.

Find affected clusters:

kubectl get kubemqclusters.core.k8s.kubemq.io -A -o json \
  | jq -r '.items[]
      | select(.spec.authentication.oidc != null
               and (((.spec.authentication.key // "") != "")
                    or ((.spec.authentication.signatureType // "") != "")))
      | "\(.metadata.namespace)/\(.metadata.name)"'

Then remove one of the two blocks — either oidc, or the key / signatureType pair — so exactly one authentication mode remains.

Authorization

Policy-based access control, off by default. Supply the policy inline (policy) or by URL (url), with optional periodic auto-reload. When enabled, exactly one of policy data, policy file, or URL must be present (authorization.go:34).

SettingTypeDefaultValid valuesDocker (config.yaml key · env var)Helm/CRD pathNotes
Enableboolfalsetrue / falseauthorization.enable · AUTHORIZATION_ENABLE— (auto-derived)Docker opt-in. On the CRD there is no enable field — the operator sets AUTHORIZATION_ENABLE=true automatically whenever policy or url is set (k8s config/authorization.go:20-25).
Policy datastring (base64)""base64-encoded policy documentauthorization.policydata · AUTHORIZATION_POLICY_DATAspec.authorization.policyBase64-encoded — the server base64-decodes the policy content. Name divergence policydatapolicy. Operator emits it base64 (k8s config/authorization.go:27).
Policy URLstring""http(s) URLauthorization.url · AUTHORIZATION_URLspec.authorization.urlRaw, NOT base64 — the server hands it verbatim to http.Get; validateURL rejects a base64 blob (authorization.go:51, k8s config/authorization.go:32). config.yaml key is authorization.url (server field Authorization.Url).
Auto-reload (seconds)int0≥ 0 (0 = disabled)authorization.autoreload · AUTHORIZATION_AUTO_RELOADspec.authorization.autoReloadReload interval in seconds (services/authorization/authorization.go:50). Negative is rejected (authorization.go:37). Name divergence autoreloadautoReload. Omitted from CRD env when 0.
Policy filestring""file pathauthorization.filepath · AUTHORIZATION_FILE_PATHValidated as a filename (authorization.go:44). config.yaml/env-only — superseded by the inline policy on the CRD (allowlist).

TLS / mTLS

Transport encryption for the interfaces. There is no enable flag — the mode is auto-derived from which artifacts are present (security.go:76): none (omit all), TLS (server cert + key), and mTLS (additionally a client ca, so both peers authenticate). The Docker config.yaml group is security.*; the Helm/CRD group is spec.tls.* — a name divergence. On Docker each artifact accepts inline data or a filename, and inline data takes precedence over filename (resource.go:19).

SettingTypeDefaultValid valuesDocker (config.yaml key · env var)Helm/CRD pathNotes
Server certstring""PEM block (data) / file path (filename)security.cert.data / security.cert.filename · SECURITY_CERT_DATA / SECURITY_CERT_FILENAMEspec.tls.certName divergence: securitytls. Required for TLS and mTLS (security.go:48). *_DATA is a raw PEM; the operator stores it in a Secret.
Server keystring""PEM block (data) / file path (filename)security.key.data / security.key.filename · SECURITY_KEY_DATA / SECURITY_KEY_FILENAMEspec.tls.keyRequired for TLS and mTLS (security.go:52). Secret on the CRD.
CA (mTLS)string""PEM block (data) / file path (filename)security.ca.data / security.ca.filename · SECURITY_CA_DATA / SECURITY_CA_FILENAMEspec.tls.caPresence promotes the mode to mTLS (client-certificate verification, security.go:79). Secret on the CRD.

On the CRD only the inline data fields (spec.tls.cert / .key / .ca) are exposed. The SECURITY_*_FILENAME keys are config.yaml/env-only and are superseded by the inline data on the CRD (allowlist). The same holds for AUTHENTICATION_JWT_CONFIG_FILE_PATH and AUTHORIZATION_FILE_PATH.

Example

Supply a TLS server certificate on each target. This is a single-setting snippet — see the Docker guide and the Kubernetes guide for complete, runnable configurations.

config.yaml
security:
  cert:
    filename: /certs/server.crt
  key:
    filename: /certs/server.key
values.yaml
tls:
  cert: |
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----
  key: |
    -----BEGIN PRIVATE KEY-----
    ...
    -----END PRIVATE KEY-----

For the full Docker delivery methods (env vars, mounted config.yaml, the CONFIG variable) see the Docker guide; for values.yaml mapped to the KubemqCluster spec see the Kubernetes guide.

Was this page helpful?

On this page