X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=src%2FPVE%2FFirewall.pm;h=3b67186ed35c18ca7ed62c0f2001c5dbb0e649bb;hp=e6791278ecc1fec1e8f4b6dda37b97d577a0ae30;hb=0e8af63ddb8a58ff2cda9d8595478a673f563d06;hpb=556ae5c18f597f525a9d6c7e9b44e64978acd78c diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index e679127..3b67186 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -10,10 +10,11 @@ use File::Path; 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); @@ -2387,21 +2388,40 @@ sub generate_tap_rules_direction { # 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); @@ -2446,14 +2466,20 @@ sub enable_host_firewall { 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 @@ -2496,15 +2522,30 @@ sub enable_host_firewall { } # 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 @@ -3456,7 +3497,7 @@ sub save_hostfw_conf { } sub compile { - my ($cluster_conf, $hostfw_conf, $vmdata) = @_; + my ($cluster_conf, $hostfw_conf, $vmdata, $corosync_conf) = @_; my $vmfw_configs; @@ -3477,6 +3518,10 @@ sub compile { $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); } @@ -3496,8 +3541,8 @@ sub compile { 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); @@ -3505,7 +3550,7 @@ sub compile { } 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 = {}; @@ -3535,7 +3580,7 @@ sub compile_iptables_filter { 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 } @@ -3782,9 +3827,11 @@ sub compile_ebtables_filter { 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); } @@ -4222,6 +4269,7 @@ sub remove_pvefw_chains { 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(); } @@ -4267,6 +4315,11 @@ sub remove_pvefw_chains_ipset { 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};