别让 Code 节点成为你自动化城堡的“后门”
兄弟们,咱们玩 n8n 的,谁还没在 Code 节点里写过几行 JavaScript 来处理数据?确实好用,灵活度拉满。但你有没有想过,如果这个工作流被别人导入,或者你不小心引入了恶意的 npm 包,这段代码能干啥?
默认情况下,n8n 的 Code 节点(基于 Node.js)几乎拥有和你 n8n 进程一样的权限。这意味着它能读取你服务器上的敏感文件(比如 .env),甚至能扫描你内网的数据库端口。这在生产环境里,简直就是“裸奔”。
今天,N8N大学 就带你把 Code 节点彻底关进“笼子”里。咱们不整虚的,直接上硬核的 Sandbox(沙箱)隔离方案,确保它只能老老实实算数,绝不能越雷池一步。
核心思路:物理隔离 vs 权限限制
在 n8n 官方文档里,关于限制 Code 节点能力的说明其实比较分散。简单来说,有两种路子:
- 粗暴型(推荐): 让 Code 节点运行在一个完全独立的容器里,和主 n8n 服务通过网络通信。这是最安全的,也是 Docker 部署下的最佳实践。
- 精细型: 在同一个容器内,通过操作系统级别的权限控制(User/Group)和内核参数(Seccomp/AppArmor)限制它。
考虑到大多数兄弟都是用 Docker 部署,咱们重点讲第一种,也是最实用的“多容器隔离”方案。
实操:构建安全的 Code 节点执行环境
咱们的目标是:主 n8n 负责调度和 UI,Code 节点跑在另一个受限的容器里。这就需要用到 n8n 的 Execution Mode(执行模式)。
步骤一:理解 n8n 的执行模式
n8n 支持把任务分发给不同的“工人”(Worker)。默认是 main 模式,所有活儿主进程自己干。我们要改成 queue 模式,这样主进程只管排队,具体的代码执行交给 Worker 容器。
这样做的好处是,我们可以给 Worker 容器极其严格的限制,而主 UI 容器保持相对开放的网络权限。
步骤二:编写 Docker Compose 文件
这是最关键的一步。我们需要定义两个服务:一个是主 n8n,一个是专门跑 Code 节点的 Worker。同时,我们要利用 Volume 挂载和 Network 配置来实现隔离。
假设你的 docker-compose.yml 文件如下(关键参数已注释):
version: '3.8'
services:
# 1. 主 n8n 实例(UI & 调度中心)
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
environment:
- N8N_EXECUTIONS_MODE=queue
- N8N_QUEUE_BULL_REDIS_HOST=redis
- N8N_QUEUE_BULL_REDIS_PORT=6379
# ... 其他环境变量
ports:
- "5678:5678"
volumes:
- ./n8n_data:/home/node/.n8n
depends_on:
- redis
# 2. 专门跑 Code 节点的 Worker(沙箱环境)
n8n-worker:
image: docker.n8n.io/n8nio/n8n
restart: always
command: worker
environment:
- N8N_EXECUTIONS_MODE=queue
- N8N_QUEUE_BULL_REDIS_HOST=redis
- N8N_QUEUE_BULL_REDIS_PORT=6379
# 关键隔离:让 Worker 无法访问宿主机文件
# 注意:这里不挂载任何 host 的 volume,除了必要的只读配置(如果有)
volumes:
# 如果你有自定义节点,可以挂载,但要确保来源可信
# - ./custom-nodes:/home/node/.n8n/custom
deploy:
resources:
limits:
memory: 512M # 限制内存,防止恶意代码 OOM 攻击
depends_on:
- redis
# 网络隔离:默认它们在同一个 docker network,但我们可以进一步限制
# 如果 Code 节点不需要访问外网,可以使用 internal_network
# 3. Redis(消息队列,必须)
redis:
image: redis:alpine
restart: always
步骤三:配置严格的网络策略(进阶)
默认 Docker Compose 会创建一个默认网络,两个容器互通。如果你想进一步限制 n8n-worker 访问内网 IP(比如你的 MySQL 数据库),你需要创建一个自定义网络。
做法:
- 创建一个名为
internal的网络,设为internal: true(禁止出站)。 - 主 n8n 挂在默认网络(可出站)。
- Worker 挂在
internal网络。
这样,Worker 容器就连你的数据库都连不上了,只能通过主 n8n 节点去拿数据,或者只处理传入的数据。这在金融、医疗等高敏感场景是必须的。
避坑指南:那些让你头疼的细节
在配置 Sandbox 的过程中,N8N大学 帮你排了几个雷:
1. Node.js 版本不一致导致的报错
n8n 的 Docker 镜像默认使用特定版本的 Node.js。如果你在 Worker 里使用了 require() 引入了二进制插件(比如某些 PDF 处理库),可能会因为容器架构或 Node 版本不匹配导致报错 Error: Cannot find module。
建议: 尽量只使用纯 JS 逻辑,避免在 Code 节点引入复杂的原生模块。如果必须用,请确保 Worker 镜像和主镜像一致,或者基于官方镜像重新构建包含依赖的 Worker 镜像。
2. 权限问题:Permission Denied
虽然我们不建议 Worker 写文件,但有些临时缓存是不可避免的。n8n 容器默认以 node 用户(UID 1000)运行。如果你挂载了宿主机目录且权限不对,就会报 EACCES: permission denied。
建议: 如果必须挂载,使用 chown -R 1000:1000 ./your_dir 赋权。
3. 内存溢出 (OOM)
Code 节点里的死循环或者大数据处理非常吃内存。如果 Worker 崩溃了,整个队列就会堵塞。
建议: 像上面的 compose 文件一样,务必给 Worker 设置 memory_limit。同时,在 n8n 的 Settings -> Usage 里查看执行日志,监控异常。
FAQ:关于 n8n 安全加固的常见疑问
Q1: 如果我用的是 n8n.cloud 或者 SaaS 版本,还需要担心这个问题吗?
A: 不需要。n8n.cloud 已经在底层做好了严格的多租户隔离和沙箱化。但对于自托管(Self-hosted)用户,这是必修课。
Q2: 这种配置会影响性能吗?
A: 会有微小的延迟。因为任务需要通过 Redis 中转,而不是在主进程直接运行。但为了安全性,这点性能损失完全可以忽略不计。而且 Worker 模式支持横向扩展,高并发下性能反而更好。
Q3: 除了 Docker 隔离,还有什么办法限制 Code 节点?
A: 还有一个偏门的办法是使用 n8n-nodes-code-vm(如果社区有维护),或者在启动 n8n 时传递 Node.js 的 --require 参数来 hook 模块加载。但这些方案维护成本高,不如 Docker 隔离来得稳健。
总结与资源
安全是自动化的基石。不要因为图省事,就把 Code 节点的权限开得过大。通过 Redis Queue + Docker Worker 的模式,你可以轻松实现代码执行环境的物理隔离,大大降低“一行代码毁所有”的风险。
我是 N8N大学 的主编,希望这篇硬核指南能帮你把 n8n 部署得更稳、更安全。如果觉得有用,别忘了分享给身边的开发者朋友们!