AKHQ:配置驱动的 Kafka 观察、权限与消息安全
AKHQ 默认状态不适合直接放进共享网络
AKHQ 把 Kafka、Schema Registry、Kafka Connect 和 ksqlDB 组织在 akhq.connections 下,页面很快就能看到 Topic、消费组和消息。便利背后有一个必须先处理的默认值:官方认证说明指出,安全与角色默认关闭,匿名用户拥有完整访问。把容器端口暴露到团队网络,再补一个反向代理登录页,并没有自动收紧 AKHQ API。
另一个容易漏掉的条件是 JWT 签名密钥。AKHQ 使用签名 JWT 承载认证状态;配置 group 却没有设置签名密钥时,官方 Groups 文档明确警告 API 不执行组角色,限制只停在 UI。批次验收因此不能只看按钮是否消失,必须直接验证后台写接口和 Kafka ACL 都拒绝。
AKHQ 适合作为配置驱动的 Kafka 观察入口。它不是 Kafka 权限系统,也不替业务判断 offset 重置、消息重放和 Topic 变更是否安全。连接、页面认证、AKHQ role、Kafka ACL 与数据遮罩是五个不同控制点。
用一份 application.yml 跑起本地实例
官方 Installation 提供 Docker、独立 JAR 和 Helm 入口。运行时与安装要求会随发行线变化,团队应按目标 release 固定 Java、镜像或 chart 版本。下面用批准的镜像标签或 digest 启动,不在长期配置中写 latest:
services:
akhq:
image: ${AKHQ_IMAGE:?set approved AKHQ tag or digest}
ports:
- "127.0.0.1:18081:8080"
environment:
MICRONAUT_CONFIG_FILES: /app/application.yml
MICRONAUT_SECURITY_TOKEN_JWT_SIGNATURES_SECRET_GENERATOR_SECRET: ${AKHQ_JWT_SECRET:?set JWT secret}
volumes:
- ./application.yml:/app/application.yml:ro
networks:
- dev-messaging
networks:
dev-messaging:
external: true必须挂载具体文件,不能把宿主机目录覆盖到 /app。官方 Docker 文档说明,覆盖整个目录会遮住镜像内的 AKHQ 文件,容器随后报 exec: ./akhq: not found。较长配置使用单一文件更容易评审;AKHQ_CONFIGURATION 或 MICRONAUT_APPLICATION_JSON 适合部署系统生成,不适合把整份含密配置塞进普通环境清单。
最小 application.yml 只接入本地 Kafka,并显式启用安全:
micronaut:
security:
enabled: true
akhq:
connections:
dev-local:
properties:
bootstrap.servers: "kafka:19092"
security.protocol: PLAINTEXT
security:
default-group: no-roles
pagination:
page-size: 25
topic-data:
size: 20
poll-timeout: 1000
kafka-max-message-length: 1048576default-group: no-roles 让未映射用户无法操作;本机临时只读可以使用 reader,但不能把匿名全局 reader 当成团队身份体系。page-size、topic-data.size、poll-timeout 和最大消息长度共同限制页面查询与内存放大。演示值不是生产标准,生产值要根据 Topic 数、消息体、并发用户和堆基线测量。
启动后查看日志并访问页面:
docker compose up -d
docker compose ps
docker compose logs --tail=120 akhq
curl -I http://127.0.0.1:18081HTTP 返回只证明 Micronaut 应用在响应。页面必须能列出目标 broker、Topic 与 consumer group,Kafka CLI 也应在同一网络看到相同集群标识。若容器能连 bootstrap、随后 broker 超时,仍要回到 Kafka 的 advertised listener,而不是在 AKHQ 中继续加地址。
connections 把多个后端放进同一个权限半径
AKHQ 的集群配置以 URL 友好的 connection key 标识集群,标准 Kafka consumer properties 放在 properties 下。Schema Registry、Connect 和 ksqlDB 是该 connection 的可选子连接,它们各自拥有 URL、认证和 TLS 状态。
SCRAM over TLS 可以这样组织:
akhq:
connections:
shared-dev:
properties:
bootstrap.servers: "broker-a.dev.example:9093,broker-b.dev.example:9093"
security.protocol: SASL_SSL
sasl.mechanism: SCRAM-SHA-512
sasl.jaas.config: >-
org.apache.kafka.common.security.scram.ScramLoginModule required
username="${KAFKA_UI_USERNAME}"
password="${KAFKA_UI_PASSWORD}";
ssl.truststore.location: /run/kafka-secrets/client.truststore.jks
ssl.truststore.password: ${KAFKA_TRUSTSTORE_PASSWORD}
schema-registry:
url: "https://schema.dev.example"
type: confluent
basic-auth-username: ${SCHEMA_REGISTRY_USERNAME}
basic-auth-password: ${SCHEMA_REGISTRY_PASSWORD}
properties:
schema.registry.ssl.truststore.location: /run/kafka-secrets/client.truststore.jks
schema.registry.ssl.truststore.password: ${KAFKA_TRUSTSTORE_PASSWORD}connection key 不允许点号,避免用域名直接当 key。Kafka 读取成功、Registry 失败时,Topic 和 group 仍可能正常,结构化消息却无法反序列化。Connect 或 ksqlDB 接入又会增加独立管理能力;观察角色若不需要它们,就不要配置。一个 connection 下能显示更多系统,不代表一个服务身份应横跨所有系统。
凭据和 truststore 由 Secret 系统只读挂载。若测试证书与 FQDN 不匹配,不能把关闭 endpoint identification 留在生产配置中。网络错误、TLS 错误、SASL 错误和 Registry 解码错误必须分别保留第一条异常,避免用管理员凭据一次性掩盖全部问题。
JWT、group 与 role 决定 API 是否真的拒绝
JWT secret 至少应满足官方 JWT 配置要求,并通过 Secret 注入。启用 Basic、LDAP 或 OIDC 后,把外部用户映射到 group;group 再引用 role,并用 patterns 和 clusters 收窄资源。
akhq:
security:
roles:
project-observer:
- resources: ["TOPIC", "TOPIC_DATA", "CONSUMER_GROUP"]
actions: ["READ"]
- resources: ["TOPIC"]
actions: ["READ_CONFIG"]
groups:
project-a-observers:
- role: project-observer
patterns: ["project-a\\..*"]
clusters: ["shared-dev"]AKHQ 的 Groups 文档列出了资源与 action 组合。TOPIC_DATA READ 与 Topic 元数据读取分开,UPDATE_OFFSET、DELETE_OFFSET、ALTER_CONFIG 和 DELETE 不应进入日常观察角色。旧配置跨越 group 权限模型的破坏性变更时,YAML 能解析不代表权限语义相同,升级必须重跑 API 反例。
先用只读用户查看 project-a.* Topic 和目标消费组,再访问其他项目 Topic,预期被拒绝。随后尝试创建测试 Topic、改配置、删除 group 或更新 offset。按钮应不可用,直接请求 AKHQ API 也要返回拒绝,后台 Kafka 身份还应被 ACL 拦住。若 API 成功而页面隐藏,先检查 JWT secret 是否真实注入、Micronaut security 是否启用,以及是否有另一份配置覆盖当前文件。
这组实验比截图有用。截图只能证明某次渲染没有按钮;API 与 Kafka 的双重拒绝才能证明操作者无法绕过前端。
默认遮罩让 Registry 故障不泄露原文
消息读取是 AKHQ 最敏感的能力。官方数据遮罩支持正则和 JSON 两类策略。json_mask_by_default 默认遮住所有字段,只按 Topic 规则放行诊断字段;Schema Registry 不可用或消息无法结构化时,AKHQ 不显示不可过滤的原文,这个失败方式更适合生产。
akhq:
security:
data-masking:
mode: json_mask_by_default
jsonMaskReplacement: "xxxx"
json-filters:
- description: expose safe diagnostics only
topic: "project-a.events"
keys:
- eventType
- traceId
- status用虚构消息验证允许字段可见,姓名、地址、token 和自由文本仍显示为替换值。再让 Registry 暂时不可用,预期 AKHQ 不回退展示原始二进制或未过滤字符串。若业务消息是多 record type、无 schema 或字段结构不稳定,遮罩策略需要专门验证;不要因为页面“不方便”就切成 none。
消息浏览会创建读取请求,从选定 offset 或时间位置拉取记录。看不到探针消息时,检查分区、起始位置、retention/compact、Topic READ、反序列化器和 kafka-max-message-length。探针 key 使用 akhq-probe-001,value 只含虚构字段。真实用户数据不应成为权限测试材料。
消费延迟与 offset 操作必须分开
AKHQ 可以查看 group 的 current offset、log end offset 和 lag。先按分区观察:单分区增长通常与热点、分配或慢消息有关;全分区同步增长再看消费者实例、处理耗时和下游故障。用 Kafka CLI 交叉验证同一 cluster、Topic 和 group,页面刷新差异不能直接解释成消息丢失。
更新 offset 是另一种权限。向早期位置移动会重放,向后移动可能跳过未处理记录。观察 group 的人不应自然获得 UPDATE_OFFSET 或 DELETE_OFFSET。临时操作先记录每个分区原位置、目标位置、消费者是否暂停、业务幂等条件和回退值,执行后再核对 lag 与业务副作用。
AKHQ 的角色只能决定谁能请求操作,不能判断重放订单事件是否重复扣款。高风险动作应进入独立临时 group 和短期 Kafka ACL,事件结束立即撤销。把 offset 按钮藏起来但让后台身份长期可写,仍然把事故半径留在控制台容器中。
JVM、分页和外部连接决定共享实例的上限
AKHQ 是 JVM 应用。大量 Topic、partition、group、并发消息浏览和反序列化会同时增加管理请求、堆对象和浏览器响应体。容量基线至少记录连接数、Topic/partition 数、group 数、最大消息体、页面并发、抓取条数、poll timeout、堆使用和 GC 暂停。出现堆持续增长时先收窄连接、分页与消息长度,再检查是否有人浏览大消息或外部 Registry/Connect 持续超时。
共享实例通常不需要和 Kafka 同等级高可用。控制台不可用不应影响生产消费。多副本会重复拉取元数据,还要验证 JWT secret、配置文件和外部会话是否一致;仅为“高可用”翻倍副本,可能先把 broker 管理请求翻倍。
升级时固定镜像或 chart、application.yml、JWT 配置、group/role、服务身份 ACL 和遮罩样例。新版本先在测试集群验证登录、API 拒绝、Registry 解码、group lag、消息遮罩和 offset 提权。失败时恢复旧镜像与旧配置,检查临时放宽的 Kafka ACL 是否已经撤销。
退出顺序从权限开始。先关闭 HTTP 入口,撤销 UI 身份集成、JWT secret、Kafka/Registry/Connect 凭据与临时 ACL;再删除 truststore 副本、日志、测试消息和容器。卸载 AKHQ 不会让服务账号自动失效。新电脑只凭批准镜像、审查过的 application.yml 模板和访问申请入口能够重建同样的只读行为,才算迁移完成。
