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. for tap-out rules, 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 and -m conntrack --ctstate RELATED,ESTABLISHED -j NFQUEUE 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. Signed-off-by: Alexandre Derumier <aderum...@odiso.com> --- example/100.fw | 7 +++++ src/PVE/Firewall.pm | 80 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/example/100.fw b/example/100.fw index 94f178d..1a3a7b7 100644 --- a/example/100.fw +++ b/example/100.fw @@ -28,6 +28,13 @@ 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 31c2f6e..a038a01 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -413,8 +413,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 @@ -436,8 +436,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 @@ -451,6 +451,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 @@ -700,7 +703,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; @@ -963,6 +966,7 @@ sub ruleset_create_vm_chain { my ($ruleset, $chain, $options, $macaddr, $direction) = @_; ruleset_create_chain($ruleset, $chain); + my $accept = generate_nfqueue($options); if (!(defined($options->{nosmurfs}) && $options->{nosmurfs} == 0)) { ruleset_addrule($ruleset, $chain, "-m conntrack --ctstate INVALID,NEW -j PVEFW-smurfs"); @@ -972,7 +976,7 @@ sub ruleset_create_vm_chain { if ($direction eq 'OUT') { ruleset_addrule($ruleset, $chain, "-p udp -m udp --sport 68 --dport 67 -j PVEFW-SET-ACCEPT-MARK"); } else { - ruleset_addrule($ruleset, $chain, "-p udp -m udp --sport 67 --dport 68 -j ACCEPT"); + ruleset_addrule($ruleset, $chain, "-p udp -m udp --sport 67 --dport 68 -j $accept"); } } @@ -981,7 +985,11 @@ 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"); + + if($direction eq 'OUT'){ + $accept = 'PVEFW-Accept'; + } + ruleset_addrule($ruleset, $chain, "-m conntrack --ctstate RELATED,ESTABLISHED -j $accept"); if ($direction eq 'OUT') { if (defined($macaddr) && !(defined($options->{macfilter}) && $options->{macfilter} == 0)) { @@ -992,7 +1000,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); @@ -1013,12 +1021,49 @@ 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) = @_; @@ -1046,7 +1091,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 @@ -1087,7 +1133,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; @@ -1098,7 +1146,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 @@ -1185,7 +1234,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"; @@ -1353,7 +1402,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) { @@ -1362,6 +1411,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" -- 1.7.10.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel