diff options
-rw-r--r-- | data/templates/ids/fastnetmon.tmpl | 60 | ||||
-rw-r--r-- | data/templates/ids/fastnetmon_networks_list.tmpl | 7 | ||||
-rw-r--r-- | debian/control | 4 | ||||
-rw-r--r-- | interface-definitions/service-ids-ddos-protection.xml.in | 117 | ||||
-rwxr-xr-x | src/conf_mode/service_ids_fastnetmon.py | 89 |
5 files changed, 276 insertions, 1 deletions
diff --git a/data/templates/ids/fastnetmon.tmpl b/data/templates/ids/fastnetmon.tmpl new file mode 100644 index 000000000..71a1b2bd7 --- /dev/null +++ b/data/templates/ids/fastnetmon.tmpl @@ -0,0 +1,60 @@ +# enable this option if you want to send logs to local syslog facility +logging:local_syslog_logging = on + +# list of all your networks in CIDR format +networks_list_path = /etc/networks_list + +# list networks in CIDR format which will be not monitored for attacks +white_list_path = /etc/networks_whitelist + +# Enable/Disable any actions in case of attack +enable_ban = on + +## How many packets will be collected from attack traffic +ban_details_records_count = 500 + +## How long (in seconds) we should keep an IP in blocked state +## If you set 0 here it completely disables unban capability +ban_time = 1900 + +# Check if the attack is still active, before triggering an unban callback with this option +# If the attack is still active, check each run of the unban watchdog +unban_only_if_attack_finished = on + +# enable per subnet speed meters +# For each subnet, list track speed in bps and pps for both directions +enable_subnet_counters = off + +{% if "mirror" in mode %} +mirror_afpacket = on +{% endif -%} + +{% if "in" in direction %} +process_incoming_traffic = on +{% endif -%} +{% if "out" in direction %} +process_outgoing_traffic = on +{% endif -%} +{% for th in threshold %} +{% if th == "fps" %} +ban_for_flows = on +threshold_flows = {{ threshold[th] }} +{% endif -%} +{% if th == "mbps" %} +ban_for_bandwidth = on +threshold_mbps = {{ threshold[th] }} +{% endif -%} +{% if th == "pps" %} +ban_for_pps = on +threshold_pps = {{ threshold[th] }} +{% endif -%} +{% endfor -%} + +{% if listen_interface %} +{% set value = listen_interface if listen_interface is string else listen_interface | join(',') %} +interfaces = {{ value }} +{% endif -%} + +{% if alert_script %} +notify_script_path = {{ alert_script }} +{% endif -%} diff --git a/data/templates/ids/fastnetmon_networks_list.tmpl b/data/templates/ids/fastnetmon_networks_list.tmpl new file mode 100644 index 000000000..d58990053 --- /dev/null +++ b/data/templates/ids/fastnetmon_networks_list.tmpl @@ -0,0 +1,7 @@ +{% if network is string %} +{{ network }} +{% else %} +{% for net in network %} +{{ net }} +{% endfor %} +{% endif %} diff --git a/debian/control b/debian/control index d2a1ad54c..6746fe647 100644 --- a/debian/control +++ b/debian/control @@ -103,7 +103,9 @@ Depends: python3, salt-minion, vyos-utils, nftables (>= 0.9.3), - conntrack + conntrack, + libatomic1, + fastnetmon Description: VyOS configuration scripts and data VyOS configuration scripts, interface definitions, and everything diff --git a/interface-definitions/service-ids-ddos-protection.xml.in b/interface-definitions/service-ids-ddos-protection.xml.in new file mode 100644 index 000000000..741533bb4 --- /dev/null +++ b/interface-definitions/service-ids-ddos-protection.xml.in @@ -0,0 +1,117 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="service"> + <children> + <node name="ids"> + <properties> + <help>Intrusion Detection System</help> + </properties> + <children> + <node name="ddos-protection" owner="${vyos_conf_scripts_dir}/service_ids_fastnetmon.py"> + <properties> + <help>FastNetMon detection and protection parameters</help> + </properties> + <children> + <leafNode name="alert-script"> + <properties> + <help>Path to fastnetmon alert script</help> + </properties> + </leafNode> + <leafNode name="direction"> + <properties> + <help>Direction for processing traffic</help> + <completionHelp> + <list>in out</list> + </completionHelp> + <constraint> + <regex>(in|out)</regex> + </constraint> + <multi/> + </properties> + </leafNode> + <leafNode name="listen-interface"> + <properties> + <help>Listen interface for mirroring traffic</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <multi/> + </properties> + </leafNode> + <node name="mode"> + <properties> + <help>Traffic capture modes</help> + </properties> + <children> + <!-- Future modes "mirror" "netflow" "combine (both)" --> + <leafNode name="mirror"> + <properties> + <help>Listen mirrored traffic mode</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <leafNode name="network"> + <properties> + <help>Define monitoring networks</help> + <valueHelp> + <format>ipv4net</format> + <description>Processed network</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + <node name="threshold"> + <properties> + <help>Attack limits thresholds</help> + </properties> + <children> + <leafNode name="fps"> + <properties> + <help>Flows per second</help> + <valueHelp> + <format><0-4294967294></format> + <description>Flows per second</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967294"/> + </constraint> + </properties> + </leafNode> + <leafNode name="mbps"> + <properties> + <help>Megabits per second</help> + <valueHelp> + <format><0-4294967294></format> + <description>Megabits per second</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967294"/> + </constraint> + </properties> + </leafNode> + <leafNode name="pps"> + <properties> + <help>Packets per second</help> + <valueHelp> + <format><0-4294967294></format> + <description>Packets per second</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967294"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/src/conf_mode/service_ids_fastnetmon.py b/src/conf_mode/service_ids_fastnetmon.py new file mode 100755 index 000000000..71bef08ae --- /dev/null +++ b/src/conf_mode/service_ids_fastnetmon.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018-2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos import ConfigError +from vyos.util import call +from vyos.template import render +from vyos import airbag +airbag.enable() + +config_file = r'/etc/fastnetmon.conf' +networks_list = r'/etc/networks_list' + +def get_config(): + conf = Config() + base = ['service', 'ids', 'ddos-protection'] + fastnetmon = conf.get_config_dict(base, key_mangling=('-', '_')) + return fastnetmon + +def verify(fastnetmon): + if not fastnetmon: + return None + + if not "mode" in fastnetmon: + raise ConfigError('ddos-protection mode is mandatory!') + + if not "network" in fastnetmon: + raise ConfigError('Required define network!') + + if not "listen_interface" in fastnetmon: + raise ConfigError('Define listen-interface is mandatory!') + + if "alert_script" in fastnetmon: + if os.path.isfile(fastnetmon["alert_script"]): + # Check script permissions + if not os.access(fastnetmon["alert_script"], os.X_OK): + raise ConfigError('Script {0} does not have permissions for execution'.format(fastnetmon["alert_script"])) + else: + raise ConfigError('File {0} does not exists!'.format(fastnetmon["alert_script"])) + +def generate(fastnetmon): + if not fastnetmon: + if os.path.isfile(config_file): + os.unlink(config_file) + if os.path.isfile(networks_list): + os.unlink(networks_list) + + return + + render(config_file, 'ids/fastnetmon.tmpl', fastnetmon, trim_blocks=True) + render(networks_list, 'ids/fastnetmon_networks_list.tmpl', fastnetmon, trim_blocks=True) + + return None + +def apply(fastnetmon): + if not fastnetmon: + # Stop fastnetmon service if removed + call('systemctl stop fastnetmon.service') + else: + call('systemctl restart fastnetmon.service') + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) |