rqlite 完全指南 / 第 8 章:安全配置
第 8 章:安全配置
配置 rqlite 的 TLS 加密、认证机制和访问控制,保护集群通信安全。
8.1 安全机制概览
rqlite 提供三层安全机制:
| 层级 | 机制 | 说明 |
|---|---|---|
| 传输层 | TLS 加密 | 加密 HTTP 和 Raft 通信 |
| 认证层 | Basic Auth / X.509 | 验证客户端身份 |
| 访问控制 | 用户权限配置 | 限制读写操作权限 |
客户端
│
├── TLS ──────────────────────────────┐
│ (证书验证、数据加密) │
│ │
├── Basic Auth ──────────────────────┐│
│ (用户名/密码) ││
│ ││
├── 访问控制 ───────────────────────┐││
│ (读/写/管理 权限) │││
│ │││
▼ ▼▼▼
┌─────────────────────────────────────────┐
│ rqlite HTTP API │
└─────────────────────────────────────────┘
8.2 TLS 加密
8.2.1 生成证书
方式一:自签名证书(开发测试)
# 创建证书目录
mkdir -p /etc/rqlite/certs
cd /etc/rqlite/certs
# 生成 CA 私钥和证书
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
-subj "/CN=rqlite-ca"
# 生成节点证书
for node in node1 node2 node3; do
# 生成私钥
openssl genrsa -out ${node}.key 2048
# 创建证书签名请求
openssl req -new -key ${node}.key -out ${node}.csr \
-subj "/CN=${node}"
# 创建 SAN 扩展文件
cat > ${node}.cnf << EOF
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${node}
DNS.2 = localhost
IP.1 = 127.0.0.1
IP.2 = 0.0.0.0
EOF
# 签发证书
openssl x509 -req -in ${node}.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out ${node}.crt -days 365 \
-extfile ${node}.cnf -extensions v3_ext
done
# 验证证书
openssl x509 -in node1.crt -text -noout | grep -A 1 "Subject Alternative"
方式二:使用 Let’s Encrypt(生产环境)
# 安装 certbot
sudo apt install certbot
# 获取证书(需域名指向服务器)
sudo certbot certonly --standalone -d rqlite.example.com
# 证书路径
# /etc/letsencrypt/live/rqlite.example.com/fullchain.pem
# /etc/letsencrypt/live/rqlite.example.com/privkey.pem
8.2.2 配置 HTTP TLS
# 服务端启动(启用 HTTP TLS)
rqlited -node-id=node1 \
-http-addr=0.0.0.0:4001 \
-raft-addr=0.0.0.0:4002 \
-http-cert=/etc/rqlite/certs/node1.crt \
-http-key=/etc/rqlite/certs/node1.key \
-disco-mode=off \
/var/lib/rqlite/data
8.2.3 配置 Raft 通信 TLS
# 同时启用 HTTP 和 Raft TLS
rqlited -node-id=node1 \
-http-addr=0.0.0.0:4001 \
-raft-addr=0.0.0.0:4002 \
-http-cert=/etc/rqlite/certs/node1.crt \
-http-key=/etc/rqlite/certs/node1.key \
-node-cert=/etc/rqlite/certs/node1.crt \
-node-key=/etc/rqlite/certs/node1.key \
-disco-mode=off \
/var/lib/rqlite/data
8.2.4 TLS 参数一览
| 参数 | 说明 |
|---|---|
-http-cert | HTTP TLS 证书文件路径 |
-http-key | HTTP TLS 私钥文件路径 |
-http-ca-cert | HTTP TLS CA 证书(客户端验证时使用) |
-node-cert | Raft 节点间 TLS 证书文件路径 |
-node-key | Raft 节点间 TLS 私钥文件路径 |
-node-ca-cert | Raft 节点间 TLS CA 证书 |
8.2.5 客户端连接 TLS
# 使用 curl 连接 TLS 启用的节点
curl --cacert /etc/rqlite/certs/ca.crt \
-G 'https://localhost:4001/db/query' \
--data-urlencode 'q=SELECT 1'
# 忽略证书验证(仅限测试,生产勿用!)
curl -k -G 'https://localhost:4001/db/query' \
--data-urlencode 'q=SELECT 1'
8.3 认证配置
8.3.1 Basic Auth 配置
rqlite 使用 JSON 配置文件定义用户和权限:
// /etc/rqlite/auth.json
[
{
"username": "admin",
"password": "StrongPassword123!",
"perm": "all"
},
{
"username": "app_user",
"password": "AppPassword456!",
"perm": "rw"
},
{
"username": "readonly",
"password": "ReadOnlyPass789!",
"perm": "ro"
}
]
权限级别说明:
| 权限 | 说明 | 可执行操作 |
|---|---|---|
all | 完全控制 | 读写 + 集群管理(join/remove) |
rw | 读写 | 查询 + 执行 + 备份 |
ro | 只读 | 仅查询(query) |
8.3.2 启用认证
# 启动时指定认证配置
rqlited -node-id=node1 \
-auth=/etc/rqlite/auth.json \
-disco-mode=off \
/var/lib/rqlite/data
8.3.3 使用认证访问 API
# 管理员用户(all 权限)
curl -u admin:StrongPassword123! \
-G 'localhost:4001/db/query' \
--data-urlencode 'q=SELECT * FROM users'
# 应用用户(rw 权限)
curl -u app_user:AppPassword456! \
-XPOST 'localhost:4001/db/execute' \
-H 'Content-Type: application/json' \
-d '[["INSERT INTO users (username) VALUES (?)", "new_user"]]'
# 只读用户(ro 权限)
curl -u readonly:ReadOnlyPass789! \
-G 'localhost:4001/db/query' \
--data-urlencode 'q=SELECT * FROM users'
# 只读用户尝试写入(会被拒绝)
curl -u readonly:ReadOnlyPass789! \
-XPOST 'localhost:4001/db/execute' \
-H 'Content-Type: application/json' \
-d '[["INSERT INTO users (username) VALUES (?)", "hack"]]'
# 响应: HTTP 401 Unauthorized
8.3.4 认证错误响应
// HTTP 401
{
"error": "unauthorized"
}
8.4 X.509 证书认证
对于高安全要求场景,rqlite 支持基于 X.509 证书的客户端认证(mTLS):
# 生成客户端证书
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr \
-subj "/CN=app-client"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out client.crt -days 365
# 启用 mTLS(服务端需要 CA 证书验证客户端)
rqlited -node-id=node1 \
-http-addr=0.0.0.0:4001 \
-http-cert=/etc/rqlite/certs/node1.crt \
-http-key=/etc/rqlite/certs/node1.key \
-http-ca-cert=/etc/rqlite/certs/ca.crt \
-disco-mode=off \
/var/lib/rqlite/data
# 客户端使用证书连接
curl --cert /etc/rqlite/certs/client.crt \
--key /etc/rqlite/certs/client.key \
--cacert /etc/rqlite/certs/ca.crt \
-G 'https://localhost:4001/db/query' \
--data-urlencode 'q=SELECT 1'
8.5 完整安全配置示例
8.5.1 生产环境配置
#!/bin/bash
# start-rqlite-secure.sh — 安全模式启动 rqlite
NODE_ID="${NODE_ID:-node1}"
HTTP_ADDR="${HTTP_ADDR:-0.0.0.0:4001}"
RAFT_ADDR="${RAFT_ADDR:-0.0.0.0:4002}"
DATA_DIR="${DATA_DIR:-/var/lib/rqlite/data}"
CERT_DIR="${CERT_DIR:-/etc/rqlite/certs}"
AUTH_FILE="${AUTH_FILE:-/etc/rqlite/auth.json}"
JOIN="${JOIN:-}"
ARGS=(
-node-id="$NODE_ID"
-http-addr="$HTTP_ADDR"
-raft-addr="$RAFT_ADDR"
-http-cert="$CERT_DIR/server.crt"
-http-key="$CERT_DIR/server.key"
-http-ca-cert="$CERT_DIR/ca.crt"
-node-cert="$CERT_DIR/server.crt"
-node-key="$CERT_DIR/server.key"
-node-ca-cert="$CERT_DIR/ca.crt"
-auth="$AUTH_FILE"
-disco-mode=off
)
if [ -n "$JOIN" ]; then
ARGS+=(-join="$JOIN")
fi
exec rqlited "${ARGS[@]}" "$DATA_DIR"
8.5.2 systemd 服务配置(安全模式)
# /etc/systemd/system/rqlited.service
[Unit]
Description=rqlite distributed SQLite (TLS enabled)
After=network.target
[Service]
Type=simple
User=rqlite
Group=rqlite
Environment=NODE_ID=node1
Environment=HTTP_ADDR=0.0.0.0:4001
Environment=RAFT_ADDR=0.0.0.0:4002
Environment=DATA_DIR=/var/lib/rqlite/data
Environment=CERT_DIR=/etc/rqlite/certs
Environment=AUTH_FILE=/etc/rqlite/auth.json
ExecStart=/opt/rqlite/start-rqlite-secure.sh
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
# 安全加固
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadOnlyPaths=/etc/rqlite
ReadWritePaths=/var/lib/rqlite
[Install]
WantedBy=multi-user.target
8.6 安全检查清单
| 检查项 | 命令/方法 | 期望结果 |
|---|---|---|
| HTTP TLS 已启用 | curl -k https://localhost:4001/status | HTTP 200 |
| Raft TLS 已启用 | 检查启动参数含 -node-cert | 配置正确 |
| 认证已启用 | curl localhost:4001/status | HTTP 401 |
| 无认证无法写入 | curl -XPOST localhost:4001/db/execute ... | HTTP 401 |
| 只读用户无法写入 | curl -u ro:pass -XPOST ... | HTTP 401 |
| 证书有效期 | openssl x509 -in cert.crt -dates | > 30 天 |
| 文件权限 | ls -la /etc/rqlite/auth.json | 600 rqlite:rqlite |
| 防火墙 | iptables -L | 仅允许必要端口 |
8.7 防火墙配置
# 仅允许应用服务器访问 HTTP API
sudo iptables -A INPUT -p tcp --dport 4001 -s 10.0.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 4001 -j DROP
# 仅允许集群节点间 Raft 通信
sudo iptables -A INPUT -p tcp --dport 4002 -s 10.0.1.10 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 4002 -s 10.0.1.11 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 4002 -s 10.0.1.12 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 4002 -j DROP
| 端口 | 用途 | 暴露范围 |
|---|---|---|
| 4001 | HTTP API | 应用服务器 |
| 4002 | Raft 通信 | 集群内部 |
8.8 常见安全问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| HTTP 401 | 认证信息缺失或错误 | 检查 auth.json 和 -u 参数 |
| TLS 握手失败 | 证书不匹配或过期 | 重新生成证书 |
| Raft 通信失败 | 节点间 TLS 配置不一致 | 所有节点使用相同 CA 签发的证书 |
| 证书 SAN 不匹配 | 证书域名/IP 不含连接地址 | 重新签发包含正确 SAN 的证书 |
8.9 本章小结
| 要点 | 内容 |
|---|---|
| TLS 加密 | HTTP 和 Raft 通信均可启用 TLS |
| 认证方式 | Basic Auth(JSON 配置文件)和 mTLS |
| 权限级别 | all(完全控制)、rw(读写)、ro(只读) |
| 生产建议 | 必须启用 TLS + 认证 + 防火墙 |
| 证书管理 | 定期检查有效期,建议使用自动化工具续签 |
上一章:第 7 章:备份与恢复 下一章:第 9 章:性能优化