public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets
@ 2024-11-18 17:43 Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 01/10] sdn: add vnet firewall configuration Stefan Hanreich
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel

## Introduction

This patch series introduces a new direction for firewall rules: forward.
Additionally this patch series introduces defining firewall rules on a vnet
level.

## Use Cases

For hosts:
* hosts utilizing NAT can define firewall rules for NATed traffic
* hosts utilizing EVPN zones can define rules for exit node traffic
* hosts acting as gateway can firewall the traffic that passes through them

For vnets:
* can create firewall rules globally without having to attach/update security
  groups to every newly created VM

This patch series is particularly useful when combined with my other current RFC
'autogenerate ipsets for sdn objects'. It enables users to quickly define rules
like:

on the host level:
* only SNAT HTTP traffic from hosts in this vnet to a specific host
* restricting traffic routed from hosts in one vnet to another vnet

on the vnet level:
* only allow DHCP/DNS traffic inside a bridge to the gateway

Not only does this streamline creating firewall rules, it also enables users to
create firewall rules that haven't been possible before and needed to rely on
external firewall appliances.

Since forwarded traffic goes *both* ways, you generally have to create two rules
in case of bi-directional traffic. It might make sense to simplify this in the
future by adding an additional option to the firewall config scheme that
specifies that rules in the other direction should also get automatically
generated.

## Usage

For creating forward rules on the cluster/host level, you simply create a new
rule with the new 'forward' direction. It uses the existing configuration files.

For creating them on a vnet level, there are new firewall configuration files
located under '/etc/pve/sdn/firewall/<vnet>.fw'. It utilizes the same
configuration format as the existing firewall configuration files. You can only
define rules with direction 'forward' on a vnet-level.

## Dependencies

depends on my other patch series 'autogenerate ipsets for sdn objects', further
instruction can be found there.

Furthermore:
* proxmox-firewall depends on proxmox-ve-rs
* pve-manager depends on pve-firewall
* pve-network depends on pve-firewall


Changes from v4 to v5:
* moved the check_vnet_access sub to the new Helpers module

Changes from v3 to v4:
* fix small bug that doesn't show warning when adding a new firewall rule on
  vnet-level with direction forward
* improve documentation further, added a new section for directions

Changes from v2 to v3:
* do not allow REJECT rules in forward chains in UI and backend - thanks @Hannes
* use arrow syntax for calling functions instead of &$ - thanks @Hannes
* set width of new VNet firewall panel via flex, to avoid weird looking panel -
  thanks @Hannes
* improve documentation - thanks @Hannes
* show a warning in the frontend when creating forward rules - thanks @Thomas

Changes from RFC to v2:
* Fixed several bugs
    * SDN Firewall folder does not automatically created (thanks @Gabriel)
    * Firewall flushes the bridge table if no guest firewall is active, even
      though VNet-level rules exist
* VNet-level firewall now matches on both input and output interface
* Introduced log option for VNet firewall
* Improved style of perl code (thanks @Thomas)
* promox-firewall now verifies the directions of rules
    * added some additional tests to verify this behavior
* added documentation

pve-firewall:

Stefan Hanreich (3):
  sdn: add vnet firewall configuration
  api: add vnet endpoints
  firewall: move to arrow syntax for calling functions

 src/PVE/API2/Firewall/Helpers.pm |  14 ++
 src/PVE/API2/Firewall/Makefile   |   1 +
 src/PVE/API2/Firewall/Rules.pm   |  84 ++++++++++++
 src/PVE/API2/Firewall/Vnet.pm    | 156 +++++++++++++++++++++++
 src/PVE/Firewall.pm              | 211 ++++++++++++++++++++++++-------
 src/PVE/Firewall/Helpers.pm      |  12 ++
 6 files changed, 434 insertions(+), 44 deletions(-)
 create mode 100644 src/PVE/API2/Firewall/Vnet.pm


pve-manager:

Stefan Hanreich (5):
  firewall: add forward direction to rule panel
  firewall: add vnet to firewall options component
  firewall: make base_url dynamically configurable in options component
  sdn: add firewall panel
  firewall: rules: show warning when creating forward rules

 www/manager6/Makefile                |   2 +
 www/manager6/dc/Config.js            |   9 +++
 www/manager6/dc/SecurityGroups.js    |   1 +
 www/manager6/grid/FirewallOptions.js |  74 +++++++++++++++----
 www/manager6/grid/FirewallRules.js   | 103 +++++++++++++++++++++++----
 www/manager6/lxc/Config.js           |   1 +
 www/manager6/node/Config.js          |   1 +
 www/manager6/qemu/Config.js          |   1 +
 www/manager6/sdn/FirewallPanel.js    |  50 +++++++++++++
 www/manager6/sdn/FirewallVnetView.js |  77 ++++++++++++++++++++
 10 files changed, 293 insertions(+), 26 deletions(-)
 create mode 100644 www/manager6/sdn/FirewallPanel.js
 create mode 100644 www/manager6/sdn/FirewallVnetView.js


pve-network:

Stefan Hanreich (1):
  firewall: add endpoints for vnet-level firewall

 src/PVE/API2/Network/SDN/Vnets.pm | 6 ++++++
 1 file changed, 6 insertions(+)


pve-docs:

Stefan Hanreich (1):
  firewall: add documentation for forward direction and vnet zone

 Makefile                      |  1 +
 gen-pve-firewall-vnet-opts.pl | 12 +++++
 pve-firewall-vnet-opts.adoc   |  8 +++
 pve-firewall.adoc             | 98 ++++++++++++++++++++++++++++++++---
 4 files changed, 112 insertions(+), 7 deletions(-)
 create mode 100755 gen-pve-firewall-vnet-opts.pl
 create mode 100644 pve-firewall-vnet-opts.adoc


Summary over all repositories:
  21 files changed, 845 insertions(+), 77 deletions(-)

-- 
Generated by git-murpp 0.6.0

_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-firewall v5 01/10] sdn: add vnet firewall configuration
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 02/10] api: add vnet endpoints Stefan Hanreich
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel; +Cc: Wolfgang Bumiller

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Tested-by: Hannes Dürr <h.duerr@proxmox.com>
---
 src/PVE/Firewall.pm         | 127 ++++++++++++++++++++++++++++++++++--
 src/PVE/Firewall/Helpers.pm |  12 ++++
 2 files changed, 132 insertions(+), 7 deletions(-)

diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm
index 8cfe55d..5f17ffa 100644
--- a/src/PVE/Firewall.pm
+++ b/src/PVE/Firewall.pm
@@ -30,6 +30,7 @@ use PVE::RS::Firewall::SDN;
 
 my $pvefw_conf_dir = "/etc/pve/firewall";
 my $clusterfw_conf_filename = "$pvefw_conf_dir/cluster.fw";
+my $vnetfw_conf_dir = "/etc/pve/sdn/firewall";
 
 # dynamically include PVE::QemuServer and PVE::LXC
 # to avoid dependency problems
@@ -1291,6 +1292,12 @@ our $cluster_option_properties = {
 	optional => 1,
 	enum => ['ACCEPT', 'REJECT', 'DROP'],
     },
+    policy_forward => {
+	description => "Forward policy.",
+	type => 'string',
+	optional => 1,
+	enum => ['ACCEPT', 'DROP'],
+    },
     log_ratelimit => {
 	description => "Log ratelimiting settings",
 	type => 'string', format => {
@@ -1330,6 +1337,8 @@ our $host_option_properties = {
 	description => "Log level for incoming traffic." }),
     log_level_out =>  get_standard_option('pve-fw-loglevel', {
 	description => "Log level for outgoing traffic." }),
+    log_level_forward =>  get_standard_option('pve-fw-loglevel', {
+	description => "Log level for forwarded traffic." }),
     tcp_flags_log_level =>  get_standard_option('pve-fw-loglevel', {
 	description => "Log level for illegal tcp flags filter." }),
     smurf_log_level =>  get_standard_option('pve-fw-loglevel', {
@@ -1477,6 +1486,23 @@ our $vm_option_properties = {
 
 };
 
+our $vnet_option_properties = {
+    enable => {
+	description => "Enable/disable firewall rules.",
+	type => 'boolean',
+	default => 0,
+	optional => 1,
+    },
+    policy_forward => {
+	description => "Forward policy.",
+	type => 'string',
+	optional => 1,
+	enum => ['ACCEPT', 'DROP'],
+    },
+    log_level_forward =>  get_standard_option('pve-fw-loglevel', {
+	description => "Log level for forwarded traffic." }),
+};
+
 
 my $addr_list_descr = "This can refer to a single IP address, an IP set ('+ipsetname') or an IP alias definition. You can also specify an address range like '20.34.101.207-201.3.9.99', or a list of IP addresses and networks (entries are separated by comma). Please do not mix IPv4 and IPv6 addresses inside such lists.";
 
@@ -1494,7 +1520,7 @@ my $rule_properties = {
 	description => "Rule type.",
 	type => 'string',
 	optional => 1,
-	enum => ['in', 'out', 'group'],
+	enum => ['in', 'out', 'forward', 'group'],
     },
     action => {
 	description => "Rule action ('ACCEPT', 'DROP', 'REJECT') or security group name.",
@@ -1652,10 +1678,20 @@ my $rule_env_iface_lookup = {
     'ct' => 1,
     'vm' => 1,
     'group' => 0,
+    'vnet' => 0,
     'cluster' => 1,
     'host' => 1,
 };
 
+my $rule_env_direction_lookup = {
+    'ct' => ['in', 'out', 'group'],
+    'vm' => ['in', 'out', 'group'],
+    'group' => ['in', 'out', 'forward'],
+    'cluster' => ['in', 'out', 'forward', 'group'],
+    'host' => ['in', 'out', 'forward', 'group'],
+    'vnet' => ['forward', 'group'],
+};
+
 sub verify_rule {
     my ($rule, $cluster_conf, $fw_conf, $rule_env, $noerr) = @_;
 
@@ -1729,8 +1765,17 @@ sub verify_rule {
     &$add_error('action', "missing property") if !$action;
 
     if ($type) {
-	if ($type eq  'in' || $type eq 'out') {
-	    &$add_error('action', "unknown action '$action'")
+	my $valid_types = $rule_env_direction_lookup->{$rule_env}
+	    or die "unknown rule_env '$rule_env'\n";
+
+	$add_error->('type', "invalid rule type '$type' for rule_env '$rule_env'")
+	    if !(grep { $_ eq $type } @$valid_types);
+
+	if ($type eq  'in' || $type eq 'out' || $type eq 'forward') {
+	    $add_error->('action', 'cannot define REJECT rules on forward chain')
+		if $type eq 'forward' && $action eq 'REJECT';
+
+	    $add_error->('action', "unknown action '$action'")
 		if $action && ($action !~ m/^(ACCEPT|DROP|REJECT)$/);
 	} elsif ($type eq 'group') {
 	    &$add_error('type', "security groups not allowed")
@@ -2836,7 +2881,7 @@ sub parse_fw_rule {
     $rule->{type} = lc($1);
     $rule->{action} = $2;
 
-    if ($rule->{type} eq  'in' || $rule->{type} eq 'out') {
+    if ($rule->{type} eq  'in' || $rule->{type} eq 'out' || $rule->{type} eq 'forward') {
 	if ($rule->{action} =~ m/^(\S+)\((ACCEPT|DROP|REJECT)\)$/) {
 	    $rule->{macro} = $1;
 	    $rule->{action} = $2;
@@ -2950,7 +2995,7 @@ sub parse_hostfw_option {
     if ($line =~ m/^(enable|nosmurfs|tcpflags|ndp|log_nf_conntrack|nf_conntrack_allow_invalid|protection_synflood|nftables):\s*(0|1)\s*$/i) {
 	$opt = lc($1);
 	$value = int($2);
-    } elsif ($line =~ m/^(log_level_in|log_level_out|tcp_flags_log_level|smurf_log_level):\s*(($loglevels)\s*)?$/i) {
+    } elsif ($line =~ m/^(log_level_(?:in|out|forward)|tcp_flags_log_level|smurf_log_level):\s*(($loglevels)\s*)?$/i) {
 	$opt = lc($1);
 	$value = $2 ? lc($3) : '';
     } elsif ($line =~ m/^(nf_conntrack_helpers):\s*(((\S+)[,]?)+)\s*$/i) {
@@ -2981,7 +3026,7 @@ sub parse_clusterfw_option {
     } elsif ($line =~ m/^(ebtables):\s*(0|1)\s*$/i) {
 	$opt = lc($1);
 	$value = int($2);
-    } elsif ($line =~ m/^(policy_(in|out)):\s*(ACCEPT|DROP|REJECT)\s*$/i) {
+    } elsif ($line =~ m/^(policy_(in|out|forward)):\s*(ACCEPT|DROP|REJECT)\s*$/i) {
 	$opt = lc($1);
 	$value = uc($3);
     } elsif ($line =~ m/^(log_ratelimit):\s*(\S+)\s*$/) {
@@ -2994,6 +3039,24 @@ sub parse_clusterfw_option {
     return ($opt, $value);
 }
 
+sub parse_vnetfw_option {
+    my ($line) = @_;
+
+    my ($opt, $value);
+
+    if ($line =~ m/^(enable):\s*(\d+)\s*$/i) {
+	$opt = lc($1);
+	$value = int($2);
+    } elsif ($line =~ m/^(policy_forward):\s*(ACCEPT|DROP)\s*$/i) {
+	$opt = lc($1);
+	$value = uc($2);
+    } else {
+	die "can't parse option '$line'\n"
+    }
+
+    return ($opt, $value);
+}
+
 sub resolve_alias {
     my ($clusterfw_conf, $fw_conf, $cidr, $scope) = @_;
 
@@ -3160,6 +3223,8 @@ sub generic_fw_config_parser {
 		    ($opt, $value) = parse_clusterfw_option($line);
 		} elsif ($rule_env eq 'host') {
 		    ($opt, $value) = parse_hostfw_option($line);
+		} elsif ($rule_env eq 'vnet') {
+		    ($opt, $value) = parse_vnetfw_option($line);
 		} else {
 		    ($opt, $value) = parse_vmfw_option($line);
 		}
@@ -3299,6 +3364,10 @@ sub lock_vmfw_conf {
     return PVE::Firewall::Helpers::lock_vmfw_conf(@_);
 }
 
+sub lock_vnetfw_conf {
+    return PVE::Firewall::Helpers::lock_vnetfw_conf(@_);
+}
+
 sub load_vmfw_conf {
     my ($cluster_conf, $rule_env, $vmid, $dir) = @_;
 
@@ -3325,7 +3394,7 @@ my $format_rules = sub {
     my $raw = '';
 
     foreach my $rule (@$rules) {
-	if ($rule->{type} eq  'in' || $rule->{type} eq 'out' || $rule->{type} eq 'group') {
+	if (grep { $_ eq $rule->{type} } qw(in out forward group)) {
 	    $raw .= '|' if defined($rule->{enable}) && !$rule->{enable};
 	    $raw .= uc($rule->{type});
 	    if ($rule->{macro}) {
@@ -3776,6 +3845,50 @@ sub save_hostfw_conf {
     }
 }
 
+sub load_vnetfw_conf {
+    my ($cluster_conf, $rule_env, $vnet, $dir) = @_;
+
+    $rule_env = 'vnet' if !defined($rule_env);
+
+    my $filename = "$vnetfw_conf_dir/$vnet.fw";
+
+    my $empty_conf = {
+	rules => [],
+	options => {},
+    };
+
+    my $vnetfw_conf = generic_fw_config_parser($filename, $cluster_conf, $empty_conf, $rule_env);
+    $vnetfw_conf->{vnet} = $vnet;
+
+    return $vnetfw_conf;
+}
+
+sub save_vnetfw_conf {
+    my ($vnet, $conf) = @_;
+
+    my $filename = "$vnetfw_conf_dir/$vnet.fw";
+
+    my $raw = '';
+
+    my $options = $conf->{options};
+    $raw .= &$format_options($options) if $options && scalar(keys %$options);
+
+    my $rules = $conf->{rules};
+    if ($rules && scalar(@$rules)) {
+	$raw .= "[RULES]\n\n";
+	$raw .= &$format_rules($rules, 1);
+	$raw .= "\n";
+    }
+
+    mkdir($vnetfw_conf_dir, 0755) if !-d $vnetfw_conf_dir;
+
+    if ($raw) {
+	PVE::Tools::file_set_contents($filename, $raw);
+    } else {
+	unlink $filename;
+    }
+}
+
 sub compile {
     my ($cluster_conf, $hostfw_conf, $vmdata, $corosync_conf) = @_;
 
diff --git a/src/PVE/Firewall/Helpers.pm b/src/PVE/Firewall/Helpers.pm
index 7dcbca3..0b465ae 100644
--- a/src/PVE/Firewall/Helpers.pm
+++ b/src/PVE/Firewall/Helpers.pm
@@ -32,6 +32,18 @@ sub lock_vmfw_conf {
     return $res;
 }
 
+sub lock_vnetfw_conf {
+    my ($vnet, $timeout, $code, @param) = @_;
+
+    die "can't lock vnet firewall config for undefined vnet\n"
+	if !defined($vnet);
+
+    my $res = PVE::Cluster::cfs_lock_firewall("vnet-$vnet", $timeout, $code, @param);
+    die $@ if $@;
+
+    return $res;
+}
+
 sub remove_vmfw_conf {
     my ($vmid) = @_;
 
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-firewall v5 02/10] api: add vnet endpoints
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 01/10] sdn: add vnet firewall configuration Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 03/10] firewall: move to arrow syntax for calling functions Stefan Hanreich
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/API2/Firewall/Helpers.pm |  14 +++
 src/PVE/API2/Firewall/Makefile   |   1 +
 src/PVE/API2/Firewall/Rules.pm   |  84 +++++++++++++++++
 src/PVE/API2/Firewall/Vnet.pm    | 156 +++++++++++++++++++++++++++++++
 src/PVE/Firewall.pm              |  10 ++
 5 files changed, 265 insertions(+)
 create mode 100644 src/PVE/API2/Firewall/Vnet.pm

diff --git a/src/PVE/API2/Firewall/Helpers.pm b/src/PVE/API2/Firewall/Helpers.pm
index a8c6fea..e7c3452 100644
--- a/src/PVE/API2/Firewall/Helpers.pm
+++ b/src/PVE/API2/Firewall/Helpers.pm
@@ -47,4 +47,18 @@ sub get_allowed_vms {
     ];
 }
 
+sub check_vnet_access {
+    my ($vnetid, $privileges) = @_;
+
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid, 1)
+	or die "invalid vnet specified";
+
+    my $zoneid = $vnet->{zone};
+
+    my $rpcenv = PVE::RPCEnvironment::get();
+    my $authuser = $rpcenv->get_user();
+
+    $rpcenv->check_any($authuser, "/sdn/zones/$zoneid/$vnetid", $privileges);
+};
+
 1;
diff --git a/src/PVE/API2/Firewall/Makefile b/src/PVE/API2/Firewall/Makefile
index 6be8261..c7c9427 100644
--- a/src/PVE/API2/Firewall/Makefile
+++ b/src/PVE/API2/Firewall/Makefile
@@ -10,6 +10,7 @@ LIB_SOURCES=			\
 	Cluster.pm		\
 	Host.pm			\
 	VM.pm			\
+	Vnet.pm			\
 	Groups.pm
 
 all:
diff --git a/src/PVE/API2/Firewall/Rules.pm b/src/PVE/API2/Firewall/Rules.pm
index 874fc37..18d7650 100644
--- a/src/PVE/API2/Firewall/Rules.pm
+++ b/src/PVE/API2/Firewall/Rules.pm
@@ -19,6 +19,25 @@ my $api_properties = {
     },
 };
 
+=head3 check_privileges_for_method($class, $method_name, $param)
+
+If the permission checks from the register_method() call are not sufficient,
+this function can be overriden for performing additional permission checks
+before API methods are executed. If the permission check fails, this function
+should die with an appropriate error message. The name of the method calling
+this function is provided by C<$method_name> and the parameters of the API
+method are provided by C<$param>
+
+Default implementation is a no-op to preserve backwards compatibility with
+existing subclasses, since this got added later on. It preserves existing
+behavior without having to change every subclass.
+
+=cut
+
+sub check_privileges_for_method {
+    my ($class, $method_name, $param) = @_;
+}
+
 sub lock_config {
     my ($class, $param, $code) = @_;
 
@@ -94,6 +113,8 @@ sub register_get_rules {
 	code => sub {
 	    my ($param) = @_;
 
+	    $class->check_privileges_for_method('get_rules', $param);
+
 	    my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
 
 	    my ($list, $digest) = PVE::Firewall::copy_list_with_digest($rules);
@@ -191,6 +212,8 @@ sub register_get_rule {
 	code => sub {
 	    my ($param) = @_;
 
+	    $class->check_privileges_for_method('get_rule', $param);
+
 	    my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
 
 	    my ($list, $digest) = PVE::Firewall::copy_list_with_digest($rules);
@@ -231,6 +254,8 @@ sub register_create_rule {
 	code => sub {
 	    my ($param) = @_;
 
+	    $class->check_privileges_for_method('create_rule', $param);
+
 	    $class->lock_config($param, sub {
 		my ($param) = @_;
 
@@ -299,6 +324,8 @@ sub register_update_rule {
 	code => sub {
 	    my ($param) = @_;
 
+	    $class->check_privileges_for_method('update_rule', $param);
+
 	    $class->lock_config($param, sub {
 		my ($param) = @_;
 
@@ -372,6 +399,8 @@ sub register_delete_rule {
 	code => sub {
 	    my ($param) = @_;
 
+	    $class->check_privileges_for_method('delete_rule', $param);
+
 	    $class->lock_config($param, sub {
 		my ($param) = @_;
 
@@ -647,4 +676,59 @@ sub save_rules {
 
 __PACKAGE__->register_handlers();
 
+package PVE::API2::Firewall::VnetRules;
+
+use strict;
+use warnings;
+use PVE::JSONSchema qw(get_standard_option);
+
+use base qw(PVE::API2::Firewall::RulesBase);
+
+__PACKAGE__->additional_parameters({
+    vnet => get_standard_option('pve-sdn-vnet-id'),
+});
+
+sub check_privileges_for_method {
+    my ($class, $method_name, $param) = @_;
+
+    if ($method_name eq 'get_rule' || $method_name eq 'get_rules') {
+	PVE::API2::Firewall::Helpers::check_vnet_access($param->{vnet}, ['SDN.Audit', 'SDN.Allocate']);
+    } elsif ($method_name =~ '(update|create|delete)_rule') {
+	PVE::API2::Firewall::Helpers::check_vnet_access($param->{vnet}, ['SDN.Allocate']);
+    } else {
+	die "unknown method: $method_name";
+    }
+}
+
+sub rule_env {
+    my ($class, $param) = @_;
+
+    return 'vnet';
+}
+
+sub lock_config {
+    my ($class, $param, $code) = @_;
+
+    PVE::Firewall::lock_vnetfw_conf($param->{vnet}, 10, $code, $param);
+}
+
+sub load_config {
+    my ($class, $param) = @_;
+
+    my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+    my $fw_conf = PVE::Firewall::load_vnetfw_conf($cluster_conf, 'vnet', $param->{vnet});
+    my $rules = $fw_conf->{rules};
+
+    return ($cluster_conf, $fw_conf, $rules);
+}
+
+sub save_rules {
+    my ($class, $param, $fw_conf, $rules) = @_;
+
+    $fw_conf->{rules} = $rules;
+    PVE::Firewall::save_vnetfw_conf($param->{vnet}, $fw_conf);
+}
+
+__PACKAGE__->register_handlers();
+
 1;
diff --git a/src/PVE/API2/Firewall/Vnet.pm b/src/PVE/API2/Firewall/Vnet.pm
new file mode 100644
index 0000000..cfba1ae
--- /dev/null
+++ b/src/PVE/API2/Firewall/Vnet.pm
@@ -0,0 +1,156 @@
+package PVE::API2::Firewall::Vnet;
+
+use strict;
+use warnings;
+
+use Storable qw(dclone);
+
+use PVE::Exception qw(raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::Firewall;
+use PVE::API2::Firewall::Rules;
+use PVE::API2::Firewall::Helpers;
+
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Firewall::VnetRules",
+    path => 'rules',
+});
+
+__PACKAGE__->register_method({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "Directory index.",
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    vnet => get_standard_option('pve-sdn-vnet-id'),
+	},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => "object",
+	    properties => {},
+	},
+	links => [ { rel => 'child', href => "{name}" } ],
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $result = [
+	    { name => 'rules' },
+	    { name => 'options' },
+	];
+
+	return $result;
+    }});
+
+my $option_properties = dclone($PVE::Firewall::vnet_option_properties);
+
+my sub add_option_properties {
+    my ($properties) = @_;
+
+    foreach my $k (keys %$option_properties) {
+	$properties->{$k} = $option_properties->{$k};
+    }
+
+    return $properties;
+};
+
+
+__PACKAGE__->register_method({
+    name => 'get_options',
+    path => 'options',
+    method => 'GET',
+    description => "Get vnet firewall options.",
+    protected => 1,
+    permissions => {
+	description => "Needs SDN.Audit or SDN.Allocate permissions on '/sdn/zones/<zone>/<vnet>'",
+	user => 'all',
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    vnet => get_standard_option('pve-sdn-vnet-id'),
+	},
+    },
+    returns => {
+	type => "object",
+	properties => $option_properties,
+    },
+    code => sub {
+	my ($param) = @_;
+
+	PVE::API2::Firewall::Helpers::check_vnet_access($param->{vnet}, ['SDN.Allocate', 'SDN.Audit']);
+
+	my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+	my $vnetfw_conf = PVE::Firewall::load_vnetfw_conf($cluster_conf, 'vnet', $param->{vnet});
+
+	return PVE::Firewall::copy_opject_with_digest($vnetfw_conf->{options});
+    }});
+
+__PACKAGE__->register_method({
+    name => 'set_options',
+    path => 'options',
+    method => 'PUT',
+    description => "Set Firewall options.",
+    protected => 1,
+    permissions => {
+	description => "Needs SDN.Allocate permissions on '/sdn/zones/<zone>/<vnet>'",
+	user => 'all',
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => add_option_properties({
+	    vnet => get_standard_option('pve-sdn-vnet-id'),
+	    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::API2::Firewall::Helpers::check_vnet_access($param->{vnet}, ['SDN.Allocate']);
+
+	PVE::Firewall::lock_vnetfw_conf($param->{vnet}, 10, sub {
+	    my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+	    my $vnetfw_conf = PVE::Firewall::load_vnetfw_conf($cluster_conf, 'vnet', $param->{vnet});
+
+	    my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($vnetfw_conf->{options});
+	    PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+	    if ($param->{delete}) {
+		for my $opt (PVE::Tools::split_list($param->{delete})) {
+		    raise_param_exc({ delete => "no such option '$opt'" })
+			if !$option_properties->{$opt};
+		    delete $vnetfw_conf->{options}->{$opt};
+		}
+	    }
+
+	    if (defined($param->{enable})) {
+		$param->{enable} = $param->{enable} ? 1 : 0;
+	    }
+
+	    for my $k (keys %$option_properties) {
+		next if !defined($param->{$k});
+		$vnetfw_conf->{options}->{$k} = $param->{$k};
+	    }
+
+	    PVE::Firewall::save_vnetfw_conf($param->{vnet}, $vnetfw_conf);
+	});
+
+	return undef;
+    }});
+
+1;
diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm
index 5f17ffa..b1e5af6 100644
--- a/src/PVE/Firewall.pm
+++ b/src/PVE/Firewall.pm
@@ -1919,6 +1919,11 @@ sub rules_modify_permissions {
 	return {
 	    check => ['perm', '/vms/{vmid}', [ 'VM.Config.Network' ]],
 	}
+    } elsif ($rule_env eq 'vnet') {
+	return {
+	    description => "Needs SDN.Allocate permissions on '/sdn/zones/<zone>/<vnet>'",
+	    user => 'all',
+	}
     }
 
     return undef;
@@ -1939,6 +1944,11 @@ sub rules_audit_permissions {
 	return {
 	    check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
 	}
+    } elsif ($rule_env eq 'vnet') {
+	return {
+	    description => "Needs SDN.Audit or SDN.Allocate permissions on '/sdn/zones/<zone>/<vnet>'",
+	    user => 'all',
+	}
     }
 
     return undef;
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-firewall v5 03/10] firewall: move to arrow syntax for calling functions
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 01/10] sdn: add vnet firewall configuration Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 02/10] api: add vnet endpoints Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 04/10] firewall: add forward direction to rule panel Stefan Hanreich
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel; +Cc: Wolfgang Bumiller

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
---
 src/PVE/Firewall.pm | 78 ++++++++++++++++++++++-----------------------
 1 file changed, 39 insertions(+), 39 deletions(-)

diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm
index b1e5af6..65b8c73 100644
--- a/src/PVE/Firewall.pm
+++ b/src/PVE/Firewall.pm
@@ -1737,12 +1737,12 @@ sub verify_rule {
 			$add_error->($name, "no such ipset '$2'")
 		    }
 		} else {
-		    &$add_error($name, "invalid ipset name '$value'");
+		    $add_error->($name, "invalid ipset name '$value'");
 		}
 	    } elsif ($value =~ m@^(guest/|dc/)?(${ip_alias_pattern})$@){
 		my $scope = $1 // "";
 		my $alias = lc($2);
-		&$add_error($name, "no such alias '$value'")
+		$add_error->($name, "no such alias '$value'")
 		    if !($cluster_conf->{aliases}->{$alias} || ($fw_conf && $fw_conf->{aliases}->{$alias}));
 
 		my $e;
@@ -1761,8 +1761,8 @@ sub verify_rule {
     my $type = $rule->{type};
     my $action = $rule->{action};
 
-    &$add_error('type', "missing property") if !$type;
-    &$add_error('action', "missing property") if !$action;
+    $add_error->('type', "missing property") if !$type;
+    $add_error->('action', "missing property") if !$action;
 
     if ($type) {
 	my $valid_types = $rule_env_direction_lookup->{$rule_env}
@@ -1778,22 +1778,22 @@ sub verify_rule {
 	    $add_error->('action', "unknown action '$action'")
 		if $action && ($action !~ m/^(ACCEPT|DROP|REJECT)$/);
 	} elsif ($type eq 'group') {
-	    &$add_error('type', "security groups not allowed")
+	    $add_error->('type', "security groups not allowed")
 		if !$allow_groups;
-	    &$add_error('action', "invalid characters in security group name")
+	    $add_error->('action', "invalid characters in security group name")
 		if $action && ($action !~ m/^${security_group_name_pattern}$/);
 	} else {
-	    &$add_error('type', "unknown rule type '$type'");
+	    $add_error->('type', "unknown rule type '$type'");
 	}
     }
 
     if ($rule->{iface}) {
-	&$add_error('type', "parameter -i not allowed for this rule type")
+	$add_error->('type', "parameter -i not allowed for this rule type")
 	    if !$allow_iface;
 	eval { PVE::JSONSchema::pve_verify_iface($rule->{iface}); };
-	&$add_error('iface', $@) if $@;
+	$add_error->('iface', $@) if $@;
     	if ($rule_env eq 'vm' || $rule_env eq 'ct') {
-	    &$add_error('iface', "value does not match the regex pattern 'net\\d+'")
+	    $add_error->('iface', "value does not match the regex pattern 'net\\d+'")
 		if $rule->{iface} !~  m/^net(\d+)$/;
 	}
     }
@@ -1802,14 +1802,14 @@ sub verify_rule {
 	if (my $preferred_name = $pve_fw_preferred_macro_names->{lc($rule->{macro})}) {
 	    $rule->{macro} = $preferred_name;
 	} else {
-	    &$add_error('macro', "unknown macro '$rule->{macro}'");
+	    $add_error->('macro', "unknown macro '$rule->{macro}'");
 	}
     }
 
     my $is_icmp = 0;
     if ($rule->{proto}) {
 	eval { pve_fw_verify_protocol_spec($rule->{proto}); };
-	&$add_error('proto', $@) if $@;
+	$add_error->('proto', $@) if $@;
 	&$set_ip_version(4) if $rule->{proto} eq 'icmp';
 	&$set_ip_version(6) if $rule->{proto} eq 'icmpv6';
 	&$set_ip_version(6) if $rule->{proto} eq 'ipv6-icmp';
@@ -1818,34 +1818,34 @@ sub verify_rule {
 
     if ($rule->{dport}) {
 	eval { parse_port_name_number_or_range($rule->{dport}, $is_icmp); };
-	&$add_error('dport', $@) if $@;
+	$add_error->('dport', $@) if $@;
 	my $proto = $rule->{proto};
-	&$add_error('proto', "missing property - 'dport' requires this property")
+	$add_error->('proto', "missing property - 'dport' requires this property")
 	    if !$proto;
-	&$add_error('dport', "protocol '$proto' does not support ports")
+	$add_error->('dport', "protocol '$proto' does not support ports")
 	    if !$PROTOCOLS_WITH_PORTS->{$proto} && !$is_icmp; #special cases
     }
 
     if (my $icmp_type = $rule ->{'icmp-type'}) {
 	my $proto = $rule->{proto};
-	&$add_error('proto', "missing property - 'icmp-type' requires this property")
+	$add_error->('proto', "missing property - 'icmp-type' requires this property")
 	    if !$is_icmp;
-	&$add_error('icmp-type', "'icmp-type' cannot be specified together with 'dport'")
+	$add_error->('icmp-type', "'icmp-type' cannot be specified together with 'dport'")
 	    if $rule->{dport};
 	if ($proto eq 'icmp' && !$icmp_type_names->{$icmp_type}) {
-	    &$add_error('icmp-type', "invalid icmp-type '$icmp_type' for proto 'icmp'");
+	    $add_error->('icmp-type', "invalid icmp-type '$icmp_type' for proto 'icmp'");
 	} elsif (($proto eq 'icmpv6' || $proto eq 'ipv6-icmp') && !$icmpv6_type_names->{$icmp_type}) {
-	    &$add_error('icmp-type', "invalid icmp-type '$icmp_type' for proto '$proto'");
+	    $add_error->('icmp-type', "invalid icmp-type '$icmp_type' for proto '$proto'");
 	}
     }
 
     if ($rule->{sport}) {
 	eval { parse_port_name_number_or_range($rule->{sport}, 0); };
-	&$add_error('sport', $@) if $@;
+	$add_error->('sport', $@) if $@;
 	my $proto = $rule->{proto};
-	&$add_error('proto', "missing property - 'sport' requires this property")
+	$add_error->('proto', "missing property - 'sport' requires this property")
 	    if !$proto;
-	&$add_error('sport', "protocol '$proto' does not support ports")
+	$add_error->('sport', "protocol '$proto' does not support ports")
 	    if !$PROTOCOLS_WITH_PORTS->{$proto};
     }
 
@@ -1854,7 +1854,7 @@ sub verify_rule {
 	    my $source_ipversion = parse_address_list($rule->{source});
 	    &$set_ip_version($source_ipversion);
 	};
-	&$add_error('source', $@) if $@;
+	$add_error->('source', $@) if $@;
 	&$check_ipset_or_alias_property('source', $ipversion);
     }
 
@@ -1863,7 +1863,7 @@ sub verify_rule {
 	    my $dest_ipversion = parse_address_list($rule->{dest});
 	    &$set_ip_version($dest_ipversion);
 	};
-	&$add_error('dest', $@) if $@;
+	$add_error->('dest', $@) if $@;
 	&$check_ipset_or_alias_property('dest', $ipversion);
     }
 
@@ -1875,10 +1875,10 @@ sub verify_rule {
 	    if (ref($err) eq "PVE::Exception" && $err->{errors}) {
 		my $eh = $err->{errors};
 		foreach my $p (keys %$eh) {
-		    &$add_error($p, $eh->{$p});
+		    $add_error->($p, $eh->{$p});
 		}
 	    } else {
-		&$add_error('macro', "$err");
+		$add_error->('macro', "$err");
 	    }
 	}
     }
@@ -3515,17 +3515,17 @@ sub save_vmfw_conf {
     my $raw = '';
 
     my $options = $vmfw_conf->{options};
-    $raw .= &$format_options($options) if $options && scalar(keys %$options);
+    $raw .= $format_options->($options) if $options && scalar(keys %$options);
 
     my $aliases = $vmfw_conf->{aliases};
-    $raw .= &$format_aliases($aliases) if $aliases && scalar(keys %$aliases);
+    $raw .= $format_aliases->($aliases) if $aliases && scalar(keys %$aliases);
 
-    $raw .= &$format_ipsets($vmfw_conf) if $vmfw_conf->{ipset};
+    $raw .= $format_ipsets->($vmfw_conf) if $vmfw_conf->{ipset};
 
     my $rules = $vmfw_conf->{rules} || [];
     if ($rules && scalar(@$rules)) {
 	$raw .= "[RULES]\n\n";
-	$raw .= &$format_rules($rules, 1);
+	$raw .= $format_rules->($rules, 1);
 	$raw .= "\n";
     }
 
@@ -3774,17 +3774,17 @@ sub save_clusterfw_conf {
     my $raw = '';
 
     my $options = $cluster_conf->{options};
-    $raw .= &$format_options($options) if $options && scalar(keys %$options);
+    $raw .= $format_options->($options) if $options && scalar(keys %$options);
 
     my $aliases = $cluster_conf->{aliases};
-    $raw .= &$format_aliases($aliases) if $aliases && scalar(keys %$aliases);
+    $raw .= $format_aliases->($aliases) if $aliases && scalar(keys %$aliases);
 
-    $raw .= &$format_ipsets($cluster_conf) if $cluster_conf->{ipset};
+    $raw .= $format_ipsets->($cluster_conf) if $cluster_conf->{ipset};
 
     my $rules = $cluster_conf->{rules};
     if ($rules && scalar(@$rules)) {
 	$raw .= "[RULES]\n\n";
-	$raw .= &$format_rules($rules, 1);
+	$raw .= $format_rules->($rules, 1);
 	$raw .= "\n";
     }
 
@@ -3798,7 +3798,7 @@ sub save_clusterfw_conf {
 		$raw .= "[group $group]\n\n";
 	    }
 
-	    $raw .= &$format_rules($rules, 0);
+	    $raw .= $format_rules->($rules, 0);
 	    $raw .= "\n";
 	}
     }
@@ -3839,12 +3839,12 @@ sub save_hostfw_conf {
     my $raw = '';
 
     my $options = $hostfw_conf->{options};
-    $raw .= &$format_options($options) if $options && scalar(keys %$options);
+    $raw .= $format_options->($options) if $options && scalar(keys %$options);
 
     my $rules = $hostfw_conf->{rules};
     if ($rules && scalar(@$rules)) {
 	$raw .= "[RULES]\n\n";
-	$raw .= &$format_rules($rules, 1);
+	$raw .= $format_rules->($rules, 1);
 	$raw .= "\n";
     }
 
@@ -3881,12 +3881,12 @@ sub save_vnetfw_conf {
     my $raw = '';
 
     my $options = $conf->{options};
-    $raw .= &$format_options($options) if $options && scalar(keys %$options);
+    $raw .= $format_options->($options) if $options && scalar(keys %$options);
 
     my $rules = $conf->{rules};
     if ($rules && scalar(@$rules)) {
 	$raw .= "[RULES]\n\n";
-	$raw .= &$format_rules($rules, 1);
+	$raw .= $format_rules->($rules, 1);
 	$raw .= "\n";
     }
 
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-manager v5 04/10] firewall: add forward direction to rule panel
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
                   ` (2 preceding siblings ...)
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 03/10] firewall: move to arrow syntax for calling functions Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 05/10] firewall: add vnet to firewall options component Stefan Hanreich
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel

Enables us to use the new forward direction as an option when creating
or editing firewall rules. By introducing firewall_type we can switch
between the available directions depending on which ruleset is being
edited.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Tested-by: Hannes Dürr <h.duerr@proxmox.com>
---
 www/manager6/dc/Config.js          |  1 +
 www/manager6/dc/SecurityGroups.js  |  1 +
 www/manager6/grid/FirewallRules.js | 81 +++++++++++++++++++++++++-----
 www/manager6/lxc/Config.js         |  1 +
 www/manager6/node/Config.js        |  1 +
 www/manager6/qemu/Config.js        |  1 +
 6 files changed, 73 insertions(+), 13 deletions(-)

diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index ddbb58b12..720edefc6 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -241,6 +241,7 @@ Ext.define('PVE.dc.Config', {
 		list_refs_url: '/cluster/firewall/refs',
 		iconCls: 'fa fa-shield',
 		itemId: 'firewall',
+		firewall_type: 'dc',
 	    },
 	    {
 		xtype: 'pveFirewallOptions',
diff --git a/www/manager6/dc/SecurityGroups.js b/www/manager6/dc/SecurityGroups.js
index 9e26b84c9..e7aa8081c 100644
--- a/www/manager6/dc/SecurityGroups.js
+++ b/www/manager6/dc/SecurityGroups.js
@@ -214,6 +214,7 @@ Ext.define('PVE.SecurityGroups', {
 	    list_refs_url: '/cluster/firewall/refs',
 	    tbar_prefix: '<b>' + gettext('Rules') + ':</b>',
 	    border: false,
+	    firewall_type: 'group',
 	},
 	{
 	    xtype: 'pveSecurityGroupList',
diff --git a/www/manager6/grid/FirewallRules.js b/www/manager6/grid/FirewallRules.js
index 11881bf79..e2809f02b 100644
--- a/www/manager6/grid/FirewallRules.js
+++ b/www/manager6/grid/FirewallRules.js
@@ -147,6 +147,24 @@ let ICMPV6_TYPE_NAMES_STORE = Ext.create('Ext.data.Store', {
     ],
 });
 
+let DEFAULT_ALLOWED_DIRECTIONS = ['in', 'out'];
+
+let ALLOWED_DIRECTIONS = {
+    'dc': ['in', 'out', 'forward'],
+    'node': ['in', 'out', 'forward'],
+    'group': ['in', 'out', 'forward'],
+    'vm': ['in', 'out'],
+    'vnet': ['forward'],
+};
+
+let DEFAULT_ALLOWED_ACTIONS = ['ACCEPT', 'REJECT', 'DROP'];
+
+let ALLOWED_ACTIONS = {
+    'in': ['ACCEPT', 'REJECT', 'DROP'],
+    'out': ['ACCEPT', 'REJECT', 'DROP'],
+    'forward': ['ACCEPT', 'DROP'],
+};
+
 Ext.define('PVE.FirewallRulePanel', {
     extend: 'Proxmox.panel.InputPanel',
 
@@ -154,6 +172,9 @@ Ext.define('PVE.FirewallRulePanel', {
 
     list_refs_url: undefined,
 
+    firewall_type: undefined,
+    action_selector: undefined,
+
     onGetValues: function(values) {
 	var me = this;
 
@@ -171,6 +192,23 @@ Ext.define('PVE.FirewallRulePanel', {
 	return values;
     },
 
+    setValidActions: function(type) {
+	let me = this;
+
+	let allowed_actions = ALLOWED_ACTIONS[type] ?? DEFAULT_ALLOWED_ACTIONS;
+	me.action_selector.setComboItems(allowed_actions.map((action) => [action, action]));
+    },
+
+    onSetValues: function(values) {
+	let me = this;
+
+	if (values.type) {
+	    me.setValidActions(values.type);
+	}
+
+	return values;
+    },
+
     initComponent: function() {
 	var me = this;
 
@@ -178,6 +216,17 @@ Ext.define('PVE.FirewallRulePanel', {
 	    throw "no list_refs_url specified";
 	}
 
+	let allowed_directions = ALLOWED_DIRECTIONS[me.firewall_type] ?? DEFAULT_ALLOWED_DIRECTIONS;
+
+	me.action_selector = Ext.create('Proxmox.form.KVComboBox', {
+	    xtype: 'proxmoxKVComboBox',
+	    name: 'action',
+	    value: 'ACCEPT',
+	    comboItems: DEFAULT_ALLOWED_ACTIONS.map((action) => [action, action]),
+	    fieldLabel: gettext('Action'),
+	    allowBlank: false,
+	});
+
 	me.column1 = [
 	    {
 		// hack: we use this field to mark the form 'dirty' when the
@@ -190,19 +239,17 @@ Ext.define('PVE.FirewallRulePanel', {
 	    {
 		xtype: 'proxmoxKVComboBox',
 		name: 'type',
-		value: 'in',
-		comboItems: [['in', 'in'], ['out', 'out']],
+		value: allowed_directions[0],
+		comboItems: allowed_directions.map((dir) => [dir, dir]),
 		fieldLabel: gettext('Direction'),
 		allowBlank: false,
+		listeners: {
+		    change: function(f, value) {
+			me.setValidActions(value);
+                    },
+                },
 	    },
-	    {
-		xtype: 'proxmoxKVComboBox',
-		name: 'action',
-		value: 'ACCEPT',
-		comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']],
-		fieldLabel: gettext('Action'),
-		allowBlank: false,
-	    },
+	    me.action_selector,
         ];
 
 	if (me.allow_iface) {
@@ -387,6 +434,8 @@ Ext.define('PVE.FirewallRuleEdit', {
 
     allow_iface: false,
 
+    firewall_type: undefined,
+
     initComponent: function() {
 	var me = this;
 
@@ -412,6 +461,7 @@ Ext.define('PVE.FirewallRuleEdit', {
 	    list_refs_url: me.list_refs_url,
 	    allow_iface: me.allow_iface,
 	    rule_pos: me.rule_pos,
+	    firewall_type: me.firewall_type,
 	});
 
 	Ext.apply(me, {
@@ -555,6 +605,8 @@ Ext.define('PVE.FirewallRules', {
     allow_groups: true,
     allow_iface: false,
 
+    firewall_type: undefined,
+
     setBaseUrl: function(url) {
         var me = this;
 
@@ -661,7 +713,7 @@ Ext.define('PVE.FirewallRules', {
 	    var type = rec.data.type;
 
 	    var editor;
-	    if (type === 'in' || type === 'out') {
+	    if (type === 'in' || type === 'out' || type === 'forward') {
 		editor = 'PVE.FirewallRuleEdit';
 	    } else if (type === 'group') {
 		editor = 'PVE.FirewallGroupRuleEdit';
@@ -670,6 +722,7 @@ Ext.define('PVE.FirewallRules', {
 	    }
 
 	    var win = Ext.create(editor, {
+		firewall_type: me.firewall_type,
 		digest: rec.data.digest,
 		allow_iface: me.allow_iface,
 		base_url: me.base_url,
@@ -694,6 +747,7 @@ Ext.define('PVE.FirewallRules', {
 	    disabled: true,
 	    handler: function() {
 		var win = Ext.create('PVE.FirewallRuleEdit', {
+		    firewall_type: me.firewall_type,
 		    allow_iface: me.allow_iface,
 		    base_url: me.base_url,
 		    list_refs_url: me.list_refs_url,
@@ -709,11 +763,12 @@ Ext.define('PVE.FirewallRules', {
 		return;
 	    }
 	    let type = rec.data.type;
-	    if (!(type === 'in' || type === 'out')) {
+	    if (!(type === 'in' || type === 'out' || type === 'forward')) {
 		return;
 	    }
 
 	    let win = Ext.create('PVE.FirewallRuleEdit', {
+		firewall_type: me.firewall_type,
 		allow_iface: me.allow_iface,
 		base_url: me.base_url,
 		list_refs_url: me.list_refs_url,
@@ -726,7 +781,7 @@ Ext.define('PVE.FirewallRules', {
 	me.copyBtn = Ext.create('Proxmox.button.Button', {
 	    text: gettext('Copy'),
 	    selModel: sm,
-	    enableFn: ({ data }) => (data.type === 'in' || data.type === 'out') && me.canEdit,
+	    enableFn: ({ data }) => (data.type === 'in' || data.type === 'out' || data.type === 'forward') && me.canEdit,
 	    disabled: true,
 	    handler: run_copy_editor,
 	});
diff --git a/www/manager6/lxc/Config.js b/www/manager6/lxc/Config.js
index 16494172f..a7191fa29 100644
--- a/www/manager6/lxc/Config.js
+++ b/www/manager6/lxc/Config.js
@@ -316,6 +316,7 @@ Ext.define('PVE.lxc.Config', {
 		    base_url: base_url + '/firewall/rules',
 		    list_refs_url: base_url + '/firewall/refs',
 		    itemId: 'firewall',
+		    firewall_type: 'vm',
 		},
 		{
 		    xtype: 'pveFirewallOptions',
diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js
index d27592ce1..c242ba461 100644
--- a/www/manager6/node/Config.js
+++ b/www/manager6/node/Config.js
@@ -293,6 +293,7 @@ Ext.define('PVE.node.Config', {
 		    base_url: '/nodes/' + nodename + '/firewall/rules',
 		    list_refs_url: '/cluster/firewall/refs',
 		    itemId: 'firewall',
+		    firewall_type: 'node',
 		},
 		{
 		    xtype: 'pveFirewallOptions',
diff --git a/www/manager6/qemu/Config.js b/www/manager6/qemu/Config.js
index 42e7f0dbd..48eb753e6 100644
--- a/www/manager6/qemu/Config.js
+++ b/www/manager6/qemu/Config.js
@@ -351,6 +351,7 @@ Ext.define('PVE.qemu.Config', {
 		    base_url: base_url + '/firewall/rules',
 		    list_refs_url: base_url + '/firewall/refs',
 		    itemId: 'firewall',
+		    firewall_type: 'vm',
 		},
 		{
 		    xtype: 'pveFirewallOptions',
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-manager v5 05/10] firewall: add vnet to firewall options component
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
                   ` (3 preceding siblings ...)
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 04/10] firewall: add forward direction to rule panel Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 06/10] firewall: make base_url dynamically configurable in " Stefan Hanreich
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel

Add the configuration options for vnet-level firewalls to the options
component. Additionally add the new policy_forward configuration
option to the datacenter-level firewall as well.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Tested-by: Hannes Dürr <h.duerr@proxmox.com>
---
 www/manager6/grid/FirewallOptions.js | 38 +++++++++++++++++++++++-----
 1 file changed, 32 insertions(+), 6 deletions(-)

diff --git a/www/manager6/grid/FirewallOptions.js b/www/manager6/grid/FirewallOptions.js
index 6aacb47be..fa482e0e4 100644
--- a/www/manager6/grid/FirewallOptions.js
+++ b/www/manager6/grid/FirewallOptions.js
@@ -2,7 +2,7 @@ Ext.define('PVE.FirewallOptions', {
     extend: 'Proxmox.grid.ObjectGrid',
     alias: ['widget.pveFirewallOptions'],
 
-    fwtype: undefined, // 'dc', 'node' or 'vm'
+    fwtype: undefined, // 'dc', 'node', 'vm' or 'vnet'
 
     base_url: undefined,
 
@@ -13,14 +13,14 @@ Ext.define('PVE.FirewallOptions', {
 	    throw "missing base_url configuration";
 	}
 
-	if (me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm') {
-	    if (me.fwtype === 'node') {
-		me.cwidth1 = 250;
-	    }
-	} else {
+	if (!['dc', 'node', 'vm', 'vnet'].includes(me.fwtype)) {
 	    throw "unknown firewall option type";
 	}
 
+	if (me.fwtype === 'node') {
+	    me.cwidth1 = 250;
+	}
+
 	let caps = Ext.state.Manager.get('GuiCap');
 	let canEdit = caps.vms['VM.Config.Network'] || caps.dc['Sys.Modify'] || caps.nodes['Sys.Modify'];
 
@@ -81,6 +81,7 @@ Ext.define('PVE.FirewallOptions', {
 			    'nf_conntrack_tcp_timeout_established', 7875, 250);
 	    add_log_row('log_level_in');
 	    add_log_row('log_level_out');
+	    add_log_row('log_level_forward');
 	    add_log_row('tcp_flags_log_level', 120);
 	    add_log_row('smurf_log_level');
 	    add_boolean_row('nftables', gettext('nftables (tech preview)'), 0);
@@ -114,6 +115,9 @@ Ext.define('PVE.FirewallOptions', {
 		    defaultValue: 'enable=1',
 		},
 	    };
+	} else if (me.fwtype === 'vnet') {
+	    add_boolean_row('enable', gettext('Firewall'), 0);
+	    add_log_row('log_level_forward');
 	}
 
 	if (me.fwtype === 'dc' || me.fwtype === 'vm') {
@@ -150,6 +154,28 @@ Ext.define('PVE.FirewallOptions', {
 	    };
 	}
 
+	if (me.fwtype === 'vnet' || me.fwtype === 'dc') {
+	    me.rows.policy_forward = {
+		header: gettext('Forward Policy'),
+		required: true,
+		defaultValue: 'ACCEPT',
+		editor: {
+		    xtype: 'proxmoxWindowEdit',
+		    subject: gettext('Forward Policy'),
+		    items: {
+			xtype: 'pveFirewallPolicySelector',
+			name: 'policy_forward',
+			value: 'ACCEPT',
+			fieldLabel: gettext('Forward Policy'),
+			comboItems: [
+			    ['ACCEPT', 'ACCEPT'],
+			    ['DROP', 'DROP'],
+			],
+		    },
+		},
+	    };
+	}
+
 	var edit_btn = new Ext.Button({
 	    text: gettext('Edit'),
 	    disabled: true,
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-manager v5 06/10] firewall: make base_url dynamically configurable in options component
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
                   ` (4 preceding siblings ...)
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 05/10] firewall: add vnet to firewall options component Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 07/10] sdn: add firewall panel Stefan Hanreich
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel

This adds the ability to dynamically configure and change the base_url
for the firewall options. This is needed for the SDN firewall dialog,
that updates the firewall components based on the selected vnet. This
avoids having to reinstantiate the component every time the user
selects a new vnet.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Tested-by: Hannes Dürr <h.duerr@proxmox.com>
---
 www/manager6/grid/FirewallOptions.js | 38 ++++++++++++++++++++++------
 1 file changed, 30 insertions(+), 8 deletions(-)

diff --git a/www/manager6/grid/FirewallOptions.js b/www/manager6/grid/FirewallOptions.js
index fa482e0e4..d98cbe22c 100644
--- a/www/manager6/grid/FirewallOptions.js
+++ b/www/manager6/grid/FirewallOptions.js
@@ -9,10 +9,6 @@ Ext.define('PVE.FirewallOptions', {
     initComponent: function() {
 	var me = this;
 
-	if (!me.base_url) {
-	    throw "missing base_url configuration";
-	}
-
 	if (!['dc', 'node', 'vm', 'vnet'].includes(me.fwtype)) {
 	    throw "unknown firewall option type";
 	}
@@ -197,23 +193,49 @@ Ext.define('PVE.FirewallOptions', {
 	};
 
 	Ext.apply(me, {
-	    url: "/api2/json" + me.base_url,
 	    tbar: [edit_btn],
-	    editorConfig: {
-		url: '/api2/extjs/' + me.base_url,
-	    },
 	    listeners: {
 		itemdblclick: () => { if (canEdit) { me.run_editor(); } },
 		selectionchange: set_button_status,
 	    },
 	});
 
+	if (me.base_url) {
+	    me.applyUrl(me.base_url);
+	} else {
+	    me.rstore = Ext.create('Proxmox.data.ObjectStore', {
+		interval: me.interval,
+		extraParams: me.extraParams,
+		rows: me.rows,
+	    });
+	}
+
 	me.callParent();
 
 	me.on('activate', me.rstore.startUpdate);
 	me.on('destroy', me.rstore.stopUpdate);
 	me.on('deactivate', me.rstore.stopUpdate);
     },
+    applyUrl: function(url) {
+	let me = this;
+
+	Ext.apply(me, {
+	    url: "/api2/json" + url,
+	    editorConfig: {
+		url: '/api2/extjs/' + url,
+	    },
+	});
+    },
+    setBaseUrl: function(url) {
+	let me = this;
+
+	me.base_url = url;
+
+	me.applyUrl(url);
+
+	me.rstore.getProxy().setConfig('url', `/api2/extjs/${url}`);
+	me.rstore.reload();
+    },
 });
 
 
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-manager v5 07/10] sdn: add firewall panel
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
                   ` (5 preceding siblings ...)
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 06/10] firewall: make base_url dynamically configurable in " Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 08/10] firewall: rules: show warning when creating forward rules Stefan Hanreich
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel

Expose the ability to create vnet-level firewalls in the PVE UI

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Tested-by: Hannes Dürr <h.duerr@proxmox.com>
---
 www/manager6/Makefile                |  2 +
 www/manager6/dc/Config.js            |  8 +++
 www/manager6/sdn/FirewallPanel.js    | 50 ++++++++++++++++++
 www/manager6/sdn/FirewallVnetView.js | 77 ++++++++++++++++++++++++++++
 4 files changed, 137 insertions(+)
 create mode 100644 www/manager6/sdn/FirewallPanel.js
 create mode 100644 www/manager6/sdn/FirewallVnetView.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index d623dfb98..2ad3853d0 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -279,6 +279,8 @@ JSSRC= 							\
 	sdn/SubnetView.js				\
 	sdn/ZoneContentView.js				\
 	sdn/ZoneContentPanel.js				\
+	sdn/FirewallPanel.js				\
+	sdn/FirewallVnetView.js				\
 	sdn/ZoneView.js					\
 	sdn/IpamEdit.js					\
 	sdn/OptionsPanel.js				\
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 720edefc6..d44554954 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -221,6 +221,14 @@ Ext.define('PVE.dc.Config', {
 		    hidden: true,
 		    iconCls: 'fa fa-map-signs',
 		    itemId: 'sdnmappings',
+		},
+		{
+		    xtype: 'pveSDNFirewall',
+		    groups: ['sdn'],
+		    title: gettext('Firewall'),
+		    hidden: true,
+		    iconCls: 'fa fa-shield',
+		    itemId: 'sdnfirewall',
 		});
 	    }
 
diff --git a/www/manager6/sdn/FirewallPanel.js b/www/manager6/sdn/FirewallPanel.js
new file mode 100644
index 000000000..0cdf31915
--- /dev/null
+++ b/www/manager6/sdn/FirewallPanel.js
@@ -0,0 +1,50 @@
+
+Ext.define('PVE.sdn.FirewallPanel', {
+    extend: 'Ext.panel.Panel',
+    alias: 'widget.pveSDNFirewall',
+
+    title: 'VNet',
+
+    initComponent: function() {
+	let me = this;
+
+	let tabPanel = Ext.create('Ext.TabPanel', {
+	    fullscreen: true,
+	    region: 'center',
+	    border: false,
+	    split: true,
+	    disabled: true,
+	    flex: 2,
+	    items: [
+		{
+		    xtype: 'pveFirewallRules',
+		    title: gettext('Rules'),
+		    list_refs_url: '/cluster/firewall/refs',
+		    firewall_type: 'vnet',
+		},
+		{
+		    xtype: 'pveFirewallOptions',
+		    title: gettext('Options'),
+		    fwtype: 'vnet',
+		},
+	    ],
+	});
+
+	let vnetPanel = Ext.createWidget('pveSDNFirewallVnetView', {
+	    title: 'VNets',
+	    region: 'west',
+	    border: false,
+	    split: true,
+	    forceFit: true,
+	    flex: 1,
+	    tabPanel,
+	});
+
+	Ext.apply(me, {
+	    layout: 'border',
+	    items: [vnetPanel, tabPanel],
+	});
+
+	me.callParent();
+    },
+});
diff --git a/www/manager6/sdn/FirewallVnetView.js b/www/manager6/sdn/FirewallVnetView.js
new file mode 100644
index 000000000..861d4b5be
--- /dev/null
+++ b/www/manager6/sdn/FirewallVnetView.js
@@ -0,0 +1,77 @@
+Ext.define('PVE.sdn.FirewallVnetView', {
+    extend: 'Ext.grid.GridPanel',
+    alias: 'widget.pveSDNFirewallVnetView',
+
+    stateful: true,
+    stateId: 'grid-sdn-vnet-firewall',
+
+    tabPanel: undefined,
+
+    getRulesPanel: function() {
+	let me = this;
+	return me.tabPanel.items.getAt(0);
+    },
+
+    getOptionsPanel: function() {
+	let me = this;
+	return me.tabPanel.items.getAt(1);
+    },
+
+    initComponent: function() {
+	let me = this;
+
+	let store = new Ext.data.Store({
+	    model: 'pve-sdn-vnet',
+	    proxy: {
+                type: 'proxmox',
+		url: "/api2/json/cluster/sdn/vnets",
+	    },
+	    sorters: {
+		property: ['zone', 'vnet'],
+		direction: 'ASC',
+	    },
+	});
+
+	let reload = () => store.load();
+
+	let sm = Ext.create('Ext.selection.RowModel', {});
+
+	Ext.apply(me, {
+	    store: store,
+	    reloadStore: reload,
+	    selModel: sm,
+	    viewConfig: {
+		trackOver: false,
+	    },
+	    columns: [
+		{
+		    header: 'ID',
+		    flex: 1,
+		    dataIndex: 'vnet',
+		},
+		{
+		    header: gettext('Zone'),
+		    flex: 1,
+		    dataIndex: 'zone',
+		},
+		{
+		    header: gettext('Alias'),
+		    flex: 1,
+		    dataIndex: 'alias',
+		},
+	    ],
+	    listeners: {
+		activate: reload,
+		show: reload,
+		select: function(_sm, rec) {
+		    me.tabPanel.setDisabled(false);
+
+		    me.getRulesPanel().setBaseUrl(`/cluster/sdn/vnets/${rec.id}/firewall/rules`);
+		    me.getOptionsPanel().setBaseUrl(`/cluster/sdn/vnets/${rec.id}/firewall/options`);
+		},
+	    },
+	});
+	store.load();
+	me.callParent();
+    },
+});
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-manager v5 08/10] firewall: rules: show warning when creating forward rules
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
                   ` (6 preceding siblings ...)
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 07/10] sdn: add firewall panel Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-network v5 09/10] firewall: add endpoints for vnet-level firewall Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-docs v5 10/10] firewall: add documentation for forward direction and vnet zone Stefan Hanreich
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel

Since forward rules only take effect when the nftables firewall is
enabled, show a warning to users that informs them of this.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 www/manager6/grid/FirewallRules.js | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/www/manager6/grid/FirewallRules.js b/www/manager6/grid/FirewallRules.js
index e2809f02b..f8ee63783 100644
--- a/www/manager6/grid/FirewallRules.js
+++ b/www/manager6/grid/FirewallRules.js
@@ -174,6 +174,7 @@ Ext.define('PVE.FirewallRulePanel', {
 
     firewall_type: undefined,
     action_selector: undefined,
+    forward_warning: undefined,
 
     onGetValues: function(values) {
 	var me = this;
@@ -199,11 +200,17 @@ Ext.define('PVE.FirewallRulePanel', {
 	me.action_selector.setComboItems(allowed_actions.map((action) => [action, action]));
     },
 
+    setForwardWarning: function(type) {
+	let me = this;
+	me.forward_warning.setHidden(type !== 'forward');
+    },
+
     onSetValues: function(values) {
 	let me = this;
 
 	if (values.type) {
 	    me.setValidActions(values.type);
+	    me.setForwardWarning(values.type);
 	}
 
 	return values;
@@ -227,6 +234,12 @@ Ext.define('PVE.FirewallRulePanel', {
 	    allowBlank: false,
 	});
 
+	me.forward_warning = Ext.create('Proxmox.form.field.DisplayEdit', {
+	    userCls: 'pmx-hint',
+	    value: gettext('Forward rules only take effect when the nftables firewall is activated in the host options'),
+	    hidden: true,
+	});
+
 	me.column1 = [
 	    {
 		// hack: we use this field to mark the form 'dirty' when the
@@ -246,6 +259,7 @@ Ext.define('PVE.FirewallRulePanel', {
 		listeners: {
 		    change: function(f, value) {
 			me.setValidActions(value);
+			me.setForwardWarning(value);
                     },
                 },
 	    },
@@ -420,9 +434,17 @@ Ext.define('PVE.FirewallRulePanel', {
 		value: '',
 		fieldLabel: gettext('Comment'),
 	    },
+	    me.forward_warning,
 	];
 
 	me.callParent();
+
+	if (me.isCreate) {
+	    // on create we never change the values, so we need to trigger this
+	    // manually
+	    me.setValidActions(me.getValues().type);
+	    me.setForwardWarning(me.getValues().type);
+	}
     },
 });
 
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-network v5 09/10] firewall: add endpoints for vnet-level firewall
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
                   ` (7 preceding siblings ...)
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 08/10] firewall: rules: show warning when creating forward rules Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-docs v5 10/10] firewall: add documentation for forward direction and vnet zone Stefan Hanreich
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel; +Cc: Wolfgang Bumiller

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
---
 src/PVE/API2/Network/SDN/Vnets.pm | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/PVE/API2/Network/SDN/Vnets.pm b/src/PVE/API2/Network/SDN/Vnets.pm
index 05915f6..e48b048 100644
--- a/src/PVE/API2/Network/SDN/Vnets.pm
+++ b/src/PVE/API2/Network/SDN/Vnets.pm
@@ -14,6 +14,7 @@ use PVE::Network::SDN::VnetPlugin;
 use PVE::Network::SDN::Subnets;
 use PVE::API2::Network::SDN::Subnets;
 use PVE::API2::Network::SDN::Ips;
+use PVE::API2::Firewall::Vnet;
 
 use Storable qw(dclone);
 use PVE::JSONSchema qw(get_standard_option);
@@ -24,6 +25,11 @@ use PVE::RESTHandler;
 
 use base qw(PVE::RESTHandler);
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Firewall::Vnet",
+    path => '{vnet}/firewall',
+});
+
 __PACKAGE__->register_method ({
     subclass => "PVE::API2::Network::SDN::Subnets",
     path => '{vnet}/subnets',
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [pve-devel] [PATCH pve-docs v5 10/10] firewall: add documentation for forward direction and vnet zone
  2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
                   ` (8 preceding siblings ...)
  2024-11-18 17:43 ` [pve-devel] [PATCH pve-network v5 09/10] firewall: add endpoints for vnet-level firewall Stefan Hanreich
@ 2024-11-18 17:43 ` Stefan Hanreich
  9 siblings, 0 replies; 11+ messages in thread
From: Stefan Hanreich @ 2024-11-18 17:43 UTC (permalink / raw)
  To: pve-devel

Added a new direction section, mostly so I can write about the forward
direction and explain its use cases.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 Makefile                      |  1 +
 gen-pve-firewall-vnet-opts.pl | 12 +++++
 pve-firewall-vnet-opts.adoc   |  8 +++
 pve-firewall.adoc             | 98 ++++++++++++++++++++++++++++++++---
 4 files changed, 112 insertions(+), 7 deletions(-)
 create mode 100755 gen-pve-firewall-vnet-opts.pl
 create mode 100644 pve-firewall-vnet-opts.adoc

diff --git a/Makefile b/Makefile
index 801a2a3..f30d77a 100644
--- a/Makefile
+++ b/Makefile
@@ -62,6 +62,7 @@ GEN_SCRIPTS=					\
 	gen-pve-firewall-macros-adoc.pl		\
 	gen-pve-firewall-rules-opts.pl		\
 	gen-pve-firewall-vm-opts.pl		\
+	gen-pve-firewall-vnet-opts.pl		\
 	gen-output-format-opts.pl
 
 API_VIEWER_FILES=							\
diff --git a/gen-pve-firewall-vnet-opts.pl b/gen-pve-firewall-vnet-opts.pl
new file mode 100755
index 0000000..c9f4f13
--- /dev/null
+++ b/gen-pve-firewall-vnet-opts.pl
@@ -0,0 +1,12 @@
+#!/usr/bin/perl
+
+use lib '.';
+use strict;
+use warnings;
+
+use PVE::Firewall;
+use PVE::RESTHandler;
+
+my $prop = $PVE::Firewall::vnet_option_properties;
+
+print PVE::RESTHandler::dump_properties($prop);
diff --git a/pve-firewall-vnet-opts.adoc b/pve-firewall-vnet-opts.adoc
new file mode 100644
index 0000000..ed1e88f
--- /dev/null
+++ b/pve-firewall-vnet-opts.adoc
@@ -0,0 +1,8 @@
+`enable`: `<boolean>` ('default =' `0`)::
+
+Enable/disable firewall rules.
+
+`policy_forward`: `<ACCEPT | DROP>` ::
+
+Forward policy.
+
diff --git a/pve-firewall.adoc b/pve-firewall.adoc
index b428703..47ab3d3 100644
--- a/pve-firewall.adoc
+++ b/pve-firewall.adoc
@@ -45,21 +45,68 @@ transparent, and we filter traffic for both protocols by default. So
 there is no need to maintain a different set of rules for IPv6.
 
 
+Directions & Zones
+------------------
+
+The Proxmox VE firewall groups the network into multiple logical zones. You can
+define rules for each zone independently. Depending on the zone, you can define
+rules for incoming, outgoing or forwarded traffic.
+
+
+Directions
+~~~~~~~~~~
+
+There are 3 directions that you can choose from when defining rules for a zone:
+
+In::
+
+Traffic that is arriving in a zone.
+
+Out::
+
+Traffic that is leaving a zone.
+
+Forward::
+
+Traffic that is passing through a zone. In the host zone this can be routed
+traffic (when the host is acting as a gateway or performing NAT). At a
+VNet-level this affects all traffic that is passing by a VNet, including
+traffic from/to bridged network interfaces.
+
+
+IMPORTANT: Creating rules for forwarded traffic is currently only possible when
+using the new xref:pve_firewall_nft[nftables-based proxmox-firewall]. Any
+forward rules will be ignored by the stock `pve-firewall` and have no effect!
+
+
 Zones
------
+~~~~~
 
-The Proxmox VE firewall groups the network into the following logical zones:
+There are 3 different zones that you can define firewall rules for:
 
 Host::
 
-Traffic from/to a cluster node
+Traffic going from/to a host, or traffic that is forwarded by a host.
+You can define rules for this zone either at the datacenter level or at the host
+level. Rules at host level take precedence over rules at datacenter level.
 
 VM::
 
-Traffic from/to a specific VM
+Traffic going from/to a VM or CT.
+You cannot define rules for forwarded traffic, only for incoming / outgoing
+traffic.
+
+VNet::
+
+Traffic passing through a SDN VNet, either from guest to guest or from host to
+guest and vice-versa.
+Since this traffic is always forwarded traffic, it is only possible to create
+rules with direction forward.
 
-For each zone, you can define firewall rules for incoming and/or
-outgoing traffic.
+
+IMPORTANT: Creating rules on a VNet-level is currently only possible when using
+the new xref:pve_firewall_nft[nftables-based proxmox-firewall]. Any VNet-level
+rules will be ignored by the stock `pve-firewall` and have no effect!
 
 
 Configuration Files
@@ -202,10 +249,46 @@ can selectively enable the firewall for each interface. This is
 required in addition to the general firewall `enable` option.
 
 
+[[pve_firewall_vnet_configuration]]
+VNet Configuration
+~~~~~~~~~~~~~~~~~~
+VNet related configuration is read from:
+
+ /etc/pve/sdn/firewall/<vnet_name>.fw
+
+This can be used for setting firewall configuration globally on a VNet level,
+without having to set firewall rules for each VM inside the VNet separately. It
+can only contain rules for the `FORWARD` direction, since there is no notion of
+incoming or outgoing traffic. This affects all traffic travelling from one
+bridge port to another, including the host interface.
+
+WARNING: This feature is currently only available for the new
+xref:pve_firewall_nft[nftables-based proxmox-firewall]
+
+Since traffic passing the `FORWARD` chain is bi-directional, you need to create
+rules for both directions if you want traffic to pass both ways. For instance if
+HTTP traffic for a specific host should be allowed, you would need to create the
+following rules:
+
+----
+FORWARD ACCEPT -dest 10.0.0.1 -dport 80
+FORWARD ACCEPT -source 10.0.0.1 -sport 80
+----
+
+`[OPTIONS]`::
+
+This is used to set VNet related firewall options.
+
+include::pve-firewall-vnet-opts.adoc[]
+
+`[RULES]`::
+
+This section contains VNet specific firewall rules.
+
 Firewall Rules
 --------------
 
-Firewall rules consists of a direction (`IN` or `OUT`) and an
+Firewall rules consists of a direction (`IN`, `OUT` or `FORWARD`) and an
 action (`ACCEPT`, `DENY`, `REJECT`). You can also specify a macro
 name. Macros contain predefined sets of rules and options. Rules can be
 disabled by prefixing them with `|`.
@@ -639,6 +722,7 @@ Ports used by {pve}
 * live migration (VM memory and local-disk data): 60000-60050 (TCP)
 
 
+[[pve_firewall_nft]]
 nftables
 --------
 
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2024-11-18 17:44 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-18 17:43 [pve-devel] [PATCH docs/firewall/manager/network v5 00/10] add forward chain firewalling for hosts and vnets Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 01/10] sdn: add vnet firewall configuration Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 02/10] api: add vnet endpoints Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-firewall v5 03/10] firewall: move to arrow syntax for calling functions Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 04/10] firewall: add forward direction to rule panel Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 05/10] firewall: add vnet to firewall options component Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 06/10] firewall: make base_url dynamically configurable in " Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 07/10] sdn: add firewall panel Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-manager v5 08/10] firewall: rules: show warning when creating forward rules Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-network v5 09/10] firewall: add endpoints for vnet-level firewall Stefan Hanreich
2024-11-18 17:43 ` [pve-devel] [PATCH pve-docs v5 10/10] firewall: add documentation for forward direction and vnet zone Stefan Hanreich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal