安全加固:限制 n8n Code 节点访问文件系统与内网 IP (Sandbox 配置)

2026-01-28 20 0

别让 Code 节点成为你自动化城堡的“后门”

兄弟们,咱们玩 n8n 的,谁还没在 Code 节点里写过几行 JavaScript 来处理数据?确实好用,灵活度拉满。但你有没有想过,如果这个工作流被别人导入,或者你不小心引入了恶意的 npm 包,这段代码能干啥?

默认情况下,n8n 的 Code 节点(基于 Node.js)几乎拥有和你 n8n 进程一样的权限。这意味着它能读取你服务器上的敏感文件(比如 .env),甚至能扫描你内网的数据库端口。这在生产环境里,简直就是“裸奔”。

今天,N8N大学 就带你把 Code 节点彻底关进“笼子”里。咱们不整虚的,直接上硬核的 Sandbox(沙箱)隔离方案,确保它只能老老实实算数,绝不能越雷池一步。

核心思路:物理隔离 vs 权限限制

在 n8n 官方文档里,关于限制 Code 节点能力的说明其实比较分散。简单来说,有两种路子:

  1. 粗暴型(推荐): 让 Code 节点运行在一个完全独立的容器里,和主 n8n 服务通过网络通信。这是最安全的,也是 Docker 部署下的最佳实践。
  2. 精细型: 在同一个容器内,通过操作系统级别的权限控制(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 数据库),你需要创建一个自定义网络。

做法:

  1. 创建一个名为 internal 的网络,设为 internal: true(禁止出站)。
  2. 主 n8n 挂在默认网络(可出站)。
  3. 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 部署得更稳、更安全。如果觉得有用,别忘了分享给身边的开发者朋友们!

相关文章

超越 SQLite:为什么生产环境必须连接 PostgreSQL 以及如何配置?
高并发架构:配置 Redis + n8n Worker 模式实现分布式任务处理
解决 OOM 崩溃:优化 n8n 大数据量处理时的内存占用 (Memory Leak) 技巧
多入口触发:如何让一个工作流同时支持 GET/POST 甚至多个不同路径的 Webhook?
控制 n8n 本身:使用 n8n Public API 自动创建、激活或导出工作流
贡献社区:将你开发的 n8n 自定义节点发布到 npm 并在本地安装测试

发布评论