X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FFirewall.pm;h=2c5e6b4e27408d0281a3f525a953809cf59bcc4e;hb=09d5f68ea808fe6a5df9e9a075e3a25c9ec471a4;hp=0c1a37afccbcc93c0de111e4f3d09b290f0f4c11;hpb=3fa83edfae216f5695bf920930832a9cdb72eb40;p=pve-firewall.git diff --git a/PVE/Firewall.pm b/PVE/Firewall.pm index 0c1a37a..2c5e6b4 100644 --- a/PVE/Firewall.pm +++ b/PVE/Firewall.pm @@ -3,7 +3,7 @@ package PVE::Firewall; use warnings; use strict; use Data::Dumper; -use Digest::MD5; +use Digest::SHA; use PVE::Tools; use PVE::QemuServer; use File::Path; @@ -137,6 +137,18 @@ sub parse_port_name_number_or_range { return ($nbports); } +my $bridge_firewall_enabled = 0; + +sub enable_bridge_firewall { + + return if $bridge_firewall_enabled; # only once + + system("echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables"); + system("echo 1 > /proc/sys/net/bridge/bridge-nf-call-ip6tables"); + + $bridge_firewall_enabled = 1; +} + my $rule_format = "%-15s %-30s %-30s %-15s %-15s %-15s\n"; sub iptables { @@ -159,12 +171,11 @@ sub iptables_get_chains { my $is_pvefw_chain = sub { my $name = shift; - return 1 if $name =~ m/^BRIDGEFW-(:?IN|OUT)$/; - return 1 if $name =~ m/^proxmoxfw-\S+$/; + return 1 if $name =~ m/^PVEFW-\S+$/; + return 1 if $name =~ m/^tap\d+i\d+-(:?IN|OUT)$/; return 1 if $name =~ m/^vmbr\d+-(:?IN|OUT)$/; return 1 if $name =~ m/^GROUP-(:?[^\s\-]+)-(:?IN|OUT)$/; - return 1 if $name =~ m/^host-(:?IN|OUT)$/; return undef; }; @@ -188,7 +199,7 @@ sub iptables_get_chains { my $chain = $1; return if !&$is_pvefw_chain($chain); $res->{$chain} = "unknown"; - } elsif ($line =~ m/^-A\s+(\S+)\s.*--log-prefix\s+\"PVESIG:(\S+)\"/) { + } elsif ($line =~ m/^-A\s+(\S+)\s.*--comment\s+\"PVESIG:(\S+)\"/) { my ($chain, $sig) = ($1, $2); return if !&$is_pvefw_chain($chain); $res->{$chain} = $sig; @@ -277,33 +288,38 @@ sub ruleset_insertrule { sub generate_bridge_chains { my ($ruleset, $bridge) = @_; - ruleset_create_chain($ruleset, "BRIDGEFW-IN"); - ruleset_create_chain($ruleset, "BRIDGEFW-OUT"); + if (!ruleset_chain_exist($ruleset, "PVEFW-BRIDGE-IN")){ + ruleset_create_chain($ruleset, "PVEFW-BRIDGE-IN"); + } - if (!ruleset_chain_exist($ruleset, "proxmoxfw-FORWARD")){ - ruleset_create_chain($ruleset, "proxmoxfw-FORWARD"); + if (!ruleset_chain_exist($ruleset, "PVEFW-BRIDGE-OUT")){ + ruleset_create_chain($ruleset, "PVEFW-BRIDGE-OUT"); + } - ruleset_addrule($ruleset, "proxmoxfw-FORWARD", "-m state --state RELATED,ESTABLISHED -j ACCEPT"); - ruleset_addrule($ruleset, "proxmoxfw-FORWARD", "-m physdev --physdev-is-in --physdev-is-bridged -j BRIDGEFW-OUT"); - ruleset_addrule($ruleset, "proxmoxfw-FORWARD", "-m physdev --physdev-is-out --physdev-is-bridged -j BRIDGEFW-IN"); + if (!ruleset_chain_exist($ruleset, "PVEFW-FORWARD")){ + ruleset_create_chain($ruleset, "PVEFW-FORWARD"); + + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m state --state RELATED,ESTABLISHED -j ACCEPT"); + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m physdev --physdev-is-in --physdev-is-bridged -j PVEFW-BRIDGE-OUT"); + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m physdev --physdev-is-out --physdev-is-bridged -j PVEFW-BRIDGE-IN"); } if (!ruleset_chain_exist($ruleset, "$bridge-IN")) { ruleset_create_chain($ruleset, "$bridge-IN"); - ruleset_addrule($ruleset, "proxmoxfw-FORWARD", "-i $bridge -j DROP"); # disable interbridge routing - ruleset_addrule($ruleset, "BRIDGEFW-IN", "-j $bridge-IN"); + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-i $bridge -j DROP"); # disable interbridge routing + ruleset_addrule($ruleset, "PVEFW-BRIDGE-IN", "-j $bridge-IN"); ruleset_addrule($ruleset, "$bridge-IN", "-j ACCEPT"); } if (!ruleset_chain_exist($ruleset, "$bridge-OUT")) { ruleset_create_chain($ruleset, "$bridge-OUT"); - ruleset_addrule($ruleset, "proxmoxfw-FORWARD", "-o $bridge -j DROP"); # disable interbridge routing - ruleset_addrule($ruleset, "BRIDGEFW-OUT", "-j $bridge-OUT"); + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o $bridge -j DROP"); # disable interbridge routing + ruleset_addrule($ruleset, "PVEFW-BRIDGE-OUT", "-j $bridge-OUT"); } } sub generate_tap_rules_direction { - my ($ruleset, $iface, $netid, $rules, $bridge, $direction) = @_; + my ($ruleset, $iface, $netid, $macaddr, $rules, $bridge, $direction) = @_; my $tapchain = "$iface-$direction"; @@ -312,6 +328,10 @@ sub generate_tap_rules_direction { ruleset_addrule($ruleset, $tapchain, "-m state --state INVALID -j DROP"); ruleset_addrule($ruleset, $tapchain, "-m state --state RELATED,ESTABLISHED -j ACCEPT"); + if ($direction eq 'OUT' && defined($macaddr)) { + ruleset_addrule($ruleset, $tapchain, "-m mac ! --mac-source $macaddr -j DROP"); + } + if ($rules) { foreach my $rule (@$rules) { next if $rule->{iface} && $rule->{iface} ne $netid; @@ -339,7 +359,7 @@ sub generate_tap_rules_direction { if ($direction eq 'OUT'){ # add tap->host rules my $rule = "-m physdev --physdev-$physdevdirection $iface -j $tapchain"; - ruleset_addrule($ruleset, "proxmoxfw-INPUT", $rule); + ruleset_addrule($ruleset, "PVEFW-INPUT", $rule); } } @@ -353,48 +373,51 @@ sub enablehostfw { my $rules = parse_fw_rules($filename, $fh); # host inbound firewall - ruleset_create_chain($ruleset, "host-IN"); + my $chain = "PVEFW-HOST-IN"; + ruleset_create_chain($ruleset, $chain); - ruleset_addrule($ruleset, "host-IN", "-m state --state INVALID -j DROP"); - ruleset_addrule($ruleset, "host-IN", "-m state --state RELATED,ESTABLISHED -j ACCEPT"); - ruleset_addrule($ruleset, "host-IN", "-i lo -j ACCEPT"); - ruleset_addrule($ruleset, "host-IN", "-m addrtype --dst-type MULTICAST -j ACCEPT"); - ruleset_addrule($ruleset, "host-IN", "-p udp -m state --state NEW -m multiport --dports 5404,5405 -j ACCEPT"); - ruleset_addrule($ruleset, "host-IN", "-p udp -m udp --dport 9000 -j ACCEPT"); #corosync + ruleset_addrule($ruleset, $chain, "-m state --state INVALID -j DROP"); + ruleset_addrule($ruleset, $chain, "-m state --state RELATED,ESTABLISHED -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-i lo -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-m addrtype --dst-type MULTICAST -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-p udp -m state --state NEW -m multiport --dports 5404,5405 -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-p udp -m udp --dport 9000 -j ACCEPT"); #corosync if ($rules->{in}) { foreach my $rule (@{$rules->{in}}) { # we use RETURN because we need to check also tap rules $rule->{action} = 'RETURN' if $rule->{action} eq 'ACCEPT'; - ruleset_generate_rule($ruleset, "host-IN", $rule); + ruleset_generate_rule($ruleset, $chain, $rule); } } - ruleset_addrule($ruleset, "host-IN", "-j LOG --log-prefix \"kvmhost-IN dropped: \" --log-level 4"); - ruleset_addrule($ruleset, "host-IN", "-j DROP"); + ruleset_addrule($ruleset, $chain, "-j LOG --log-prefix \"kvmhost-IN dropped: \" --log-level 4"); + ruleset_addrule($ruleset, $chain, "-j DROP"); # host outbound firewall - ruleset_create_chain($ruleset, "host-OUT"); - ruleset_addrule($ruleset, "host-OUT", "-m state --state INVALID -j DROP"); - ruleset_addrule($ruleset, "host-OUT", "-m state --state RELATED,ESTABLISHED -j ACCEPT"); - ruleset_addrule($ruleset, "host-OUT", "-o lo -j ACCEPT"); - ruleset_addrule($ruleset, "host-OUT", "-m addrtype --dst-type MULTICAST -j ACCEPT"); - ruleset_addrule($ruleset, "host-OUT", "-p udp -m state --state NEW -m multiport --dports 5404,5405 -j ACCEPT"); - ruleset_addrule($ruleset, "host-OUT", "-p udp -m udp --dport 9000 -j ACCEPT"); #corosync + $chain = "PVEFW-HOST-OUT"; + ruleset_create_chain($ruleset, $chain); + + ruleset_addrule($ruleset, $chain, "-m state --state INVALID -j DROP"); + ruleset_addrule($ruleset, $chain, "-m state --state RELATED,ESTABLISHED -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-o lo -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-m addrtype --dst-type MULTICAST -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-p udp -m state --state NEW -m multiport --dports 5404,5405 -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-p udp -m udp --dport 9000 -j ACCEPT"); #corosync if ($rules->{out}) { foreach my $rule (@{$rules->{out}}) { # we use RETURN because we need to check also tap rules $rule->{action} = 'RETURN' if $rule->{action} eq 'ACCEPT'; - ruleset_generate_rule($ruleset, "host-OUT", $rule); + ruleset_generate_rule($ruleset, $chain, $rule); } } - ruleset_addrule($ruleset, "host-OUT", "-j LOG --log-prefix \"kvmhost-OUT dropped: \" --log-level 4"); - ruleset_addrule($ruleset, "host-OUT", "-j DROP"); + ruleset_addrule($ruleset, $chain, "-j LOG --log-prefix \"kvmhost-OUT dropped: \" --log-level 4"); + ruleset_addrule($ruleset, $chain, "-j DROP"); - ruleset_addrule($ruleset, "proxmoxfw-OUTPUT", "-j host-OUT"); - ruleset_addrule($ruleset, "proxmoxfw-INPUT", "-j host-IN"); + ruleset_addrule($ruleset, "PVEFW-OUTPUT", "-j PVEFW-HOST-OUT"); + ruleset_addrule($ruleset, "PVEFW-INPUT", "-j PVEFW-HOST-IN"); } sub generate_group_rules { @@ -422,9 +445,9 @@ sub generate_group_rules { if ($rules->{out}) { foreach my $rule (@{$rules->{out}}) { - # we go the BRIDGEFW-IN because we need to check also other tap rules + # we go the PVEFW-BRIDGE-IN because we need to check also other tap rules # (and group rules can be set on any bridge, so we can't go to VMBRXX-IN) - $rule->{action} = 'BRIDGEFW-IN' if $rule->{action} eq 'ACCEPT'; + $rule->{action} = 'PVEFW-BRIDGE-IN' if $rule->{action} eq 'ACCEPT'; ruleset_generate_rule($rule, $chain, $rule); } } @@ -591,8 +614,8 @@ sub compile { my $ruleset = {}; # setup host firewall rules - ruleset_create_chain($ruleset, "proxmoxfw-INPUT"); - ruleset_create_chain($ruleset, "proxmoxfw-OUTPUT"); + ruleset_create_chain($ruleset, "PVEFW-INPUT"); + ruleset_create_chain($ruleset, "PVEFW-OUTPUT"); enablehostfw($ruleset); @@ -614,11 +637,11 @@ sub compile { generate_bridge_chains($ruleset, $bridge); - generate_tap_rules_direction($ruleset, $iface, $netid, $rules->{$vmid}->{in}, $bridge, 'IN'); - generate_tap_rules_direction($ruleset, $iface, $netid, $rules->{$vmid}->{out}, $bridge, 'OUT'); + my $macaddr = $net->{macaddr}; + generate_tap_rules_direction($ruleset, $iface, $netid, $macaddr, $rules->{$vmid}->{in}, $bridge, 'IN'); + generate_tap_rules_direction($ruleset, $iface, $netid, $macaddr, $rules->{$vmid}->{out}, $bridge, 'OUT'); } } - return $ruleset; } @@ -630,7 +653,7 @@ sub get_ruleset_status { my $statushash = {}; foreach my $chain (sort keys %$ruleset) { - my $digest = Digest::MD5->new(); + my $digest = Digest::SHA->new('sha1'); foreach my $cmd (@{$ruleset->{$chain}}) { $digest->add("$cmd\n"); } @@ -674,15 +697,14 @@ sub print_ruleset { sub print_sig_rule { my ($chain, $sig) = @_; - # Note: This rule should never match! We just use this hack to store a SHA1 checksum - # used to detect changes - return "-A $chain -j LOG --log-prefix \"PVESIG:$sig\" -p tcp -s \"127.128.129.130\" --dport 1\n"; + # We just use this to store a SHA1 checksum used to detect changes + return "-A $chain -m comment --comment \"PVESIG:$sig\"\n"; } -sub compile_and_start { - my ($verbose) = @_; +sub apply_ruleset { + my ($ruleset, $verbose) = @_; - my $ruleset = compile(); + enable_bridge_firewall(); my $cmdlist = "*filter\n"; # we pass this to iptables-restore; @@ -697,16 +719,16 @@ sub compile_and_start { $cmdlist .= ":$chain - [0:0]\n"; } - my $rule = "INPUT -j proxmoxfw-INPUT"; + my $rule = "INPUT -j PVEFW-INPUT"; if (!PVE::Firewall::iptables_rule_exist($rule)) { $cmdlist .= "-A $rule\n"; } - $rule = "OUTPUT -j proxmoxfw-OUTPUT"; + $rule = "OUTPUT -j PVEFW-OUTPUT"; if (!PVE::Firewall::iptables_rule_exist($rule)) { $cmdlist .= "-A $rule\n"; } - $rule = "FORWARD -j proxmoxfw-FORWARD"; + $rule = "FORWARD -j PVEFW-FORWARD"; if (!PVE::Firewall::iptables_rule_exist($rule)) { $cmdlist .= "-A $rule\n"; }