Hi,
Few comments: * I'd say you would enable this on a cluster-level, for all nodes * Please allow a whitelist to be added * Feature request: When fail2ban on a node bans an IP, also add it to a blocklist used by VM's with a Proxmox firewall enabled. -- Mark Schouten CTO, Tuxis B.V. | https://www.tuxis.nl/ <m...@tuxis.nl> | +31 318 200208 From: Oguz Bektas <o.bek...@proxmox.com> To: <pve-devel@lists.proxmox.com> Sent: 2021-08-25 11:47 Subject: [pve-devel] [PATCH firewall] implement fail2ban backend adds a section "[FAIL2BAN]" in the hostfw configuration, which allows the properties 'maxretry' and 'bantime' (in minutes) for the GUI ports. Signed-off-by: Oguz Bektas <o.bek...@proxmox.com> --- RFC->PATCH: * better parser regex to allow comments in hostfw * use heredoc for multiline file contents * check if filter file exists, and if the jail configuration has changed before writing it out * removed the unrelated empty lines that i forgot, and the debug print :D * error out if we can't parse an option debian/control | 1 + src/PVE/Firewall.pm | 79 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 4684c5b..377c9ae 100644 --- a/debian/control +++ b/debian/control @@ -17,6 +17,7 @@ Package: pve-firewall Architecture: any Conflicts: ulogd, Depends: ebtables, + fail2ban, ipset, iptables, libpve-access-control, diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index edc5336..ed13ca5 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -1347,6 +1347,26 @@ our $host_option_properties = { }, }; +our $fail2ban_option_properties = { + enable => { + description => "Enable or disable fail2ban on a node.", + type => 'boolean', + default => 1, + }, + maxretry => { + description => "Amount of failed tries to ban after.", + type => 'integer', + minimum => 1, + default => 3, + }, + bantime => { + description => "Minutes to ban suspicious IPs.", + type => 'integer', + minimum => 1, + default => 5, + }, +}; + our $vm_option_properties = { enable => { description => "Enable/disable firewall rules.", @@ -2407,6 +2427,39 @@ sub ruleset_generate_vm_rules { } } +sub generate_fail2ban_config { + my ($maxretry, $bantime) = @_; + + my $bantime_seconds = $bantime * 60; + + my $fail2ban_filter = <<CONFIG; +[Definition] +failregex = pvedaemon\\[.*authentication failure; rhost=<HOST> user=.* msg=.* +ignoreregex = +CONFIG + my $filter_path = '/etc/fail2ban/filter.d/proxmox.conf'; + PVE::Tools::file_set_contents($filter_path, $fail2ban_filter) unless -f $filter_path; + + + my $fail2ban_jail = <<CONFIG; +[proxmox] +enabled = true +port = https,http,8006 +filter = proxmox +logpath = /var/log/daemon.log +maxretry = $maxretry +bantime = $bantime_seconds +CONFIG + + my $jail_path = "/etc/fail2ban/jail.d/proxmox.conf"; + my $current_fail2ban_jail = PVE::Tools::file_get_contents($jail_path); + + if ($current_fail2ban_jail ne $fail2ban_jail) { + PVE::Tools::file_set_contents($jail_path, $fail2ban_jail); + run_command([qw(systemctl try-reload-or-restart fail2ban.service)]); + } +} + sub generate_nfqueue { my ($options) = @_; @@ -2937,6 +2990,16 @@ sub parse_alias { return undef; } +sub parse_fail2ban_option { + my ($line) = @_; + + if ($line =~ m/^(maxretry|bantime):\s+(\d+)\s*(?:#\s*(.*?)\s*)?$/) { + return ($1, $2 // $fail2ban_option_properties->{$1}->{default}); + } else { + die "error parsing fail2ban options: $line"; + } +} + sub generic_fw_config_parser { my ($filename, $cluster_conf, $empty_conf, $rule_env) = @_; @@ -2965,6 +3028,11 @@ sub generic_fw_config_parser { my $prefix = "$filename (line $linenr)"; + if ($empty_conf->{fail2ban} && ($line =~ m/^\[fail2ban\]$/i)) { + $section = 'fail2ban'; + next; + } + if ($empty_conf->{options} && ($line =~ m/^\[options\]$/i)) { $section = 'options'; next; @@ -3046,6 +3114,13 @@ sub generic_fw_config_parser { $res->{aliases}->{lc($data->{name})} = $data; }; warn "$prefix: $@" if $@; + } elsif ($section eq 'fail2ban') { + my ($opt, $value) = eval { parse_fail2ban_option($line) }; + if (my $err = $@) { + warn "fail2ban parsing error: $err"; + next; + } + $res->{fail2ban}->{$opt} = $value; } elsif ($section eq 'rules') { my $rule; eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, $res, $rule_env); }; @@ -3620,7 +3695,7 @@ sub load_hostfw_conf { $filename = $hostfw_conf_filename if !defined($filename); - my $empty_conf = { rules => [], options => {}}; + my $empty_conf = { rules => [], options => {}, fail2ban => {}}; return generic_fw_config_parser($filename, $cluster_conf, $empty_conf, 'host'); } @@ -4590,6 +4665,8 @@ sub update { } my $hostfw_conf = load_hostfw_conf($cluster_conf); + my $fail2ban_opts = $hostfw_conf->{fail2ban}; + generate_fail2ban_config($fail2ban_opts->{maxretry}, $fail2ban_opts->{bantime}); my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = compile($cluster_conf, $hostfw_conf); -- 2.30.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel