强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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-certHTTP TLS 证书文件路径
-http-keyHTTP TLS 私钥文件路径
-http-ca-certHTTP TLS CA 证书(客户端验证时使用)
-node-certRaft 节点间 TLS 证书文件路径
-node-keyRaft 节点间 TLS 私钥文件路径
-node-ca-certRaft 节点间 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/statusHTTP 200
Raft TLS 已启用检查启动参数含 -node-cert配置正确
认证已启用curl localhost:4001/statusHTTP 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.json600 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
端口用途暴露范围
4001HTTP API应用服务器
4002Raft 通信集群内部

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 章:性能优化