Added support for Crowdsec, an open-source and collaborative IPS (Intrusion Prevention System)

This commit is contained in:
Ylian Saint-Hilaire 2022-07-06 20:34:04 -07:00
parent 79d32b62e3
commit 947d9094cb
7 changed files with 46 additions and 7 deletions

View File

@ -159,7 +159,7 @@
"agentBlockedIP": { "type": [ "string", "array" ], "default": null, "description": "When set, agents from these denied IP address ranges will not be able to connect to the server. Example: \"192.168.2.100,192.168.1.0/24\"" }, "agentBlockedIP": { "type": [ "string", "array" ], "default": null, "description": "When set, agents from these denied IP address ranges will not be able to connect to the server. Example: \"192.168.2.100,192.168.1.0/24\"" },
"authLog": { "type": "string", "default": null, "description": "File path and name of the authentication log to be created. This log can be parsed by Fail2ban." }, "authLog": { "type": "string", "default": null, "description": "File path and name of the authentication log to be created. This log can be parsed by Fail2ban." },
"InterUserMessaging": { "type": "array", "uniqueItems": true, "items": { "type": "string" }, "description": "Users in this list are allowed to send and receive inter-user messages. This can be used to implement bots or other software where MeshCentral is used as data transport. See \"interuser\" websocket command in the code." }, "InterUserMessaging": { "type": "array", "uniqueItems": true, "items": { "type": "string" }, "description": "Users in this list are allowed to send and receive inter-user messages. This can be used to implement bots or other software where MeshCentral is used as data transport. See \"interuser\" websocket command in the code." },
"manageAllDeviceGroups": { "type": "array", "uniqueItems": true, "items": { "type": "string" }, "description": "Users in this list are allowed to see and manage all device groups within their domain." }, "manageAllDeviceGroups": { "type": "array", "uniqueItems": true, "items": { "type": " string" }, "description": "Users in this list are allowed to see and manage all device groups within their domain." },
"manageCrossDomain": { "type": "array", "uniqueItems": true, "items": { "type": "string" }, "description": "Users in this list are allowed to manage all users in all domains." }, "manageCrossDomain": { "type": "array", "uniqueItems": true, "items": { "type": "string" }, "description": "Users in this list are allowed to manage all users in all domains." },
"localDiscovery": { "localDiscovery": {
"type": "object", "type": "object",
@ -195,6 +195,16 @@
}, },
"required": [ "iceServers" ] "required": [ "iceServers" ]
}, },
"crowdsec": {
"type": "object",
"additionalProperties": true,
"description": "Enabled the MeshCentral built-in Crowdsec bouncer. This section is passed directly to the bouncer, all of the settings are documented at https://www.npmjs.com/package/@crowdsec/express-bouncer",
"properties": {
"url": { "type": "string", "description": "The URL of your LAPI instance. Ex: http://localhost:8080" },
"apiKey": { "type": "string", "description": "The bouncer key (generated via cscli)" }
},
"required": [ "url", "apiKey" ]
},
"autoBackup": { "autoBackup": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -698,7 +698,7 @@ function CreateMeshCentralServer(config, args) {
obj.args = args = config2.settings; obj.args = args = config2.settings;
// Lower case all keys in the config file // Lower case all keys in the config file
obj.common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']); obj.common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'crowdsec']);
// Grad some of the values from the original config.json file if present. // Grad some of the values from the original config.json file if present.
if ((config.settings.vault != null) && (config2.settings != null)) { config2.settings.vault = config.settings.vault; } if ((config.settings.vault != null) && (config2.settings != null)) { config2.settings.vault = config.settings.vault; }
@ -1196,7 +1196,7 @@ function CreateMeshCentralServer(config, args) {
for (i in args) { config2.settings[i] = args[i]; } for (i in args) { config2.settings[i] = args[i]; }
// Lower case all keys in the config file // Lower case all keys in the config file
common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']); common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'crowdsec']);
// Grad some of the values from the original config.json file if present. // Grad some of the values from the original config.json file if present.
config2['mysql'] = config['mysql']; config2['mysql'] = config['mysql'];
@ -1219,7 +1219,7 @@ function CreateMeshCentralServer(config, args) {
}; };
// Time to start the server of real. // Time to start the server of real.
obj.StartEx1b = function () { obj.StartEx1b = async function () {
var i; var i;
// Setup certificate operations // Setup certificate operations
@ -1232,6 +1232,12 @@ function CreateMeshCentralServer(config, args) {
}) })
} }
// Start CrowdSec bouncer if needed: https://www.crowdsec.net/
if (typeof obj.args.crowdsec == 'object') {
const expressCrowdsecBouncer = require("@crowdsec/express-bouncer");
try { obj.crowdsecMiddleware = await expressCrowdsecBouncer(obj.args.crowdsec); } catch (ex) { delete obj.crowdsecMiddleware; }
}
// Check if self update is allowed. If running as a Windows service, self-update is not possible. // Check if self update is allowed. If running as a Windows service, self-update is not possible.
if (obj.fs.existsSync(obj.path.join(__dirname, 'daemon'))) { obj.serverSelfWriteAllowed = false; } if (obj.fs.existsSync(obj.path.join(__dirname, 'daemon'))) { obj.serverSelfWriteAllowed = false; }
@ -3515,7 +3521,7 @@ function getConfig(createSampleConfig) {
// Lower case all keys in the config file // Lower case all keys in the config file
try { try {
require('./common.js').objKeysToLower(config, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']); require('./common.js').objKeysToLower(config, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'crowdsec']);
} catch (ex) { } catch (ex) {
console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.'); console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.');
process.exit(); process.exit();
@ -3713,6 +3719,7 @@ function mainStart() {
if (nodemailer || (config.smtp != null) || (config.sendmail != null)) { modules.push('nodemailer'); } // Add SMTP support if (nodemailer || (config.smtp != null) || (config.sendmail != null)) { modules.push('nodemailer'); } // Add SMTP support
if (sendgrid || (config.sendgrid != null)) { modules.push('@sendgrid/mail'); } // Add SendGrid support if (sendgrid || (config.sendgrid != null)) { modules.push('@sendgrid/mail'); } // Add SendGrid support
if (args.translate) { modules.push('jsdom'); modules.push('esprima'); modules.push('minify-js'); modules.push('html-minifier'); } // Translation support if (args.translate) { modules.push('jsdom'); modules.push('esprima'); modules.push('minify-js'); modules.push('html-minifier'); } // Translation support
if (typeof config.settings.crowdsec == 'object') { modules.push('@crowdsec/express-bouncer'); } // Add CrowdSec bounser module (https://www.npmjs.com/package/@crowdsec/express-bouncer)
// Setup encrypted zip support if needed // Setup encrypted zip support if needed
if (config.settings.autobackup && config.settings.autobackup.zippassword) { if (config.settings.autobackup && config.settings.autobackup.zippassword) {

View File

@ -37,6 +37,8 @@
"sample-config-advanced.json" "sample-config-advanced.json"
], ],
"dependencies": { "dependencies": {
"@crowdsec/express-bouncer": "^0.1.0",
"@yetzt/nedb": "^1.8.0",
"archiver": "^5.3.1", "archiver": "^5.3.1",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"cbor": "~5.2.0", "cbor": "~5.2.0",
@ -45,13 +47,21 @@
"express": "^4.17.0", "express": "^4.17.0",
"express-handlebars": "^5.3.5", "express-handlebars": "^5.3.5",
"express-ws": "^4.0.0", "express-ws": "^4.0.0",
"image-size": "^1.0.1",
"ipcheck": "^0.1.0", "ipcheck": "^0.1.0",
"loadavg-windows": "^1.1.1",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"multiparty": "^4.2.1", "multiparty": "^4.2.1",
"@yetzt/nedb": "^1.8.0",
"node-forge": "^1.0.0", "node-forge": "^1.0.0",
"node-windows": "^0.1.4",
"otplib": "^10.2.3",
"pg": "^8.7.1",
"pgtools": "^0.3.2",
"ssh2": "^1.11.0",
"web-push": "^3.5.0",
"ws": "^5.2.3", "ws": "^5.2.3",
"yauzl": "^2.10.0" "yauzl": "^2.10.0",
"yubikeyotp": "^0.2.0"
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"

View File

@ -39,6 +39,9 @@ module.exports.CreateRedirServer = function (parent, db, args, func) {
res.redirect('https://' + host + ':' + httpsPort + req.url); res.redirect('https://' + host + ':' + httpsPort + req.url);
} }
// Setup CrowdSec bouncer middleware if needed
if (parent.crowdsecMiddleware != null) { obj.app.use(parent.crowdsecMiddleware); }
/* /*
// Return the current domain of the request // Return the current domain of the request
function getDomain(req) { function getDomain(req) {

View File

@ -154,6 +154,10 @@
"trustedFqdn": "sample.com", "trustedFqdn": "sample.com",
"ip": "192.168.1.1" "ip": "192.168.1.1"
}, },
"_crowdsec": {
"url": "http://localhost:8080",
"apiKey": "BOUNCER_API_KEY"
},
"_plugins": { "enabled": true } "_plugins": { "enabled": true }
}, },
"_domaindefaults": { "_domaindefaults": {

View File

@ -36,6 +36,9 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates,
var tlsSessionStoreCount = 0; // Number of cached TLS session information in store. var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
function serverStart() { function serverStart() {
// Setup CrowdSec bouncer middleware if needed
if (parent.crowdsecMiddleware != null) { obj.app.use(parent.crowdsecMiddleware); }
if (args.trustedproxy) { if (args.trustedproxy) {
// Reverse proxy should add the "X-Forwarded-*" headers // Reverse proxy should add the "X-Forwarded-*" headers
try { try {

View File

@ -5706,9 +5706,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
obj.tlsAltServer.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null); }); obj.tlsAltServer.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null); });
obj.expressWsAlt = require('express-ws')(obj.agentapp, obj.tlsAltServer, { wsOptions: { perMessageDeflate: (args.wscompression === true) } }); obj.expressWsAlt = require('express-ws')(obj.agentapp, obj.tlsAltServer, { wsOptions: { perMessageDeflate: (args.wscompression === true) } });
} }
if (parent.crowdsecMiddleware != null) { obj.agentapp.use(parent.crowdsecMiddleware); } // Setup CrowdSec bouncer middleware if needed
} }
// Setup middleware // Setup middleware
if (parent.crowdsecMiddleware != null) { obj.app.use(parent.crowdsecMiddleware); } // Setup CrowdSec bouncer middleware if needed
obj.app.engine('handlebars', obj.exphbs({ defaultLayout: false })); obj.app.engine('handlebars', obj.exphbs({ defaultLayout: false }));
obj.app.set('view engine', 'handlebars'); obj.app.set('view engine', 'handlebars');
if (obj.args.trustedproxy) { if (obj.args.trustedproxy) {