云端部署

用 Flowise 环境检查脚本排查自托管生产配置风险

编写 .env 检查器,发现 Flowise 自托管环境中的弱认证、危险工具依赖、宽松 CORS、默认密钥和数据库 SSL 配置问题。

BINBHO · 图文教程

自托管 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/

可视化搭流程很方便,环境变量才是生产的门锁。