new-api 系列(七):生产环境部署与安全加固

系列目录

  1. (一)认识 new-api——新一代 AI API 管理网关
  2. (二)安装部署——从零搭建 new-api
  3. (三)渠道配置——接入各类模型提供商
  4. (四)令牌与用户管理
  5. (五)计价模型与成本控制
  6. (六)监控、日志与告警
  7. (七)生产环境部署与安全加固(本文)
  8. (八)进阶——多实例、负载均衡与 SSO
  9. (九)接入国内支付——支付宝、微信与自动充值
  10. (十)接入国际支付——Stripe、PayPal 与海外收银

# 生产环境部署与安全加固 ## 反向代理 - Nginx:功能全面,手动证书 - Caddy:自动 HTTPS,配置简洁 - 流式响应配置(关闭缓冲) ## HTTPS / TLS - Let's Encrypt 免费证书 - 自动续期 - HSTS 安全头 - TLS 1.2+ 最低版本 ## 数据库运维 - SQLite → MySQL 迁移 - MySQL 连接池调优 - 定时备份脚本 - 灾难恢复演练 ## 安全加固 - 修改默认管理员账号 - 防火墙规则 - fail2ban 防暴力破解 - 最小权限原则 - 定期更新 new-api ## 备份策略 - 数据库定时 dump - 配置文件备份 - 异地/云存储备份

反向代理方案

new-api 直接监听 :3000 是 HTTP 明文传输,不适合公网暴露。生产环境的标准做法是在前面架一层反向代理,由反代处理 HTTPS、域名、安全头等。

方案对比

特性 Nginx Caddy
HTTPS 配置 手动申请证书 + cron 续期 自动申请 + 自动续期
性能 极高,久经考验 优秀,日常够用
配置复杂度 中等
流式响应 需手动关缓冲 一句 flush_interval -1
插件生态 极其丰富 较少但够用
推荐场景 对性能有极致要求 追求简洁运维

Nginx 方案

# /etc/nginx/sites-available/new-api
server {
    listen 443 ssl http2;
    server_name api.example.com;

    # SSL 证书(Let's Encrypt)
    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    # TLS 安全配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # 安全头
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options DENY always;

    # 客户端请求体大小(大文件上传场景)
    client_max_body_size 10m;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        # 传递真实客户端 IP(new-api 日志和 IP 白名单依赖)
        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;

        # 流式响应(SSE)必须关闭缓冲
        proxy_buffering    off;
        proxy_cache        off;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;

        # WebSocket 支持(如需要)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

# HTTP 强制跳转 HTTPS
server {
    listen 80;
    server_name api.example.com;
    return 301 https://$host$request_uri;
}

启用配置:

sudo ln -s /etc/nginx/sites-available/new-api /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Let's Encrypt 证书申请

# 使用 certbot
sudo certbot --nginx -d api.example.com

# 或 acme.sh
acme.sh --issue -d api.example.com --nginx

Caddy 方案(推荐新手)

# /etc/caddy/Caddyfile
api.example.com {
    reverse_proxy 127.0.0.1:3000 {
        flush_interval -1   # 关闭缓冲,流式输出关键配置
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
    }

    # 安全头
    header {
        Strict-Transport-Security "max-age=63072000"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
    }
}

Caddy 会自动向 Let's Encrypt 申请证书并定期续期,整个过程不需要手动操作

flowchart LR A["配置 Caddyfile"] --> B["启动 Caddy"] B --> C["自动申请证书"] C --> D["HTTPS 服务就绪"] D --> E["定期自动续期"]

数据库:SQLite → MySQL 迁移

SQLite 在请求量小的时候没问题,但当日志表超过几十万行,或并发用户超过 20 人时,建议迁移到 MySQL。

MySQL 安装

# Ubuntu / Debian
sudo apt install mysql-server
sudo mysql_secure_installation

# 创建数据库和用户
sudo mysql -u root <<SQL
CREATE DATABASE new_api CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'newapi'@'127.0.0.1' IDENTIFIED BY 'your-strong-password';
GRANT ALL PRIVILEGES ON new_api.* TO 'newapi'@'127.0.0.1';
FLUSH PRIVILEGES;
SQL

配置 new-api 连接 MySQL

./new-api \
  --port 3000 \
  --db-type mysql \
  --mysql-dsn "newapi:your-strong-password@tcp(127.0.0.1:3306)/new_api?charset=utf8mb4&parseTime=True&loc=Local"

首次启动时 new-api 会自动创建所有需要的表。

从 SQLite 迁移数据

new-api 目前没有内置的一键迁移工具。如果需要保留历史数据,可以:

  1. 从 SQLite 导出关键数据(用户、令牌、渠道)为 CSV
  2. 在 MySQL 环境中重新创建这些资源
  3. 旧日志保留在 SQLite 备份中备查

或者直接用 SQLite 工具导出导入:

# 导出 SQLite 数据
sqlite3 new-api.db .dump > backup.sql

# 转换后导入 MySQL(注意 SQL 语法差异,需手动调整)

大多数情况下,迁移到 MySQL 时重新配置渠道和令牌比折腾数据迁移更快。日志数据可以保留在旧的 SQLite 文件中备查。

备份策略

备份什么

需备份 位置 说明
数据库文件 ~/new-api/new-api.db(SQLite)或 MySQL dump 用户、令牌、渠道、日志全在这
配置文件 systemd unit 文件 启动参数
日志文件 ~/new-api/logs/ 可选,排查历史问题用

SQLite 自动备份脚本

#!/bin/bash
# /usr/local/bin/backup-newapi.sh

BACKUP_DIR="/backup/new-api"
DB_FILE="$HOME/new-api/new-api.db"
RETENTION_DAYS=30

mkdir -p "$BACKUP_DIR"

# 备份数据库(SQLite 支持在线备份)
sqlite3 "$DB_FILE" ".backup '$BACKUP_DIR/new-api-$(date +%Y%m%d-%H%M%S).db'"

# 清理旧备份
find "$BACKUP_DIR" -name "*.db" -mtime +$RETENTION_DAYS -delete

echo "Backup completed: $(date)"
# 加入 crontab(每天凌晨 3 点)
crontab -e
# 添加:
0 3 * * * /usr/local/bin/backup-newapi.sh >> /var/log/newapi-backup.log 2>&1

MySQL 备份

#!/bin/bash
# MySQL 备份脚本
BACKUP_DIR="/backup/new-api"
mysqldump -u newapi -p'your-password' --single-transaction new_api \
  > "$BACKUP_DIR/new-api-mysql-$(date +%Y%m%d-%H%M%S).sql"

异地备份

备份文件存在本地还不够——服务器故障可能导致数据丢失。建议再加一层:

  • rsync 到另一台机器rsync -avz /backup/new-api/ user@backup-server:/backup/
  • 云存储rclone sync /backup/new-api/ s3:my-backup-bucket/new-api/
  • GitHub 私有仓库:适用于配置文件的版本管理

安全加固

管理员账号

  • 不使用默认 root / 123456
  • 密码 16 位以上随机字符串
  • 创建专属管理员账号,禁用默认 root

防火墙

# 只开放必要端口
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp          # SSH
sudo ufw allow 443/tcp         # HTTPS(反代)
sudo ufw allow 80/tcp           # HTTP(证书申请需要)
sudo ufw enable

# 注意:不要开放 3000 端口!new-api 只监听 127.0.0.1
flowchart LR A["公网"] -->|":443"| B["Nginx / Caddy"] B -->|"127.0.0.1:3000"| C["new-api"] C --> D["上游 API"]

new-api 只绑定 127.0.0.1,外网无法直接访问。所有流量必须经过反代。

fail2ban 防暴力破解

new-api 登录接口如果暴露到公网,建议加一层 fail2ban 防护。

# /etc/fail2ban/jail.local
[new-api]
enabled  = true
port     = http,https
filter   = new-api
logpath  = /var/log/nginx/api.example.com.access.log
maxretry = 10
bantime  = 3600
findtime = 600

最小权限原则

  • new-api 进程以普通用户运行(不要 root)
  • systemd 配置中加 NoNewPrivileges=yes
  • 数据库用户只给必要权限(不要 GRANT ALL 如果不需要)
  • API Key 和密钥不写入版本控制

定期更新

# 检查 GitHub Releases
curl -s https://api.github.com/repos/Calcium-Ion/new-api/releases/latest | grep tag_name

关注更新日志中的安全修复,及时升级。

SSL 证书自动续期

Let's Encrypt(certbot)

# certbot 默认会自动续期(systemd timer)
sudo systemctl status certbot.timer

# 手动测试续期
sudo certbot renew --dry-run

Let's Encrypt(acme.sh)

acme.sh --renew -d api.example.com --force

Caddy

完全自动,无需任何操作。Caddy 在证书到期前 30 天自动续期。

性能调优

new-api 本身

  • Go 程序本身已经足够高效,一般不需要调优
  • 如果请求量很大(>1000 QPM),考虑用 MySQL 替代 SQLite
  • 启动参数中可控制日志级别(--log-level warn 减少日志 I/O)

Nginx

# 连接数优化
worker_processes auto;
worker_connections 1024;

# 静态文件缓存
location /assets/ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

MySQL

# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
max_connections = 100
innodb_buffer_pool_size = 256M    # 按服务器内存调整
innodb_log_file_size = 64M

系列导航

系列导航

实操清单

  • Caddy 反向代理已配置(airouter.k330.com,HTTPS 自动签发,flush_interval -1 流式支持)
  • HTTPS 正常(Caddy 自动续期,无需手动操作)
  • 验证流式响应:用 curl 发一条请求,确认 token 逐步返回
  • ⚠️ 封锁 3000 端口(当前 new-api 监听 *:3000,外网可直接访问,存在安全风险):
    • ufw allow 22 && ufw deny 3000 && ufw enable
    • iptables -A INPUT -p tcp --dport 3000 ! -s 127.0.0.1 -j DROP
  • 确认 3000 端口对外不可达(curl http://外网IP:3000 应超时)
  • ⚠️ 设置数据库定时备份(当前无任何备份计划):crontab -e 添加每日备份
  • 配置异地备份(rsync / rclone 推送到对象存储)
  • 执行一次备份恢复演练(停服务 → 替换 db → 重启 → 验证数据)
  • 独立管理员账号已创建(sdmike),已替代默认 root
  • 配置 fail2ban 防暴力登录(可选)