X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=src%2FPVE%2FFirewall.pm;h=1319bfbed3af437d6fe8c87851150eba9325e4a1;hp=b08cea57312b9e6a54680e24abced4e4de4c47be;hb=033a15b372734fcfb390c3b747f67bfa4643dabd;hpb=0398480886f82189988edcd00fa20683cbbed4ce diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index b08cea5..1319bfb 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); @@ -213,7 +214,10 @@ my $pve_fw_macros = { ], 'Ceph' => [ "Ceph Storage Cluster traffic (Ceph Monitors, OSD & MDS Deamons)", + # Legacy port for protocol v1 { action => 'PARAM', proto => 'tcp', dport => '6789' }, + # New port for protocol v2 + { action => 'PARAM', proto => 'tcp', dport => '3300' }, { action => 'PARAM', proto => 'tcp', dport => '6800:7300' }, ], 'CVS' => [ @@ -1747,25 +1751,25 @@ sub enable_bridge_firewall { sub iptables_restore_cmdlist { my ($cmdlist) = @_; - run_command("/sbin/iptables-restore -n", input => $cmdlist, errmsg => "iptables_restore_cmdlist"); + run_command(['iptables-restore', '-n'], input => $cmdlist, errmsg => "iptables_restore_cmdlist"); } sub ip6tables_restore_cmdlist { my ($cmdlist) = @_; - run_command("/sbin/ip6tables-restore -n", input => $cmdlist, errmsg => "iptables_restore_cmdlist"); + run_command(['ip6tables-restore', '-n'], input => $cmdlist, errmsg => "iptables_restore_cmdlist"); } sub ipset_restore_cmdlist { my ($cmdlist) = @_; - run_command("/sbin/ipset restore", input => $cmdlist, errmsg => "ipset_restore_cmdlist"); + run_command(['ipset', 'restore'], input => $cmdlist, errmsg => "ipset_restore_cmdlist"); } sub ebtables_restore_cmdlist { my ($cmdlist) = @_; - run_command("/sbin/ebtables-restore", input => $cmdlist, errmsg => "ebtables_restore_cmdlist"); + run_command(['ebtables-restore'], input => $cmdlist, errmsg => "ebtables_restore_cmdlist"); } sub iptables_get_chains { @@ -1824,7 +1828,7 @@ sub iptables_get_chains { } }; - run_command("/sbin/$iptablescmd-save", outfunc => $parser); + run_command(["$iptablescmd-save"], outfunc => $parser); return wantarray ? ($res, $hooks) : $res; } @@ -1868,7 +1872,7 @@ sub ipset_get_chains { } }; - run_command("/sbin/ipset save", outfunc => $parser); + run_command(['ipset', 'save'], outfunc => $parser); # compute digest for each chain foreach my $chain (keys %$chains) { @@ -1899,7 +1903,7 @@ sub ebtables_get_chains { } }; - run_command("/sbin/ebtables-save", outfunc => $parser); + run_command(['ebtables-save'], outfunc => $parser); # compute digest for each chain and store rules as well foreach my $chain (keys %$chains) { $res->{$chain}->{rules} = $chains->{$chain}; @@ -2359,10 +2363,10 @@ sub generate_tap_rules_direction { my $ipfilter_ipset = compute_ipset_chain_name($vmid, $ipfilter_name, $ipversion) if $options->{ipfilter} || $vmfw_conf->{ipset}->{$ipfilter_name}; - # create chain with mac and ip filter - ruleset_create_vm_chain($ruleset, $tapchain, $ipversion, $options, $macaddr, $ipfilter_ipset, $direction); - if ($options->{enable}) { + # create chain with mac and ip filter + ruleset_create_vm_chain($ruleset, $tapchain, $ipversion, $options, $macaddr, $ipfilter_ipset, $direction); + ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $tapchain, $netid, $direction, $options, $ipversion, $vmid); ruleset_generate_vm_ipsrules($ruleset, $options, $direction, $iface); @@ -2395,13 +2399,32 @@ sub generate_tap_rules_direction { } 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 +2469,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 +2525,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 + } - 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"); + # 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}; + + 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 +3500,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 +3521,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 +3544,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 +3553,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 +3583,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 } @@ -3544,19 +3592,19 @@ sub compile_iptables_filter { eval { my $conf = $vmdata->{qemu}->{$vmid}; my $vmfw_conf = $vmfw_configs->{$vmid}; - return if !$vmfw_conf; + return if !$vmfw_conf || !$vmfw_conf->{options}->{enable}; foreach my $netid (sort keys %$conf) { next if $netid !~ m/^net(\d+)$/; my $net = PVE::QemuServer::parse_net($conf->{$netid}); next if !$net->{firewall}; - my $iface = "tap${vmid}i$1"; + my $iface = "tap${vmid}i$1"; my $macaddr = $net->{macaddr}; generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, - $vmfw_conf, $vmid, 'IN', $ipversion); + $vmfw_conf, $vmid, 'IN', $ipversion); generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, - $vmfw_conf, $vmid, 'OUT', $ipversion); + $vmfw_conf, $vmid, 'OUT', $ipversion); } }; warn $@ if $@; # just to be sure - should not happen @@ -3564,29 +3612,28 @@ sub compile_iptables_filter { # generate firewall rules for LXC containers foreach my $vmid (sort keys %{$vmdata->{lxc}}) { - eval { - my $conf = $vmdata->{lxc}->{$vmid}; - my $vmfw_conf = $vmfw_configs->{$vmid}; - return if !$vmfw_conf; - - if ($vmfw_conf->{options}->{enable}) { - foreach my $netid (sort keys %$conf) { - next if $netid !~ m/^net(\d+)$/; - my $net = PVE::LXC::Config->parse_lxc_network($conf->{$netid}); - next if !$net->{firewall}; - my $iface = "veth${vmid}i$1"; - my $macaddr = $net->{hwaddr}; - generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, - $vmfw_conf, $vmid, 'IN', $ipversion); - generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, - $vmfw_conf, $vmid, 'OUT', $ipversion); - } - } - }; - warn $@ if $@; # just to be sure - should not happen + eval { + my $conf = $vmdata->{lxc}->{$vmid}; + my $vmfw_conf = $vmfw_configs->{$vmid}; + return if !$vmfw_conf || !$vmfw_conf->{options}->{enable}; + + foreach my $netid (sort keys %$conf) { + next if $netid !~ m/^net(\d+)$/; + my $net = PVE::LXC::Config->parse_lxc_network($conf->{$netid}); + next if !$net->{firewall}; + + my $iface = "veth${vmid}i$1"; + my $macaddr = $net->{hwaddr}; + generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, + $vmfw_conf, $vmid, 'IN', $ipversion); + generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, + $vmfw_conf, $vmid, 'OUT', $ipversion); + } + }; + warn $@ if $@; # just to be sure - should not happen } - if(ruleset_chain_exist($ruleset, "PVEFW-IPS")){ + if (ruleset_chain_exist($ruleset, "PVEFW-IPS")){ ruleset_insertrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate RELATED,ESTABLISHED", "-j PVEFW-IPS"); } @@ -3988,8 +4035,8 @@ sub get_ebtables_cmdlist { foreach my $chain (sort keys %$statushash) { my $stat = $statushash->{$chain}; - next if ($stat->{action} eq 'delete'); $changes = 1 if ($stat->{action} !~ 'ignore|exists'); + next if ($stat->{action} eq 'delete'); foreach my $cmd (@{$statushash->{$chain}->{'rules'}}) { if ($chain eq 'FORWARD' && $cmd eq $append_pve_to_forward) { @@ -4214,7 +4261,7 @@ sub update_nf_conntrack_logging { my $tmpfile = "$pve_fw_status_dir/log_nf_conntrack"; PVE::Tools::file_set_contents($tmpfile, $value); - PVE::Tools::run_command([qw(systemctl try-reload-or-restart pvefw-logger.service)]); + run_command([qw(systemctl try-reload-or-restart pvefw-logger.service)]); $log_nf_conntrack_enabled = $value; } } @@ -4224,6 +4271,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(); } @@ -4269,6 +4317,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};