保护Node-RED安全

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

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

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

启用HTTPS访问

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

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

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

至少,选项应包括:

  • key - PEM格式的私钥,以StringBuffer形式提供
  • cert - PEM格式的证书链,提供为StringBuffer
For a guide on how to generate certificates, you can follow 本指南.

默认的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安全性

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

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

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

要在编辑器和Admin 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算法进行安全哈希处理。

Note : in previous releases of Node-RED, the setting httpAdminAuth could be used to enable HTTP Basic Authentication on the editor. This option is deprecated and should not be used.

生成密码哈希值

如果您使用的是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 的认证模块。这些模块封装了特定策略的一些细节,使其更易于使用。同时它们也可以作为模板,用于其他类似策略的认证。

以下示例展示了如何配置以在不使用我们提供的auth模块的情况下对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。例如,如果您通过http://localhost:1880访问编辑器,则应使用http://localhost:1880/auth/strategy/callback

默认情况下,callbackURL会监听GET请求。如需改用POST请求,请将callbackMethod设置为POST

设置默认用户

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

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

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

用户权限

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

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

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

Admin API的每个方法都定义了访问所需的权限级别。 该权限模型基于资源。例如,要获取当前流配置, 用户需要具备flows.read权限。但要更新流配置则需要 flows.write权限。

令牌过期

默认情况下,访问令牌在创建7天后会过期。我们目前不支持通过刷新令牌来延长有效期。

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

adminAuth: {
    sessionExpiryTime: 86400,
    ...
}

访问管理API

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

自定义用户认证

与其在设置文件中硬编码用户信息,也可以插入自定义代码来验证用户身份。这使得与现有认证方案的集成成为可能。

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

  • 将以下内容保存到名为/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函数。当向admin api发出的请求不包含Node-RED可识别的认证令牌时,将调用此函数。该函数会接收请求中提供的令牌,并应返回一个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=。编辑器将在本地存储该令牌,并在所有后续请求中使用它。

HTTP节点安全性

HTTP In节点暴露的路由可以通过基本身份验证进行安全保护。

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

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

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

通过httpStatic属性定义的任何静态内容访问权限都可以使用httpStaticAuth属性进行保护,后者采用相同的格式。

Note : in previous releases of Node-RED, the pass property was expected to be an MD5 hash. This is cryptographically insecure, so has been superseded with bcrypt, as used by adminAuth. For backwards compatibility, MD5 hashes are still supported - but they are not recommended.

自定义中间件

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

http-in节点的自定义中间件

对于HTTP In节点,中间件是通过httpNodeMiddleware设置提供的。

以下设置是一个限制http-in节点中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节点开始的流程需要较长时间处理。 当达到限制时,端点将返回默认消息"请求过多,请稍后再试"。

Admin 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();
},

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