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.
| Setting | Type | Default | Valid values | Docker (config.yaml key · env var) | Helm/CRD path | Notes |
|---|---|---|---|---|---|---|
| Enable | bool | false | true / false | authentication.enable · AUTHENTICATION_ENABLE | spec.authentication.enable | Off 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 ⇒ JWT | authentication.type · AUTHENTICATION_TYPE | spec.authentication.type | Only the exact value oidc triggers OIDC; empty or any other value ⇒ JWT (authentication.go:103). |
| JWT signature type | string | "" | HS256·HS384·HS512·RS256·RS384·RS512·ES256·ES384·ES512 | authentication.jwtconfig.signaturetype · AUTHENTICATION_JWT_CONFIG_SIGNATURE_TYPE | spec.authentication.signatureType | Required when JWT is enabled (authentication.go:19). The 9 algorithms come from pkg/authentication/jwt.go:10-20. Name divergence jwtconfig.signaturetype ↔ signatureType. Operator stores it in a Secret. |
| JWT key | string | "" | HMAC secret / PEM public key | authentication.jwtconfig.key · AUTHENTICATION_JWT_CONFIG_KEY | spec.authentication.key | Verification key, read verbatim. Either key or filePath is required (authentication.go:22). Name divergence jwtconfig.key ↔ key. Operator stores it in a Secret. |
| JWT key file | string | "" | file path | authentication.jwtconfig.filepath · AUTHENTICATION_JWT_CONFIG_FILE_PATH | — | Alternative 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 config | string (base64 JSON) | "" | base64-encoded OIDC JSON | authentication.config · AUTHENTICATION_CONFIG | spec.authentication.oidc | Docker 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).
| Setting | Type | Default | Valid values | Docker (config.yaml key · env var) | Helm/CRD path | Notes |
|---|---|---|---|---|---|---|
| Issuer | string | "" | issuer URL | (part of authentication.config JSON) · — | spec.authentication.oidc.issuer | Required (authentication.go:51). |
| Client ID | string | "" | OAuth2 client id | (part of authentication.config JSON) · — | spec.authentication.oidc.clientID | Required unless skipClientIDCheck is set (authentication.go:54). |
| Skip client-ID check | bool | false | true / false | (part of authentication.config JSON) · — | spec.authentication.oidc.skipClientIDCheck | Disables audience validation. |
| Skip expiry check | bool | false | true / false | (part of authentication.config JSON) · — | spec.authentication.oidc.skipExpiryCheck | Insecure — accepts expired tokens; logs a warning (authentication.go:64). |
| Skip issuer check | bool | false | true / false | (part of authentication.config JSON) · — | spec.authentication.oidc.skipIssuerCheck | Insecure — accepts any issuer; logs a warning (authentication.go:67). |
| Insecure skip signature check | bool | false | true / false | (part of authentication.config JSON) · — | spec.authentication.oidc.insecureSkipSignatureCheck | Insecure — 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).
| Setting | Type | Default | Valid values | Docker (config.yaml key · env var) | Helm/CRD path | Notes |
|---|---|---|---|---|---|---|
| Enable | bool | false | true / false | authorization.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 data | string (base64) | "" | base64-encoded policy document | authorization.policydata · AUTHORIZATION_POLICY_DATA | spec.authorization.policy | Base64-encoded — the server base64-decodes the policy content. Name divergence policydata ↔ policy. Operator emits it base64 (k8s config/authorization.go:27). |
| Policy URL | string | "" | http(s) URL | authorization.url · AUTHORIZATION_URL | spec.authorization.url | Raw, 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) | int | 0 | ≥ 0 (0 = disabled) | authorization.autoreload · AUTHORIZATION_AUTO_RELOAD | spec.authorization.autoReload | Reload interval in seconds (services/authorization/authorization.go:50). Negative is rejected (authorization.go:37). Name divergence autoreload ↔ autoReload. Omitted from CRD env when 0. |
| Policy file | string | "" | file path | authorization.filepath · AUTHORIZATION_FILE_PATH | — | Validated 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).
| Setting | Type | Default | Valid values | Docker (config.yaml key · env var) | Helm/CRD path | Notes |
|---|---|---|---|---|---|---|
| Server cert | string | "" | PEM block (data) / file path (filename) | security.cert.data / security.cert.filename · SECURITY_CERT_DATA / SECURITY_CERT_FILENAME | spec.tls.cert | Name divergence: security ↔ tls. Required for TLS and mTLS (security.go:48). *_DATA is a raw PEM; the operator stores it in a Secret. |
| Server key | string | "" | PEM block (data) / file path (filename) | security.key.data / security.key.filename · SECURITY_KEY_DATA / SECURITY_KEY_FILENAME | spec.tls.key | Required 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_FILENAME | spec.tls.ca | Presence 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.
security:
cert:
filename: /certs/server.crt
key:
filename: /certs/server.keytls:
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?