背景:此前全环境共用一把 JWT 密钥(config/jwt/jwt-private.pem)。它相当于 "能冒充 9001/9002/9003 的万能钥匙"(实测边界:签名有效 + 用户存在且启用才通过, 伪造新用户与使用禁用账号都会被拒)。为避免同一把密钥将来又变成生产密钥, 本次引入开发专用密钥,并把签发侧收敛到配置。 改动: 1. 新增 tools/generate_jwt_keys.py:可复现地生成 RS256 密钥对(PKCS#8 / SPKI), 打印公钥 SHA-256 指纹便于核对服务端加载的是否同一把;密钥已存在时默认拒绝 覆盖,避免误操作导致所有已签发令牌立即失效。 2. 生成开发专用密钥到 config/jwt/dev/(该目录整体已被 .gitignore 忽略,不入库)。 3. 三个工具脚本不再硬编码私钥路径,改为读配置:acceptance_check 与 demo_agent_e2e 走 get_settings().jwt_private_key_path,smoke_check 因刻意不依赖 app 包而读 JWT_PRIVATE_KEY_PATH 环境变量。今后轮换密钥只需改 .env 一处。 4. .env、.env.example 与 Settings 默认值统一指向 config/jwt/dev/。 5. 新增 docs/21-JWT密钥管理与轮换.md:密钥分工(服务端只读公钥, JWT_PRIVATE_KEY_PATH 在 app/ 中无任何读取点,故生产机可只挂公钥)、 克隆后必须自行生成、多人共用一个服务时必须共用同一把私钥、 轮换的影响面与生产部署要点、安全红线。 6. 记录一处易被忽略的问题:生产环境的 JWT_ISSUER / JWT_AUDIENCE 也应与开发不同, 否则开发环境签发的令牌在生产上依然有效——这比换密钥更容易漏。 说明:本次提交不含任何密钥文件(.env 与 config/jwt/ 均在 .gitignore 中)。 旧密钥 config/jwt/jwt-private.pem 已退役但保留未删,配置不再引用它, 用它签发的令牌会被拒绝。 验证:ruff 通过、mypy 103 文件无错、unit+contract 447 passed、integration 29 passed、 acceptance_check --production 7 PASS、demo_agent_e2e 9/9 PASS——均使用新密钥完成 签发与验签。
56 lines
1.8 KiB
Bash
56 lines
1.8 KiB
Bash
APP_ENV=local
|
|
APP_NAME=jr-agent-platform
|
|
LOG_LEVEL=INFO
|
|
TIMEZONE=Asia/Shanghai
|
|
|
|
JWT_ISSUER=jr-local
|
|
JWT_AUDIENCE=jr-agent-platform
|
|
JWT_ALGORITHM=RS256
|
|
# 开发专用密钥,由 tools/generate_jwt_keys.py 生成。config/jwt/ 已被 .gitignore 忽略,
|
|
# 所以克隆仓库后**必须自己生成一次**,否则服务会因为读不到公钥而启动失败。
|
|
# 生产环境:在生产机上单独生成一套,用环境变量覆盖这两行,不要复用开发密钥。
|
|
JWT_PRIVATE_KEY_PATH=config/jwt/dev/jwt-private.pem
|
|
JWT_PUBLIC_KEY_PATH=config/jwt/dev/jwt-public.pem
|
|
JWT_CLOCK_SKEW_SECONDS=30
|
|
|
|
MYSQL_DSN=mysql+asyncmy://jr_app:change-me@127.0.0.1:3306/jr_agent
|
|
MYSQL_POOL_SIZE=5
|
|
MYSQL_MAX_OVERFLOW=10
|
|
MYSQL_TX_ISOLATION=READ COMMITTED
|
|
|
|
REDIS_URL=redis://127.0.0.1:6379/0
|
|
REDIS_CONNECT_TIMEOUT_SECONDS=2
|
|
|
|
MESSAGE_BROKER_URL=
|
|
MESSAGE_BROKER_RUN_TOPIC=agent.run
|
|
MESSAGE_BROKER_OUTBOX_TOPIC=agent.outbox
|
|
MESSAGE_BROKER_DLQ_TOPIC=agent.dlq
|
|
|
|
MILVUS_URI=http://127.0.0.1:19530
|
|
MILVUS_TOKEN=
|
|
MILVUS_COLLECTION=jr_memory
|
|
|
|
NEO4J_URI=bolt://127.0.0.1:7687
|
|
NEO4J_DATABASE=neo4j
|
|
NEO4J_USERNAME=neo4j
|
|
NEO4J_PASSWORD=
|
|
|
|
MODEL_ROUTER_CONFIG_REF=local
|
|
MODEL_DEFAULT_ENDPOINT=
|
|
MODEL_FALLBACK_ENDPOINT=
|
|
|
|
SSE_HEARTBEAT_SECONDS=15
|
|
SSE_MAX_CONNECTION_SECONDS=300
|
|
WORKER_LEASE_SECONDS=60
|
|
WORKER_RETRY_LIMIT=3
|
|
WORKER_POLL_SECONDS=1
|
|
SSE_CHUNK_CHARACTERS=256
|
|
|
|
# 限流:按“用户 + 方法 + 路由”在窗口内计数,超限返回 429 RATE_LIMITED + Retry-After。
|
|
# 默认 600/60s(10 QPS)是为本机验收与集成测试留足余量的宽松值;生产按真实容量收紧。
|
|
# Redis 不可用时自动降级为放行(限流是保护措施,不能因后端故障拒绝正常请求)。
|
|
RATE_LIMIT_ENABLED=true
|
|
RATE_LIMIT_WINDOW_SECONDS=60
|
|
RATE_LIMIT_MAX_REQUESTS=600
|
|
RATE_LIMIT_KEY_PREFIX=jr:rate_limit
|