new plugin: security/crowdsec (#2945)

This is a plugin we developed to provide configuration and a basic UI for the crowdsec IDS and IPS. It depends on a couple of binaries recently added to ports.conf

Adding machines (servers, other firewalls) and advanced configuration are not managed by the UI but available from the command line.
This commit is contained in:
mmetc
2022-05-10 11:59:42 +02:00
committed by GitHub
parent ffcd201992
commit ce4469f458
41 changed files with 2090 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
#!/bin/sh
# Removing the plugin from the web interface will autoremove the dependencies
# too, and here we have to delete the files in rc.conf.d (because they are
# generated from templates when the configuration is saved, and the package
# system did not keep track of them).
# But.. If the plugin is removed from the command line (which does not happen
# outside of testing conditions), the crowdsec and bouncer services will not be
# removed. However, since we deleted the files that enabled these services,
# they will be disabled at the next reboot.
rm -f /etc/rc.conf.d/crowdsec \
/etc/rc.conf.d/crowdsec_firewall \
/etc/rc.conf.d/oscrowdsec
# Remove aliases and with them, the rules. We don't have plugin files
# anymore so we do that on the fly.
/usr/local/bin/php <<'EOT'
<?php
@include_once("config.inc");
@include_once("certs.inc");
@include_once("util.inc");
use OPNsense\Firewall\Alias;
use OPNsense\Core\Config;
function removeAlias($name)
{
$model = new Alias();
foreach ($model->aliases->alias->iterateItems() as $index => $alias) {
if (strval($alias->name) == $name) {
if ($model->aliases->alias->del($index)) {
$model->serializeToConfig();
Config::getInstance()->save();
}
}
}
}
removeAlias('crowdsec_blacklists');
removeAlias('crowdsec6_blacklists');
EOT
# apply the configuration changes to the packet filter
configctl filter reload
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
# the configuration file used in reconfigure (i.e. settings.json) may eventually
# have credentials, so we create a directory to contain it -- the directory
# permissions will be copied to the file while generating the jinja template.
# shellcheck disable=SC2174
mkdir -p -m 0700 /usr/local/etc/crowdsec/opnsense
configctl crowdsec reconfigure
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
# need to temporarily stop the bouncer to remove all the rules
service crowdsec_firewall stop >/dev/null 2>&1 | :
# the rest of the cleanup is done in the post-deinstall script, otherwise
# the plugin recreates the objects during "filter reload".
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2021 Crowdsec
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+10
View File
@@ -0,0 +1,10 @@
PLUGIN_NAME= crowdsec
PLUGIN_VERSION= 0.2
PLUGIN_DEVEL= yes
#PLUGIN_REVISION= 1
PLUGIN_DEPENDS= crowdsec
PLUGIN_COMMENT= Lightweight and collaborative security engine
PLUGIN_MAINTAINER= marco@crowdsec.net
PLUGIN_WWW= https://crowdsec.net/
.include "../../Mk/plugins.mk"
+49
View File
@@ -0,0 +1,49 @@
Crowdsec is an open-source, lightweight software, detecting peers with
aggressive behaviors to prevent them from accessing your systems. Its user
friendly design and assistance offers a low technical barrier of entry and
nevertheless a high security gain.
WWW: https://crowdsec.net/
Plugin Changelog
================
0.2
* first published release
* added options `lapi_enabled`, `crowdsec_firewall_verbose`
* removed options `crowdsec_flags`, `crowdsec_firewall_flags`
* changed default for `agent_enabled`, `firewall_bouncer_enabled` to 1
0.1
* fixed packet tags with ipv6
* custom `crowdsec_flags`, `crosdsec_firewall_flags`
0.0.9
* fixed the javascript, 0.0.8 had a syntax error
* new option: rules_tag
* new option: lapi_manual_configuration
* ipv4/ipv6 validation with regexp
0.0.8
* crowdsec update 1.3.2
* configurable `rules_log` and LAPI address/port
0.0.7
* automated removal of Alias objects when the plugin is uninstalled
0.0.6
* crowdsec update 1.3.1.r1
* bouncer update to 0.0.23.r1
* automated creation of Alias and Rule objects
0.0.5
* fixed an issue that prevented the bouncer from banning IPs on opnsense
* fixed support for notification plugins
@@ -0,0 +1,9 @@
# DO NOT EDIT THIS FILE -- OPNsense auto-generated file
#
# User-defined crontab files can be loaded via /etc/cron.d
# or /usr/local/etc/cron.d and follow the same format as
# /etc/crontab, see the crontab(5) manual page.
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
#minute hour mday month wday who command
0 1,13 * * * root /usr/local/opnsense/scripts/OPNsense/CrowdSec/hub-upgrade.sh
@@ -0,0 +1,15 @@
#
# Before 22.1, OPNsense used circular logs under /var/log/*.log that
# can still be around. They are old, in binary format and are not needed by crowdsec.
#
# For this reason we don't scan /var/log/*.log, but some plugins can write
# their (plaintext) logs in that location, in such case add their pathnames too.
#
filenames:
# ssh
- /var/log/audit/*.log
# web admin
- /var/log/lighttpd/*.log
labels:
type: syslog
@@ -0,0 +1,95 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
use OPNsense\Core\Config;
use OPNsense\Firewall\Alias;
use OPNsense\Firewall\Plugin;
function add_alias_if_not_exist($name, $description, $proto) {
$model = new Alias();
if ($model->getByName($name) != null) {
return;
}
$new_alias = $model->aliases->alias->Add();
$new_alias->name = $name;
$new_alias->description = $description;
$new_alias->proto = $proto;
$new_alias->type = 'external';
$model->serializeToConfig();
Config::getInstance()->save();
}
function crowdsec_firewall(Plugin $fw)
{
global $config;
$general = $config['OPNsense']['crowdsec']['general'];
$bouncer_enabled = isset($general['firewall_bouncer_enabled']) && $general['firewall_bouncer_enabled'];
if (!$bouncer_enabled) {
return;
}
$rules_log_enabled = isset($general['rules_log']) && $general['rules_log'];
$rules_tag = "";
if (isset($general['rules_tag'])) {
$rules_tag = $general['rules_tag'];
};
add_alias_if_not_exist('crowdsec_blacklists', 'CrowdSec (IPv4)', 'IPv4');
// https://github.com/opnsense/core/blob/master/src/opnsense/mvc/app/library/OPNsense/Firewall/FilterRule.php
$fw->registerFilterRule(
1, /* priority */
array(
'ipprotocol' => 'inet',
'descr' => 'CrowdSec (IPv4)',
'from' => '$crowdsec_blacklists', # $ to reference an alias
'type' => 'block',
'log' => $rules_log_enabled,
'tag' => $rules_tag,
'label' => 'blocked by crowdsec',
'quick' => true
),
null
);
add_alias_if_not_exist('crowdsec6_blacklists', 'CrowdSec (IPv6)', 'IPv6');
$fw->registerFilterRule(
1, /* priority */
array(
'ipprotocol' => 'inet6',
'descr' => 'CrowdSec (IPv6)',
'from' => '$crowdsec6_blacklists', # $ to reference an alias
'type' => 'block',
'log' => $rules_log_enabled,
'tag' => $rules_tag,
'label' => 'blocked by crowdsec',
'quick' => true
),
null
);
}
function crowdsec_services()
{
$services[] = array(
'description' => 'CrowdSec',
'configd' => array(
'restart' => array('crowdsec restart'),
'start' => array('crowdsec start'),
'stop' => array('crowdsec stop'),
),
'name' => 'crowdsec'
);
return $services;
}
+104
View File
@@ -0,0 +1,104 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: oscrowdsec
# REQUIRE: NETWORKING syslogd
# BEFORE: DAEMON
# KEYWORD: shutdown
. /etc/rc.subr
name="oscrowdsec"
rcvar="oscrowdsec_enable"
load_rc_config $name
: ${oscrowdsec_enable="NO"}
oscrowdsec_start () {
#
# Start, or stop the services according to the plugin's configuration.
# When starting -> error if the services are already running
# When stopping -> no error
#
if service crowdsec enabled; then
service crowdsec start
else
service crowdsec stop || :
fi
if service crowdsec_firewall enabled; then
service crowdsec_firewall start
else
service crowdsec_firewall stop || :
fi
# XXX should complain if they were not stopped?
# service crowdsec status
# if [ $? -eq 0 ]; then
# debug "oscrowdsec_start: crowdsec is still running"
# return 0
# fi
}
oscrowdsec_stop () {
# Always stop the services, enabled or not, running or not. No errors.
service crowdsec stop || :
service crowdsec_firewall stop || :
# XXX should complain if they were running and have not been stopped?
}
oscrowdsec_restart () {
oscrowdsec_stop || :
oscrowdsec_start
}
oscrowdsec_status () {
# return error if at least one program is not running
ret=0
if service crowdsec status; then
ret=$?
fi
if service crowdsec_firewall status; then
if [ $ret -eq 0 ]; then
ret=$?
fi
fi
return $ret
}
oscrowdsec_reload () {
# Here we take it easy. the bouncer does not even support reload
oscrowdsec_restart
}
case $1 in
start)
oscrowdsec_start
exit $?
;;
stop)
oscrowdsec_stop
exit $?
;;
restart)
oscrowdsec_restart
exit $?
;;
status)
oscrowdsec_status
exit $?
;;
reload)
oscrowdsec_reload
exit $?
;;
esac
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
# https://docs.opnsense.org/development/backend/autorun.html
/usr/local/opnsense/scripts/OPNsense/CrowdSec/hub-upgrade.sh
@@ -0,0 +1,33 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\CrowdSec\CrowdSec;
use OPNsense\Core\Backend;
/**
* @package OPNsense\CrowdSec
*/
class AlertsController extends ApiControllerBase
{
/**
* retrieve list of alerts
* @return array of alerts
* @throws \OPNsense\Base\ModelException
* @throws \ReflectionException
*/
public function getAction()
{
$backend = new Backend();
$bckresult = json_decode(trim($backend->configdRun("crowdsec alerts-list")), true);
if ($bckresult !== null) {
// only return valid json type responses
return $bckresult;
}
return array("message" => "unable to list alerts");
}
}
@@ -0,0 +1,33 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\CrowdSec\CrowdSec;
use OPNsense\Core\Backend;
/**
* @package OPNsense\CrowdSec
*/
class BouncersController extends ApiControllerBase
{
/**
* retrieve list of bouncers
* @return array of bouncers
* @throws \OPNsense\Base\ModelException
* @throws \ReflectionException
*/
public function getAction()
{
$backend = new Backend();
$bckresult = json_decode(trim($backend->configdRun("crowdsec bouncers-list")), true);
if ($bckresult !== null) {
// only return valid json type responses
return $bckresult;
}
return array("message" => "unable to list bouncers");
}
}
@@ -0,0 +1,33 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\CrowdSec\CrowdSec;
use OPNsense\Core\Backend;
/**
* @package OPNsense\CrowdSec
*/
class CollectionsController extends ApiControllerBase
{
/**
* retrieve list of collections
* @return array of collections
* @throws \OPNsense\Base\ModelException
* @throws \ReflectionException
*/
public function getAction()
{
$backend = new Backend();
$bckresult = json_decode(trim($backend->configdRun("crowdsec collections-list")), true);
if ($bckresult !== null) {
// only return valid json type responses
return $bckresult;
}
return array("message" => "unable to list collections");
}
}
@@ -0,0 +1,53 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\CrowdSec\CrowdSec;
use OPNsense\Core\Backend;
/**
* @package OPNsense\CrowdSec
*/
class DecisionsController extends ApiControllerBase
{
/**
* retrieve list of decisions
* @return array of decisions
* @throws \OPNsense\Base\ModelException
* @throws \ReflectionException
*/
public function getAction()
{
$backend = new Backend();
$bckresult = json_decode(trim($backend->configdRun("crowdsec decisions-list")), true);
if ($bckresult !== null) {
// only return valid json type responses
return $bckresult;
}
return array("message" => "unable to list decisions");
}
public function deleteAction($decision_id)
{
if ($this->request->isDelete()) {
$backend = new Backend();
$bckresult = $backend->configdRun("crowdsec decisions-delete ${decision_id}");
if ($bckresult !== null) {
// why does the action return \n\n for empty output?
if (trim($bckresult) === '') {
return array("message" => "OK");
}
// TODO handle error
return array("message" => $bckresult);
}
return array("message" => "OK");
} else {
$this->response->setStatusCode(405, "Method Not Allowed");
$this->response->setHeader("Allow", "DELETE");
}
}
}
@@ -0,0 +1,17 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiMutableModelControllerBase;
/**
* @package OPNsense\CrowdSec
*/
class GeneralController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'general';
protected static $internalModelClass = '\OPNsense\CrowdSec\General';
}
@@ -0,0 +1,33 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\CrowdSec\CrowdSec;
use OPNsense\Core\Backend;
/**
* @package OPNsense\CrowdSec
*/
class MachinesController extends ApiControllerBase
{
/**
* retrieve list of registered machines
* @return array of machines
* @throws \OPNsense\Base\ModelException
* @throws \ReflectionException
*/
public function getAction()
{
$backend = new Backend();
$bckresult = json_decode(trim($backend->configdRun("crowdsec machines-list")), true);
if ($bckresult !== null) {
// only return valid json type responses
return $bckresult;
}
return array("message" => "unable to list machines");
}
}
@@ -0,0 +1,33 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\CrowdSec\CrowdSec;
use OPNsense\Core\Backend;
/**
* @package OPNsense\CrowdSec
*/
class ParsersController extends ApiControllerBase
{
/**
* retrieve list of registered parsers
* @return array of parsers
* @throws \OPNsense\Base\ModelException
* @throws \ReflectionException
*/
public function getAction()
{
$backend = new Backend();
$bckresult = json_decode(trim($backend->configdRun("crowdsec parsers-list")), true);
if ($bckresult !== null) {
// only return valid json type responses
return $bckresult;
}
return array("message" => "unable to list parsers");
}
}
@@ -0,0 +1,33 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\CrowdSec\CrowdSec;
use OPNsense\Core\Backend;
/**
* @package OPNsense\CrowdSec
*/
class PostoverflowsController extends ApiControllerBase
{
/**
* retrieve list of registered postoverflows
* @return array of postoverflows
* @throws \OPNsense\Base\ModelException
* @throws \ReflectionException
*/
public function getAction()
{
$backend = new Backend();
$bckresult = json_decode(trim($backend->configdRun("crowdsec postoverflows-list")), true);
if ($bckresult !== null) {
// only return valid json type responses
return $bckresult;
}
return array("message" => "unable to list postoverflows");
}
}
@@ -0,0 +1,33 @@
<?php
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
namespace OPNsense\CrowdSec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\CrowdSec\CrowdSec;
use OPNsense\Core\Backend;
/**
* @package OPNsense\CrowdSec
*/
class ScenariosController extends ApiControllerBase
{
/**
* retrieve list of registered scenarios
* @return array of scenarios
* @throws \OPNsense\Base\ModelException
* @throws \ReflectionException
*/
public function getAction()
{
$backend = new Backend();
$bckresult = json_decode(trim($backend->configdRun("crowdsec scenarios-list")), true);
if ($bckresult !== null) {
// only return valid json type responses
return $bckresult;
}
return array("message" => "unable to list scenarios");
}
}

Some files were not shown because too many files have changed in this diff Show More