mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add TLS node and update MQTT/HTTP nodes to use it
This commit is contained in:
parent
b744491dd2
commit
1e2521c37a
73
nodes/core/io/05-tls.html
Normal file
73
nodes/core/io/05-tls.html
Normal file
@ -0,0 +1,73 @@
|
||||
<!--
|
||||
Copyright 2016 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="tls-config">
|
||||
<div class="form-row">
|
||||
<label style="width: 120px;" for="node-config-input-cert"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.cert"></span></label>
|
||||
<input style="width: 60%;" type="text" id="node-config-input-cert" data-i18n="[placeholder]tls.placeholder.cert">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label style="width: 120px;" for="node-config-input-key"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.key"></span></label>
|
||||
<input style="width: 60%;" type="text" id="node-config-input-key" data-i18n="[placeholder]tls.placeholder.key">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label style="width: 120px;" for="node-config-input-ca"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.ca"></span></label>
|
||||
<input style="width: 60%;" type="text" id="node-config-input-ca" data-i18n="[placeholder]tls.placeholder.ca">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-config-input-verifyservercert" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-config-input-verifyservercert" style="width: 70%;" data-i18n="tls.label.verify-server-cert"></label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label style="width: 120px;" for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input style="width: 60%;" type="text" id="node-config-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="tls-config">
|
||||
<p>Configuration options for TLS connections.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('tls-config',{
|
||||
category: 'config',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
cert: {value:"", validate: function(v) {
|
||||
var currentKey = $("#node-config-input-key").val();
|
||||
if (currentKey === undefined) {
|
||||
currentKey = this.key;
|
||||
}
|
||||
return currentKey === '' || v != '';
|
||||
}},
|
||||
key: {value:"", validate: function(v) {
|
||||
var currentCert = $("#node-config-input-cert").val();
|
||||
if (currentCert === undefined) {
|
||||
currentCert = this.cert;
|
||||
}
|
||||
return currentCert === '' || v != '';
|
||||
}},
|
||||
ca: {value:""},
|
||||
verifyservercert: {value: true}
|
||||
},
|
||||
label: function() {
|
||||
return this.name || this._("tls.tls");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
69
nodes/core/io/05-tls.js
Normal file
69
nodes/core/io/05-tls.js
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var fs = require('fs');
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
|
||||
function TLSConfig(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.valid = true;
|
||||
var certPath = n.cert.trim();
|
||||
var keyPath = n.key.trim();
|
||||
var caPath = n.ca.trim();
|
||||
|
||||
if ( (certPath.length > 0) !== (keyPath.length > 0)) {
|
||||
this.valid = false;
|
||||
this.error(RED._("tls.error.missing-file"));
|
||||
return;
|
||||
}
|
||||
this.verifyservercert = n.verifyservercert;
|
||||
|
||||
try {
|
||||
if (certPath) {
|
||||
this.cert = fs.readFileSync(certPath);
|
||||
}
|
||||
if (keyPath) {
|
||||
this.key = fs.readFileSync(keyPath);
|
||||
}
|
||||
if (caPath) {
|
||||
this.ca = fs.readFileSync(caPath);
|
||||
}
|
||||
} catch(err) {
|
||||
this.valid = false;
|
||||
this.error(err.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("tls-config",TLSConfig);
|
||||
|
||||
TLSConfig.prototype.addTLSOptions = function(opts) {
|
||||
if (this.valid) {
|
||||
if (this.key) {
|
||||
opts.key = this.key;
|
||||
}
|
||||
if (this.cert) {
|
||||
opts.cert = this.cert;
|
||||
}
|
||||
if (this.ca) {
|
||||
opts.ca = this.ca;
|
||||
}
|
||||
opts.rejectUnauthorized = this.verifyservercert;
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
}
|
@ -150,11 +150,17 @@
|
||||
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.label.port"></span></label>
|
||||
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:45px">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-config-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-config-input-usetls" style="width: auto" data-i18n="mqtt.label.use-tls"></label>
|
||||
<div id="node-config-row-tls" class="hide">
|
||||
<label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-config-input-tls"><span data-i18n="mqtt.label.tls-config"></span></label><input style="width: 300px;" type="text" id="node-config-input-tls">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-clientid"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.clientid"></span></label>
|
||||
<input type="text" id="node-config-input-clientid" data-i18n="[placeholder]mqtt.placeholder.clientid">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-keepalive" style="width: auto"><i class="fa fa-clock-o"></i> <span data-i18n="mqtt.label.keepalive"></span></label>
|
||||
<input type="text" id="node-config-input-keepalive" style="width: 50px">
|
||||
@ -175,14 +181,6 @@
|
||||
<label for="node-config-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
|
||||
<input type="password" id="node-config-input-password">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-config-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-config-input-usetls" style="width: 70%;" data-i18n="mqtt.label.use-tls"></label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-config-input-verifyservercert" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-config-input-verifyservercert" style="width: 70%;" data-i18n="mqtt.label.verify-server-cert"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mqtt-broker-tab-birth" style="display:none">
|
||||
<div class="form-row">
|
||||
@ -240,6 +238,7 @@
|
||||
defaults: {
|
||||
broker: {value:"",required:true},
|
||||
port: {value:1883,required:true,validate:RED.validators.number()},
|
||||
tls: {type:"tls-config",required: false},
|
||||
clientid: { value:"", validate: function(v) {
|
||||
if ($("#node-config-input-clientid").length) {
|
||||
// Currently editing the node
|
||||
@ -303,10 +302,6 @@
|
||||
this.usetls = false;
|
||||
$("#node-config-input-usetls").prop("checked",false);
|
||||
}
|
||||
if (typeof this.verifyservercert === 'undefined'){
|
||||
this.verifyservercert = true;
|
||||
$("#node-config-input-verifyservercert").prop("checked",true);
|
||||
}
|
||||
if (typeof this.compatmode === 'undefined'){
|
||||
this.compatmode = true;
|
||||
$("#node-config-input-compatmode").prop('checked', true);
|
||||
@ -326,11 +321,9 @@
|
||||
|
||||
function updateTLSOptions() {
|
||||
if ($("#node-config-input-usetls").is(':checked')) {
|
||||
$("#node-config-input-verifyservercert").prop("disabled", false);
|
||||
$("#node-config-input-verifyservercert").next().css("color","");
|
||||
$("#node-config-row-tls").show();
|
||||
} else {
|
||||
$("#node-config-input-verifyservercert").prop("disabled", true);
|
||||
$("#node-config-input-verifyservercert").next().css("color","#aaa");
|
||||
$("#node-config-row-tls").hide();
|
||||
}
|
||||
}
|
||||
updateTLSOptions();
|
||||
@ -350,6 +343,11 @@
|
||||
$("#node-config-input-cleansession").on("click",function() {
|
||||
updateClientId();
|
||||
});
|
||||
},
|
||||
oneditsave: function() {
|
||||
if (!$("#node-config-input-usetls").is(':checked')) {
|
||||
$("#node-config-input-tls").val("");
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -114,8 +114,18 @@ module.exports = function(RED) {
|
||||
this.options.protocolId = 'MQIsdp';
|
||||
this.options.protocolVersion = 3;
|
||||
}
|
||||
|
||||
this.options.rejectUnauthorized = (this.verifyservercert == "true" || this.verifyservercert === true)
|
||||
if (this.usetls && n.tls) {
|
||||
var tlsNode = RED.nodes.getNode(n.tls);
|
||||
if (tlsNode) {
|
||||
tlsNode.addTLSOptions(this.options);
|
||||
}
|
||||
}
|
||||
// If there's no rejectUnauthorized already, then this could be an
|
||||
// old config where this option was provided on the broker node and
|
||||
// not the tls node
|
||||
if (typeof this.options.rejectUnauthorized === 'undefined') {
|
||||
this.options.rejectUnauthorized = (this.verifyservercert == "true" || this.verifyservercert === true);
|
||||
}
|
||||
|
||||
if (n.willTopic) {
|
||||
this.options.will = {
|
||||
@ -284,6 +294,9 @@ module.exports = function(RED) {
|
||||
done();
|
||||
});
|
||||
this.client.end();
|
||||
} if (this.connecting) {
|
||||
node.client.end();
|
||||
done();
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Copyright 2013, 2015 IBM Corp.
|
||||
Copyright 2013, 2016 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -29,19 +29,31 @@
|
||||
<label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="httpin.label.url"></span></label>
|
||||
<input type="text" id="node-input-url" placeholder="http://">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-usetls" style="width: auto" data-i18n="httpin.use-tls"></label>
|
||||
<div id="node-row-tls" class="hide">
|
||||
<label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-useAuth" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-useAuth" style="width: 70%;"><span data-i18n="httpin.basicauth"></span></label>
|
||||
<div style="margin-left: 20px" class="node-input-useAuth-row hide">
|
||||
<div class="form-row">
|
||||
<label for="node-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
|
||||
<input type="text" id="node-input-user">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
|
||||
<input type="password" id="node-input-password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row node-input-useAuth-row">
|
||||
<label for="node-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
|
||||
<input type="text" id="node-input-user">
|
||||
</div>
|
||||
<div class="form-row node-input-useAuth-row">
|
||||
<label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
|
||||
<input type="password" id="node-input-password">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-ret"><i class="fa fa-arrow-left"></i> <span data-i18n="httpin.label.return"></span></label>
|
||||
<select type="text" id="node-input-ret" style="width:72%;">
|
||||
@ -91,8 +103,7 @@
|
||||
method:{value:"GET"},
|
||||
ret: {value:"txt"},
|
||||
url:{value:""},
|
||||
//user -> credentials
|
||||
//pass -> credentials
|
||||
tls: {type:"tls-config",required: false}
|
||||
},
|
||||
credentials: {
|
||||
user: {type:"text"},
|
||||
@ -108,14 +119,6 @@
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
if (this.credentials.user || this.credentials.has_password) {
|
||||
$('#node-input-useAuth').prop('checked', true);
|
||||
$(".node-input-useAuth-row").show();
|
||||
} else {
|
||||
$('#node-input-useAuth').prop('checked', false);
|
||||
$(".node-input-useAuth-row").hide();
|
||||
}
|
||||
|
||||
$("#node-input-useAuth").change(function() {
|
||||
if ($(this).is(":checked")) {
|
||||
$(".node-input-useAuth-row").show();
|
||||
@ -125,7 +128,29 @@
|
||||
$('#node-input-password').val('');
|
||||
}
|
||||
});
|
||||
if (this.credentials.user || this.credentials.has_password) {
|
||||
$('#node-input-useAuth').prop('checked', true);
|
||||
} else {
|
||||
$('#node-input-useAuth').prop('checked', false);
|
||||
}
|
||||
$("#node-input-useAuth").change();
|
||||
|
||||
function updateTLSOptions() {
|
||||
if ($("#node-input-usetls").is(':checked')) {
|
||||
$("#node-row-tls").show();
|
||||
} else {
|
||||
$("#node-row-tls").hide();
|
||||
}
|
||||
}
|
||||
if (this.tls) {
|
||||
$('#node-input-usetls').prop('checked', true);
|
||||
} else {
|
||||
$('#node-input-usetls').prop('checked', false);
|
||||
}
|
||||
updateTLSOptions();
|
||||
$("#node-input-usetls").on("click",function() {
|
||||
updateTLSOptions();
|
||||
});
|
||||
$("#node-input-ret").change(function() {
|
||||
if ($("#node-input-ret").val() === "obj") {
|
||||
$("#tip-json").show();
|
||||
@ -133,6 +158,11 @@
|
||||
$("#tip-json").hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
oneditsave: function() {
|
||||
if (!$("#node-input-usetls").is(':checked')) {
|
||||
$("#node-input-tls").val("_ADD_");
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -24,13 +24,16 @@ module.exports = function(RED) {
|
||||
|
||||
function HTTPRequest(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
var nodeUrl = n.url;
|
||||
var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
|
||||
var nodeMethod = n.method || "GET";
|
||||
if (n.tls) {
|
||||
var tlsNode = RED.nodes.getNode(n.tls);
|
||||
}
|
||||
this.ret = n.ret || "txt";
|
||||
if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; }
|
||||
else { this.reqTimeout = 120000; }
|
||||
var node = this;
|
||||
|
||||
var prox, noprox;
|
||||
if (process.env.http_proxy != null) { prox = process.env.http_proxy; }
|
||||
@ -54,7 +57,11 @@ module.exports = function(RED) {
|
||||
}
|
||||
// url must start http:// or https:// so assume http:// if not set
|
||||
if (!((url.indexOf("http://") === 0) || (url.indexOf("https://") === 0))) {
|
||||
url = "http://"+url;
|
||||
if (tlsNode) {
|
||||
url = "https://"+url;
|
||||
} else {
|
||||
url = "http://"+url;
|
||||
}
|
||||
}
|
||||
|
||||
var method = nodeMethod.toUpperCase() || "GET";
|
||||
@ -133,6 +140,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
else { node.warn("Bad proxy url: "+process.env.http_proxy); }
|
||||
}
|
||||
if (tlsNode) {
|
||||
tlsNode.addTLSOptions(opts);
|
||||
}
|
||||
var req = ((/^https/.test(urltotest))?https:http).request(opts,function(res) {
|
||||
(node.ret === "bin") ? res.setEncoding('binary') : res.setEncoding('utf8');
|
||||
msg.statusCode = res.statusCode;
|
||||
@ -172,6 +182,7 @@ module.exports = function(RED) {
|
||||
req.abort();
|
||||
});
|
||||
req.on('error',function(err) {
|
||||
node.error(err,msg);
|
||||
msg.payload = err.toString() + " : " + url;
|
||||
msg.statusCode = err.code;
|
||||
node.send(msg);
|
||||
|
@ -123,6 +123,23 @@
|
||||
"event": "Event name"
|
||||
}
|
||||
},
|
||||
"tls": {
|
||||
"tls": "TLS configuration",
|
||||
"label": {
|
||||
"cert": "Certificate",
|
||||
"key": "Private Key",
|
||||
"ca": "CA Certificate",
|
||||
"verify-server-cert":"Verify server certificate"
|
||||
},
|
||||
"placeholder": {
|
||||
"cert":"path to certificate (PEM format)",
|
||||
"key":"path to private key (PEM format)",
|
||||
"ca":"path to CA certificate (PEM format)"
|
||||
},
|
||||
"error": {
|
||||
"missing-file": "No certificate/key file provided"
|
||||
}
|
||||
},
|
||||
"exec": {
|
||||
"spawnerr": "Spawn command must be just the command - no spaces or extra parameters",
|
||||
"badstdout": "Bad STDOUT",
|
||||
@ -240,6 +257,7 @@
|
||||
"keepalive": "Keep alive time (s)",
|
||||
"cleansession": "Use clean session",
|
||||
"use-tls": "Enable secure (SSL/TLS) connection",
|
||||
"tls-config":"TLS Configuration",
|
||||
"verify-server-cert":"Verify server certificate",
|
||||
"compatmode": "Use legacy MQTT 3.1 support"
|
||||
},
|
||||
@ -279,7 +297,9 @@
|
||||
"return": "Return"
|
||||
},
|
||||
"setby": "- set by msg.method -",
|
||||
"basicauth": "Use basic authentication?",
|
||||
"basicauth": "Use basic authentication",
|
||||
"use-tls": "Enable secure (SSL/TLS) connection",
|
||||
"tls-config":"TLS Configuration",
|
||||
"utf8": "a UTF-8 string",
|
||||
"binary": "a binary buffer",
|
||||
"json": "a parsed JSON object",
|
||||
|
Loading…
Reference in New Issue
Block a user