sub pve_verify_ip_or_cidr_or_alias {
my ($cidr, $noerr) = @_;
- return if $cidr =~ m@^(dc/|vm/)?(?:$ip_alias_pattern)$@;
+ return if $cidr =~ m@^(dc/|guest/)?(?:$ip_alias_pattern)$@;
return pve_verify_ip_or_cidr($cidr, $noerr);
}
return;
}
- if ($str =~ m@^(dc/|vm/)?${ip_alias_pattern}$@) {
+ if ($str =~ m@^(dc/|guest/)?${ip_alias_pattern}$@) {
die "alias name too long\n" if length($str) > $max_alias_name_length;
return;
}
default => 0,
optional => 1
},
+ nftables => {
+ description => "Enable nftables based firewall (tech preview)",
+ type => 'boolean',
+ default => 0,
+ optional => 1,
+ },
};
our $vm_option_properties = {
if (my $value = $rule->{$name}) {
if ($value =~ m/^\+/) {
- if ($value =~ m@^\+(vm/|dc/)?(${ipset_name_pattern})$@) {
+ if ($value =~ m@^\+(guest/|dc/)?(${ipset_name_pattern})$@) {
&$add_error($name, "no such ipset '$2'")
if !($cluster_conf->{ipset}->{$2} || ($fw_conf && $fw_conf->{ipset}->{$2}));
} else {
&$add_error($name, "invalid ipset name '$value'");
}
- } elsif ($value =~ m@^(vm/|dc/)?(${ip_alias_pattern})$@){
+ } elsif ($value =~ m@^(guest/|dc/)?(${ip_alias_pattern})$@){
my $scope = $1 // "";
my $alias = lc($2);
&$add_error($name, "no such alias '$value'")
if ($scope ne 'dc/' && $fw_conf) {
$e = $fw_conf->{aliases}->{$alias};
}
- if ($scope ne 'vm/' && !$e && $cluster_conf) {
+ if ($scope ne 'guest/' && !$e && $cluster_conf) {
$e = $cluster_conf->{aliases}->{$alias};
}
my $match;
if ($adr =~ m/^\+/) {
- if ($adr =~ m@^\+(vm/|dc/)?(${ipset_name_pattern})$@) {
+ if ($adr =~ m@^\+(guest/|dc/)?(${ipset_name_pattern})$@) {
my $scope = $1 // "";
my $name = $2;
my $ipset_chain;
if ($scope ne 'dc/' && $fw_conf && $fw_conf->{ipset}->{$name}) {
$ipset_chain = compute_ipset_chain_name($fw_conf->{vmid}, $name, $ipversion);
- } elsif ($scope ne 'vm/' && $cluster_conf && $cluster_conf->{ipset}->{$name}) {
+ } elsif ($scope ne 'guest/' && $cluster_conf && $cluster_conf->{ipset}->{$name}) {
$ipset_chain = compute_ipset_chain_name(0, $name, $ipversion);
} else {
die "no such ipset '$name'\n";
} else {
die "invalid security group name '$adr'\n";
}
- } elsif ($adr =~ m@^(dc/|vm/)?(${ip_alias_pattern})$@){
+ } elsif ($adr =~ m@^(dc/|guest/)?(${ip_alias_pattern})$@){
my $scope = $1 // "";
my $alias = lc($2);
my $e;
if ($scope ne 'dc/' && $fw_conf) {
$e = $fw_conf->{aliases}->{$alias};
}
- if ($scope ne 'vm/' && !$e && $cluster_conf) {
+ if ($scope ne 'guest/' && !$e && $cluster_conf) {
$e = $cluster_conf->{aliases}->{$alias};
}
die "no such alias '$adr'\n" if !$e;
my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog";
- if ($line =~ m/^(enable|nosmurfs|tcpflags|ndp|log_nf_conntrack|nf_conntrack_allow_invalid|protection_synflood):\s*(0|1)\s*$/i) {
+ 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) {
sub resolve_alias {
my ($clusterfw_conf, $fw_conf, $cidr, $scope) = @_;
+ # When we're on the cluster level, the cluster config only gets
+ # saved into fw_conf, so we need some extra handling here (to
+ # stay consistent)
+ my ($cluster_config, $local_config);
+ if (!$clusterfw_conf) {
+ ($cluster_config, $local_config) = ($fw_conf, undef);
+ } else {
+ ($cluster_config, $local_config) = ($clusterfw_conf, $fw_conf);
+ }
+
my $alias = lc($cidr);
my $e;
- if ($scope ne 'dc/' && $fw_conf) {
- $e = $fw_conf->{aliases}->{$alias};
+ if ($scope ne 'dc/' && $local_config) {
+ $e = $local_config->{aliases}->{$alias};
}
- if ($scope ne 'vm/' && !$e && $clusterfw_conf) {
- $e = $clusterfw_conf->{aliases}->{$alias};
+ if ($scope ne 'guest/' && !$e && $cluster_config) {
+ $e = $cluster_config->{aliases}->{$alias};
}
die "no such alias '$cidr'\n" if !$e;;
}
eval {
- if ($cidr =~ m@^(dc/|vm/)?(${ip_alias_pattern}$)@) {
+ if ($cidr =~ m@^(dc/|guest/)?(${ip_alias_pattern}$)@) {
my $scope = $1 // "";
my $alias = $2;
resolve_alias($cluster_conf, $res, $alias, $scope); # make sure alias exists
my $raw = '';
$raw .= "[ALIASES]\n\n";
- foreach my $k (keys %$aliases) {
+ foreach my $k (sort keys %$aliases) {
my $e = $aliases->{$k};
$raw .= "$e->{name} $e->{cidr}";
$raw .= " # " . encode('utf8', $e->{comment})
next if $entry->{errors}; # skip entries with errors
eval {
my ($cidr, $ver);
- if ($entry->{cidr} =~ m@^(dc/|vm/)?(${ip_alias_pattern})$@) {
+ if ($entry->{cidr} =~ m@^(dc/|guest/)?(${ip_alias_pattern})$@) {
my $scope = $1 // "";
my $alias = $2;
($cidr, $ver) = resolve_alias($clusterfw_conf, $fw_conf, $alias, $scope);
ebtables_restore_cmdlist(get_ebtables_cmdlist({}));
}
-sub init {
- my $cluster_conf = load_clusterfw_conf();
- my $cluster_options = $cluster_conf->{options};
- my $enable = $cluster_options->{enable};
+sub is_nftables {
+ my ($cluster_conf, $host_conf) = @_;
- return if !$enable;
+ if (!-x "/usr/libexec/proxmox/proxmox-firewall") {
+ return 0;
+ }
+
+ $cluster_conf = load_clusterfw_conf() if !defined($cluster_conf);
+ $host_conf = load_hostfw_conf($cluster_conf) if !defined($host_conf);
+
+ return $host_conf->{options}->{nftables};
+}
+
+sub is_enabled_and_not_nftables {
+ my ($cluster_conf, $host_conf) = @_;
+
+ $cluster_conf = load_clusterfw_conf() if !defined($cluster_conf);
+ $host_conf = load_hostfw_conf($cluster_conf) if !defined($host_conf);
+
+ return $cluster_conf->{options}->{enable} && !is_nftables($cluster_conf, $host_conf);
+}
+
+sub init {
+ return if !is_enabled_and_not_nftables();
# load required modules here
}
+# This is checked in proxmox-firewall to avoid log-spam due to failing to parse the config
+my $FORCE_NFT_DISABLE_FLAG_FILE = "/run/proxmox-nftables-firewall-force-disable";
+
sub update {
my $code = sub {
my $cluster_conf = load_clusterfw_conf();
- my $cluster_options = $cluster_conf->{options};
+ my $hostfw_conf = load_hostfw_conf($cluster_conf);
- if (!$cluster_options->{enable}) {
+ if (!is_enabled_and_not_nftables($cluster_conf, $hostfw_conf)) {
+ unlink($FORCE_NFT_DISABLE_FLAG_FILE)
+ or $!{ENOENT} or warn "failed to unlink flag file '$FORCE_NFT_DISABLE_FLAG_FILE' - $!\n";
PVE::Firewall::remove_pvefw_chains();
return;
}
+ if (! -e $FORCE_NFT_DISABLE_FLAG_FILE) {
+ open(my $_fh, '>', $FORCE_NFT_DISABLE_FLAG_FILE)
+ or warn "failed to create flag file '$FORCE_NFT_DISABLE_FLAG_FILE' – $!\n";
+ }
- my $hostfw_conf = load_hostfw_conf($cluster_conf);
my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = compile($cluster_conf, $hostfw_conf);