OpenSearch Dashboards:Tenant、查询工作面与最小权限实践
同一用户看见两套 Dashboard,往往不是缓存问题
OpenSearch Dashboards 开启多租户后,Global、Private 和 Custom Tenant 各自保存一套 index pattern、visualization 与 dashboard。用户在 Global Tenant 建好图表,切到 Private Tenant 后看到空列表;刷新、清缓存甚至重建浏览器配置都不会让对象出现,因为它们本来就在不同的 Saved Objects 空间里。
更麻烦的是,Tenant 只管控制台对象,不自动收窄业务索引权限。一个用户看不到某张 Dashboard,仍可能通过 Dev Tools 读取索引;另一个用户能打开共享图表,也可能因为缺少 index permission 得到 403。排障时必须把三件事分开:登录身份由谁认证,当前 Tenant 或 Workspace 允许操作哪些对象,OpenSearch role 允许访问哪些 cluster action 与 index pattern。
OpenSearch Dashboards 是独立运行的 Web 服务。它使用专门的服务用户维护内部索引和会话,用户请求则通过 Security 插件映射到 backend role、tenant permission 和 index permission。预定义的 kibana_server 角色只属于 Dashboards 服务身份,官方明确不应把它分配给人类用户;名字里仍有 Kibana,是兼容历史,不代表它可以连接 Elastic Stack。
版本、插件和后端是一套发布单元
OpenSearch Dashboards 应与 OpenSearch 及已安装插件保持相同的 major、minor、patch。Docker tag、离线包、Helm chart 和插件清单都要落到同一份发布记录中。只升级 Dashboards 镜像而不校准后端与插件,常见结果不是某个图表失效,而是服务在启动阶段报告版本不兼容。
官方提供 Docker、tarball、RPM、Debian、Helm 与 Windows 等安装入口,发行包已经包含 Node.js。只有在明确验证兼容范围和企业运行时政策后才替换内置 Node.js;日常部署优先使用发行包自带运行时,减少升级时的组合数量。
OpenSearch Dashboards 不是 Kibana 的换皮版本。两者虽然都常用 5601、都有 Discover 和 Dev Tools,也共享一部分历史术语,但 Space/Tenant、Security 插件、配置键、插件 API、对象迁移和发布节奏已经分开。后端是 Elasticsearch 时使用 Kibana;后端是 OpenSearch 时,才由本文这条工具链接管。
用隔离容器看清最短调用链
为了观察服务依赖,可以在本机回环地址启动一个关闭 Security 插件的短期实验。关闭安全只为缩短本地验证,不是部署建议。共享网络、远程服务器和云主机不得使用这段配置。
: "${OPENSEARCH_VERSION:?set an exact OpenSearch release version}"
docker network create dashboards-lab
docker run -d --name opensearch-dashboards-lab \
--network dashboards-lab \
-p 127.0.0.1:19200:9200 \
-e discovery.type=single-node \
-e DISABLE_SECURITY_PLUGIN=true \
-e OPENSEARCH_JAVA_OPTS="-Xms1g -Xmx1g" \
opensearchproject/opensearch:${OPENSEARCH_VERSION}
docker run -d --name dashboards-lab \
--network dashboards-lab \
-p 127.0.0.1:15601:5601 \
-e 'OPENSEARCH_HOSTS=["http://opensearch-dashboards-lab:9200"]' \
-e DISABLE_SECURITY_DASHBOARDS_PLUGIN=true \
opensearchproject/opensearch-dashboards:${OPENSEARCH_VERSION}验证时不要停在容器状态。根 API 要返回 OpenSearch 产品与版本,Dashboards 日志要进入 ready 状态,浏览器入口要能响应:
curl -fsS http://127.0.0.1:19200/
docker logs --tail 120 dashboards-lab
curl -I http://127.0.0.1:15601/若日志停在后端连接、插件初始化或 Saved Objects 阶段,先比较两端镜像 tag 与插件集合。若 OpenSearch 因内存退出,保留退出码和 JVM 日志;反复重启只会让最早的证据滚出日志窗口。
配置文件最危险的不是密码字段,而是身份传播断了
受控部署中的 opensearch_dashboards.yml 可以从下面这组边界开始:
server.host: "127.0.0.1"
server.port: 5601
opensearch.hosts:
- "https://os-coordinator-a.example.test:9200"
- "https://os-coordinator-b.example.test:9200"
opensearch.username: "kibanaserver"
opensearch.password: "${OPENSEARCH_DASHBOARDS_PASSWORD}"
opensearch.ssl.certificateAuthorities:
- "/etc/opensearch-dashboards/certs/root-ca.pem"
opensearch.ssl.verificationMode: full
opensearch.requestHeadersAllowlist:
- authorization
- securitytenant
opensearch_security.multitenancy.enabled: true
opensearch_security.readonly_mode.roles:
- "dashboards_read_only"opensearch.username 是 Dashboards 服务用户,不是日常查询用户。它需要 kibana_server 角色来维护内部对象和系统索引,但不能把这个角色复用给人。登录用户的认证与授权信息还要经过请求头传播到 Security 插件;启用多租户时,authorization 和 securitytenant 都必须在 allowlist 中。漏掉 securitytenant 会让 Tenant 选择失效,官方文档指出 Dashboards 甚至可能进入红色状态。
readonly_mode.roles 主要改变界面表现。它可以隐藏部分写入口,却不能替代 OpenSearch 的 cluster、index 与 tenant permission。真正的生产只读身份应在服务端角色中无法执行写索引、改模板、删除索引和集群管理动作;用户绕过 UI 直接调用 REST API 时也必须被拒绝。
后端证书使用 verificationMode: full,同时验证证书链和主机名。Dashboards 密码、OIDC client secret 和 TLS 私钥由 Secret 管理器挂载,仓库只保存变量引用。把 server.host 设为 0.0.0.0 会扩大监听面,通常应让进程只面对受控反向代理,再由网络策略限制 Dashboards 到协调节点的访问。
先用 Dev Tools 建立基准,再看 Discover
本地实验里写入三条无敏感信息的文档。索引前缀和字段名故意与真实业务分离,防止示例被复制后误指向生产对象。
NOW="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
curl -fsS -X PUT 'http://127.0.0.1:19200/dashboards-lab-logs' \
-H 'Content-Type: application/json' \
-d '{
"mappings": {"properties": {
"@timestamp": {"type": "date"},
"service": {"type": "keyword"},
"status": {"type": "keyword"},
"trace_id": {"type": "keyword"},
"message": {"type": "text"}
}}
}'
curl -fsS -X POST 'http://127.0.0.1:19200/_bulk?refresh=wait_for' \
-H 'Content-Type: application/x-ndjson' \
--data-binary "{\"index\":{\"_index\":\"dashboards-lab-logs\"}}
{\"@timestamp\":\"${NOW}\",\"service\":\"checkout\",\"status\":\"ERROR\",\"trace_id\":\"trace-a\",\"message\":\"payment timeout\"}
{\"index\":{\"_index\":\"dashboards-lab-logs\"}}
{\"@timestamp\":\"${NOW}\",\"service\":\"checkout\",\"status\":\"OK\",\"trace_id\":\"trace-b\",\"message\":\"request complete\"}
{\"index\":{\"_index\":\"dashboards-lab-logs\"}}
{\"@timestamp\":\"${NOW}\",\"service\":\"catalog\",\"status\":\"OK\",\"trace_id\":\"trace-c\",\"message\":\"request complete\"}
"Bulk 响应的 errors 应为 false。在 Dashboards Dev Tools 中查询 checkout 的两条记录:
GET dashboards-lab-logs/_search
{
"size": 10,
"timeout": "5s",
"track_total_hits": 1000,
"_source": ["@timestamp", "service", "status", "trace_id"],
"sort": [{"@timestamp": "asc"}],
"query": {
"bool": {"filter": [
{"term": {"service": "checkout"}},
{"range": {"@timestamp": {"gte": "now-5m", "lte": "now"}}}
]}
}
}预期返回两条,响应不包含 message。Dev Tools 直接代理 OpenSearch REST API;size 只收窄结果集,不保证底层少扫分片,timeout 也不能替代服务端资源控制。看到 timed_out、分片失败或非精确总命中时,应保留响应状态再判断,不要只抄屏幕里的数字。
在当前 Tenant 创建只覆盖 dashboards-lab-logs 的 index pattern,时间字段使用 @timestamp。Discover 选择相同的近期窗口,并用 DQL 查询 service: checkout,结果也应为两条。Query Workbench 的 SQL/PPL 与 Discover 的 DQL 或 Lucene 是不同语言;保存查询时必须连同入口和语言一起记录,不能把一段表达式当成跨页面通用语法。
Dev Tools 有数据而 Discover 为空,优先核对当前 Tenant、index pattern、时间字段、浏览器时区和 DQL 过滤器。这样能把 UI 对象错误与采集故障分开,避免用扩大索引通配符来碰运气。
Tenant 权限和索引权限要分别做反证
多租户默认可以提供 Global、Private 与 Custom Tenant。Global 用于有权限用户之间共享对象,Private 只属于当前用户,Custom 则通过角色授予读写。关键 Dashboard 不应只存在某位员工的 Private Tenant;否则人员退出后,团队知道图表曾经存在,却没有稳定的所有权入口。
Security 插件里的角色至少涉及三类能力:cluster permissions 管集群级动作,index permissions 管目标索引和允许的 action,tenant permissions 管 Saved Objects 的读取或写入。Dashboards 的只读 UI 角色只是第四层提示。
| 现象 | 第一检查点 | 不能直接得出的结论 |
|---|---|---|
| Tenant 列表没有目标项 | role mapping 与 tenant permission | 用户不能读取该 Tenant 关联的业务索引 |
Dashboard 可打开但图表 403 | index pattern 与 index permission | Saved Objects 已损坏 |
| Dev Tools 菜单被隐藏 | readonly mode 与功能配置 | REST API 写操作已被服务端禁止 |
| Private Tenant 有图、Global 没有 | 当前 Tenant 与对象归属 | 浏览器缓存需要清理 |
只读身份必须用危险 API 反证。在 Dev Tools 删除一个不存在的探针索引:
DELETE dashboards-permission-probe-do-not-create正确结果是 403,错误体包含被拒绝的 action。404 不是合格结果,它说明该用户已有删除权限,只是索引恰好不存在。若请求成功,停止后续操作,收回角色映射并检查审计日志;不要创建一个真实索引再删一次“确认”。
kibana_user 等历史命名角色也要谨慎评估。它允许登录 Dashboards,并包含对相关内部索引的能力,仍需与业务数据的只读 index permission 组合。kibana_server 则只给服务用户,不能用于人工会话。
Workspace 出现后,别把它与 Tenant 混成同一个词
OpenSearch Dashboards 的 Workspace 提供面向工作区的 Saved Objects 组织与 ACL。Workspace viewer、content producer、administrator 和 dashboard admin 对应不同对象职责,底层 permission mode 又区分 workspace 本身的 read/write 与资产库的 library_read/library_write。
这与 Tenant 的角色授权不是简单改名。团队采用 Workspace 时,应先确认当前发行线、Security 插件和 savedObjects.permission.enabled 配置,再决定 Tenant 与 Workspace 如何并存或迁移。如果未安装 Security 插件且对象权限关闭,用户可能获得过宽的管理能力;不能把“建立了 Workspace”直接当作完成授权。
无论使用 Tenant 还是 Workspace,业务数据仍由 OpenSearch index permission 保护。对象 ACL 解决谁能看和改 Dashboard,索引角色解决 Dashboard 能读到哪些数据。架构评审图中最好画成两条平行链,而不是把 Workspace 包在索引权限外面造成错误安全感。
401、403、红色状态各自指向不同层
浏览器收到 401 时,先检查会话过期、反向代理认证头、OIDC/SAML 回调和 Dashboards 服务身份。若所有用户同时失败,服务日志和后端连接通常比个人浏览器更有区分度。
403 表明身份通常已经映射成功,接下来读取错误体中的 action、index 与 tenant 信息。索引读取、字段能力、保存对象和集群管理是不同权限,不要把它们统一修成 all_access。临时管理员账号能验证页面代码是否正常,却不能作为生产修复。
Dashboards 红色状态常见于后端不可达、版本或插件不配对、服务用户权限不足,以及多租户请求头未放行。把 securitytenant 从 allowlist 漏掉,表面会像 UI 启动故障,根因却在身份传播。一次有效取证至少保留服务启动日志、后端健康、当前配置摘要和角色映射,而不是只截红色状态页。
对象导出、截图和查询历史都要按数据副本处理
Index pattern、visualization、dashboard 与 saved search 可以导出为 NDJSON。文件可能包含内部索引、查询、字段、Data Source 引用和 URL;导入到另一个 Tenant 或 Workspace 时,还要核对插件、目标 Data Source 与冲突处理。它适合迁移控制台资产,不替代包含系统索引与安全状态的 OpenSearch snapshot。
Discover 隐藏字段并不会改变原文档。截图要检查域名、用户名、过滤器、Trace ID 与展开内容,CSV 要限制字段、时间窗、行数和落盘位置。Console 历史与导出的请求文本更危险:里面保存的是可以再次执行的 REST 方法。导入历史前先逐条核对目标集群,删除认证头、真实索引名、个人数据和破坏性请求。
审计日志宜记录用户、backend role、Tenant 或 Workspace、HTTP 方法、目标索引、响应状态和不含个人信息的 X-Opaque-Id。不要为了“证据完整”把整个请求体和结果集复制到应用日志,那会把搜索集群里的敏感数据扩散到第二套日志系统。
搜索慢,不一定是 Dashboards 慢
一次 Discover 刷新通常不只发一条 _search。字段能力、自动补全、Saved Objects、主查询和后台会话可能同时发生。时间窗、index pattern 和分片数量决定 fan-out;聚合基数与返回字段决定内存和网络;浏览器还要承担结果渲染。
Dashboards 侧观察 Node.js 堆、事件循环、请求延迟、状态与插件错误;OpenSearch 侧观察 search thread pool、rejected、task、慢日志、断路器、分片和磁盘水位。增加 Dashboards 副本只能扩展 Web 层,不能抵消一次无界查询。默认时间窗、业务索引别名、并发限制、服务端资源保护与预定义查询才是成本边界。
反向代理超时后,后端查询可能继续运行。排障请求使用 X-Opaque-Id,再到 task 和审计日志核对取消传播与资源释放。团队阈值来自压测和 SLO,示例中的 size 与时间窗口只用于验证链路,不能复制成生产容量结论。
清理本地环境,也清理角色与数据副本
本页实验的退出顺序是先删明确命名的索引,再删两个容器和专用网络:
curl -fsS -X DELETE 'http://127.0.0.1:19200/dashboards-lab-logs'
docker rm -f dashboards-lab opensearch-dashboards-lab
docker network rm dashboards-lab
unset OPENSEARCH_VERSION NOW没有创建的 volume 不需要猜测性清理。若执行时自行增加了挂载,先解析它的确切路径或 volume 名称,确认只属于本实验,再单独处理;不要使用全局 prune。
生产退出还包括撤销临时 role mapping、回收服务密码与 OIDC secret、失效旧会话、转移 Private Tenant 中仍有价值的对象、销毁导出和截图。秘密若进入命令行、容器 inspect、日志或聊天记录,删除容器不能完成回滚,必须轮换凭证并复核审计日志。
OpenSearch Dashboards 真正可交付的状态,是团队能解释服务用户为何拥有 kibana_server 而人类用户不能拥有,当前对象落在哪个 Tenant 或 Workspace,index permission 如何把数据范围收紧,危险 API 为何稳定返回 403,一次查询怎样关联到后端资源,NDJSON、CSV 与截图最终由谁销毁。只有这些责任能被复核,控制台的便利才不会以过宽权限和无人认领的资产为代价。
