X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=PVE%2FFirewall.pm;h=ea24cfb40069adf915e233843182b6511bdcdf09;hp=09703b086f57873dda4f17735635c25bb8a955d8;hb=7b291cabd7a69a441cae666b2c94ab49106cf592;hpb=85c6eaed064ef404a2c290388ed5b94582792348 diff --git a/PVE/Firewall.pm b/PVE/Firewall.pm index 09703b0..ea24cfb 100644 --- a/PVE/Firewall.pm +++ b/PVE/Firewall.pm @@ -389,6 +389,7 @@ sub get_etc_services { if ($line =~ m!^(\S+)\s+(\S+)/(tcp|udp).*$!) { $services->{byid}->{$2}->{name} = $1; + $services->{byid}->{$2}->{port} = $2; $services->{byid}->{$2}->{$3} = 1; $services->{byname}->{$1} = $services->{byid}->{$2}; } @@ -457,9 +458,13 @@ sub parse_port_name_number_or_range { my $nbports = 0; foreach my $item (split(/,/, $str)) { my $portlist = ""; + my $oldpon = undef; foreach my $pon (split(':', $item, 2)) { + $pon = $services->{byname}->{$pon}->{port} if $services->{byname}->{$pon}->{port}; if ($pon =~ m/^\d+$/){ die "invalid port '$pon'\n" if $pon < 0 && $pon > 65535; + die "port '$pon' must be bigger than port '$oldpon' \n" if $oldpon && ($pon < $oldpon); + $oldpon = $pon; }else{ die "invalid port $services->{byname}->{$pon}\n" if !$services->{byname}->{$pon}; } @@ -507,7 +512,7 @@ sub iptables_get_chains { 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/^vmbr\d+-(:?FW|IN|OUT)$/; return 1 if $name =~ m/^GROUP-(:?[^\s\-]+)-(:?IN|OUT)$/; return undef; @@ -627,38 +632,35 @@ sub ruleset_insertrule { sub generate_bridge_chains { my ($ruleset, $bridge) = @_; - if (!ruleset_chain_exist($ruleset, "PVEFW-BRIDGE-IN")){ - ruleset_create_chain($ruleset, "PVEFW-BRIDGE-IN"); - } - - if (!ruleset_chain_exist($ruleset, "PVEFW-BRIDGE-OUT")){ - ruleset_create_chain($ruleset, "PVEFW-BRIDGE-OUT"); - } - if (!ruleset_chain_exist($ruleset, "PVEFW-FORWARD")){ ruleset_create_chain($ruleset, "PVEFW-FORWARD"); - ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate 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, "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-FW")) { + ruleset_create_chain($ruleset, "$bridge-FW"); + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o $bridge -m physdev --physdev-is-bridged -j $bridge-FW"); + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-i $bridge -m physdev --physdev-is-bridged -j $bridge-FW"); + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o $bridge -j DROP"); # disable interbridge routing + ruleset_addrule($ruleset, "PVEFW-FORWARD", "-i $bridge -j DROP"); # disable interbridge routing } if (!ruleset_chain_exist($ruleset, "$bridge-OUT")) { ruleset_create_chain($ruleset, "$bridge-OUT"); - ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o $bridge -j DROP"); # disable interbridge routing - ruleset_addrule($ruleset, "PVEFW-BRIDGE-OUT", "-j $bridge-OUT"); + ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-bridged --physdev-is-in -j $bridge-OUT"); + } + + if (!ruleset_chain_exist($ruleset, "$bridge-IN")) { + ruleset_create_chain($ruleset, "$bridge-IN"); + ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-bridged --physdev-is-out -j $bridge-IN"); } } sub generate_tap_rules_direction { - my ($ruleset, $group_rules, $iface, $netid, $macaddr, $rules, $bridge, $direction) = @_; + my ($ruleset, $group_rules, $iface, $netid, $macaddr, $vmfw_conf, $bridge, $direction) = @_; + + my $rules = $vmfw_conf->{lc($direction)}; + my $options = $vmfw_conf->{options}; my $tapchain = "$iface-$direction"; @@ -682,17 +684,40 @@ sub generate_tap_rules_direction { generate_group_rules($ruleset, $group_rules, $2); } ruleset_generate_rule($ruleset, $tapchain, $rule); - ruleset_addrule($ruleset, $tapchain, "-m mark --mark 1 -g $bridge-IN") + ruleset_addrule($ruleset, $tapchain, "-m mark --mark 1 -j RETURN") if $direction eq 'OUT'; } else { - $rule->{action} = "$bridge-IN" if $rule->{action} eq 'ACCEPT' && $direction eq 'OUT'; + $rule->{action} = "RETURN" if $rule->{action} eq 'ACCEPT' && $direction eq 'OUT'; ruleset_generate_rule($ruleset, $tapchain, $rule); } } } - ruleset_addrule($ruleset, $tapchain, "-j LOG --log-prefix \"$tapchain-dropped: \" --log-level 4"); - ruleset_addrule($ruleset, $tapchain, "-j DROP"); + # implement policy + my $policy; + + if ($direction eq 'OUT') { + $policy = $options->{'policy-out'} || 'ACCEPT'; # allow everything by default + } else { + $policy = $options->{'policy-in'} || 'DROP'; # allow everything by default + } + + if ($policy eq 'ACCEPT') { + if ($direction eq 'OUT') { + ruleset_addrule($ruleset, $tapchain, "-j RETURN"); + } else { + ruleset_addrule($ruleset, $tapchain, "-j ACCEPT"); + } + } elsif ($policy eq 'DROP') { + ruleset_addrule($ruleset, $tapchain, "-j LOG --log-prefix \"$tapchain-dropped: \" --log-level 4"); + ruleset_addrule($ruleset, $tapchain, "-j DROP"); + } elsif ($policy eq 'REJECT') { + ruleset_addrule($ruleset, $tapchain, "-j LOG --log-prefix \"$tapchain-reject: \" --log-level 4"); + ruleset_addrule($ruleset, $tapchain, "-j REJECT"); + } else { + # should not happen + die "internal error: unknown policy '$policy'"; + } # plug the tap chain to bridge chain my $physdevdirection = $direction eq 'IN' ? "out" : "in"; @@ -1109,23 +1134,29 @@ sub compile { my $ruleset = {}; - # setup host firewall rules ruleset_create_chain($ruleset, "PVEFW-INPUT"); ruleset_create_chain($ruleset, "PVEFW-OUTPUT"); + ruleset_create_chain($ruleset, "PVEFW-FORWARD"); ruleset_create_chain($ruleset, "PVEFW-SET-ACCEPT-MARK"); ruleset_addrule($ruleset, "PVEFW-SET-ACCEPT-MARK", "-j MARK --set-mark 1"); + my $enable_hostfw = 0; $filename = "/etc/pve/local/host.fw"; if (my $fh = IO::File->new($filename, O_RDONLY)) { my $host_rules = parse_host_fw_rules($filename, $fh); + + $enable_hostfw = 1; + enablehostfw($ruleset, $host_rules, $group_rules); } # generate firewall rules for QEMU VMs foreach my $vmid (keys %{$vmdata->{qemu}}) { my $conf = $vmdata->{qemu}->{$vmid}; - next if !$rules->{$vmid}; + my $vmfw_conf = $rules->{$vmid}; + next if !$vmfw_conf; + next if defined($vmfw_conf->{options}->{enable}) && ($vmfw_conf->{options}->{enable} == 0); foreach my $netid (keys %$conf) { next if $netid !~ m/^net(\d+)$/; @@ -1138,16 +1169,19 @@ sub compile { $bridge .= "v$net->{tag}" if $net->{tag}; + generate_bridge_chains($ruleset, $bridge); my $macaddr = $net->{macaddr}; - generate_tap_rules_direction($ruleset, $group_rules, $iface, $netid, $macaddr, $rules->{$vmid}->{in}, $bridge, 'IN'); - generate_tap_rules_direction($ruleset, $group_rules, $iface, $netid, $macaddr, $rules->{$vmid}->{out}, $bridge, 'OUT'); + generate_tap_rules_direction($ruleset, $group_rules, $iface, $netid, $macaddr, $vmfw_conf, $bridge, 'IN'); + generate_tap_rules_direction($ruleset, $group_rules, $iface, $netid, $macaddr, $vmfw_conf, $bridge, 'OUT'); } } - # allow traffic from lo (ourself) - ruleset_addrule($ruleset, "PVEFW-INPUT", "-i lo -j ACCEPT"); + if ($enable_hostfw) { + # allow traffic from lo (ourself) + ruleset_addrule($ruleset, "PVEFW-INPUT", "-i lo -j ACCEPT"); + } return $ruleset; } @@ -1265,6 +1299,9 @@ sub apply_ruleset { } foreach my $chain (keys %$statushash) { next if $statushash->{$chain}->{action} ne 'delete'; + next if $chain eq 'PVEFW-INPUT'; + next if $chain eq 'PVEFW-OUTPUT'; + next if $chain eq 'PVEFW-FORWARD'; $cmdlist .= "-X $chain\n"; }