diff --git a/certoperations.js b/certoperations.js index 4dadcfed..ad6514c5 100644 --- a/certoperations.js +++ b/certoperations.js @@ -284,6 +284,26 @@ module.exports.CertificateOperations = function (parent) { return r; } + // Return a text file from a remote HTTPS server + obj.loadTextFile = function (url, tag, func) { + const u = require('url').parse(url); + if (u.protocol == 'https:') { + // Read from HTTPS + const https = require('https'); + https.get(url, function(resp) { + var data = ''; + resp.on('data', function(chunk) { data += chunk; }); + resp.on('end', function () { func(url, data, tag); }); + resp.on('error', function (chunk) { func(url, null, tag); }); + }).on('error', function (err) { func(url, null, tag); }); + } else if (u.protocol == 'file:') { + // Read a file + obj.fs.readFile(url.substring(7), 'utf8', function (err, data) { + func(url, err ? null : data, tag); + }); + } else { func(url, null, tag); } + }; + // Return the certificate of the remote HTTPS server obj.loadCertificate = function (url, hostname, tag, func) { const u = require('url').parse(url); @@ -304,7 +324,7 @@ module.exports.CertificateOperations = function (parent) { } else if (u.protocol == 'file:') { // Read the certificate from a file obj.fs.readFile(url.substring(7), 'utf8', function (err, data) { - if (err) { func(url, null, tag); return; } + if (err) { func(url, null, hostname, tag); return; } var x1 = data.indexOf('-----BEGIN CERTIFICATE-----'), x2 = data.indexOf('-----END CERTIFICATE-----'); if ((x1 >= 0) && (x2 > x1)) { func(url, Buffer.from(data.substring(x1 + 27, x2), 'base64').toString('binary'), hostname, tag); diff --git a/meshcentral.js b/meshcentral.js index cbbb0694..ca75258b 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -1325,8 +1325,36 @@ function CreateMeshCentralServer(config, args) { } } + // Update proxy certificates if (obj.supportsProxyCertificatesRequest == true) { obj.updateProxyCertificates(true); } - obj.StartEx4(); // Keep going + + // Load CloudFlare trusted proxies list if needed + if ((obj.config.settings.trustedproxy != null) && (obj.config.settings.trustedproxy.toLowerCase() == 'cloudflare')) { + delete obj.args.trustedproxy; + delete obj.config.settings.trustedproxy; + obj.certificateOperations.loadTextFile('https://www.cloudflare.com/ips-v4', null, function (url, data, tag) { + if (data != null) { + if (Array.isArray(obj.args.trustedproxy) == false) { obj.args.trustedproxy = []; } + var ipranges = data.split('\n'); + for (var i in ipranges) { if (ipranges[i] != '') { obj.args.trustedproxy.push(ipranges[i]); } } + obj.certificateOperations.loadTextFile('https://www.cloudflare.com/ips-v6', null, function (url, data, tag) { + if (data != null) { + var ipranges = data.split('\n'); + for (var i in ipranges) { if (ipranges[i] != '') { obj.args.trustedproxy.push(ipranges[i]); } } + obj.config.settings.trustedproxy = obj.args.trustedproxy; + } else { + addServerWarning("Unable to load CloudFlare trusted proxy IPv6 address list."); + } + obj.StartEx4(); // Keep going + }); + } else { + addServerWarning("Unable to load CloudFlare trusted proxy IPv4 address list."); + obj.StartEx4(); // Keep going + } + }); + } else { + obj.StartEx4(); // Keep going + } } // Start the server with the given certificates diff --git a/webserver.js b/webserver.js index 0efa39ee..9ef54dbe 100644 --- a/webserver.js +++ b/webserver.js @@ -4885,8 +4885,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (typeof req.connection.remoteAddress == 'string') { ipex = (req.connection.remoteAddress.startsWith('::ffff:')) ? req.connection.remoteAddress.substring(7) : req.connection.remoteAddress; } if ( (obj.args.trustedproxy === true) || - ((typeof obj.args.trustedproxy == 'object') && (obj.args.trustedproxy.indexOf(ipex) >= 0)) || - ((typeof obj.args.tlsoffload == 'object') && (obj.args.tlsoffload.indexOf(ipex) >= 0)) + ((typeof obj.args.trustedproxy == 'object') && (isIPMatch(ipex, obj.args.trustedproxy))) || + ((typeof obj.args.tlsoffload == 'object') && (isIPMatch(ipex, obj.args.tlsoffload))) ) { // Get client IP if (req.headers['cf-connecting-ip']) { // Use CloudFlare IP address if present @@ -6606,6 +6606,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } catch (ex) { console.log(ex); func(fd, tag); } } + // Perform a IP match against a list + function isIPMatch(ip, matchList) { + const ipcheck = require('ipcheck'); + for (var i in matchList) { if (ipcheck.match(ip, matchList[i]) == true) return true; } + return false; + } + // This is the invalid login throttling code obj.badLoginTable = {}; obj.badLoginTableLastClean = 0;