# Security (Auth · TLS) (/configure/reference/security)



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).

<Callout type="warn">
  **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.
</Callout>

## Authentication [#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&#x60;). **`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 [#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`). |

<Callout type="warn">
  **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`).
</Callout>

<Callout type="warn">
  **`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.
</Callout>

<Callout type="warn">
  **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:

  ```bash
  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.
</Callout>

## Authorization [#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&#x60;). **`config.yaml`/env-only** — superseded by the inline `policy` on the CRD (allowlist).                                                                                                  |

## TLS / mTLS [#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.                                        |

<Callout type="info">
  On the CRD only the inline **data** fields (`spec.tls.cert` / `.key` / `.ca`) are exposed.
  The `SECURITY_*_FILENAME&#x60; 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`.
</Callout>

## Example [#example]

Supply a TLS server certificate on each target. This is a single-setting snippet — see the
[Docker guide](/configure/docker) and the
[Kubernetes guide](/configure/kubernetes) for complete, runnable configurations.

<Tabs items="[&#x22;Docker&#x22;, &#x22;Helm&#x22;]">
  <Tab value="Docker">
    ```yaml title="config.yaml"
    security:
      cert:
        filename: /certs/server.crt
      key:
        filename: /certs/server.key
    ```
  </Tab>

  <Tab value="Helm">
    ```yaml title="values.yaml"
    tls:
      cert: |
        -----BEGIN CERTIFICATE-----
        ...
        -----END CERTIFICATE-----
      key: |
        -----BEGIN PRIVATE KEY-----
        ...
        -----END PRIVATE KEY-----
    ```
  </Tab>
</Tabs>

For the full Docker delivery methods (env vars, mounted `config.yaml`, the `CONFIG`
variable) see the [Docker guide](/configure/docker); for `values.yaml` mapped to
the `KubemqCluster` spec see the [Kubernetes guide](/configure/kubernetes).
