BROKABROKA
Sign inDownload CommunityRequest a demo
GuideSecurity

Hardening a self-hosted operations console

BROKA arrives closed: no default credentials, no start without its secrets, and every change it makes and every request it refuses recorded. What is left is a short list of decisions only you can make, and this is the order to make them in once the first administrator exists.

10 min read

An operations console holds broker credentials and can write to production. Hardening it is a matter of deciding, in the right order, what the installation may do and who may make it do it; the defaults already refuse. Eight decisions, most of them a screen each. A reviewer who wants the same ground as a document can request the security and deployment overview.

1 — Give it the narrowest broker credentials that work

This is the decision BROKA cannot make for you, and it comes first because the other seven do not survive getting it wrong. Everything BROKA does on a broker, it does as the identity you gave it. Its guardrails stop a person; they are not enforced by your broker. So the credential in a connection should be able to do what you intend to do through BROKA, and nothing more — a connection used for reading should authenticate as an identity that can only read. A connection test can pass with a caveat naming what that identity leaves missing — a Redis identity not permitted to run INFO, for instance, is told at the test that instance metrics will be unavailable.

The Edit Redis instance form for redis-cache-stage, signed in as the ACL user viewer, with its Read-only switch off, after Test connection: OK in 20 ms, followed by a warning that this Redis identity is not permitted to run INFO, so instance metrics will be unavailable, and that granting it on the Redis side enables them.
The test passes and says what the identity leaves out. The fix is on the broker, where the identity lives, not in BROKA.

2 — Set each environment's policy

An environment's guard policy is not a permission and no role overrides it. Read-only refuses every write — creating a topic, purging a queue, resetting an offset, deleting keys — for administrators too. Guarded allows writes but refuses any that arrives without a reason: the confirmation grows a Reason field, a write with no confirmation of its own opens a small A reason is required window first, and what you type lands on the audit entry. Open is open — except that a destructive operation, such as a delete, a purge, a trim or an offset reset, asks for a reason in every environment.

The same form sets the environment's default classification: what a topic, queue, stream or exchange created there is classified as unless its creator chooses another, and what every audit entry about that resource then records.

The Edit environment sheet for Production: its name and colour, the write policy set to Guarded — every write asks for a reason, the default classification set to Internal, and the Allow insecure TLS switch left off.
The write policy applies to every connection in the environment whatever the operator's permissions, and the TLS switch is off by default. Both changes are audited.

Mark production read-only first. No role or grant makes an exception to it — a write there means changing the environment's policy, and that change is itself audited. A refused write is written to the trail as a blocked action, with the reason, the actor, the target and the environment — a trail that only holds successes cannot answer the question an access review actually asks. Any single connection carries its own read-only switch as well, refused even inside an open environment.

3 — Separate who may look from who may change

Four built-in roles: Admin runs the installation, Operator does the work, Viewer reads, and Auditor reads the audit trail — verifies it, produces an evidence pack, runs an access review (Commercial) — and can change nothing at all. Give the audit permission to the people who review access: the effective-access report — Operations ▸ Access Review (Commercial) — is gated on it rather than on user management, so a reviewer can see access without being able to change it, and reading it is itself recorded.

Access review on its Accounts tab, beside By permission: every account with its roles, the administrative permissions it holds such as alerts.manage and connections.manage, how many permissions it has in total and when it last signed in, many of them marked never signed in.
The screen says of itself that it never changes access. The administrative column lifts the permissions worth questioning out of the total.

Two rules decide everything underneath. A deny always wins, over a role and over a grant, including for an administrator — though only another administrator can place one on an administrator. Grants are additive, so a narrow grant on one cluster's topics can be given without any broad permission. And a change takes effect at once: changing someone's roles, permissions or team membership, or disabling them, invalidates every sign-in token they hold, and their next request is refused.

Scripts and pipelines sign in with an API token, minted under Profile ▸ API tokens. It acts as the person who minted it, with their permissions as they stand at each request, so it never outlives an access change or a disabled account; its secret is shown once, and no token can mint or revoke another.

In Commercial an AI assistant reaches BROKA the same way, through the MCP server with a token of its own. Leave Settings ▸ MCP Off — the default — until someone needs it, and prefer Read only when they do; keep mcp.use, which only the Admin role carries, to the people who use an assistant; and give the assistant a token scoped to the work, with an expiry. What it does is recorded like anything else, marked as coming through MCP.

4 — Keep the brokers' own authorisation honest

None of that is written to a broker. BROKA's roles decide what someone may do inside the console; each broker keeps enforcing its own authorisation underneath, and BROKA never bypasses it. The console shows each broker's model in the broker's own terms:

Broker What you review there
Kafka ACL rules per principal — resource, allow or deny, operations, host. A prefix rule carries a Prefix badge, because a literal name may itself end in *; denies sort to the top. Reading the ACLs is itself audited (Commercial).
RabbitMQ Tags decide console access; permissions decide messaging, as three patterns per virtual host. An empty pattern denies everything, and the console renders it as denies all rather than blank.
Redis ACL users as Redis's own rule text — key patterns, channel patterns, command categories read from your server. Saving is declarative, so a removed line is a removed grant. A nopass user is marked as a finding.

One asymmetry is worth knowing before you delete anything. Removing a Redis ACL user disconnects every connection authenticated as it, immediately. Deleting a RabbitMQ user leaves already-authenticated connections working until they close — Close connections in the user's row menu is what makes a delete or a password change take effect now, and it asks for a reason, which RabbitMQ passes to every client it disconnects.

5 — Terminate TLS in front, verify certificates behind

BROKA publishes one port, for the console, and terminates no TLS: nginx inside the UI image listens on plain HTTP. Put a reverse proxy in front and terminate there. The orchestrator, the broker service and PostgreSQL are not published at all, and must not be.

Behind it, certificate verification towards your brokers is on by default and is a per-connection setting: turning it off is something a person does deliberately, connection by connection. Review the connections you have for any that skip it, and let an environment forbid it outright where that is the policy — it then refuses to save such a connection. A private CA is uploaded on the connection itself, for Kafka, Redis and RabbitMQ alike; where a platform has no such field, a private-CA broker is reached by installing the CA in the broker service's trust store.

6 — Protect the key nobody can recover

Connection credentials are encrypted with a key of their own, wrapped by the installation's key-encrypting key. That key is never in the database. A copy of your database, taken by any means, does not contain the material to decrypt what is in it — so the key belongs somewhere your database backups are not, and a backup of one without the other is useless.

Settings › Security: Sign out after inactivity set to 1 hour; the Password policy at its defaults — a minimum length of 12, no expiry, and an uppercase letter, a lowercase letter, a digit and a symbol required; Read auditing on Privileged reads; Audit retention at 6 months; Audit forwarding to the Kafka connection kafka-core-eu and the topic broka.audit, with nothing waiting to be forwarded; and the Secret storage card with Rotate key-encryption key.
The trail's two controls, a forwarding destination and a statement. The key-encryption key comes from the deployment environment, never the database, and the two together are what decrypts a credential.

Replacing that key on an installation that already has connections makes every stored secret permanently unrecoverable, with no error at startup. Rotating it is a different operation: the new key goes into the deployment with the old one kept as the previous key, and Rotate key-encryption key on the same screen re-wraps every stored key onto the new one, asks for a reason, and reports each store it covered. Secrets are never returned by the API: reading a connection tells you which kinds are set, never their values.

7 — Decide what the trail records, and for how long

Reads are recorded in Commercial only, and even there most are not. A small number are, by default: reading users and permissions, reading a cluster's ACLs, reading a connector's configuration, exports and evidence packs. Reading messages is recorded at the start of the session and at the end, with how many records were scanned and matched — and the filter you searched with is never recorded, because a search term is often the identifier the record is sensitive for. Settings ▸ Security sets this tier: Privileged reads is the default, All reads adds the routine listings, and Off records changes only — except a read of contents a data masking rule covers, shown unmasked to someone allowed to see them, which is recorded whatever the tier.

Every record is an OpenAuditModel 1.0 event, sealed into one hash chain per installation, and clicking an entry in the audit log opens the event exactly as the chain holds it.

The Audit event panel for broker.consume-session.open on the topic payments.authorised, tagged Success, Production, read and Administrator: the actor, the connection kafka-core-eu, the time, the request context with its address, session and user agent, and the sealed event as JSON, whose consume block reads filterSet true and filterMode contains.
A read in a guarded environment, recorded: the event says a filter was set and how it matched, and the term itself is not among its fields.

Retention keeps at most six months and cannot be switched off; shortening it requires the security permission and a reason, and is itself recorded. If your reviews look further back than six months, forward the trail, because anything older exists only in that copy and a record never forwarded is still deleted at 180 days. A file or an HTTP endpoint is set in the deployment environment, in either edition; in Commercial, a Kafka or RabbitMQ connection you have already registered is chosen in Settings ▸ Security.

Verification reports the first break it finds, and it holds only against someone who cannot write the database: the chain is unkeyed and lives there too, so keep the evidence pack's checkpoint, or a forwarded copy, out of that person's reach. Nor can it prove that nothing was removed from the newest end, because a truncated chain is internally consistent. A change to BROKA and its record commit together, with no exempt list; a change on a broker cannot be taken back, so its record is written the moment the broker answers.

8 — On Redis, a read can be an outage

KEYS blocks the server for a full keyspace walk, and on a large instance that is seconds during which nothing else is served. BROKA never issues it — every listing, analysis and delete-by-pattern sweep uses SCAN. There is no live command stream in the console either; command counters answer what a server is doing without a feed. Key names are themselves a disclosure, so listing them is an explicit act that needs write permission on the connection, and every page listed is recorded — how many keys, never their names or the pattern. A pattern sweep refuses a blank pattern, can be counted before it removes anything, asks for a reason in every environment, and unlinks rather than deletes so a large collection does not stall every other client. Redis keeps no history: once keys are unlinked, the audit entry is the only record that they existed.

Network posture

Whether the installation needs outbound access depends on one thing.

What goes out, unprompted
Community Nothing. No licence, no activation, nothing to call.
Commercial, activated offline Nothing. You paste a signed key; the online path is not attempted.
Commercial, activated online One HTTPS call to activate, then roughly one a day to refresh. It carries licence and installation identifiers, never anything about your brokers.

BROKA sends nothing on its own initiative — no telemetry, no usage reporting, no call home.

Three things you switch on do send outward. A notification channel carries the severity, the rule, the metric and threshold, the connection's name and the names of the targets that breached to whatever Slack, Teams or webhook endpoint you named (those channels are Commercial); message contents never travel. The installation's SMTP relay sends through the mail server you give it. Audit forwarding to an HTTP endpoint posts every sealed audit event to the address you set; forwarding to a file, or to your own Kafka or RabbitMQ, stays inside your estate. None exists until you create it — but where a topic or queue name is itself information, that is the decision to take deliberately rather than by default.

The MCP server (Commercial) works the other way: an assistant connects in, on the console's own port at /api/mcp, and only while Settings ▸ MCP is on; BROKA calls out to no AI service.

What this article does not cover

  • The brokers themselves. TLS on the broker, network policy around it, its own credential rotation — that is the broker's documentation.
  • Alerting. Signals such as a bulk read (Commercial) or a self-granted permission are recorded here; turning a threshold into a page is Alerts, which has its own rules, its own channels and its own decisions to take.
  • The deployment. Secrets, ports and backups have their own list — the production deployment checklist.

Try it yourself

The hardening lab puts a TLS-terminating reverse proxy in front of the install recipe, closes the console's plain HTTP port and generates the four secrets.

Applies to BROKA 1.0 · Community and Commercial.