默认情况下,Node-RED 编辑器没有访问保护。任何能访问其 IP 地址的人都可以进入编辑器并部署更改。只有在可信网络中运行时,这样的默认设置才合适。本文介绍三部分保护:启用 HTTPS、保护编辑器与 Admin API,以及保护 HTTP 节点和静态内容。
配置在 settings.js 中完成,修改后需要让运行时加载新配置。
启用 HTTPS
在设置文件中提供 https 属性,其值是传给 Node.js HTTPS 服务器的选项对象。常见选项是 PEM 格式的 key 和 cert,可以是字符串或 Buffer;证书可以包含完整证书链。详见 Node.js HTTPS API。
静态证书示例:
https: {
key: require("fs").readFileSync('privkey.pem'),
cert: require("fs").readFileSync('cert.pem')
},
从 Node-RED 1.1 起,https 也可以是返回选项对象的函数,或者返回一个解析为选项对象的 Promise:
https: function() {
return new Promise((resolve, reject) => {
var key, cert;
// Do some work to obtain valid certificates
// ...
resolve({
key: key,
cert: cert
});
});
}
这只是获取证书的结构示例,必须补上实际读取或获取证书的逻辑。
刷新证书
从 Node-RED 1.1 起,配合 Node.js 11 或以上版本,可以不重启运行时而刷新 HTTPS 证书。https 必须是函数,并设置 httpsRefreshInterval,单位为小时。运行时定期重新调用该函数。若证书暂时不需要更新,可以返回 undefined 或 null,继续使用现有证书。
保护编辑器和 Admin API
adminAuth 为编辑器和 Admin API 启用身份验证。它支持用户名/密码以及 Passport 策略。
用户名和密码
adminAuth: {
type: "credentials",
users: [{
username: "admin",
password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
permissions: "*"
}, {
username: "george",
password: "$2b$08$wuAqPiKJlVN27eF5qJp.RuQYuy6ZYONW7a/UWYxDTtwKFCdB8F19y",
permissions: "read"
}]
},
users 是允许访问的用户数组。密码使用 bcrypt 哈希,不能直接填明文。示例中 admin 的密码是 password,仅用于演示,部署时应换成自己的强密码及其哈希。permissions: "*" 允许完整读写;"read" 只允许查看。
旧的 httpAdminAuth 设置已经弃用,不应再用于新配置。
生成密码哈希
Node-RED 1.1 起可以执行:
node-red admin hash-pw
较早版本可安装并使用 node-red-admin 的命令:
node-red-admin hash-pw
也可直接调用 bcryptjs:
node -e "console.log(require('bcryptjs').hashSync(process.argv[1], 8));" your-password-here
OAuth/OpenID 等外部身份验证
可以使用 Passport 策略,包括 Twitter、GitHub 等。所需策略模块必须安装在 Node-RED 能加载到的位置。以下是 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是策略名。strategy是策略实现类。label是登录按钮文字,icon是 Font Awesome 图标类。options直接传给策略构造函数,具体字段由策略决定。verify完成身份验证后调用done,第二个参数提供用户档案;运行时需要从中取得用户名,再确定权限。
autoLogin: true 可以使登录页自动转向身份提供者。回调地址应为编辑器地址加 /auth/strategy/callback。默认使用 GET;需要 POST 回调的策略可设置 callbackMethod: "POST"。
匿名默认权限
可选的 default 指定未登录用户的权限。例如允许匿名读取,登录用户按用户列表获得权限:
adminAuth: {
type: "credentials",
users: [
// list of users
],
default: {
permissions: "read"
}
}
权限
在 Node-RED 0.14 之前,只支持 "*" 与 "read"。从 0.14 起,权限可以是字符串或数组,并可以对应 Admin API 的具体操作,例如 flows.read、flows.write。具体 API 所需权限见 Admin API。
令牌有效期
访问令牌默认有效七天,不会自动续期。sessionExpiryTime 以秒为单位覆盖该期限;例如设为 86400 表示一天。
自定义用户身份验证
可以把用户查询与验证交给外部系统。在 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 users() above
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" });
});
}
};
users() 负责根据用户名返回用户对象或 null;authenticate() 验证用户名/密码组合;可选 default() 返回匿名用户或 null。valid 是原文中的占位条件,需要自行实现验证逻辑,示例并非可直接运行的身份验证模块。设置文件引用该模块:
adminAuth: require("./user-authentication")
自定义令牌验证
从 Node-RED 1.1 起,可自定义访问令牌验证。这适用于外部 OAuth 令牌、自动化访问,或把编辑器嵌入已经登录的应用。
在 adminAuth 中提供 tokens 函数。当令牌不属于 Node-RED 自身发行的令牌时,运行时调用该函数。函数应返回 Promise,解析为包含 username、permissions 的用户对象,或在令牌无效时解析为 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 to indicate this user does not exist
resolve(null);
}
});
}
}
上述 valid 同样需要接入真实令牌验证。若请求头是 Authorization: Bearer ...,只传入令牌部分;采用其他 Authorization 格式时,传入完整头值。tokenHeader 可指定别的头,例如 "x-my-custom-token"。
在编辑器中传入令牌
在编辑器 URL 后添加 ?access_token=<ACCESS_TOKEN> 可以预先提供令牌。编辑器会将它保存到本地,供后续请求使用。
保护 HTTP 节点和静态内容
httpNodeAuth 为 HTTP In 节点提供 HTTP Basic 身份验证:
httpNodeAuth: {
user: "user",
pass: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."
},
pass 是 bcrypt 哈希。httpStaticAuth 以同样方式保护 httpStatic 提供的静态内容。旧 MD5 哈希仍为兼容性而支持,但不推荐使用。
自定义 HTTP 中间件
httpNodeMiddleware 用于 HTTP In 节点,Node-RED 1.1 起的 httpAdminMiddleware 用于编辑器和 Admin API。它们是 Express 中间件,可做额外验证或限制。
例如使用 express-rate-limit 限制请求频率,减少内存耗尽风险。先在 ~/.node-red/ 安装所需模块,再配置:
// Run `npm install express-rate-limit` on `~/.node-red/` directory in advance
var rateLimit = require("express-rate-limit");
module.exports = {
httpNodeMiddleware: rateLimit({
windowMs: 1000,
max: 10
})
}
这个示例每秒允许最多 10 次请求,超过限制时中间件返回默认的 Too Many Requests 响应。模块接口须与所安装版本匹配。
编辑器侧也可以设置响应头,例如限制同源页面嵌入:
httpAdminMiddleware: function(req, res, next) {
res.set('X-Frame-Options', 'sameorigin');
next();
},
也可以在这些中间件中接入应用所需的其他验证逻辑。
原文:Securing Node-RED。Node-RED 项目及贡献者;中文翻译。网站源码采用 Apache License 2.0,原版权含 © 2016 JS Foundation and other contributors、© 2013, 2016 IBM Corp.;许可、版权及修改记录应随转载保留。示例账户、哈希和令牌均来自公开文档,不代表可用的私有凭据。











暂无评论内容