保护 Node-RED

默认情况下,Node-RED 编辑器不安全——任何能够访问其 IP 地址的人都可以访问编辑器并部署更改。

这仅适用于您在受信任网络上运行的情况。

本指南介绍了如何保护 Node-RED。安全分为三个部分:

启用 HTTPS 访问

要通过 HTTPS(而非默认的 HTTP)访问 Node-RED 编辑器,您可以使用设置文件中的 https 配置选项。

从 Node-RED 1.1.0 开始,https 选项可以是一组静态配置选项,也可以是返回选项的函数。

完整的选项集在此处有文档说明

选项至少应包括:

  • key - PEM 格式的私钥,以 StringBuffer 提供
  • cert - PEM 格式的证书链,以 StringBuffer 提供
有关如何生成证书的指南,您可以参考本指南

默认的 Node-RED 设置文件包含一个被注释掉的 https 部分,可用于从本地文件加载证书。

https: {
    key: require("fs").readFileSync('privkey.pem'),
    cert: require("fs").readFileSync('cert.pem')
},

自 Node-RED 1.1.0 起

如果 https 属性是一个函数,它可以用于返回选项对象。该函数可以选择返回一个 Promise,该 Promise 将解析为选项对象,从而允许其异步完成。

https: function() {
    return new Promise((resolve, reject) => {
        var key, cert;
        // Do some work to obtain valid certificates
        // ...
        resolve({
            key: key
            cert: cert
        })
    });
}

刷新 HTTPS 证书

自 Node-RED 1.1.0 起

可以将 Node-RED 配置为定期刷新其 HTTPS 证书,而无需重新启动 Node-RED。为此:

  1. 必须使用 Node.js 11 或更高版本
  2. https 设置必须是一个可以调用以获取更新证书的函数
  3. httpsRefreshInterval 设置为 Node-RED 应多久(以小时为单位)调用 https 函数以获取更新详细信息。

https 函数应确定当前证书是否将在下一个 httpsRefreshInterval 期间内过期,如果是,则生成一组新证书。如果不需要更新,函数可以返回 undefinednull

编辑器和管理 API 安全

编辑器和管理 API 支持两种类型的身份验证:

  • 基于用户名/密码凭证的身份验证
  • 针对任何 OAuth/OpenID 提供商(如 Twitter 或 GitHub)的身份验证

基于用户名/密码的身份验证

要在编辑器和管理 API 上启用用户身份验证,请取消注释设置文件中的 adminAuth 属性

adminAuth: {
    type: "credentials",
    users: [
        {
            username: "admin",
            password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
            permissions: "*"
        },
        {
            username: "george",
            password: "$2b$08$wuAqPiKJlVN27eF5qJp.RuQYuy6ZYONW7a/UWYxDTtwKFCdB8F19y",
            permissions: "read"
        }
    ]
}

users 属性是一个用户对象数组。这允许您定义多个用户,每个用户可以拥有不同的权限。

上面的示例配置定义了两个用户。一个名为 admin,拥有编辑器内的所有权限,密码为 password。另一个名为 george,拥有只读访问权限。

请注意,密码使用 bcrypt 算法安全地哈希处理。

注意:在 Node-RED 之前的版本中,设置 httpAdminAuth 可用于在编辑器上启用 HTTP 基本身份验证。此选项已弃用,不应使用。

生成密码哈希

如果您使用的是 Node-RED 1.1.0 或更高版本,您可以使用以下命令:

node-red admin hash-pw

对于旧版本的 Node-RED,您可以:

  • 安装独立的node-red-admin 命令行工具并使用以下命令:

     node-red-admin hash-pw
    
  • 或者,找到 Node-RED 的安装目录并使用以下命令:

     node -e "console.log(require('bcryptjs').hashSync(process.argv[1], 8));" your-password-here
    

在所有情况下,您都将获得密码的哈希版本,然后可以将其粘贴到设置文件中。

基于 OAuth/OpenID 的身份验证

要使用外部身份验证源,Node-RED 可以利用 Passport 提供的各种策略。

Node-RED 身份验证模块适用于 TwitterGitHub。它们封装了一些特定于策略的详细信息,使其更易于使用。但它们也可以用作与其他类似策略进行身份验证的模板。

以下示例展示了如何在使用我们提供的身份验证模块的情况下配置 Twitter 身份验证。

adminAuth: {
    type:"strategy",
    strategy: {
        name: "twitter",
        label: 'Sign in with Twitter',
        icon:"fa-twitter",
        strategy: require("passport-twitter").Strategy,
        options: {
            consumerKey: TWITTER_APP_CONSUMER_KEY,
            consumerSecret: TWITTER_APP_CONSUMER_SECRET,
            callbackURL: "http://example.com/auth/strategy/callback",
            verify: function(token, tokenSecret, profile, done) {
                done(null, profile);
            }
        },
    },
    users: [
       { username: "knolleary",permissions: ["*"]}
    ]
}

strategy 属性接受以下选项:

  • name - 正在使用的 Passport 策略的名称
  • strategy - Passport 策略模块
  • label/icon - 在登录页面上使用。icon 可以是任何 FontAwesome 图标名称。
  • options - 创建 Passport 策略时传递给它的选项对象。请参阅策略自己的文档以了解其要求。有关 callbackURLcallbackMethod 的说明,请参阅下文。
  • verify - 策略使用的验证函数。如果用户有效,它必须将用户配置文件作为第二个参数调用 done。预计该配置文件具有 username 属性,用于与有效用户列表进行对照。Passport 尝试标准化用户配置文件对象,因此大多数策略都提供此属性。
  • autoLogin - 布尔值,当为 true 时,将自动重定向到身份验证提供商,而不是要求用户单击按钮。

策略使用的 callbackURL 是身份验证提供商在身份验证尝试后将重定向到的地址。它必须是您的 Node-RED 编辑器的 URL,并在路径中添加 /auth/strategy/callback。例如,如果您通过 https://:1880 访问编辑器,您将使用 https://:1880/auth/strategy/callback

默认情况下,callbackURL 将侦听 GET 请求。要改用 POST 请求,请将 callbackMethod 设置为 POST

设置默认用户

上面的示例配置将阻止任何人访问编辑器,除非他们登录。

在某些情况下,允许所有人一定程度的访问是可取的。通常,这将是授予编辑器只读访问权限。为此,可以将 default 属性添加到 adminAuth 设置中以定义默认用户:

adminAuth: {
    type: "credentials",
    users: [ /* list of users */ ],
    default: {
        permissions: "read"
    }
}

用户权限

在 Node-RED 0.14 之前,用户可以拥有以下两种权限之一:

  • * - 完全访问
  • read - 只读访问

从 Node-RED 0.14 开始,权限可以更加细粒度,为了支持这一点,该属性可以是像以前一样的单个字符串,也可以是包含多个权限的数组。

管理 API 的每个方法都定义了访问它所需的权限级别。权限模型是基于资源的。例如,要获取当前流程配置,用户将需要 flows.read 权限。但要更新流程,他们将需要 flows.write 权限。

令牌过期

默认情况下,访问令牌在创建后 7 天过期。我们目前不支持刷新令牌以延长此期限。

可以通过设置 adminAuth 设置的 sessionExpiryTime 属性来自定义过期时间。这定义了令牌的有效时间(以秒为单位)。例如,将令牌设置为 1 天后过期:

adminAuth: {
    sessionExpiryTime: 86400,
    ...
}

访问管理 API

设置 adminAuth 属性后,管理 API 文档描述了如何访问 API。

自定义用户身份验证

除了将用户硬编码到设置文件中,还可以插入自定义代码来验证用户。这使得与现有身份验证方案集成成为可能。

以下示例展示了如何使用外部模块来提供自定义身份验证代码。

  • 将以下内容保存到名为 <node-red>/user-authentication.js 的文件中:
module.exports = {
   type: "credentials",
   users: function(username) {
       return new Promise(function(resolve) {
           // Do whatever work is needed to check username is a valid
           // user.
           if (valid) {
               // Resolve with the user object. It must contain
               // properties 'username' and 'permissions'
               var user = { username: "admin", permissions: "*" };
               resolve(user);
           } else {
               // Resolve with null to indicate this user does not exist
               resolve(null);
           }
       });
   },
   authenticate: function(username,password) {
       return new Promise(function(resolve) {
           // Do whatever work is needed to validate the username/password
           // combination.
           if (valid) {
               // Resolve with the user object. Equivalent to having
               // called users(username);
               var user = { username: "admin", permissions: "*" };
               resolve(user);
           } else {
               // Resolve with null to indicate the username/password pair
               // were not valid.
               resolve(null);
           }
       });
   },
   default: function() {
       return new Promise(function(resolve) {
           // Resolve with the user object for the default user.
           // If no default user exists, resolve with null.
           resolve({anonymous: true, permissions:"read"});
       });
   }
}
  • 将 settings.js 中的 adminAuth 属性设置为加载此模块:
adminAuth: require("./user-authentication")

自定义身份验证令牌

自 Node-RED 1.1.0 起

在某些情况下,您可能需要使用自己的身份验证令牌,而不是使用 Node-RED 生成的令牌。例如:

  • 您希望使用基于 OAuth 的用户身份验证,但您还需要对管理 API 进行自动化访问,而管理 API 无法执行 OAuth 所需的交互式身份验证步骤。
  • 您希望将 Node-RED 集成到现有系统中,在该系统中用户已经登录,并且您不希望他们在访问编辑器时再次登录。

adminAuth 设置可以包含一个 tokens 函数。如果管理 API 的请求不包含 Node-RED 识别为自己的身份验证令牌,则将调用此函数。它将请求中提供的令牌传递给函数,并应返回一个 Promise,该 Promise 将解析为已验证的用户,如果令牌无效则解析为 null

adminAuth: {
    ...
    tokens: function(token) {
        return new Promise(function(resolve, reject) {
            // Do whatever work is needed to check token is valid
            if (valid) {
                // Resolve with the user object. It must contain
                // properties 'username' and 'permissions'
                var user = { username: 'admin', permissions: '*' };
                resolve(user);
            } else {
                // Resolve with null as this user does not exist
                resolve(null);
            }
        });
    },
    ...
}

默认情况下,它将使用 Authorization HTTP 标头,并期望 Bearer 类型令牌——仅将令牌的值传递给函数。如果它不是 Bearer 类型令牌,则 Authorization 标头的完整值(包含类型和值)将传递给函数。

要使用不同的 HTTP 标头,可以使用 tokenHeader 设置来标识要使用的标头:

adminAuth: {
    ...
    tokens: function(token) {
        ...
    },
    tokenHeader: "x-my-custom-token"
}
使用自定义令牌访问编辑器

要使用自定义令牌访问编辑器而无需登录提示,请在 URL 中添加 ?access_token=<ACCESS_TOKEN>。编辑器将在本地存储该令牌并将其用于所有未来的请求。

HTTP 节点安全

HTTP In 节点公开的路由可以使用基本身份验证进行保护。

settings.js 文件中的 httpNodeAuth 属性可用于定义允许访问路由的单个用户名和密码。

httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."},

pass 属性使用与 adminAuth 相同的格式。有关更多信息,请参阅生成密码哈希

通过 httpStaticAuth 属性(使用相同格式)可以保护 httpStatic 属性定义的任何静态内容的访问。

注意:在 Node-RED 之前的版本中,pass 属性预期为 MD5 哈希。这在密码学上是不安全的,因此已被 bcrypt 取代,与 adminAuth 使用的相同。为了向后兼容,仍然支持 MD5 哈希——但不推荐使用它们。

自定义中间件

可以提供自定义 HTTP 中间件,该中间件将添加到所有 HTTP In 节点之前,并且从 Node-RED 1.1.0 开始,将添加到所有管理/编辑器路由之前。

HTTP 输入节点的自定义中间件

对于 HTTP In 节点,中间件作为 httpNodeMiddleware 设置提供。

以下设置是限制 HTTP 输入节点中 HTTP 访问速率的示例。

// Run `npm install express-rate-limit` on `~/.node-red/` directory in advance
var rateLimit = require("express-rate-limit");
module.exports = {
    httpNodeMiddleware: rateLimit({
        windowMs: 1000, // 1000 milliseconds is set as the window time.
        max: 10 // limit access rate to 10 requests/second
    })
}

使用此配置,Node-RED 进程可以避免内存耗尽,即使从 http-in 节点开始的流程需要时间处理。达到限制时,端点将返回默认消息:“请求过多,请稍后重试。”

管理 API 的自定义中间件

对于管理/编辑器路由,中间件作为 httpAdminMiddleware 设置提供。

例如,以下中间件可用于在所有管理/编辑器请求上设置 X-Frame-Options HTTP 标头。这可用于控制编辑器如何嵌入到其他页面中。

httpAdminMiddleware: function(req, res, next) {
    // Set the X-Frame-Options header to limit where the editor
    // can be embedded
    res.set('X-Frame-Options', 'sameorigin');
    next();
},

其他可能的用途是为路由添加额外的安全层或请求验证。