自托管 Flowise 时,
.env决定了能不能被陌生人打开、自定义工具能不能读到本机文件。官方提供很长的环境变量表,漏一项就可能把服务暴露在公网。 这篇做check_env.py:对照官方变量检查登录保护、弱口令、TOOL_FUNCTION_*沙箱、CORS、SSL、默认 session secret 等。
先看清成品
- ok/.env 通过关键项;bad/.env 检出 5 个必须修复项(无登录、弱口令、TOOL_FUNCTION 放开、puppeteer、ALLOW_BUILTIN 叠加)。
- 另有默认 session secret / CORS=* 等建议项。
- 3 项 pytest 通过。
合规提醒:用自己的 Flowise 实例与模型 API Key,不共享账号;密钥放环境变量或官方凭据管理,不要写进导出的 chatflow JSON。只处理你有权处理的数据;生产环境务必开启登录保护。费用以各模型供应商官网为准,本文不写价格。
运行说明:我没有安装完整的 Flowise 服务(需要 Node.js 全局包或 Docker)。真实运行过的是:导出 JSON 校验器、Prediction 客户端对 mock 服务的请求、.env 检查脚本,以及 pytest(2026-10-23,UTC+8)。Flowise 服务端行为全部来自官方文档。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
check_env.py |
环境检查 |
ok/.env |
相对稳妥示例 |
bad/.env |
危险示例 |
test_91c.py |
3 项测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.11+,pip install pytest |
| Flowise(可选) | 官方:Node 18.15+/20+,npx flowise start;或 Docker |
| API Key | 在 Flowise 控制台创建,或使用本文 mock |
关键事实
| 事实 | 来源 |
|---|---|
生产应设置登录:旧版 FLOWISE_USERNAME/FLOWISE_PASSWORD,或新版 JWT 相关密钥 |
官方 Environment Variables |
TOOL_FUNCTION_BUILTIN_DEP / TOOL_FUNCTION_EXTERNAL_DEP 控制自定义工具能 import 什么;官方 .env.example 警告不要随意加 puppeteer/playwright |
同上 |
FLOWISE_SECRETKEY_OVERWRITE 丢失会导致已加密凭据无法解密 |
同上 |
EXPRESS_SESSION_SECRET 默认值不安全,应更换 |
同上 |
| 数据库可用 SQLite(默认)或 Postgres/MySQL;公网建议开 SSL | 官方 Self-hosting / Env |
文件结构
91-flowise-C-src/
├── check_env.py
├── ok/.env
├── bad/.env
└── test_91c.py
第 1 步:检查脚本
#!/usr/bin/env python3
"""Check a Flowise .env for common production footguns (from official env docs)."""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
BOOL_TRUE = {"1", "true", "yes", "on"}
def parse_env(text: str) -> dict[str, str]:
out: dict[str, str] = {}
for line in text.splitlines():
s = line.strip()
if not s or s.startswith("#"):
continue
if "=" not in s:
continue
k, v = s.split("=", 1)
out[k.strip()] = v.strip().strip('"').strip("'")
return out
def check(env: dict[str, str]) -> list[str]:
errs: list[str] = []
warns: list[str] = []
# Auth: deprecated username/password OR modern JWT secrets should be set for public deploy.
has_user = bool(env.get("FLOWISE_USERNAME") and env.get("FLOWISE_PASSWORD"))
has_jwt = bool(env.get("JWT_AUTH_TOKEN_SECRET") and env.get("TOKEN_HASH_SECRET"))
if not has_user and not has_jwt:
errs.append("未设置登录保护:请设置 FLOWISE_USERNAME/FLOWISE_PASSWORD(旧)或 JWT_AUTH_TOKEN_SECRET + TOKEN_HASH_SECRET(新)")
if env.get("FLOWISE_PASSWORD") in {"1234", "password", "admin", "flowise"}:
errs.append("FLOWISE_PASSWORD 是常见弱口令")
if env.get("JWT_AUTH_TOKEN_SECRET") in {"AUDIENCE", "ISSUER", "flowise", "secret", "changeme"}:
errs.append("JWT_AUTH_TOKEN_SECRET 仍是默认/弱值")
if env.get("EXPRESS_SESSION_SECRET") in {"flowise", "secret", None} or env.get("EXPRESS_SESSION_SECRET") == "flowise":
warns.append("EXPRESS_SESSION_SECRET 仍是默认值 flowise(官方建议改掉)")
# Dangerous tool deps
if env.get("TOOL_FUNCTION_BUILTIN_DEP", "").strip() == "*":
errs.append("TOOL_FUNCTION_BUILTIN_DEP=* 允许全部内置模块,生产环境风险高")
if "puppeteer" in env.get("TOOL_FUNCTION_EXTERNAL_DEP", "").lower() or "playwright" in env.get(
"TOOL_FUNCTION_EXTERNAL_DEP", ""
).lower():
errs.append("TOOL_FUNCTION_* 中出现 puppeteer/playwright(官方 .env.example 明确警告)")
if env.get("ALLOW_BUILTIN_DEP", "").lower() in BOOL_TRUE and env.get("TOOL_FUNCTION_BUILTIN_DEP") == "*":
errs.append("ALLOW_BUILTIN_DEP=true 且 BUILTIN_DEP=* 叠加,沙箱几乎失效")
# Encryption key overwrite recommended for stable decrypt
if not env.get("FLOWISE_SECRETKEY_OVERWRITE"):
warns.append("未设置 FLOWISE_SECRETKEY_OVERWRITE:加密密钥丢失后凭据无法解密(官方说明)")
# CORS wide open
if env.get("CORS_ORIGINS", "").strip() == "*":
warns.append("CORS_ORIGINS=* 允许任意来源跨域")
# Postgres without SSL
if env.get("DATABASE_TYPE", "").lower() in {"postgres", "postgresdb", "mysql"}:
if env.get("DATABASE_SSL", "false").lower() not in BOOL_TRUE:
warns.append("使用外部数据库但 DATABASE_SSL 未开启")
# Port bind hint via APP_URL
app = env.get("APP_URL", "http://localhost:3000")
if app.startswith("http://") and "localhost" not in app and "127.0.0.1" not in app:
warns.append(f"APP_URL 是明文 HTTP:{app}(公网建议 HTTPS)")
return errs, warns
def main(argv=None) -> int:
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("envfile", type=Path)
args = p.parse_args(argv)
env = parse_env(args.envfile.read_text(encoding="utf-8"))
errs, warns = check(env)
for w in warns:
print(f"! {w}")
if errs:
print(f"✗ {args.envfile.name}: {len(errs)} 个必须修复的问题")
for e in errs:
print(f" - {e}")
return 1
print(f"✓ {args.envfile.name}: 关键检查通过(仍有 {len(warns)} 条建议)")
return 0
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:危险示例
PORT=3000
FLOWISE_PASSWORD=1234
TOOL_FUNCTION_BUILTIN_DEP=*
TOOL_FUNCTION_EXTERNAL_DEP=puppeteer,cheerio
ALLOW_BUILTIN_DEP=true
CORS_ORIGINS=*
APP_URL=http://flowise.example.com
DATABASE_TYPE=postgres
DATABASE_SSL=false
第 3 步:测试
from pathlib import Path
from check_env import check, main, parse_env
ROOT = Path(__file__).parent
def test_ok():
errs, warns = check(parse_env((ROOT / "ok/.env").read_text()))
assert errs == []
def test_bad():
errs, warns = check(parse_env((ROOT / "bad/.env").read_text()))
assert len(errs) >= 3
assert any("弱口令" in e or "登录保护" in e for e in errs)
assert any("puppeteer" in e.lower() or "BUILTIN" in e for e in errs)
def test_cli():
assert main([str(ROOT / "ok/.env")]) == 0
assert main([str(ROOT / "bad/.env")]) == 1
第 4 步:运行
# 2026-10-23 12:37 UTC+8
$ python check_env.py ok/.env
✓ .env: 关键检查通过(仍有 0 条建议)
$ python check_env.py bad/.env
! EXPRESS_SESSION_SECRET 仍是默认值 flowise(官方建议改掉)
! 未设置 FLOWISE_SECRETKEY_OVERWRITE:加密密钥丢失后凭据无法解密(官方说明)
! CORS_ORIGINS=* 允许任意来源跨域
! 使用外部数据库但 DATABASE_SSL 未开启
! APP_URL 是明文 HTTP:http://flowise.example.com(公网建议 HTTPS)
✗ .env: 5 个必须修复的问题
- 未设置登录保护:请设置 FLOWISE_USERNAME/FLOWISE_PASSWORD(旧)或 JWT_AUTH_TOKEN_SECRET + TOKEN_HASH_SECRET(新)
- FLOWISE_PASSWORD 是常见弱口令
- TOOL_FUNCTION_BUILTIN_DEP=* 允许全部内置模块,生产环境风险高
- TOOL_FUNCTION_* 中出现 puppeteer/playwright(官方 .env.example 明确警告)
- ALLOW_BUILTIN_DEP=true 且 BUILTIN_DEP=* 叠加,沙箱几乎失效
$ python -m pytest -q
3 passed
怎么检查结果?
- bad 必须出现「未设置登录保护」和 TOOL_FUNCTION 相关错误。
- ok 可以有建议(!),但不能有必须修复的 ✗。
- 对照官方 Environment Variables 页核对变量名是否仍有效。
调试示例
「开了用户名密码就安全」不够。 bad/.env 同时写了 TOOL_FUNCTION_BUILTIN_DEP=* 和 ALLOW_BUILTIN_DEP=true,自定义 Function 节点几乎能 import 任意内置模块;再配上 puppeteer,等于给聊天流程开了浏览器自动化。官方 .env.example 对 puppeteer/playwright 有明确警告。检查器把这些标成必须修复,而不仅是建议。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 忘记加密密钥 | SECRETKEY_OVERWRITE 丢了 | 凭据需重建;备份该值 |
| 公网能打开 UI | 没开登录 | 设置用户名密码或 JWT |
| CORS 报错 | origins 过严 | 按前端域名白名单,避免 * |
模板规则
- 公网必须登录保护。
- 不要把 TOOL_FUNCTION_* 设成 *。
- 定期跑 check_env.py。
项目资源
| 用途 | 地址 |
|---|---|
| Environment Variables(官方) | https://docs.flowiseai.com/configuration/environment-variables |
| Self-hosting(官方) | https://docs.flowiseai.com/configuration/running-in-production |
| Flowise 文档 | https://docs.flowiseai.com/ |
可视化搭流程很方便,环境变量才是生产的门锁。