X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=src%2FPVE%2FFirewall.pm;h=2eb8a0ec55dec82d60c835e44f476d0d1a33478e;hp=d62fb113a329d809ccc39bb377a2d03f7bb11114;hb=9c7e08582e838e283333e9d49d59737d5e877628;hpb=b47ecc889a8db369a6324feebffabbe49d5516e6 diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index d62fb11..2eb8a0e 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -655,6 +655,84 @@ sub cleanup_fw_rule { return $r; } +my $rule_properties = { + pos => { + description => "Update rule at position .", + type => 'integer', + minimum => 0, + optional => 1, + }, + digest => { + type => 'string', + optional => 1, + }, + type => { + type => 'string', + optional => 1, + enum => ['in', 'out', 'group'], + }, + action => { + type => 'string', + optional => 1, + }, + source => { + type => 'string', + optional => 1, + }, + dest => { + type => 'string', + optional => 1, + }, + proto => { + type => 'string', + optional => 1, + }, + enable => { + type => 'boolean', + optional => 1, + }, + sport => { + type => 'string', + optional => 1, + }, + dport => { + type => 'string', + optional => 1, + }, + comment => { + type => 'string', + optional => 1, + }, +}; + +sub add_rule_properties { + my ($properties) = @_; + + foreach my $k (keys %$rule_properties) { + $properties->{$k} = $rule_properties->{$k}; + } + + return $properties; +} + +sub copy_rule_data { + my ($rule, $param) = @_; + + foreach my $k (keys %$rule_properties) { + if (defined(my $v = $param->{$k})) { + if ($v eq '' || $v eq '-') { + delete $rule->{$k}; + } else { + $rule->{$k} = $v; + } + } else { + delete $rule->{$k}; + } + } + return $rule; +} + +# core functions my $bridge_firewall_enabled = 0; sub enable_bridge_firewall { @@ -700,7 +778,7 @@ sub iptables_get_chains { return 1 if $name =~ m/^venet0-\d+-(:?IN|OUT)$/; - return 1 if $name =~ m/^vmbr\d+-(:?FW|IN|OUT)$/; + return 1 if $name =~ m/^vmbr\d+-(:?FW|IN|OUT|IPS)$/; return 1 if $name =~ m/^GROUP-(:?[^\s\-]+)-(:?IN|OUT)$/; return undef; @@ -1056,6 +1134,25 @@ sub generate_nfqueue { return $action; } +sub ruleset_generate_vm_ipsrules { + my ($ruleset, $options, $direction, $iface, $bridge) = @_; + + if ($options->{ips} && $direction eq 'IN') { + my $nfqueue = generate_nfqueue($options); + + if (!ruleset_chain_exist($ruleset, "$bridge-IPS")) { + ruleset_create_chain($ruleset, "PVEFW-IPS"); + } + + if (!ruleset_chain_exist($ruleset, "$bridge-IPS")) { + ruleset_create_chain($ruleset, "$bridge-IPS"); + ruleset_insertrule($ruleset, "PVEFW-IPS", "-o $bridge -m physdev --physdev-is-out -j $bridge-IPS"); + } + + ruleset_addrule($ruleset, "$bridge-IPS", "-m physdev --physdev-out $iface --physdev-is-bridged -j $nfqueue"); + } +} + sub generate_venet_rules_direction { my ($ruleset, $groups_conf, $vmfw_conf, $vmid, $ip, $direction) = @_; @@ -1127,6 +1224,8 @@ sub generate_tap_rules_direction { ruleset_generate_vm_rules($ruleset, $rules, $groups_conf, $tapchain, $netid, $direction, $options); + ruleset_generate_vm_ipsrules($ruleset, $options, $direction, $iface, $bridge); + # implement policy my $policy; @@ -1430,7 +1529,7 @@ sub parse_hostfw_option { my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog"; - if ($line =~ m/^(enable|dhcp|nosmurfs|tcpflags|allow_bridge_route):\s*(0|1)\s*$/i) { + if ($line =~ m/^(enable|dhcp|nosmurfs|tcpflags|allow_bridge_route|optimize):\s*(0|1)\s*$/i) { $opt = lc($1); $value = int($2); } elsif ($line =~ m/^(log_level_in|log_level_out|tcp_flags_log_level|smurf_log_level):\s*(($loglevels)\s*)?$/i) { @@ -1786,6 +1885,40 @@ sub load_security_groups { return $groups_conf; } +sub save_security_groups { + my ($groups_conf) = @_; + + my $raw = ''; + my $filename = "/etc/pve/firewall/groups.fw"; + + foreach my $group (sort keys %{$groups_conf->{rules}}) { + my $rules = $groups_conf->{rules}->{$group}; + $raw .= "[group $group]\n\n"; + + foreach my $rule (@$rules) { + if ($rule->{type} eq 'in' || $rule->{type} eq 'out') { + $raw .= '|' if defined($rule->{enable}) && !$rule->{enable}; + $raw .= uc($rule->{type}); + $raw .= " " . $rule->{action}; + $raw .= " " . ($rule->{source} || '-'); + $raw .= " " . ($rule->{dest} || '-'); + $raw .= " " . ($rule->{proto} || '-'); + $raw .= " " . ($rule->{dport} || '-'); + $raw .= " " . ($rule->{sport} || '-'); + $raw .= " # " . encode('utf8', $rule->{comment}) + if $rule->{comment} && $rule->{comment} !~ m/^\s*$/; + $raw .= "\n"; + } else { + die "implement me '$rule->{type}'"; + } + } + + $raw .= "\n"; + } + + PVE::Tools::file_set_contents($filename, $raw); +} + sub load_hostfw_conf { my $hostfw_conf = {}; @@ -1820,6 +1953,8 @@ sub compile { enable_host_firewall($ruleset, $hostfw_conf, $groups_conf) if $hostfw_enable; + my $ips_enable = undef; + # generate firewall rules for QEMU VMs foreach my $vmid (keys %{$vmdata->{qemu}}) { my $conf = $vmdata->{qemu}->{$vmid}; @@ -1827,6 +1962,8 @@ sub compile { next if !$vmfw_conf; next if defined($vmfw_conf->{options}->{enable}) && ($vmfw_conf->{options}->{enable} == 0); + $ips_enable = 1 if $vmfw_conf->{options}->{ips}; + foreach my $netid (keys %$conf) { next if $netid !~ m/^net(\d+)$/; my $net = PVE::QemuServer::parse_net($conf->{$netid}); @@ -1856,6 +1993,8 @@ sub compile { next if !$vmfw_conf; next if defined($vmfw_conf->{options}->{enable}) && ($vmfw_conf->{options}->{enable} == 0); + $ips_enable = 1 if $vmfw_conf->{options}->{ips}; + if ($conf->{ip_address} && $conf->{ip_address}->{value}) { my $ip = $conf->{ip_address}->{value}; generate_venet_rules_direction($ruleset, $groups_conf, $vmfw_conf, $vmid, $ip, 'IN'); @@ -1884,6 +2023,13 @@ sub compile { } } + if($hostfw_options->{optimize}){ + + my $accept = $ips_enable ? "PVEFW-IPS" : "ACCEPT"; + ruleset_insertrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate RELATED,ESTABLISHED -j $accept"); + ruleset_insertrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate INVALID -j DROP"); + } + # fixme: what log level should we use here? my $loglevel = get_option_log_level($hostfw_options, "log_level_out");