Signed-off-by: Alexandre Derumier <aderum...@odiso.com> This add ips (like suricata) support through nfqueues.
this create a new chain PVEFW-Accept -A PVEFW-Accept -o vmbr14 -m physdev --physdev-is-out -j vmbr14-IPS -A vmbr14-IPS -m physdev --physdev-out tap110i0 --physdev-is-bridged -j NFQUEUE --queue-num 0 --queue-bypass -A PVEFW-Accept -o vmbr1 -m physdev --physdev-is-out -j vmbr1-IPS -A vmbr1-IPS -m physdev --physdev-out tap123i0 --physdev-is-bridged -j NFQUEUE --queue-num 0 --queue-bypass -A PVEFW-Accept -j ACCEPT it's using --queue-bypass (only available in 3.10 kernel), so it's suricata daemon is down, packets are not dropped. PVEFW-Accept is always use when connection is already established -m conntrack --ctstate RELATED,ESTABLISHED -j PVEFW-Accept in tap-in chain, I replace -j ACCEPT by -j NFQUEUE when ips is enabled group-in rules always replace ACCEPT by PVEFW-Accept vmid.fw ------- ips: 1 ips_queues: 0:3 1 or more queues can be defined (if we want cpu loadbalancing, or dedicated queue for a specific vm). If not defined, default queue 0 is used. --- example/100.fw | 6 ++++ src/PVE/Firewall.pm | 85 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 73 insertions(+), 18 deletions(-) diff --git a/example/100.fw b/example/100.fw index 94f178d..880e6c3 100644 --- a/example/100.fw +++ b/example/100.fw @@ -28,6 +28,12 @@ tcpflags: 1 # enable DHCP dhcp: 1 +# enable ips +ips: 1 + +# specify nfqueue queues (optionnal) +#ips_queues: 0 +ips_queues: 0:3 [RULES] diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 0f2bc11..1c6577a 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -266,7 +266,7 @@ my $pve_fw_macros = { { action => 'PARAM', proto => 'tcp', dport => '953' }, ], 'Razor' => [ - { action => 'ACCEPT', proto => 'tcp', dport => '2703' }, + { action => 'PVEFW-Accept', proto => 'tcp', dport => '2703' }, ], 'Rdate' => [ { action => 'PARAM', proto => 'tcp', dport => '37' }, @@ -402,8 +402,8 @@ my $pve_std_chains = { # we are not interested in BROADCAST/MULTICAST/ANYCAST { action => 'PVEFW-DropBroadcast' }, # ACCEPT critical ICMP types - { action => 'ACCEPT', proto => 'icmp', dport => 'fragmentation-needed' }, - { action => 'ACCEPT', proto => 'icmp', dport => 'time-exceeded' }, + { action => 'PVEFW-Accept', proto => 'icmp', dport => 'fragmentation-needed' }, + { action => 'PVEFW-Accept', proto => 'icmp', dport => 'time-exceeded' }, # Drop packets with INVALID state "-m conntrack --ctstate INVALID -j DROP", # Drop Microsoft SMB noise @@ -425,8 +425,8 @@ my $pve_std_chains = { # we are not interested in BROADCAST/MULTICAST/ANYCAST { action => 'PVEFW-DropBroadcast' }, # ACCEPT critical ICMP types - { action => 'ACCEPT', proto => 'icmp', dport => 'fragmentation-needed' }, - { action => 'ACCEPT', proto => 'icmp', dport => 'time-exceeded' }, + { action => 'PVEFW-Accept', proto => 'icmp', dport => 'fragmentation-needed' }, + { action => 'PVEFW-Accept', proto => 'icmp', dport => 'time-exceeded' }, # Drop packets with INVALID state "-m conntrack --ctstate INVALID -j DROP", # Drop Microsoft SMB noise @@ -440,6 +440,9 @@ my $pve_std_chains = { # Drop DNS replies { action => 'DROP', proto => 'udp', sport => 53 }, ], + 'PVEFW-Accept' => [ + { action => 'ACCEPT' }, + ], 'PVEFW-tcpflags' => [ # same as shorewall tcpflags action. # Packets arriving on this interface are checked for som illegal combinations of TCP flags @@ -898,7 +901,7 @@ sub generate_bridge_chains { ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-out -j $bridge-IN"); ruleset_addrule($ruleset, "$bridge-FW", "-m mark --mark 1 -j ACCEPT"); # accept traffic to unmanaged bridge ports - ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-out -j ACCEPT "); + ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-out -j PVEFW-Accept"); } } @@ -939,7 +942,11 @@ sub ruleset_create_vm_chain { } if (!(defined($options->{dhcp}) && $options->{dhcp} == 0)) { - ruleset_addrule($ruleset, $chain, "-p udp -m udp --dport 67:68 -j ACCEPT"); + my $accept = "ACCEPT"; + if($direction eq 'IN'){ + $accept = generate_nfqueue($options); + } + ruleset_addrule($ruleset, $chain, "-p udp -m udp --dport 67:68 -j $accept"); } if ($options->{tcpflags}) { @@ -947,7 +954,7 @@ sub ruleset_create_vm_chain { } ruleset_addrule($ruleset, $chain, "-m conntrack --ctstate INVALID -j DROP"); - ruleset_addrule($ruleset, $chain, "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-m conntrack --ctstate RELATED,ESTABLISHED -j PVEFW-Accept"); if ($direction eq 'OUT') { if (defined($macaddr) && !(defined($options->{macfilter}) && $options->{macfilter} == 0)) { @@ -958,7 +965,7 @@ sub ruleset_create_vm_chain { } sub ruleset_generate_vm_rules { - my ($ruleset, $rules, $groups_conf, $chain, $netid, $direction) = @_; + my ($ruleset, $rules, $groups_conf, $chain, $netid, $direction, $options) = @_; my $lc_direction = lc($direction); @@ -979,12 +986,48 @@ sub ruleset_generate_vm_rules { ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }); } else { - ruleset_generate_rule($ruleset, $chain, $rule, { REJECT => "PVEFW-reject" }); + my $accept = generate_nfqueue($options); + ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => $accept , REJECT => "PVEFW-reject" }); } } } } +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, "$bridge-IPS"); + ruleset_insertrule($ruleset, "PVEFW-Accept", "-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_nfqueue { + my ($options) = @_; + my $action = ""; + if($options->{ips}){ + $action = "NFQUEUE"; + if($options->{ips_queues} && $options->{ips_queues} =~ m/^(\d+)(:(\d+))?$/) { + if(defined($3) && defined($1)) { + $action .= " --queue-balance $1:$3"; + }elsif (defined($1)) { + $action .= " --queue-num $1"; + } + } + $action .= " --queue-bypass"; + }else{ + $action = "ACCEPT"; + } + + return $action; +} + sub generate_venet_rules_direction { my ($ruleset, $groups_conf, $vmfw_conf, $vmid, $ip, $direction) = @_; @@ -1001,7 +1044,7 @@ sub generate_venet_rules_direction { ruleset_create_vm_chain($ruleset, $chain, $options, undef, $direction); - ruleset_generate_vm_rules($ruleset, $rules, $groups_conf, $chain, 'venet', $direction); + ruleset_generate_vm_rules($ruleset, $rules, $groups_conf, $chain, 'venet', $direction, $options); # implement policy my $policy; @@ -1012,7 +1055,8 @@ sub generate_venet_rules_direction { $policy = $options->{policy_in} || 'DROP'; # allow nothing by default } - my $accept_action = $direction eq 'OUT' ? "PVEFW-SET-ACCEPT-MARK" : "ACCEPT"; + my $accept = generate_nfqueue($options); + my $accept_action = $direction eq 'OUT' ? "PVEFW-SET-ACCEPT-MARK" : $accept; ruleset_add_chain_policy($ruleset, $chain, $vmid, $policy, $loglevel, $accept_action); # plug into FORWARD, INPUT and OUTPUT chain @@ -1053,7 +1097,9 @@ sub generate_tap_rules_direction { ruleset_create_vm_chain($ruleset, $tapchain, $options, $macaddr, $direction); - ruleset_generate_vm_rules($ruleset, $rules, $groups_conf, $tapchain, $netid, $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; @@ -1064,7 +1110,8 @@ sub generate_tap_rules_direction { $policy = $options->{policy_in} || 'DROP'; # allow nothing by default } - my $accept_action = $direction eq 'OUT' ? "PVEFW-SET-ACCEPT-MARK" : "ACCEPT"; + my $accept = generate_nfqueue($options); + my $accept_action = $direction eq 'OUT' ? "PVEFW-SET-ACCEPT-MARK" : $accept; ruleset_add_chain_policy($ruleset, $tapchain, $vmid, $policy, $loglevel, $accept_action); # plug the tap chain to bridge chain @@ -1152,7 +1199,7 @@ sub generate_group_rules { foreach my $rule (@$rules) { next if $rule->{type} ne 'in'; - ruleset_generate_rule($ruleset, $chain, $rule, { REJECT => "PVEFW-reject" }); + ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => "PVEFW-Accept", REJECT => "PVEFW-reject" }); } $chain = "GROUP-${group}-OUT"; @@ -1320,7 +1367,7 @@ sub parse_vmfw_option { my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog"; - if ($line =~ m/^(enable|dhcp|macfilter|nosmurfs|tcpflags):\s*(0|1)\s*$/i) { + if ($line =~ m/^(enable|dhcp|macfilter|nosmurfs|tcpflags|ips):\s*(0|1)\s*$/i) { $opt = lc($1); $value = int($2); } elsif ($line =~ m/^(log_level_in|log_level_out):\s*(($loglevels)\s*)?$/i) { @@ -1329,6 +1376,9 @@ sub parse_vmfw_option { } elsif ($line =~ m/^(policy_(in|out)):\s*(ACCEPT|DROP|REJECT)\s*$/i) { $opt = lc($1); $value = uc($3); + } elsif ($line =~ m/^(ips_queues):\s*((\d+)(:(\d+))?)\s*$/i) { + $opt = lc($1); + $value = $2; } else { chomp $line; die "can't parse option '$line'\n" @@ -1755,8 +1805,7 @@ sub compile { } # fixme: this is an optimization? if so, we should also drop INVALID packages? - ruleset_insertrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT"); - + ruleset_insertrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate RELATED,ESTABLISHED -j PVEFW-Accept"); # fixme: what log level should we use here? my $loglevel = get_option_log_level($hostfw_options, "log_level_out"); -- 1.7.10.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel