基于FastAPI构建安全的Ollama服务代理方案

一、方案架构设计

1.1 代理模式的核心价值

传统Ollama部署方案直接暴露HTTP API接口,存在三大安全隐患:

  • 缺乏请求身份验证机制
  • 无法限制接口访问频率
  • 难以追踪请求来源

本方案采用”FastAPI代理层+Ollama服务层”的分层架构,通过以下机制实现安全管控:

  • 统一认证入口:所有请求必须携带有效API Key
  • 请求流量过滤:拦截非法请求并记录审计日志
  • 协议转换能力:支持RESTful到gRPC等协议转换(扩展场景)

1.2 技术栈选型依据

选择FastAPI作为代理层框架基于以下优势:

  • 原生支持异步IO,处理高并发请求时延迟降低60%
  • 内置数据验证和序列化机制,减少样板代码
  • 自动生成OpenAPI文档,便于接口管理
  • 与Starlette中间件体系深度集成,扩展性强

二、核心实现步骤

2.1 环境准备与依赖安装

  1. # 创建虚拟环境(推荐使用Python 3.9+)
  2. python -m venv ollama-proxy-env
  3. source ollama-proxy-env/bin/activate
  4. # 安装核心依赖
  5. pip install fastapi uvicorn httpx python-dotenv

2.2 FastAPI网关实现

基础路由定义

  1. from fastapi import FastAPI, Request, HTTPException, Depends
  2. from fastapi.security import APIKeyHeader
  3. import httpx
  4. import os
  5. from dotenv import load_dotenv
  6. load_dotenv()
  7. app = FastAPI(title="Ollama Secure Proxy")
  8. # 配置API Key校验
  9. api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
  10. API_KEY = os.getenv("API_KEY", "default-key-should-be-overridden")
  11. async def verify_api_key(api_key: str = Depends(api_key_header)):
  12. if api_key != API_KEY:
  13. raise HTTPException(status_code=401, detail="Invalid API Key")
  14. return api_key

请求转发逻辑

  1. OLLAMA_ENDPOINT = "http://localhost:11434" # 默认Ollama服务地址
  2. @app.post("/api/generate")
  3. async def generate_text(
  4. request: Request,
  5. api_key: str = Depends(verify_api_key)
  6. ):
  7. # 提取原始请求体
  8. request_body = await request.json()
  9. async with httpx.AsyncClient() as client:
  10. try:
  11. # 转发请求到Ollama服务
  12. response = await client.post(
  13. f"{OLLAMA_ENDPOINT}/api/generate",
  14. json=request_body
  15. )
  16. response.raise_for_status()
  17. return response.json()
  18. except httpx.HTTPStatusError as e:
  19. raise HTTPException(status_code=e.response.status_code, detail=e.response.text)

2.3 服务启动配置

创建main.py并添加以下内容:

  1. import uvicorn
  2. if __name__ == "__main__":
  3. uvicorn.run(
  4. "app:app",
  5. host="0.0.0.0",
  6. port=8000,
  7. reload=True # 生产环境应设为False
  8. )

启动命令:

  1. uvicorn app:app --host 0.0.0.0 --port 8000

三、安全增强措施

3.1 API Key生命周期管理

密钥生成策略

  1. import secrets
  2. import string
  3. def generate_secure_key(length=32):
  4. alphabet = string.ascii_letters + string.digits + "-._~"
  5. return ''.join(secrets.choice(alphabet) for _ in range(length))
  6. # 生成并保存密钥
  7. secure_key = generate_secure_key()
  8. with open(".api_key", "w") as f:
  9. f.write(secure_key)

密钥存储方案对比

存储方式 安全性 便捷性 适用场景
环境变量 ★★★★☆ ★★★★☆ 容器化部署
密钥管理服务 ★★★★★ ★★★☆☆ 企业级多服务共享
加密配置文件 ★★★★☆ ★★★☆☆ 传统服务器部署
内存硬编码 ★☆☆☆☆ ★★★★★ 绝对禁止使用

3.2 请求限流机制

  1. from slowapi import Limiter
  2. from slowapi.util import get_remote_address
  3. limiter = Limiter(key_func=get_remote_address)
  4. app.state.limiter = limiter
  5. @app.post("/api/generate")
  6. @limiter.limit("10/minute") # 每分钟10次请求限制
  7. async def rate_limited_generate(...):
  8. # 原有实现代码
  9. pass

3.3 审计日志记录

  1. import logging
  2. from datetime import datetime
  3. logging.basicConfig(
  4. filename="api_audit.log",
  5. level=logging.INFO,
  6. format="%(asctime)s - %(levelname)s - %(message)s"
  7. )
  8. async def log_request(request: Request, api_key: str):
  9. logging.info(
  10. f"API Access - Key: {api_key[:4]}*** - "
  11. f"Path: {request.url.path} - "
  12. f"Client: {request.client.host if request.client else 'unknown'}"
  13. )
  14. # 在路由处理函数中添加
  15. @app.post("/api/generate")
  16. async def generate_with_logging(...):
  17. await log_request(request, api_key)
  18. # 原有实现代码

四、生产环境部署建议

4.1 容器化部署方案

  1. FROM python:3.9-slim
  2. WORKDIR /app
  3. COPY requirements.txt .
  4. RUN pip install --no-cache-dir -r requirements.txt
  5. COPY . .
  6. ENV API_KEY=your-secure-key-here
  7. CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

4.2 反向代理配置(Nginx示例)

  1. server {
  2. listen 443 ssl;
  3. server_name api.example.com;
  4. ssl_certificate /path/to/cert.pem;
  5. ssl_certificate_key /path/to/key.pem;
  6. location / {
  7. proxy_pass http://localhost:8000;
  8. proxy_set_header Host $host;
  9. proxy_set_header X-Real-IP $remote_addr;
  10. # 启用WebSocket支持(如需)
  11. proxy_http_version 1.1;
  12. proxy_set_header Upgrade $http_upgrade;
  13. proxy_set_header Connection "upgrade";
  14. }
  15. }

4.3 监控告警设置

建议集成以下监控指标:

  • 请求成功率(Success Rate)
  • 平均响应时间(Avg Latency)
  • 密钥验证失败次数(Auth Failures)
  • 限流触发次数(Rate Limit Trips)

可通过Prometheus+Grafana或主流云服务商的监控服务实现可视化。

五、常见问题处理

5.1 CORS配置问题

  1. from fastapi.middleware.cors import CORSMiddleware
  2. app.add_middleware(
  3. CORSMiddleware,
  4. allow_origins=["*"], # 生产环境应指定具体域名
  5. allow_methods=["*"],
  6. allow_headers=["*"],
  7. )

5.2 超时设置优化

  1. @app.post("/api/generate")
  2. async def generate_with_timeout(...):
  3. try:
  4. async with httpx.AsyncClient(timeout=30.0) as client: # 30秒超时
  5. # 原有请求逻辑
  6. except httpx.TimeoutException:
  7. raise HTTPException(status_code=504, detail="Request timeout")

5.3 密钥轮换方案

  1. 生成新密钥并更新环境变量
  2. 保留旧密钥48小时用于过渡
  3. 修改验证逻辑支持多密钥验证
  4. 48小时后移除旧密钥

六、扩展功能建议

  1. 多模型支持:通过路径参数区分不同模型

    1. @app.post("/api/generate/{model_name}")
    2. async def model_specific_generate(...):
    3. # 实现代码
  2. 请求参数校验:使用Pydantic模型

    1. from pydantic import BaseModel
    2. class GenerateRequest(BaseModel):
    3. prompt: str
    4. model: str = "llama2"
    5. temperature: float = 0.7
    6. @app.post("/api/generate")
    7. async def validated_generate(request: GenerateRequest):
    8. # 直接使用request.model等属性
  3. 响应缓存:对相同请求参数的结果进行缓存

  4. 请求追踪:集成OpenTelemetry实现分布式追踪

本方案通过分层架构设计,在保持Ollama核心功能的同时,构建了完善的安全防护体系。实际部署时建议结合具体业务需求,在密钥管理、监控告警等方面进行深度定制,构建企业级模型服务接口。