ClickHouse 安全加固实战:访问控制与加密配置

ClickHouse 安全加固的关键在于三个默认项:监听地址、default 账号密码与端口暴露面。很多团队为了图省事直接把 8123、9000 端口暴露在公网,只用一句「反正是内网库」当安全边界,结果一行未授权的 SELECT * FROM system.users 就能拿到全部账号信息。本文从主机、账号、权限、传输、审计五个层面给出可复制的配置步骤,适用于自建的 ClickHouse 单机或小集群环境。

适用场景

  • 自建 ClickHouse 用于日志、埋点、订单等分析场景,且同时存在多个业务方读写;
  • 分析实例与业务库混部在同一内网,需要防止业务侧越权访问其他租户数据;
  • 需要通过等保或内部审计,要求数据库层面具备账号隔离、传输加密与操作留痕。

前置条件

  • ClickHouse 22.8 及以上版本,具备 root 或 sudo 权限;
  • 配置文件位于 /etc/clickhouse-server/config.xml 与 users.xml;
  • OpenSSL 1.1.1 及以上,用于生成自签证书;
  • 已确认客户端连接方式:原生 TCP 9000 端口或 HTTP 8123 端口。

原理说明

默认配置里最危险的三处

第一是监听地址。ClickHouse 在未显式声明 listen_host 时会监听所有可用接口,一旦主机有公网 IP,数据库就等同于对公网开放。第二是 default 账号默认无密码,且该账号自带 access_management 权限,可自行创建用户与授权。第三是表函数与外部表,具备查询权限的用户可通过 url()、file() 等表函数读取服务器本地文件或发起外连请求,构成事实上的 SSRF 与任意文件读取通道。

加固思路

按「先缩小暴露面,再收敛身份与权限,最后补齐加密与审计」的顺序推进。其中权限收敛收益最大:即使账号口令泄露,只读用户也拿不到写入与建表能力,破坏范围被限制在数据读取。审计则解决「事后说不清」的问题。

操作步骤

第一步:收敛监听地址与端口

编辑 /etc/clickhouse-server/config.xml,显式限定监听地址。多网卡场景可写多条 listen_host。

<!-- 只监听本机与内网,不再监听 0.0.0.0 -->
<listen_host>127.0.0.1</listen_host>
<listen_host>10.0.0.20</listen_host>

<tcp_port>9000</tcp_port>
<http_port>8123</http_port>
<!-- 若应用只走原生协议,可直接把 HTTP 端口设为 0 关闭 -->

主机层再用防火墙做一次兜底,形成「配置 + 防火墙」双重限制:

# 只允许内网应用网段访问原生协议端口
sudo ufw allow from 10.0.0.0/8 to any port 9000 proto tcp
sudo ufw deny 8123
sudo ufw deny 9000

# 生效核实:确认没有 0.0.0.0 或 :: 监听
sudo ss -lntp | grep -E '8123|9000'

预期输出中每一行的本地地址都应是 127.0.0.1 或内网 IP,出现 0.0.0.0、:: 说明配置未生效。

第二步:为 default 账号设置密码并限制来源网段

生成 SHA256 摘要后写入 users.xml,避免以明文形式保存口令:

echo -n 'MyStr0ng-P@ss-2026' | sha256sum | awk '{print $1}'

把输出填入 /etc/clickhouse-server/users.xml 的 default 用户节点:

<users>
  <default>
    <password_sha256_hex>复制上一步输出的 64 位摘要</password_sha256_hex>
    <networks>
      <ip>127.0.0.1</ip>
      <ip>10.0.0.0/8</ip>
    </networks>
    <profile>default</profile>
    <quota>default</quota>
    <access_management>1</access_management>
  </default>
</users>

注意 access_management 保留给管理员账号;日常业务连接一律使用下一步创建的独立账号,不要继续用 default。

第三步:按角色拆分最小权限账号

ClickHouse 20.4 之后支持 SQL 标准的用户、角色、配额与行策略管理,可直接在客户端执行:

-- 只读角色:仅允许查询指定库
CREATE ROLE analyst;
GRANT SELECT ON analytics.* TO analyst;

-- 应用账号:限定来源网段与认证方式
CREATE USER app_reader IDENTIFIED WITH sha256_password BY 'R3ad-0nly-2026'
  HOST IP '10.0.0.0/8';
GRANT analyst TO app_reader;

-- 写入账号单独隔离,只给后台任务使用
CREATE USER etl_writer IDENTIFIED WITH sha256_password BY 'Etl-Writ3-2026'
  HOST IP '10.0.1.0/24';
GRANT SELECT, INSERT ON analytics.* TO etl_writer;

-- 多租户行级隔离:分析人员只能看到自己租户的数据
CREATE ROW POLICY tenant_filter ON analytics.events
  FOR SELECT USING tenant_id = 1001 TO analyst;

核心原则是一个用途一个账号:ETL、BI 看板、临时排查分别使用不同账号,任意一个泄露都能单独吊销,且能通过审计日志精确定位来源。

第四步:只读 profile 与资源配额

权限之外还要限制「用量」,否则一个失控的 SELECT 就能把节点内存打满。先在 users.xml 中定义只读 profile:

<profiles>
  <readonly>
    <readonly>2</readonly>
    <max_execution_time>30</max_execution_time>
    <max_memory_usage>4000000000</max_memory_usage>
    <max_result_rows>1000000</max_result_rows>
    <log_queries>1</log_queries>
  </readonly>
</profiles>

readonly 设为 2 表示禁止一切写入且不允许修改会话级设置,比只读 1 更严格。再用 SQL 配额做时间维度的限流:

CREATE QUOTA app_quota FOR INTERVAL 1 hour
  MAX queries = 2000,
      result_rows = 100000000,
      memory_usage = 5000000000
  TO app_reader;

第五步:启用 TLS 加密传输

内网嗅探与容器跨节点流量都可能导致口令明文外泄,生产环境建议开启 TLS:

sudo mkdir -p /etc/clickhouse-server/certs && cd /etc/clickhouse-server/certs
sudo openssl req -x509 -newkey rsa:4096 -sha256 -days 825 -nodes \
  -keyout server.key -out server.crt \
  -subj "/C=CN/O=Example/CN=ch.example.com" \
  -addext "subjectAltName=DNS:ch.example.com,IP:10.0.0.20"
sudo chown clickhouse:clickhouse server.key server.crt
sudo chmod 600 server.key

然后在 config.xml 中追加:

<https_port>8443</https_port>
<tcp_port_secure>9440</tcp_port_secure>
<openSSL>
  <server>
    <certificateFile>/etc/clickhouse-server/certs/server.crt</certificateFile>
    <privateKeyFile>/etc/clickhouse-server/certs/server.key</privateKeyFile>
    <verificationMode>strict</verificationMode>
    <disableProtocols>sslv2,sslv3,tlsv1,tlsv1_1</disableProtocols>
    <preferServerCiphers>true</preferServerCiphers>
  </server>
</openSSL>

配置完成后应用侧连接串改为 clickhouse://ch.example.com:9440,并指定 CA 证书校验;同时把 9000、8123 的防火墙规则收紧,避免加密形同虚设。

第六步:打开查询审计日志

<query_log>
  <database>system</database>
  <table>query_log</table>
  <partition_by>toYYYYMM(event_date)</partition_by>
  <flush_interval_milliseconds>7500</flush_interval_milliseconds>
  <ttl>event_date + INTERVAL 90 DAY DELETE</ttl>
</query_log>

TTL 设为 90 天,既满足审计取证需要,也避免 system 库无限膨胀。若节点无需访问外网,直接在主机防火墙禁止该实例的出站流量,这比依赖任何单条配置项都更可靠,可一并封死通过 url() 等表函数发起的 SSRF 与数据外传。

配置验证

# 1 服务健康检查
curl -s 'http://127.0.0.1:8123/ping'          # 预期返回 Ok.

# 2 监听面复核
sudo ss -lntp | grep -E '8123|9000|8443|9440'

# 3 用业务账号连接,确认认证生效
clickhouse-client --host 10.0.0.20 --port 9440 --secure \
  --user app_reader --password 'R3ad-0nly-2026'

# 4 确认账号与来源限制已登记
SELECT name, storage FROM system.users;

# 5 确认最小权限生效:应报权限错误
SELECT * FROM system.users;                   -- 用 app_reader 执行预期被拒

# 6 审计日志是否在写入
SELECT event_time, user, query_kind, query_duration_ms, read_rows, query
FROM system.query_log
WHERE type = 'QueryFinish' AND event_time > now() - INTERVAL 1 DAY
ORDER BY event_time DESC LIMIT 20;

六项全部通过才算加固完成;其中第 5 项必须验证失败路径,只测成功路径容易漏掉授权过宽的问题。

常见问题

加了密码之后客户端报 authentication failed,怎么排查?

先确认口令摘要是否对应:users.xml 中若同时残留明文 <password> 与 <password_sha256_hex>,实际以更严格的一项为准,需删掉多余节点。其次检查 <networks>,来源 IP 不在白名单时 ClickHouse 会直接把连接判为未授权,日志中会记录 Authentication failed,容易误判为口令错误。

业务反馈开了只读 profile 之后容器里的看板跑不动了,怎么办?

readonly=2 会禁止修改会话级设置,部分 BI 工具依赖 max_execution_time 等参数动态调整。做法是给看板账号单独建一个 readonly=1 的 profile,并同时在 profile 里写死上限值,既满足工具可用性,又不放开写入权限。

是否需要给 ClickHouse 单独做备份?

需要,而且备份要落到与数据盘不同的存储上,防止勒索软件同盘加密。ClickHouse 22.8 起支持原生 BACKUP 语句,例如 BACKUP DATABASE analytics TO Disk('backups', 'analytics-2026-10-12.zip'),配合对象存储的版本控制与保留策略即可;恢复演练务必定期做,未验证过的备份不算备份。

总结

ClickHouse 安全加固的动作并不复杂,难的是把暴露面收敛、账号最小权限、传输加密、审计留痕四件事同时做完。建议按本文顺序执行:先改 listen_host 并加防火墙,再给 default 设口令,随后按角色拆账号、配只读 profile 与配额,最后开 TLS 与 query_log。加固完成后把配置纳入版本管理,避免下次升级配置文件被覆盖而回退到不安全状态。