Added CloudFlare auto-loading of trusted proxy IP addresses.
Ylian Saint-Hilaire committed
Dec 10, 2020 at 13:56 UTC
370d890b8649799a79969f2d6c2543f91bef5b9e
3 files changed
+59
-4
certoperations.js
+21
-1
@@ -284,6 +284,26 @@ module.exports.CertificateOperations = function (parent) {
284
return r;
285
}
286
287
+ // Return a text file from a remote HTTPS server
288
+ obj.loadTextFile = function (url, tag, func) {
289
+ const u = require('url').parse(url);
290
+ if (u.protocol == 'https:') {
291
+ // Read from HTTPS
292
+ const https = require('https');
293
+ https.get(url, function(resp) {
294
+ var data = '';
295
+ resp.on('data', function(chunk) { data += chunk; });
296
+ resp.on('end', function () { func(url, data, tag); });
297
+ resp.on('error', function (chunk) { func(url, null, tag); });
298
+ }).on('error', function (err) { func(url, null, tag); });
299
+ } else if (u.protocol == 'file:') {
300
+ // Read a file
301
+ obj.fs.readFile(url.substring(7), 'utf8', function (err, data) {
302
+ func(url, err ? null : data, tag);
303
+ });
304
+ } else { func(url, null, tag); }
305
+ };
306
+
307
// Return the certificate of the remote HTTPS server
308
obj.loadCertificate = function (url, hostname, tag, func) {
309
const u = require('url').parse(url);
@@ -304,7 +324,7 @@ module.exports.CertificateOperations = function (parent) {
324
} else if (u.protocol == 'file:') {
325
// Read the certificate from a file
326
obj.fs.readFile(url.substring(7), 'utf8', function (err, data) {
307
- if (err) { func(url, null, tag); return; }
327
+ if (err) { func(url, null, hostname, tag); return; }
328
var x1 = data.indexOf('-----BEGIN CERTIFICATE-----'), x2 = data.indexOf('-----END CERTIFICATE-----');
329
if ((x1 >= 0) && (x2 > x1)) {
330
func(url, Buffer.from(data.substring(x1 + 27, x2), 'base64').toString('binary'), hostname, tag);
meshcentral.js
+29
-1
@@ -1325,8 +1325,36 @@ function CreateMeshCentralServer(config, args) {
1325
}
1326
}
1327
1328
+ // Update proxy certificates
1329
if (obj.supportsProxyCertificatesRequest == true) { obj.updateProxyCertificates(true); }
1329
- obj.StartEx4(); // Keep going
1330
+
1331
+ // Load CloudFlare trusted proxies list if needed
1332
+ if ((obj.config.settings.trustedproxy != null) && (obj.config.settings.trustedproxy.toLowerCase() == 'cloudflare')) {
1333
+ delete obj.args.trustedproxy;
1334
+ delete obj.config.settings.trustedproxy;
1335
+ obj.certificateOperations.loadTextFile('https://www.cloudflare.com/ips-v4', null, function (url, data, tag) {
1336
+ if (data != null) {
1337
+ if (Array.isArray(obj.args.trustedproxy) == false) { obj.args.trustedproxy = []; }
1338
+ var ipranges = data.split('\n');
1339
+ for (var i in ipranges) { if (ipranges[i] != '') { obj.args.trustedproxy.push(ipranges[i]); } }
1340
+ obj.certificateOperations.loadTextFile('https://www.cloudflare.com/ips-v6', null, function (url, data, tag) {
1341
+ if (data != null) {
1342
+ var ipranges = data.split('\n');
1343
+ for (var i in ipranges) { if (ipranges[i] != '') { obj.args.trustedproxy.push(ipranges[i]); } }
1344
+ obj.config.settings.trustedproxy = obj.args.trustedproxy;
1345
+ } else {
1346
+ addServerWarning("Unable to load CloudFlare trusted proxy IPv6 address list.");
1347
+ }
1348
+ obj.StartEx4(); // Keep going
1349
+ });
1350
+ } else {
1351
+ addServerWarning("Unable to load CloudFlare trusted proxy IPv4 address list.");
1352
+ obj.StartEx4(); // Keep going
1353
+ }
1354
+ });
1355
+ } else {
1356
+ obj.StartEx4(); // Keep going
1357
+ }
1358
}
1359
1360
// Start the server with the given certificates
webserver.js
+9
-2
@@ -4885,8 +4885,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4885
if (typeof req.connection.remoteAddress == 'string') { ipex = (req.connection.remoteAddress.startsWith('::ffff:')) ? req.connection.remoteAddress.substring(7) : req.connection.remoteAddress; }
4886
if (
4887
(obj.args.trustedproxy === true) ||
4888
- ((typeof obj.args.trustedproxy == 'object') && (obj.args.trustedproxy.indexOf(ipex) >= 0)) ||
4889
- ((typeof obj.args.tlsoffload == 'object') && (obj.args.tlsoffload.indexOf(ipex) >= 0))
4888
+ ((typeof obj.args.trustedproxy == 'object') && (isIPMatch(ipex, obj.args.trustedproxy))) ||
4889
+ ((typeof obj.args.tlsoffload == 'object') && (isIPMatch(ipex, obj.args.tlsoffload)))
4890
) {
4891
// Get client IP
4892
if (req.headers['cf-connecting-ip']) { // Use CloudFlare IP address if present
@@ -6606,6 +6606,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
6606
} catch (ex) { console.log(ex); func(fd, tag); }
6607
}
6608
6609
+ // Perform a IP match against a list
6610
+ function isIPMatch(ip, matchList) {
6611
+ const ipcheck = require('ipcheck');
6612
+ for (var i in matchList) { if (ipcheck.match(ip, matchList[i]) == true) return true; }
6613
+ return false;
6614
+ }
6615
+
6616
// This is the invalid login throttling code
6617
obj.badLoginTable = {};
6618
obj.badLoginTableLastClean = 0;