use IO::File;
use Net::IP;
use POSIX;
-use Socket qw(AF_INET6 inet_ntop inet_pton);
+use Socket qw(AF_INET AF_INET6 inet_ntop inet_pton);
use Storable qw(dclone);
use PVE::Cluster;
+use PVE::Corosync;
use PVE::Exception qw(raise raise_param_exc);
use PVE::INotify;
use PVE::JSONSchema qw(register_standard_option get_standard_option);
# plug the tap chain to bridge chain
if ($direction eq 'IN') {
ruleset_addrule($ruleset, "PVEFW-FWBR-IN",
- "-m physdev --physdev-is-bridged --physdev-out $iface", "-j $tapchain", $loglevel, 'FWBR-IN: ', $vmid);
+ "-m physdev --physdev-is-bridged --physdev-out $iface", "-j $tapchain");
} else {
ruleset_addrule($ruleset, "PVEFW-FWBR-OUT",
- "-m physdev --physdev-is-bridged --physdev-in $iface", "-j $tapchain", $loglevel, 'FWBR-OUT: ', $vmid);
+ "-m physdev --physdev-is-bridged --physdev-in $iface", "-j $tapchain");
}
}
sub enable_host_firewall {
- my ($ruleset, $hostfw_conf, $cluster_conf, $ipversion) = @_;
+ my ($ruleset, $hostfw_conf, $cluster_conf, $ipversion, $corosync_conf) = @_;
my $options = $hostfw_conf->{options};
my $cluster_options = $cluster_conf->{options};
my $rules = $hostfw_conf->{rules};
my $cluster_rules = $cluster_conf->{rules};
+ # corosync preparation
+ my $corosync_rule = "-p udp --dport 5404:5405";
+ my $corosync_local_addresses = {};
+ my $multicast_enabled;
+ my $local_hostname = PVE::INotify::nodename();
+ if (defined($corosync_conf)) {
+ PVE::Corosync::for_all_corosync_addresses($corosync_conf, $ipversion, sub {
+ my ($node_name, $node_ip, $node_ipversion, $key) = @_;
+
+ if ($node_name eq $local_hostname) {
+ $corosync_local_addresses->{$key} = $node_ip;
+ }
+ });
+
+ # allow multicast only if enabled in config
+ my $corosync_transport = $corosync_conf->{main}->{totem}->{transport};
+ $multicast_enabled = defined($corosync_transport) && $corosync_transport eq 'udp';
+ }
+
# host inbound firewall
my $chain = "PVEFW-HOST-IN";
ruleset_create_chain($ruleset, $chain);
ruleset_addrule($ruleset, $chain, "$mngmntsrc -p tcp --dport 3128", "-j $accept_action"); # SPICE Proxy
ruleset_addrule($ruleset, $chain, "$mngmntsrc -p tcp --dport 22", "-j $accept_action"); # SSH
- my $localnet = $cluster_conf->{aliases}->{local_network}->{cidr};
- my $localnet_ver = $cluster_conf->{aliases}->{local_network}->{ipversion};
+ # corosync inbound rules
+ if (defined($corosync_conf)) {
+ ruleset_addrule($ruleset, $chain, "-m addrtype --dst-type MULTICAST $corosync_rule", "-j $accept_action")
+ if $multicast_enabled;
- # corosync
- if ($localnet && ($ipversion == $localnet_ver)) {
- my $corosync_rule = "-p udp --dport 5404:5405";
- ruleset_addrule($ruleset, $chain, "-s $localnet -d $localnet $corosync_rule", "-j $accept_action");
- ruleset_addrule($ruleset, $chain, "-s $localnet -m addrtype --dst-type MULTICAST $corosync_rule", "-j $accept_action");
+ PVE::Corosync::for_all_corosync_addresses($corosync_conf, $ipversion, sub {
+ my ($node_name, $node_ip, $node_ipversion, $key) = @_;
+ my $destination = $corosync_local_addresses->{$key};
+
+ if ($node_name ne $local_hostname && defined($destination)) {
+ # accept only traffic on same ring
+ ruleset_addrule($ruleset, $chain, "-d $destination -s $node_ip $corosync_rule", "-j $accept_action");
+ }
+ });
}
# implement input policy
}
# allow standard traffic on cluster network
+ my $localnet = $cluster_conf->{aliases}->{local_network}->{cidr};
+ my $localnet_ver = $cluster_conf->{aliases}->{local_network}->{ipversion};
+
if ($localnet && ($ipversion == $localnet_ver)) {
ruleset_addrule($ruleset, $chain, "-d $localnet -p tcp --dport 8006", "-j $accept_action"); # PVE API
ruleset_addrule($ruleset, $chain, "-d $localnet -p tcp --dport 22", "-j $accept_action"); # SSH
ruleset_addrule($ruleset, $chain, "-d $localnet -p tcp --dport 5900:5999", "-j $accept_action"); # PVE VNC Console
ruleset_addrule($ruleset, $chain, "-d $localnet -p tcp --dport 3128", "-j $accept_action"); # SPICE Proxy
+ }
+
+ # corosync outbound rules
+ if (defined($corosync_conf)) {
+ ruleset_addrule($ruleset, $chain, "-m addrtype --dst-type MULTICAST $corosync_rule", "-j $accept_action")
+ if $multicast_enabled;
+
+ PVE::Corosync::for_all_corosync_addresses($corosync_conf, $ipversion, sub {
+ my ($node_name, $node_ip, $node_ipversion, $key) = @_;
+ my $source = $corosync_local_addresses->{$key};
- my $corosync_rule = "-p udp --dport 5404:5405";
- ruleset_addrule($ruleset, $chain, "-d $localnet $corosync_rule", "-j $accept_action");
- ruleset_addrule($ruleset, $chain, "-m addrtype --dst-type MULTICAST $corosync_rule", "-j $accept_action");
+ if ($node_name ne $local_hostname && defined($source)) {
+ # accept only traffic on same ring
+ ruleset_addrule($ruleset, $chain, "-s $source -d $node_ip $corosync_rule", "-j $accept_action");
+ }
+ });
}
# implement output policy
}
sub compile {
- my ($cluster_conf, $hostfw_conf, $vmdata) = @_;
+ my ($cluster_conf, $hostfw_conf, $vmdata, $corosync_conf) = @_;
my $vmfw_configs;
$hostfw_conf = load_hostfw_conf($cluster_conf, undef) if !$hostfw_conf;
+ # cfs_update is handled by daemon or API
+ $corosync_conf = PVE::Cluster::cfs_read_file("corosync.conf")
+ if !defined($corosync_conf) && PVE::Corosync::check_conf_exists(1);
+
$vmdata = read_local_vm_config();
$vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, undef);
}
push @{$cluster_conf->{ipset}->{management}}, { cidr => $localnet };
- my $ruleset = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, 4);
- my $rulesetv6 = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, 6);
+ my $ruleset = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, 4);
+ my $rulesetv6 = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, 6);
my $ebtables_ruleset = compile_ebtables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata);
my $ipset_ruleset = compile_ipsets($cluster_conf, $vmfw_configs, $vmdata);
}
sub compile_iptables_filter {
- my ($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $ipversion) = @_;
+ my ($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, $ipversion) = @_;
my $ruleset = {};
my $hostfw_enable = !(defined($hostfw_options->{enable}) && ($hostfw_options->{enable} == 0));
if ($hostfw_enable) {
- eval { enable_host_firewall($ruleset, $hostfw_conf, $cluster_conf, $ipversion); };
+ eval { enable_host_firewall($ruleset, $hostfw_conf, $cluster_conf, $ipversion, $corosync_conf); };
warn $@ if $@; # just to be sure - should not happen
}
push(@$arpfilter, $ip);
}
}
- if($net->{ip} && $vmfw_conf->{options}->{ipfilter}) {
- $net->{ip} =~ s|/(\d+)$||;
- push(@$arpfilter, $net->{ip});
+ if (defined(my $ip = $net->{ip}) && $vmfw_conf->{options}->{ipfilter}) {
+ # ebtables changes this to a .0/MASK network but we just
+ # want the address here, no network - see #2193
+ $ip =~ s|/(\d+)$||;
+ push @$arpfilter, $ip;
}
generate_tap_layer2filter($ruleset, $iface, $macaddr, $vmfw_conf, $vmid, $arpfilter);
}
PVE::Firewall::remove_pvefw_chains_iptables("iptables");
PVE::Firewall::remove_pvefw_chains_iptables("ip6tables");
PVE::Firewall::remove_pvefw_chains_ipset();
+ PVE::Firewall::remove_pvefw_chains_ebtables();
}
ipset_restore_cmdlist($cmdlist) if $cmdlist;
}
+sub remove_pvefw_chains_ebtables {
+ # apply empty ruleset = remove all our chains
+ ebtables_restore_cmdlist(get_ebtables_cmdlist({}));
+}
+
sub init {
my $cluster_conf = load_clusterfw_conf();
my $cluster_options = $cluster_conf->{options};