* [pve-devel] [PATCH-SERIES manager firewall v4 0/2] fix #1065: implement fail2ban api and gui
@ 2021-10-11 10:57 Oguz Bektas
2021-10-11 10:57 ` [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API Oguz Bektas
2021-10-11 10:57 ` [pve-devel] [PATCH v4 manager 2/2] fix #1065: ui: fail2ban gui for nodes Oguz Bektas
0 siblings, 2 replies; 8+ messages in thread
From: Oguz Bektas @ 2021-10-11 10:57 UTC (permalink / raw)
To: pve-devel
v3->v4:
* fix adding default values when enabling via API for the first time (to
avoid generating empty jail configuration)
pve-manager:
Oguz Bektas (1):
ui: fail2ban gui for nodes
www/manager6/Makefile | 1 +
www/manager6/grid/Fail2banOptions.js | 51 ++++++++++++++++++++++++++++
www/manager6/node/Config.js | 7 ++++
3 files changed, 59 insertions(+)
create mode 100644 www/manager6/grid/Fail2banOptions.js
pve-firewall:
Oguz Bektas (1):
implement fail2ban backend and API
debian/control | 1 +
src/PVE/API2/Firewall/Host.pm | 98 +++++++++++++++++++++++++++++++++
src/PVE/Firewall.pm | 101 +++++++++++++++++++++++++++++++++-
3 files changed, 199 insertions(+), 1 deletion(-)
--
2.30.2
^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API
2021-10-11 10:57 [pve-devel] [PATCH-SERIES manager firewall v4 0/2] fix #1065: implement fail2ban api and gui Oguz Bektas
@ 2021-10-11 10:57 ` Oguz Bektas
2021-10-19 13:43 ` Dominik Csapak
2021-10-11 10:57 ` [pve-devel] [PATCH v4 manager 2/2] fix #1065: ui: fail2ban gui for nodes Oguz Bektas
1 sibling, 1 reply; 8+ messages in thread
From: Oguz Bektas @ 2021-10-11 10:57 UTC (permalink / raw)
To: pve-devel
adds a section "[FAIL2BAN]" in the hostfw configuration, which allows
the properties 'maxretry' and 'bantime' (in minutes) for the GUI ports.
enable: whether fail2ban jail is enabled or not
maxretry: amount of login tries allowed
bantime: amount of minutes to ban suspicious host
the configuration file is derived from our wiki [0]
example API usage
=====
$ pvesh set /nodes/localhost/firewall/fail2ban --enable 1 --bantime 10 --maxretry 3
$ pvesh get /nodes/localhost/firewall/fail2ban
┌──────────┬───────┐
│ key │ value │
╞══════════╪═══════╡
│ bantime │ 10 │
├──────────┼───────┤
│ enable │ 1 │
├──────────┼───────┤
│ maxretry │ 3 │
└──────────┴───────┘
$ pvesh set /nodes/localhost/firewall/fail2ban --bantime 100
$ pvesh get /nodes/localhost/firewall/fail2ban
┌──────────┬───────┐
│ key │ value │
╞══════════╪═══════╡
│ bantime │ 100 │
├──────────┼───────┤
│ enable │ 1 │
├──────────┼───────┤
│ maxretry │ 3 │
└──────────┴───────┘
$ pvesh set /nodes/localhost/firewall/fail2ban --enable 0
$ pvesh get /nodes/localhost/firewall/fail2ban
┌──────────┬───────┐
│ key │ value │
╞══════════╪═══════╡
│ bantime │ 100 │
├──────────┼───────┤
│ enable │ 0 │
├──────────┼───────┤
│ maxretry │ 3 │
└──────────┴───────┘
=====
[0]: https://pve.proxmox.com/wiki/Fail2ban
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v3->v4:
* fix default values when enabling via API
debian/control | 1 +
src/PVE/API2/Firewall/Host.pm | 98 +++++++++++++++++++++++++++++++++
src/PVE/Firewall.pm | 101 +++++++++++++++++++++++++++++++++-
3 files changed, 199 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/API2/Firewall/Host.pm b/src/PVE/API2/Firewall/Host.pm
index b66ca55..535f188 100644
--- a/src/PVE/API2/Firewall/Host.pm
+++ b/src/PVE/API2/Firewall/Host.pm
@@ -62,6 +62,17 @@ my $add_option_properties = sub {
return $properties;
};
+my $fail2ban_properties = $PVE::Firewall::fail2ban_option_properties;
+
+my $add_fail2ban_properties = sub {
+ my ($properties) = @_;
+
+ foreach my $k (keys %$fail2ban_properties) {
+ $properties->{$k} = $fail2ban_properties->{$k};
+ }
+
+ return $properties;
+};
__PACKAGE__->register_method({
name => 'get_options',
@@ -148,6 +159,93 @@ __PACKAGE__->register_method({
return undef;
}});
+__PACKAGE__->register_method({
+ name => 'get_fail2ban',
+ path => 'fail2ban',
+ method => 'GET',
+ description => "Get host firewall fail2ban options.",
+ proxyto => 'node',
+ permissions => {
+ check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ },
+ },
+ returns => {
+ type => "object",
+ properties => $fail2ban_properties,
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+ my $hostfw_conf = PVE::Firewall::load_hostfw_conf($cluster_conf);
+
+ return PVE::Firewall::copy_opject_with_digest($hostfw_conf->{fail2ban});
+ }});
+
+
+
+__PACKAGE__->register_method({
+ name => 'set_fail2ban',
+ path => 'fail2ban',
+ method => 'PUT',
+ description => "Set host firewall fail2ban options.",
+ protected => 1,
+ proxyto => 'node',
+ permissions => {
+ check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => &$add_fail2ban_properties({
+ node => get_standard_option('pve-node'),
+ delete => {
+ type => 'string', format => 'pve-configid-list',
+ description => "A list of settings you want to delete.",
+ optional => 1,
+ },
+ digest => get_standard_option('pve-config-digest'),
+ }),
+ },
+ returns => { type => "null" },
+ code => sub {
+ my ($param) = @_;
+ PVE::Firewall::lock_hostfw_conf(10, sub {
+ my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+ my $hostfw_conf = PVE::Firewall::load_hostfw_conf($cluster_conf);
+
+ my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($hostfw_conf->{fail2ban});
+ PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+ if ($param->{delete}) {
+ foreach my $opt (PVE::Tools::split_list($param->{delete})) {
+ raise_param_exc({ delete => "no such option '$opt'" })
+ if !$fail2ban_properties->{$opt};
+ delete $hostfw_conf->{fail2ban}->{$opt};
+ }
+ }
+
+ if (defined($param->{enable})) {
+ $param->{enable} = $param->{enable} ? 1 : 0;
+ $hostfw_conf->{fail2ban}->{maxretry} = $param->{maxretry} ? $param->{maxretry} : $fail2ban_properties->{maxretry}->{default};
+ $hostfw_conf->{fail2ban}->{bantime} = $param->{bantime} ? $param->{bantime} : $fail2ban_properties->{bantime}->{default};
+ }
+
+ foreach my $k (keys %$fail2ban_properties) {
+ next if !defined($param->{$k});
+ $hostfw_conf->{fail2ban}->{$k} = $param->{$k};
+ }
+
+ PVE::Firewall::save_hostfw_conf($hostfw_conf);
+ });
+
+ return undef;
+ }});
+
__PACKAGE__->register_method({
name => 'log',
path => 'log',
diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm
index edc5336..92b77a4 100644
--- a/src/PVE/Firewall.pm
+++ b/src/PVE/Firewall.pm
@@ -1347,6 +1347,29 @@ our $host_option_properties = {
},
};
+our $fail2ban_option_properties = {
+ enable => {
+ description => "Enable or disable fail2ban on a node.",
+ type => 'boolean',
+ optional => 1,
+ default => 1,
+ },
+ maxretry => {
+ description => "Amount of failed tries to ban after.",
+ type => 'integer',
+ optional => 1,
+ minimum => 1,
+ default => 3,
+ },
+ bantime => {
+ description => "Minutes to ban suspicious IPs.",
+ type => 'integer',
+ optional => 1,
+ minimum => 1,
+ default => 5,
+ },
+};
+
our $vm_option_properties = {
enable => {
description => "Enable/disable firewall rules.",
@@ -2407,6 +2430,41 @@ sub ruleset_generate_vm_rules {
}
}
+sub generate_fail2ban_config {
+ my ($fail2ban_opts) = @_;
+
+ my $enable = $fail2ban_opts->{enable} ? 'true' : 'false';
+ my $maxretry = $fail2ban_opts->{maxretry};
+ my $bantime = $fail2ban_opts->{bantime} * 60; # convert minutes to seconds
+
+ 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) if !-f $filter_path;
+
+
+ my $fail2ban_jail = <<CONFIG;
+[proxmox]
+enabled = $enable
+port = https,http,8006
+filter = proxmox
+logpath = /var/log/daemon.log
+maxretry = $maxretry
+bantime = $bantime
+CONFIG
+
+ my $jail_path = "/etc/fail2ban/jail.d/proxmox.conf";
+ my $current_fail2ban_jail = PVE::Tools::file_get_contents($jail_path) if -f $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 +2995,16 @@ sub parse_alias {
return undef;
}
+sub parse_fail2ban_option {
+ my ($line) = @_;
+
+ if ($line =~ m/^(enable|maxretry|bantime):\s+(\d+)(?:\s*#.*)?$/) {
+ return ($1, int($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 +3033,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 +3119,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 "$err";
+ next;
+ }
+ $res->{fail2ban}->{$opt} = $value;
} elsif ($section eq 'rules') {
my $rule;
eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, $res, $rule_env); };
@@ -3251,6 +3331,21 @@ my $format_options = sub {
return $raw;
};
+my $format_fail2ban = sub {
+ my ($fail2ban_options) = @_;
+
+ my $raw = '';
+
+ $raw .= "[FAIL2BAN]\n\n";
+ foreach my $opt (keys %$fail2ban_options) {
+ $raw .= "$opt: $fail2ban_options->{$opt}\n";
+ }
+ $raw .= "\n";
+
+ return $raw;
+
+};
+
my $format_aliases = sub {
my ($aliases) = @_;
@@ -3620,7 +3715,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');
}
@@ -3630,7 +3725,9 @@ sub save_hostfw_conf {
my $raw = '';
my $options = $hostfw_conf->{options};
+ my $fail2ban_options = $hostfw_conf->{fail2ban};
$raw .= &$format_options($options) if $options && scalar(keys %$options);
+ $raw .= &$format_fail2ban($fail2ban_options) if $fail2ban_options && scalar(keys %$fail2ban_options);
my $rules = $hostfw_conf->{rules};
if ($rules && scalar(@$rules)) {
@@ -4590,6 +4687,8 @@ sub update {
}
my $hostfw_conf = load_hostfw_conf($cluster_conf);
+ my $fail2ban_opts = $hostfw_conf->{fail2ban};
+ generate_fail2ban_config($fail2ban_opts) if scalar(keys %$fail2ban_opts);
my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = compile($cluster_conf, $hostfw_conf);
--
2.30.2
^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH v4 manager 2/2] fix #1065: ui: fail2ban gui for nodes
2021-10-11 10:57 [pve-devel] [PATCH-SERIES manager firewall v4 0/2] fix #1065: implement fail2ban api and gui Oguz Bektas
2021-10-11 10:57 ` [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API Oguz Bektas
@ 2021-10-11 10:57 ` Oguz Bektas
2021-10-19 13:47 ` Dominik Csapak
1 sibling, 1 reply; 8+ messages in thread
From: Oguz Bektas @ 2021-10-11 10:57 UTC (permalink / raw)
To: pve-devel
adds a simple grid for fail2ban options into the node config panel
---
v4:
* no changes
www/manager6/Makefile | 1 +
www/manager6/grid/Fail2banOptions.js | 51 ++++++++++++++++++++++++++++
www/manager6/node/Config.js | 7 ++++
3 files changed, 59 insertions(+)
create mode 100644 www/manager6/grid/Fail2banOptions.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 7d491f57..ad9fe58a 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -74,6 +74,7 @@ JSSRC= \
grid/BackupView.js \
grid/FirewallAliases.js \
grid/FirewallOptions.js \
+ grid/Fail2banOptions.js \
grid/FirewallRules.js \
grid/PoolMembers.js \
grid/Replication.js \
diff --git a/www/manager6/grid/Fail2banOptions.js b/www/manager6/grid/Fail2banOptions.js
new file mode 100644
index 00000000..5de0c18c
--- /dev/null
+++ b/www/manager6/grid/Fail2banOptions.js
@@ -0,0 +1,51 @@
+Ext.define('PVE.Fail2banOptions', {
+ extend: 'Proxmox.grid.ObjectGrid',
+ alias: ['widget.pveFail2banOptions'],
+
+ base_url: undefined,
+
+ initComponent: function() {
+ var me = this;
+
+ me.rows = {};
+
+ me.add_boolean_row('enable', gettext("Enable Fail2Ban"));
+ me.add_integer_row('maxretry', gettext("Max retries"));
+ me.add_integer_row('bantime', gettext("Minutes to ban"));
+
+ var edit_btn = new Ext.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ handler: function() { me.run_editor(); },
+ });
+
+ var set_button_status = function() {
+ var sm = me.getSelectionModel();
+ var rec = sm.getSelection()[0];
+
+ if (!rec) {
+ edit_btn.disable();
+ return;
+ }
+ var rowdef = me.rows[rec.data.key];
+ edit_btn.setDisabled(!rowdef.editor);
+ };
+
+ Ext.apply(me, {
+ url: "/api2/json" + me.base_url,
+ tbar: [edit_btn],
+ editorConfig: {
+ url: "/api2/extjs" + me.base_url,
+ },
+ listeners: {
+ itemdblclick: me.run_editor,
+ selectionchange: set_button_status,
+ },
+ });
+
+ me.callParent();
+ me.on('activate', me.rstore.startUpdate);
+ me.on('destroy', me.rstore.stopUpdate);
+ me.on('deactivate', me.rstore.stopUpdate);
+ },
+});
diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js index 68f80391..9dbe8d0c 100644
--- a/www/manager6/node/Config.js
+++ b/www/manager6/node/Config.js
@@ -276,6 +276,13 @@ Ext.define('PVE.node.Config', {
base_url: '/nodes/' + nodename + '/firewall/options',
fwtype: 'node',
itemId: 'firewall-options',
+ },
+ {
+ xtype: 'pveFail2banOptions',
+ iconCls: 'fa fa-legal',
+ title: gettext('Fail2ban'),
+ base_url: '/nodes/' + nodename + '/firewall/fail2ban',
+ itemId: 'fail2ban-options',
});
}
--
2.30.2
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API
2021-10-11 10:57 ` [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API Oguz Bektas
@ 2021-10-19 13:43 ` Dominik Csapak
2021-10-20 12:11 ` Oguz Bektas
2021-10-20 14:09 ` Thomas Lamprecht
0 siblings, 2 replies; 8+ messages in thread
From: Dominik Csapak @ 2021-10-19 13:43 UTC (permalink / raw)
To: Proxmox VE development discussion, Oguz Bektas
while the code looks ok IMHO, i have some general questions:
* does it really make sense to hard depend on fail2ban?
could it not also make sense to have it as 'recommends' or 'suggests'?
setting enabled to 1 could then check if its installed and
raise an error
* if we do not plan to add more fail2ban options in our config,
i would rather see a combined fail2ban option (propertystring?)
that would go into the general host firewall options
that way we would not have to c&p the whole config parsing/setting api
and could have a single new option line in the gui instead
of a whole new panel with only 3 options (i think the majority of our
users will not use fail2ban)
does that make sense to you?
On 10/11/21 12:57, Oguz Bektas wrote:
> adds a section "[FAIL2BAN]" in the hostfw configuration, which allows
> the properties 'maxretry' and 'bantime' (in minutes) for the GUI ports.
>
> enable: whether fail2ban jail is enabled or not
> maxretry: amount of login tries allowed
> bantime: amount of minutes to ban suspicious host
>
> the configuration file is derived from our wiki [0]
>
> example API usage
> =====
> $ pvesh set /nodes/localhost/firewall/fail2ban --enable 1 --bantime 10 --maxretry 3
>
> $ pvesh get /nodes/localhost/firewall/fail2ban
> ┌──────────┬───────┐
> │ key │ value │
> ╞══════════╪═══════╡
> │ bantime │ 10 │
> ├──────────┼───────┤
> │ enable │ 1 │
> ├──────────┼───────┤
> │ maxretry │ 3 │
> └──────────┴───────┘
>
> $ pvesh set /nodes/localhost/firewall/fail2ban --bantime 100
> $ pvesh get /nodes/localhost/firewall/fail2ban
> ┌──────────┬───────┐
> │ key │ value │
> ╞══════════╪═══════╡
> │ bantime │ 100 │
> ├──────────┼───────┤
> │ enable │ 1 │
> ├──────────┼───────┤
> │ maxretry │ 3 │
> └──────────┴───────┘
>
> $ pvesh set /nodes/localhost/firewall/fail2ban --enable 0
> $ pvesh get /nodes/localhost/firewall/fail2ban
> ┌──────────┬───────┐
> │ key │ value │
> ╞══════════╪═══════╡
> │ bantime │ 100 │
> ├──────────┼───────┤
> │ enable │ 0 │
> ├──────────┼───────┤
> │ maxretry │ 3 │
> └──────────┴───────┘
> =====
>
> [0]: https://pve.proxmox.com/wiki/Fail2ban
>
> Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
> ---
> v3->v4:
> * fix default values when enabling via API
>
>
> debian/control | 1 +
> src/PVE/API2/Firewall/Host.pm | 98 +++++++++++++++++++++++++++++++++
> src/PVE/Firewall.pm | 101 +++++++++++++++++++++++++++++++++-
> 3 files changed, 199 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/API2/Firewall/Host.pm b/src/PVE/API2/Firewall/Host.pm
> index b66ca55..535f188 100644
> --- a/src/PVE/API2/Firewall/Host.pm
> +++ b/src/PVE/API2/Firewall/Host.pm
> @@ -62,6 +62,17 @@ my $add_option_properties = sub {
> return $properties;
> };
>
> +my $fail2ban_properties = $PVE::Firewall::fail2ban_option_properties;
> +
> +my $add_fail2ban_properties = sub {
> + my ($properties) = @_;
> +
> + foreach my $k (keys %$fail2ban_properties) {
> + $properties->{$k} = $fail2ban_properties->{$k};
> + }
> +
> + return $properties;
> +};
>
> __PACKAGE__->register_method({
> name => 'get_options',
> @@ -148,6 +159,93 @@ __PACKAGE__->register_method({
> return undef;
> }});
>
> +__PACKAGE__->register_method({
> + name => 'get_fail2ban',
> + path => 'fail2ban',
> + method => 'GET',
> + description => "Get host firewall fail2ban options.",
> + proxyto => 'node',
> + permissions => {
> + check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
> + },
> + parameters => {
> + additionalProperties => 0,
> + properties => {
> + node => get_standard_option('pve-node'),
> + },
> + },
> + returns => {
> + type => "object",
> + properties => $fail2ban_properties,
> + },
> + code => sub {
> + my ($param) = @_;
> +
> + my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
> + my $hostfw_conf = PVE::Firewall::load_hostfw_conf($cluster_conf);
> +
> + return PVE::Firewall::copy_opject_with_digest($hostfw_conf->{fail2ban});
> + }});
> +
> +
> +
> +__PACKAGE__->register_method({
> + name => 'set_fail2ban',
> + path => 'fail2ban',
> + method => 'PUT',
> + description => "Set host firewall fail2ban options.",
> + protected => 1,
> + proxyto => 'node',
> + permissions => {
> + check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
> + },
> + parameters => {
> + additionalProperties => 0,
> + properties => &$add_fail2ban_properties({
> + node => get_standard_option('pve-node'),
> + delete => {
> + type => 'string', format => 'pve-configid-list',
> + description => "A list of settings you want to delete.",
> + optional => 1,
> + },
> + digest => get_standard_option('pve-config-digest'),
> + }),
> + },
> + returns => { type => "null" },
> + code => sub {
> + my ($param) = @_;
> + PVE::Firewall::lock_hostfw_conf(10, sub {
> + my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
> + my $hostfw_conf = PVE::Firewall::load_hostfw_conf($cluster_conf);
> +
> + my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($hostfw_conf->{fail2ban});
> + PVE::Tools::assert_if_modified($digest, $param->{digest});
> +
> + if ($param->{delete}) {
> + foreach my $opt (PVE::Tools::split_list($param->{delete})) {
> + raise_param_exc({ delete => "no such option '$opt'" })
> + if !$fail2ban_properties->{$opt};
> + delete $hostfw_conf->{fail2ban}->{$opt};
> + }
> + }
> +
> + if (defined($param->{enable})) {
> + $param->{enable} = $param->{enable} ? 1 : 0;
> + $hostfw_conf->{fail2ban}->{maxretry} = $param->{maxretry} ? $param->{maxretry} : $fail2ban_properties->{maxretry}->{default};
> + $hostfw_conf->{fail2ban}->{bantime} = $param->{bantime} ? $param->{bantime} : $fail2ban_properties->{bantime}->{default};
> + }
> +
> + foreach my $k (keys %$fail2ban_properties) {
> + next if !defined($param->{$k});
> + $hostfw_conf->{fail2ban}->{$k} = $param->{$k};
> + }
> +
> + PVE::Firewall::save_hostfw_conf($hostfw_conf);
> + });
> +
> + return undef;
> + }});
> +
> __PACKAGE__->register_method({
> name => 'log',
> path => 'log',
> diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm
> index edc5336..92b77a4 100644
> --- a/src/PVE/Firewall.pm
> +++ b/src/PVE/Firewall.pm
> @@ -1347,6 +1347,29 @@ our $host_option_properties = {
> },
> };
>
> +our $fail2ban_option_properties = {
> + enable => {
> + description => "Enable or disable fail2ban on a node.",
> + type => 'boolean',
> + optional => 1,
> + default => 1,
> + },
> + maxretry => {
> + description => "Amount of failed tries to ban after.",
> + type => 'integer',
> + optional => 1,
> + minimum => 1,
> + default => 3,
> + },
> + bantime => {
> + description => "Minutes to ban suspicious IPs.",
> + type => 'integer',
> + optional => 1,
> + minimum => 1,
> + default => 5,
> + },
> +};
> +
> our $vm_option_properties = {
> enable => {
> description => "Enable/disable firewall rules.",
> @@ -2407,6 +2430,41 @@ sub ruleset_generate_vm_rules {
> }
> }
>
> +sub generate_fail2ban_config {
> + my ($fail2ban_opts) = @_;
> +
> + my $enable = $fail2ban_opts->{enable} ? 'true' : 'false';
> + my $maxretry = $fail2ban_opts->{maxretry};
> + my $bantime = $fail2ban_opts->{bantime} * 60; # convert minutes to seconds
> +
> + 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) if !-f $filter_path;
> +
> +
> + my $fail2ban_jail = <<CONFIG;
> +[proxmox]
> +enabled = $enable
> +port = https,http,8006
> +filter = proxmox
> +logpath = /var/log/daemon.log
> +maxretry = $maxretry
> +bantime = $bantime
> +CONFIG
> +
> + my $jail_path = "/etc/fail2ban/jail.d/proxmox.conf";
> + my $current_fail2ban_jail = PVE::Tools::file_get_contents($jail_path) if -f $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 +2995,16 @@ sub parse_alias {
> return undef;
> }
>
> +sub parse_fail2ban_option {
> + my ($line) = @_;
> +
> + if ($line =~ m/^(enable|maxretry|bantime):\s+(\d+)(?:\s*#.*)?$/) {
> + return ($1, int($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 +3033,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 +3119,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 "$err";
> + next;
> + }
> + $res->{fail2ban}->{$opt} = $value;
> } elsif ($section eq 'rules') {
> my $rule;
> eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, $res, $rule_env); };
> @@ -3251,6 +3331,21 @@ my $format_options = sub {
> return $raw;
> };
>
> +my $format_fail2ban = sub {
> + my ($fail2ban_options) = @_;
> +
> + my $raw = '';
> +
> + $raw .= "[FAIL2BAN]\n\n";
> + foreach my $opt (keys %$fail2ban_options) {
> + $raw .= "$opt: $fail2ban_options->{$opt}\n";
> + }
> + $raw .= "\n";
> +
> + return $raw;
> +
> +};
> +
> my $format_aliases = sub {
> my ($aliases) = @_;
>
> @@ -3620,7 +3715,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');
> }
>
> @@ -3630,7 +3725,9 @@ sub save_hostfw_conf {
> my $raw = '';
>
> my $options = $hostfw_conf->{options};
> + my $fail2ban_options = $hostfw_conf->{fail2ban};
> $raw .= &$format_options($options) if $options && scalar(keys %$options);
> + $raw .= &$format_fail2ban($fail2ban_options) if $fail2ban_options && scalar(keys %$fail2ban_options);
>
> my $rules = $hostfw_conf->{rules};
> if ($rules && scalar(@$rules)) {
> @@ -4590,6 +4687,8 @@ sub update {
> }
>
> my $hostfw_conf = load_hostfw_conf($cluster_conf);
> + my $fail2ban_opts = $hostfw_conf->{fail2ban};
> + generate_fail2ban_config($fail2ban_opts) if scalar(keys %$fail2ban_opts);
>
> my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = compile($cluster_conf, $hostfw_conf);
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [pve-devel] [PATCH v4 manager 2/2] fix #1065: ui: fail2ban gui for nodes
2021-10-11 10:57 ` [pve-devel] [PATCH v4 manager 2/2] fix #1065: ui: fail2ban gui for nodes Oguz Bektas
@ 2021-10-19 13:47 ` Dominik Csapak
0 siblings, 0 replies; 8+ messages in thread
From: Dominik Csapak @ 2021-10-19 13:47 UTC (permalink / raw)
To: Proxmox VE development discussion, Oguz Bektas
looks mostly ok (besides my comment about the propertystring and options
thing of the previous patch)
comment inline:
On 10/11/21 12:57, Oguz Bektas wrote:
> adds a simple grid for fail2ban options into the node config panel
>
> ---
> v4:
> * no changes
>
>
> www/manager6/Makefile | 1 +
> www/manager6/grid/Fail2banOptions.js | 51 ++++++++++++++++++++++++++++
> www/manager6/node/Config.js | 7 ++++
> 3 files changed, 59 insertions(+)
> create mode 100644 www/manager6/grid/Fail2banOptions.js
>
> diff --git a/www/manager6/Makefile b/www/manager6/Makefile
> index 7d491f57..ad9fe58a 100644
> --- a/www/manager6/Makefile
> +++ b/www/manager6/Makefile
> @@ -74,6 +74,7 @@ JSSRC= \
> grid/BackupView.js \
> grid/FirewallAliases.js \
> grid/FirewallOptions.js \
> + grid/Fail2banOptions.js \
> grid/FirewallRules.js \
> grid/PoolMembers.js \
> grid/Replication.js \
> diff --git a/www/manager6/grid/Fail2banOptions.js b/www/manager6/grid/Fail2banOptions.js
> new file mode 100644
> index 00000000..5de0c18c
> --- /dev/null
> +++ b/www/manager6/grid/Fail2banOptions.js
> @@ -0,0 +1,51 @@
> +Ext.define('PVE.Fail2banOptions', {
> + extend: 'Proxmox.grid.ObjectGrid',
> + alias: ['widget.pveFail2banOptions'],
> +
> + base_url: undefined,
> +
> + initComponent: function() {
> + var me = this;
> +
> + me.rows = {};
> +
> + me.add_boolean_row('enable', gettext("Enable Fail2Ban"));
> + me.add_integer_row('maxretry', gettext("Max retries"));
> + me.add_integer_row('bantime', gettext("Minutes to ban"));
> +
> + var edit_btn = new Ext.Button({
> + text: gettext('Edit'),
> + disabled: true,
> + handler: function() { me.run_editor(); },
> + });
> +
> + var set_button_status = function() {
> + var sm = me.getSelectionModel();
> + var rec = sm.getSelection()[0];
> +
> + if (!rec) {
> + edit_btn.disable();
> + return;
> + }
> + var rowdef = me.rows[rec.data.key];
> + edit_btn.setDisabled(!rowdef.editor);
> + };
> +
> + Ext.apply(me, {
> + url: "/api2/json" + me.base_url,
> + tbar: [edit_btn],
> + editorConfig: {
> + url: "/api2/extjs" + me.base_url,
> + },
> + listeners: {
> + itemdblclick: me.run_editor,
> + selectionchange: set_button_status,
> + },
> + });
i know its mostly copy&pasted, but i'd still rather see a more
declarative approach to this, even if it just defining the functions
inline here.
> +
> + me.callParent();
> + me.on('activate', me.rstore.startUpdate);
> + me.on('destroy', me.rstore.stopUpdate);
> + me.on('deactivate', me.rstore.stopUpdate);
> + },
> +});
> diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js index 68f80391..9dbe8d0c 100644
> --- a/www/manager6/node/Config.js
> +++ b/www/manager6/node/Config.js
> @@ -276,6 +276,13 @@ Ext.define('PVE.node.Config', {
> base_url: '/nodes/' + nodename + '/firewall/options',
> fwtype: 'node',
> itemId: 'firewall-options',
> + },
> + {
> + xtype: 'pveFail2banOptions',
> + iconCls: 'fa fa-legal',
> + title: gettext('Fail2ban'),
> + base_url: '/nodes/' + nodename + '/firewall/fail2ban',
> + itemId: 'fail2ban-options',
> });
> }
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API
2021-10-19 13:43 ` Dominik Csapak
@ 2021-10-20 12:11 ` Oguz Bektas
2021-10-20 14:12 ` Thomas Lamprecht
2021-10-20 14:09 ` Thomas Lamprecht
1 sibling, 1 reply; 8+ messages in thread
From: Oguz Bektas @ 2021-10-20 12:11 UTC (permalink / raw)
To: Dominik Csapak; +Cc: Proxmox VE development discussion
On Tue, Oct 19, 2021 at 03:43:49PM +0200, Dominik Csapak wrote:
> while the code looks ok IMHO, i have some general questions:
> * does it really make sense to hard depend on fail2ban?
> could it not also make sense to have it as 'recommends' or 'suggests'?
> setting enabled to 1 could then check if its installed and
> raise an error
>
> * if we do not plan to add more fail2ban options in our config,
> i would rather see a combined fail2ban option (propertystring?)
> that would go into the general host firewall options
>
> that way we would not have to c&p the whole config parsing/setting api
> and could have a single new option line in the gui instead
> of a whole new panel with only 3 options (i think the majority of our
> users will not use fail2ban)
>
> does that make sense to you?
>
well if we hide it like that inside the menu, then surely nobody will
use it ;)
separate panel makes it more visible IMO. it shouldn't be hidden 3
layers deep in the options menu (Host -> Firewall -> Options -> Fail2ban
-> Enable) for such a simple feature, i think a lot more people would
use it if it's placed in a visible location (Host -> Fail2ban ->
Enable). if you really insist on putting it in the firewall options menu
then i'll have to insist for it to be installed & enabled by default :)
i didn't see any good reason to not have it installed and enabled with
sane defaults since it doesn't hurt anything IMHO... that's why i went
with hard dependency instead of 'recommends' or 'suggests'.
also i wanted to have a separate API endpoint which is why i didnt go for
adding it into the options section.
though we can still make it take a property string as option i suppose.
oguz
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API
2021-10-19 13:43 ` Dominik Csapak
2021-10-20 12:11 ` Oguz Bektas
@ 2021-10-20 14:09 ` Thomas Lamprecht
1 sibling, 0 replies; 8+ messages in thread
From: Thomas Lamprecht @ 2021-10-20 14:09 UTC (permalink / raw)
To: Proxmox VE development discussion, Dominik Csapak, Oguz Bektas
On 19.10.21 15:43, Dominik Csapak wrote:
> while the code looks ok IMHO, i have some general questions:
> * does it really make sense to hard depend on fail2ban?
> could it not also make sense to have it as 'recommends' or 'suggests'?
> setting enabled to 1 could then check if its installed and
> raise an error
fwiw, it does not make sense to me to have a hard dependency here, as I pointed
out in pretty much every revision of this series, that and most other things (e.g.,
trying if we can simply generate the rules here ourself) where rather ignored so
after the third iteration I went "tit for tat" and ignored the whole thing..
>
> * if we do not plan to add more fail2ban options in our config,
> i would rather see a combined fail2ban option (propertystring?)
> that would go into the general host firewall options
>
> that way we would not have to c&p the whole config parsing/setting api
> and could have a single new option line in the gui instead
> of a whole new panel with only 3 options (i think the majority of our
> users will not use fail2ban)
would make much more sense, it's an simple option and bringing down UX by
crowding the interface for a simple an option that one sets one-time only
anyway seems not ideal to me..
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API
2021-10-20 12:11 ` Oguz Bektas
@ 2021-10-20 14:12 ` Thomas Lamprecht
0 siblings, 0 replies; 8+ messages in thread
From: Thomas Lamprecht @ 2021-10-20 14:12 UTC (permalink / raw)
To: Oguz Bektas, Dominik Csapak, Proxmox VE development discussion
On 20.10.21 14:11, Oguz Bektas wrote:
> On Tue, Oct 19, 2021 at 03:43:49PM +0200, Dominik Csapak wrote:
>> while the code looks ok IMHO, i have some general questions:
>> * does it really make sense to hard depend on fail2ban?
>> could it not also make sense to have it as 'recommends' or 'suggests'?
>> setting enabled to 1 could then check if its installed and
>> raise an error
>>
>> * if we do not plan to add more fail2ban options in our config,
>> i would rather see a combined fail2ban option (propertystring?)
>> that would go into the general host firewall options
>>
>> that way we would not have to c&p the whole config parsing/setting api
>> and could have a single new option line in the gui instead
>> of a whole new panel with only 3 options (i think the majority of our
>> users will not use fail2ban)
>
>>
>> does that make sense to you?
>>
>
> well if we hide it like that inside the menu, then surely nobody will
> use it ;)
>
> separate panel makes it more visible IMO. it shouldn't be hidden 3
> layers deep in the options menu (Host -> Firewall -> Options -> Fail2ban
> -> Enable) for such a simple feature, i think a lot more people would
> use it if it's placed in a visible location (Host -> Fail2ban ->
> Enable). if you really insist on putting it in the firewall options menu
> then i'll have to insist for it to be installed & enabled by default :)
With that arguing every option would need to be its own panel placed at the
top-level.. PVE's interface is complex as is, sensible grouping simple one time
option actually helps in UX and to find stuff, documentation can ensure features
are found, they provide a central searchable place for that, after all.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2021-10-20 14:12 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-11 10:57 [pve-devel] [PATCH-SERIES manager firewall v4 0/2] fix #1065: implement fail2ban api and gui Oguz Bektas
2021-10-11 10:57 ` [pve-devel] [PATCH v4 firewall 1/2] implement fail2ban backend and API Oguz Bektas
2021-10-19 13:43 ` Dominik Csapak
2021-10-20 12:11 ` Oguz Bektas
2021-10-20 14:12 ` Thomas Lamprecht
2021-10-20 14:09 ` Thomas Lamprecht
2021-10-11 10:57 ` [pve-devel] [PATCH v4 manager 2/2] fix #1065: ui: fail2ban gui for nodes Oguz Bektas
2021-10-19 13:47 ` Dominik Csapak
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox