Rekor 透明日志完整教程 / 08 - 私有实例部署
第 8 章:私有实例部署
本章介绍如何部署和运维私有 Rekor 实例,适用于企业内网、离线环境或需要自定义配置的场景。
8.1 为什么需要私有实例?
8.1.1 公共实例 vs 私有实例
| 考量 | 公共实例 (rekor.sigstore.dev) | 私有实例 |
|---|---|---|
| 成本 | 免费 | 需要服务器和运维成本 |
| 速率限制 | 有 | 自定义 |
| 数据主权 | 数据公开,存储在 Sigstore 基础设施 | 数据完全自控 |
| 网络访问 | 需要互联网 | 内网可用 |
| 定制化 | 不可定制 | 完全可定制 |
| 高可用 | 由 Sigstore 维护 | 自行保障 |
| 审计合规 | 数据在外部 | 数据在企业内部 |
8.1.2 适用场景
| 场景 | 原因 |
|---|---|
| 企业内网开发 | 无法访问公网 |
| 合规要求 | 数据必须留在企业内部 |
| 高写入量 | 公共实例速率限制无法满足 |
| 定制化需求 | 需要自定义条目类型或验证逻辑 |
| 混合部署 | 公共 + 私有双日志策略 |
8.2 架构设计
8.2.1 私有 Rekor 部署架构
┌────────────────────────────────────────────────────────────────────┐
│ 私有 Rekor 部署架构 │
│ │
│ ┌──────────┐ ┌─────────────────────────────────────────────┐ │
│ │ 客户端 │────►│ 负载均衡器 (Nginx/HAProxy) │ │
│ │ rekor-cli│ └──────────────────┬──────────────────────────┘ │
│ │ cosign │ │ │
│ └──────────┘ ┌────────────┼────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │rekor- │ │rekor- │ │rekor- │ │
│ │server-1 │ │server-2 │ │server-3 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └────────────┼────────────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ Trillian │ │
│ │ Log Server │ │
│ └───────┬───────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ Trillian │ │
│ │ Log Signer │ │
│ └───────┬───────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ MySQL / │ │
│ │ PostgreSQL │ │
│ └───────────────┘ │
│ │
│ ┌───────────────┐ │
│ │ Redis │ │
│ │ (可选缓存) │ │
│ └───────────────┘ │
└────────────────────────────────────────────────────────────────────┘
8.2.2 最小化部署 vs 生产部署
| 组件 | 最小化部署 | 生产部署 |
|---|---|---|
| rekor-server | 1 实例 | 3+ 实例 |
| Trillian Log Server | 1 实例 | 2+ 实例 |
| Trillian Log Signer | 1 实例 | 1 实例(单主) |
| MySQL/PostgreSQL | 1 实例 | 主从复制 |
| Redis | 不需要 | 集群模式 |
| 负载均衡器 | 不需要 | Nginx/HAProxy |
| 监控 | 不需要 | Prometheus + Grafana |
8.3 前置依赖安装
8.3.1 安装 MySQL
# Ubuntu/Debian
sudo apt update
sudo apt install -y mysql-server
# 启动 MySQL
sudo systemctl start mysql
sudo systemctl enable mysql
# 创建 Trillian 数据库
mysql -u root -p << 'EOF'
CREATE DATABASE trillian;
CREATE USER 'trillian'@'localhost' IDENTIFIED BY 'trillian_password';
GRANT ALL PRIVILEGES ON trillian.* TO 'trillian'@'localhost';
FLUSH PRIVILEGES;
EOF
8.3.2 安装 Trillian
# 克隆 Trillian 仓库
git clone https://github.com/google/trillian.git
cd trillian
# 编译
go build -o trillian-log-server ./cmd/trillian_log_server
go build -o trillian-log-signer ./cmd/trillian_log_signer
go build -o createtree ./cmd/createtree
# 移动到 PATH
sudo mv trillian-log-server trillian-log-signer createtree /usr/local/bin/
# 初始化数据库
mysql -u root -p trillian < storage/mysql/schema/storage.sql
8.3.3 安装 Redis(可选)
# Ubuntu/Debian
sudo apt install -y redis-server
sudo systemctl start redis-server
sudo systemctl enable redis-server
# 验证
redis-cli ping
# PONG
8.4 Trillian 配置
8.4.1 启动 Trillian Log Server
# 启动 Trillian Log Server
trillian-log-server \
--mysql_uri="trillian:trillian_password@tcp(localhost:3306)/trillian" \
--rpc_endpoint=localhost:8090 \
--http_endpoint=localhost:8091 \
--logtostderr
# 后台运行
nohup trillian-log-server \
--mysql_uri="trillian:trillian_password@tcp(localhost:3306)/trillian" \
--rpc_endpoint=localhost:8090 \
--http_endpoint=localhost:8091 \
--logtostderr \
> /var/log/trillian-server.log 2>&1 &
8.4.2 启动 Trillian Log Signer
# 启动 Trillian Log Signer
trillian-log-signer \
--mysql_uri="trillian:trillian_password@tcp(localhost:3306)/trillian" \
--rpc_endpoint=localhost:8092 \
--http_endpoint=localhost:8093 \
--logtostderr \
--force_master
# 后台运行
nohup trillian-log-signer \
--mysql_uri="trillian:trillian_password@tcp(localhost:3306)/trillian" \
--rpc_endpoint=localhost:8092 \
--http_endpoint=localhost:8093 \
--logtostderr \
--force_master \
> /var/log/trillian-signer.log 2>&1 &
8.4.3 创建 Trillian 树
# 创建透明日志树
createtree \
--admin_server=localhost:8090 \
--tree_type=LOG \
--signature_algorithm=ECDSA
# 记录返回的 Tree ID,后续配置 rekor-server 需要
# 例如: Created tree with ID: 1234567890
8.5 Rekor Server 配置
8.5.1 配置文件
创建 Rekor Server 配置文件:
# /etc/rekor/rekor-config.yaml
rekor_server:
address: 0.0.0.0
port: 3000
trillian:
log_server:
address: localhost
port: 8090
log_id: 1234567890 # 使用 createtree 返回的 ID
redis:
address: localhost
port: 6379
# 如果不需要 Redis,注释以上两行
enable_attestation_storage: true
attestation_storage:
backend: blob
blob:
bucket: "rekor-attestations"
# 或使用本地文件系统
path: "/var/lib/rekor/attestations"
max_apibase_log_entries: 0
log:
type: "log"
level: "info"
8.5.2 启动 Rekor Server
# 基本启动
rekor-server serve \
--trillian_log_server.address=localhost \
--trillian_log_server.port=8090 \
--rekor_server.address=0.0.0.0 \
--rekor_server.port=3000 \
--log_type=log \
--log_level=info
# 使用配置文件启动(如果支持)
rekor-server serve --config=/etc/rekor/rekor-config.yaml
# 后台运行
nohup rekor-server serve \
--trillian_log_server.address=localhost \
--trillian_log_server.port=8090 \
--rekor_server.address=0.0.0.0 \
--rekor_server.port=3000 \
> /var/log/rekor-server.log 2>&1 &
8.5.3 验证服务
# 测试 Rekor Server
curl -s http://localhost:3000/api/v1/log | jq '.'
# 使用 rekor-cli
rekor-cli loginfo --rekor_server=http://localhost:3000
# 测试写入
echo "test" > /tmp/test.txt
cosign sign-blob \
--rekor-url=http://localhost:3000 \
--key cosign.key \
--yes \
/tmp/test.txt
8.6 系统服务配置
8.6.1 systemd 服务文件
创建 systemd 服务文件以管理 Rekor 和 Trillian:
# /etc/systemd/system/trillian-log-server.service
[Unit]
Description=Trillian Log Server
After=network.target mysql.service
Requires=mysql.service
[Service]
Type=simple
User=trillian
Group=trillian
ExecStart=/usr/local/bin/trillian-log-server \
--mysql_uri=trillian:trillian_password@tcp(localhost:3306)/trillian \
--rpc_endpoint=localhost:8090 \
--http_endpoint=localhost:8091 \
--logtostderr
Restart=always
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/trillian-log-signer.service
[Unit]
Description=Trillian Log Signer
After=network.target mysql.service trillian-log-server.service
Requires=mysql.service
[Service]
Type=simple
User=trillian
Group=trillian
ExecStart=/usr/local/bin/trillian-log-signer \
--mysql_uri=trillian:trillian_password@tcp(localhost:3306)/trillian \
--rpc_endpoint=localhost:8092 \
--http_endpoint=localhost:8093 \
--logtostderr \
--force_master
Restart=always
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/rekor-server.service
[Unit]
Description=Rekor Server
After=network.target trillian-log-server.service redis.service
Requires=trillian-log-server.service
[Service]
Type=simple
User=rekor
Group=rekor
ExecStart=/usr/local/bin/rekor-server serve \
--trillian_log_server.address=localhost \
--trillian_log_server.port=8090 \
--rekor_server.address=0.0.0.0 \
--rekor_server.port=3000 \
--redis_server.address=localhost \
--redis_server.port=6379 \
--log_type=log \
--log_level=info
Restart=always
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
8.6.2 启用服务
# 创建用户
sudo useradd -r -s /bin/false trillian
sudo useradd -r -s /bin/false rekor
# 创建目录
sudo mkdir -p /var/lib/rekor/attestations
sudo chown rekor:rekor /var/lib/rekor/attestations
# 重载 systemd
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start trillian-log-server
sudo systemctl start trillian-log-signer
sudo systemctl start rekor-server
# 设置开机自启
sudo systemctl enable trillian-log-server
sudo systemctl enable trillian-log-signer
sudo systemctl enable rekor-server
# 检查状态
sudo systemctl status rekor-server
8.7 Nginx 反向代理
8.7.1 Nginx 配置
# /etc/nginx/sites-available/rekor
upstream rekor_backend {
server 127.0.0.1:3000;
# 多实例时添加更多后端
# server 127.0.0.1:3001;
# server 127.0.0.1:3002;
}
server {
listen 80;
server_name rekor.internal.mycompany.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name rekor.internal.mycompany.com;
ssl_certificate /etc/nginx/ssl/rekor.crt;
ssl_certificate_key /etc/nginx/ssl/rekor.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 速率限制
limit_req_zone $binary_remote_addr zone=rekor_limit:10m rate=100r/s;
location / {
limit_req zone=rekor_limit burst=200 nodelay;
proxy_pass http://rekor_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时设置
proxy_connect_timeout 10s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
# 缓冲设置
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 16k;
}
# 健康检查
location /healthz {
access_log off;
proxy_pass http://rekor_backend/api/v1/log;
}
}
8.7.2 启用配置
sudo ln -s /etc/nginx/sites-available/rekor /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
8.8 存储配置
8.8.1 数据库优化
-- MySQL 优化配置 (my.cnf)
[mysqld]
# InnoDB 设置
innodb_buffer_pool_size = 4G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
# 连接设置
max_connections = 200
wait_timeout = 600
# 查询缓存(MySQL 8.0 之前)
# query_cache_type = 1
# query_cache_size = 256M
# 慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
8.8.2 存储容量规划
| 数据项 | 大小(估算) | 说明 |
|---|---|---|
| 叶子节点 | ~1KB/条 | 每个 Rekor 条目 |
| Merkle 节点 | ~32B/节点 | SHA-256 哈希值 |
| 索引数据 | ~200B/条 | 搜索索引 |
| 证明数据 | ~1KB/证明 | 包含证明 |
容量计算示例(100 万条目):
叶子节点: 1,000,000 × 1KB = ~1 GB
Merkle 节点: 1,000,000 × 32B × 2 = ~64 MB
索引数据: 1,000,000 × 200B = ~200 MB
总计: ~1.3 GB(不含日志和备份)
8.8.3 备份策略
#!/bin/bash
# backup-rekor.sh - Rekor 数据库备份脚本
BACKUP_DIR="/var/backup/rekor"
DATE=$(date +%Y%m%d_%H%M%S)
MYSQL_USER="trillian"
MYSQL_PASS="trillian_password"
DATABASE="trillian"
mkdir -p "$BACKUP_DIR"
# 数据库备份
mysqldump -u "$MYSQL_USER" -p"$MYSQL_PASS" "$DATABASE" | gzip > "$BACKUP_DIR/trillian_$DATE.sql.gz"
# 清理 30 天前的备份
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete
echo "备份完成: $BACKUP_DIR/trillian_$DATE.sql.gz"
# 设置定时备份
# crontab -e
0 2 * * * /opt/rekor/scripts/backup-rekor.sh >> /var/log/rekor-backup.log 2>&1
8.9 高可用配置
8.9.1 高可用架构
┌────────────────────────────────────────────────────────────────────┐
│ 高可用 Rekor 部署 │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 负载均衡器 (Keepalived) │ │
│ │ VIP: 192.168.1.100 │ │
│ └────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ │ │ │ │
│ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │
│ │ rekor-1 │ │ rekor-2 │ │ rekor-3 │ │
│ │ 192.168.1.1│ │ 192.168.1.2│ │ 192.168.1.3│ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Trillian Cluster │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Log Svr 1 │ │Log Svr 2 │ │Log Signer│ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────┬─────────────────────────┘ │
│ │ │
│ ┌────────────────────────┴─────────────────────────┐ │
│ │ MySQL Cluster │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Master │ │ Slave 1 │ │ Slave 2 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────┘
8.9.2 MySQL 主从复制
-- 主服务器配置 (my.cnf)
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = trillian
-- 从服务器配置 (my.cnf)
[mysqld]
server-id = 2
relay_log = /var/log/mysql/mysql-relay-bin.log
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = trillian
read_only = 1
-- 在从服务器上设置复制
CHANGE MASTER TO
MASTER_HOST='192.168.1.10',
MASTER_USER='replication_user',
MASTER_PASSWORD='replication_password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=0;
START SLAVE;
SHOW SLAVE STATUS\G
8.10 监控配置
8.10.1 Prometheus 配置
# prometheus.yml
scrape_configs:
- job_name: 'rekor-server'
static_configs:
- targets: ['localhost:3000']
metrics_path: /metrics
scrape_interval: 15s
- job_name: 'trillian-log-server'
static_configs:
- targets: ['localhost:8091']
metrics_path: /metrics
scrape_interval: 15s
- job_name: 'trillian-log-signer'
static_configs:
- targets: ['localhost:8093']
metrics_path: /metrics
scrape_interval: 15s
8.10.2 关键监控指标
| 指标 | 说明 | 警告阈值 |
|---|---|---|
rekor_http_requests_total | HTTP 请求总数 | - |
rekor_http_request_duration_seconds | 请求延迟 | P99 > 1s |
trillian_sequencer_tree_size | 树大小 | 持续不增长 |
trillian_sequencer_latency_seconds | 排序延迟 | > 10s |
mysql_global_status_threads_connected | MySQL 连接数 | > 150 |
mysql_global_status_slow_queries | 慢查询数量 | > 0 |
8.10.3 Grafana Dashboard
{
"dashboard": {
"title": "Rekor Server Dashboard",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(rekor_http_requests_total[5m])",
"legendFormat": "{{method}} {{path}}"
}
]
},
{
"title": "Request Latency (P99)",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.99, rate(rekor_http_request_duration_seconds_bucket[5m]))",
"legendFormat": "P99 Latency"
}
]
},
{
"title": "Tree Size",
"type": "stat",
"targets": [
{
"expr": "trillian_sequencer_tree_size",
"legendFormat": "Entries"
}
]
}
]
}
}
8.11 安全加固
8.11.1 TLS 配置
# 生成自签名证书(测试环境)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/rekor.key \
-out /etc/ssl/certs/rekor.crt \
-subj "/CN=rekor.internal.mycompany.com"
# 生产环境建议使用企业 CA 签发的证书
8.11.2 认证配置
# 如果需要 API 认证,配置 Nginx basic auth
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd rekor_user
# 在 Nginx 配置中添加
# auth_basic "Rekor API";
# auth_basic_user_file /etc/nginx/.htpasswd;
8.11.3 防火墙规则
# UFW 配置
sudo ufw allow 22/tcp # SSH
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 80/tcp # HTTP (重定向到 HTTPS)
sudo ufw deny 3000/tcp # 阻止直接访问 Rekor
sudo ufw deny 8090/tcp # 阻止直接访问 Trillian RPC
sudo ufw deny 3306/tcp # 阻止直接访问 MySQL
sudo ufw enable
8.12 注意事项
日志同步:私有实例与公共实例是独立的,签名记录不会互相同步。如果需要跨实例验证,需要使用一致性证明。
数据库性能:Trillian 的 Sequencer 对数据库性能敏感。建议使用 SSD 存储并优化 MySQL 配置。
证书管理:确保 TLS 证书的定期更新,建议使用 Let’s Encrypt 或企业 CA。
容量规划:随着日志增长,数据库大小会持续增加。定期监控存储使用情况。
8.13 本章小结
| 组件 | 最小配置 | 推荐配置 |
|---|---|---|
| rekor-server | 1 实例, 2C4G | 3 实例, 4C8G |
| Trillian Log Server | 1 实例, 2C4G | 2 实例, 4C8G |
| Trillian Log Signer | 1 实例, 1C2G | 1 实例, 2C4G |
| MySQL | 1 实例, 2C4G, 50GB SSD | 主从, 4C8G, 200GB SSD |
| Redis | 可选 | 集群模式, 4G 内存 |
扩展阅读
下一章:09 - Docker 部署 — 使用 Docker 和 Kubernetes 部署 Rekor。