Added support for Crowdsec, an open-source and collaborative IPS (Intrusion Prevention System)
Ylian Saint-Hilaire committed
Jul 6, 2022 at 20:34 UTC
947d9094cbc2b52b4cba8afdc54963bb9d21fb6a
7 files changed
+46
-7
meshcentral-config-schema.json
+11
-1
@@ -159,7 +159,7 @@
159
"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\"" },
160
"authLog": { "type": "string", "default": null, "description": "File path and name of the authentication log to be created. This log can be parsed by Fail2ban." },
161
"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." },
162
- "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." },
162
+ "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." },
163
"manageCrossDomain": { "type": "array", "uniqueItems": true, "items": { "type": "string" }, "description": "Users in this list are allowed to manage all users in all domains." },
164
"localDiscovery": {
165
"type": "object",
@@ -195,6 +195,16 @@
195
},
196
"required": [ "iceServers" ]
197
},
198
+ "crowdsec": {
199
+ "type": "object",
200
+ "additionalProperties": true,
201
+ "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",
202
+ "properties": {
203
+ "url": { "type": "string", "description": "The URL of your LAPI instance. Ex: http://localhost:8080" },
204
+ "apiKey": { "type": "string", "description": "The bouncer key (generated via cscli)" }
205
+ },
206
+ "required": [ "url", "apiKey" ]
207
+ },
208
"autoBackup": {
209
"type": "object",
210
"properties": {
meshcentral.js
+11
-4
@@ -698,7 +698,7 @@ function CreateMeshCentralServer(config, args) {
698
obj.args = args = config2.settings;
699
700
// Lower case all keys in the config file
701
- obj.common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']);
701
+ obj.common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'crowdsec']);
702
703
// Grad some of the values from the original config.json file if present.
704
if ((config.settings.vault != null) && (config2.settings != null)) { config2.settings.vault = config.settings.vault; }
@@ -1196,7 +1196,7 @@ function CreateMeshCentralServer(config, args) {
1196
for (i in args) { config2.settings[i] = args[i]; }
1197
1198
// Lower case all keys in the config file
1199
- common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']);
1199
+ common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'crowdsec']);
1200
1201
// Grad some of the values from the original config.json file if present.
1202
config2['mysql'] = config['mysql'];
@@ -1219,7 +1219,7 @@ function CreateMeshCentralServer(config, args) {
1219
};
1220
1221
// Time to start the server of real.
1222
- obj.StartEx1b = function () {
1222
+ obj.StartEx1b = async function () {
1223
var i;
1224
1225
// Setup certificate operations
@@ -1232,6 +1232,12 @@ function CreateMeshCentralServer(config, args) {
1232
})
1233
}
1234
1235
+ // Start CrowdSec bouncer if needed: https://www.crowdsec.net/
1236
+ if (typeof obj.args.crowdsec == 'object') {
1237
+ const expressCrowdsecBouncer = require("@crowdsec/express-bouncer");
1238
+ try { obj.crowdsecMiddleware = await expressCrowdsecBouncer(obj.args.crowdsec); } catch (ex) { delete obj.crowdsecMiddleware; }
1239
+ }
1240
+
1241
// Check if self update is allowed. If running as a Windows service, self-update is not possible.
1242
if (obj.fs.existsSync(obj.path.join(__dirname, 'daemon'))) { obj.serverSelfWriteAllowed = false; }
1243
@@ -3515,7 +3521,7 @@ function getConfig(createSampleConfig) {
3521
3522
// Lower case all keys in the config file
3523
try {
3518
- require('./common.js').objKeysToLower(config, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']);
3524
+ require('./common.js').objKeysToLower(config, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'crowdsec']);
3525
} catch (ex) {
3526
console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.');
3527
process.exit();
@@ -3713,6 +3719,7 @@ function mainStart() {
3719
if (nodemailer || (config.smtp != null) || (config.sendmail != null)) { modules.push('nodemailer'); } // Add SMTP support
3720
if (sendgrid || (config.sendgrid != null)) { modules.push('@sendgrid/mail'); } // Add SendGrid support
3721
if (args.translate) { modules.push('jsdom'); modules.push('esprima'); modules.push('minify-js'); modules.push('html-minifier'); } // Translation support
3722
+ if (typeof config.settings.crowdsec == 'object') { modules.push('@crowdsec/express-bouncer'); } // Add CrowdSec bounser module (https://www.npmjs.com/package/@crowdsec/express-bouncer)
3723
3724
// Setup encrypted zip support if needed
3725
if (config.settings.autobackup && config.settings.autobackup.zippassword) {
package.json
+12
-2
@@ -37,6 +37,8 @@
37
"sample-config-advanced.json"
38
],
39
"dependencies": {
40
+ "@crowdsec/express-bouncer": "^0.1.0",
41
+ "@yetzt/nedb": "^1.8.0",
42
"archiver": "^5.3.1",
43
"body-parser": "^1.19.0",
44
"cbor": "~5.2.0",
@@ -45,13 +47,21 @@
47
"express": "^4.17.0",
48
"express-handlebars": "^5.3.5",
49
"express-ws": "^4.0.0",
50
+ "image-size": "^1.0.1",
51
"ipcheck": "^0.1.0",
52
+ "loadavg-windows": "^1.1.1",
53
"minimist": "^1.2.5",
54
"multiparty": "^4.2.1",
51
- "@yetzt/nedb": "^1.8.0",
55
"node-forge": "^1.0.0",
56
+ "node-windows": "^0.1.4",
57
+ "otplib": "^10.2.3",
58
+ "pg": "^8.7.1",
59
+ "pgtools": "^0.3.2",
60
+ "ssh2": "^1.11.0",
61
+ "web-push": "^3.5.0",
62
"ws": "^5.2.3",
54
- "yauzl": "^2.10.0"
63
+ "yauzl": "^2.10.0",
64
+ "yubikeyotp": "^0.2.0"
65
},
66
"engines": {
67
"node": ">=10.0.0"
redirserver.js
+3
@@ -39,6 +39,9 @@ module.exports.CreateRedirServer = function (parent, db, args, func) {
39
res.redirect('https://' + host + ':' + httpsPort + req.url);
40
}
41
42
+ // Setup CrowdSec bouncer middleware if needed
43
+ if (parent.crowdsecMiddleware != null) { obj.app.use(parent.crowdsecMiddleware); }
44
+
45
/*
46
// Return the current domain of the request
47
function getDomain(req) {
sample-config-advanced.json
+4
@@ -154,6 +154,10 @@
154
"trustedFqdn": "sample.com",
155
"ip": "192.168.1.1"
156
},
157
+ "_crowdsec": {
158
+ "url": "http://localhost:8080",
159
+ "apiKey": "BOUNCER_API_KEY"
160
+ },
161
"_plugins": { "enabled": true }
162
},
163
"_domaindefaults": {
webrelayserver.js
+3
@@ -36,6 +36,9 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates,
36
var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
37
38
function serverStart() {
39
+ // Setup CrowdSec bouncer middleware if needed
40
+ if (parent.crowdsecMiddleware != null) { obj.app.use(parent.crowdsecMiddleware); }
41
+
42
if (args.trustedproxy) {
43
// Reverse proxy should add the "X-Forwarded-*" headers
44
try {
webserver.js
+2
@@ -5706,9 +5706,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
5706
obj.tlsAltServer.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null); });
5707
obj.expressWsAlt = require('express-ws')(obj.agentapp, obj.tlsAltServer, { wsOptions: { perMessageDeflate: (args.wscompression === true) } });
5708
}
5709
+ if (parent.crowdsecMiddleware != null) { obj.agentapp.use(parent.crowdsecMiddleware); } // Setup CrowdSec bouncer middleware if needed
5710
}
5711
5712
// Setup middleware
5713
+ if (parent.crowdsecMiddleware != null) { obj.app.use(parent.crowdsecMiddleware); } // Setup CrowdSec bouncer middleware if needed
5714
obj.app.engine('handlebars', obj.exphbs({ defaultLayout: false }));
5715
obj.app.set('view engine', 'handlebars');
5716
if (obj.args.trustedproxy) {