From f3255cd83a13e8fb29e096ae94bdf2e8f970bdf6 Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Mon, 6 Sep 2010 15:29:20 -0700 Subject: [PATCH] Rework blacklisting Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Rules.pm | 67 ++++++++++++++++++++++------- Shorewall/Perl/Shorewall/Zones.pm | 59 +++++++++++++++++++++---- Shorewall/changelog.txt | 2 + Shorewall/releasenotes.txt | 15 +++++++ manpages/shorewall-blacklist.xml | 48 +++++++++++++++------ manpages/shorewall-interfaces.xml | 30 ++++++++++++- manpages6/shorewall6-blacklist.xml | 32 ++++++++++++++ manpages6/shorewall6-interfaces.xml | 33 +++++++++++--- 8 files changed, 242 insertions(+), 44 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm index 788d84ebf..367f771a8 100644 --- a/Shorewall/Perl/Shorewall/Rules.pm +++ b/Shorewall/Perl/Shorewall/Rules.pm @@ -48,14 +48,10 @@ our @EXPORT = qw( process_tos our @EXPORT_OK = qw( process_rule process_rule1 initialize ); our $VERSION = '4.4_13'; -# -# Set to one if we find a SECTION -# our $macro_nest_level; our $current_param; our @param_stack; our $family; - # # When splitting a line in the rules file, don't pad out the columns with '-' if the first column contains one of these # @@ -217,16 +213,19 @@ sub add_rule_pair( $$$$ ) { sub setup_blacklist() { - my $hosts = find_hosts_by_option 'blacklist'; + my $hosts = find_hosts_by_option1 'blacklist', BL_IN; + my $hosts1 = find_hosts_by_option1 'blacklist', BL_OUT; my $chainref; + my $chainref1; my ( $level, $disposition ) = @config{'BLACKLIST_LOGLEVEL', 'BLACKLIST_DISPOSITION' }; my $target = $disposition eq 'REJECT' ? 'reject' : $disposition; # # We go ahead and generate the blacklist chain and jump to it, even if it turns out to be empty. That is necessary # for 'refresh' to work properly. # - if ( @$hosts ) { - $chainref = dont_delete new_standard_chain 'blacklst'; + if ( @$hosts || @$hosts1 ) { + $chainref = dont_delete new_standard_chain 'blacklst' if @$hosts; + $chainref1 = dont_delete new_standard_chain 'blackout' if @$hosts1; if ( defined $level && $level ne '' ) { my $logchainref = new_standard_chain 'blacklog'; @@ -250,7 +249,7 @@ sub setup_blacklist() { while ( read_a_line ) { if ( $first_entry ) { - unless ( @$hosts ) { + unless ( @$hosts || @$hosts1 ) { warning_message qq(The entries in $fn have been ignored because there are no 'blacklist' interfaces); close_file; last BLACKLIST; @@ -265,6 +264,8 @@ sub setup_blacklist() { $options = 'from' if $options eq '-'; + warning_message "'$options' entry ignored because there are no matching interfaces", next unless @$hosts || $options eq 'to'; + for ( split /,/, $options ) { fatal_error "Invalid OPTION ($_)" unless /^(from|to)$/; $direction = $_; @@ -274,19 +275,31 @@ sub setup_blacklist() { $chainref , NO_RESTRICT , do_proto( $protocol , $ports, '' ) , - $direction eq 'from' ? $networks : '', - $direction eq 'to' ? $networks : '', + $networks, + '', '' , $target , '' , $target , - '' ); + '' ) if $chainref && $options eq 'from'; + + expand_rule( + $chainref1 , + NO_RESTRICT , + do_proto( $protocol , $ports, '' ) , + $networks, + '', + '' , + $target , + '' , + $target , + '' ) if $chainref1 && $options eq 'to'; progress_message " \"$currentline\" added to blacklist"; } warning_message q(There are interfaces or hosts with the 'blacklist' option but the 'blacklist' file is empty) if $first_entry && @$hosts; - } elsif ( @$hosts ) { + } elsif ( @$hosts || @$hosts1 ) { warning_message q(There are interfaces or hosts with the 'blacklist' option, but the 'blacklist' file is either missing or has zero size); } @@ -307,8 +320,27 @@ sub setup_blacklist() { set_interface_option $interface, 'use_input_chain', 1; set_interface_option $interface, 'use_forward_chain', 1; - progress_message " Blacklisting enabled on ${interface}:${network}"; + progress_message " Type 1 blacklisting enabled on ${interface}:${network}"; } + + for my $hostref ( @$hosts1 ) { + my $interface = $hostref->[0]; + my $ipsec = $hostref->[1]; + my $policy = have_ipsec ? "-m policy --pol $ipsec --dir in " : ''; + my $network = $hostref->[2]; + my $source = match_source_net $network; + my $target = source_exclusion( $hostref->[3], $chainref1 ); + + for my $chain ( first_chains $interface ) { + add_jump $filter_table->{$chain} , $target, 0, "${source}${state}${policy}"; + } + + set_interface_option $interface, 'use_input_chain', 1; + set_interface_option $interface, 'use_forward_chain', 1; + + progress_message " Type 2 blacklisting enabled on ${interface}:${network}"; + } + } } @@ -1849,6 +1881,7 @@ sub generate_matrix() { our %input_jump_added = (); our %output_jump_added = (); our %forward_jump_added = (); + my %needs_bl_jump = (); progress_message2 'Generating Rule Matrix...'; # @@ -1977,7 +2010,8 @@ sub generate_matrix() { my $ipsec_in_match = match_ipsec_in $zone , $hostref; my $ipsec_out_match = match_ipsec_out $zone , $hostref; my $exclusions = $hostref->{exclusions}; - + my $blacklist = $hostref->{options}{blacklist} & BL_OUT; + for my $net ( @{$hostref->{hosts}} ) { my $dest = match_dest_net $net; @@ -1989,10 +2023,11 @@ sub generate_matrix() { my $interfacematch = ''; my $use_output = 0; - if ( @vservers || use_output_chain( $interface, $interfacechainref ) || ( @{$interfacechainref->{rules}} && ! $chain1ref ) ) { + if ( @vservers || use_output_chain( $interface, $interfacechainref ) || ( ( $blacklist || @{$interfacechainref->{rules}} ) && ! $chain1ref ) ) { $outputref = $interfacechainref; add_jump $filter_table->{OUTPUT}, $outputref, 0, match_dest_dev( $interface ) unless $output_jump_added{$interface}++; $use_output = 1; + $needs_bl_jump{output_chain $interface} = 1 if $blacklist; unless ( lc $net eq IPv6_LINKLOCAL ) { for my $vzone ( vserver_zones ) { @@ -2002,6 +2037,7 @@ sub generate_matrix() { } else { $outputref = $filter_table->{OUTPUT}; $interfacematch = match_dest_dev $interface; + $needs_bl_jump{output_chain $interface} = 1 if $blacklist; } add_jump $outputref , $nextchain, 0, join( '', $interfacematch, $dest, $ipsec_out_match ); @@ -2255,6 +2291,7 @@ sub generate_matrix() { add_jump $frwd_ref , $last_chain, 1 if $frwd_ref && $last_chain; } + add_jump( $filter_table->{$_}, $filter_table->{blackout} , 0 , '' , 0 , 0 ) for keys %needs_bl_jump; add_interface_jumps @interfaces unless $interface_jumps_added; my %builtins = ( mangle => [ qw/PREROUTING INPUT FORWARD POSTROUTING/ ] , diff --git a/Shorewall/Perl/Shorewall/Zones.pm b/Shorewall/Perl/Shorewall/Zones.pm index 0f8216756..ac9fc84fa 100644 --- a/Shorewall/Perl/Shorewall/Zones.pm +++ b/Shorewall/Perl/Shorewall/Zones.pm @@ -41,6 +41,8 @@ our @EXPORT = qw( NOTHING IP BPORT IPSEC + BL_IN + BL_OUT determine_zones zone_report @@ -78,6 +80,7 @@ our @EXPORT = qw( NOTHING compile_updown validate_hosts_file find_hosts_by_option + find_hosts_by_option1 all_ipsets have_ipsec ); @@ -94,7 +97,12 @@ use constant { NOTHING => 'NOTHING', IPSECPROTO => 'ah|esp|ipcomp', IPSECMODE => 'tunnel|transport' }; - +# +# blacklist option values +# +use constant { + BL_IN => 1 , + BL_OUT => 2 }; # # Zone Table. # @@ -231,7 +239,7 @@ sub initialize( $ ) { if ( $family == F_IPV4 ) { %validinterfaceoptions = (arp_filter => BINARY_IF_OPTION, arp_ignore => ENUM_IF_OPTION, - blacklist => SIMPLE_IF_OPTION + IF_OPTION_HOST, + blacklist => ENUM_IF_OPTION, bridge => SIMPLE_IF_OPTION, detectnets => OBSOLETE_IF_OPTION, dhcp => SIMPLE_IF_OPTION, @@ -264,7 +272,7 @@ sub initialize( $ ) { sourceonly => 1, ); } else { - %validinterfaceoptions = ( blacklist => SIMPLE_IF_OPTION + IF_OPTION_HOST, + %validinterfaceoptions = ( blacklist => ENUM_IF_OPTION, bridge => SIMPLE_IF_OPTION, dhcp => SIMPLE_IF_OPTION, maclist => SIMPLE_IF_OPTION + IF_OPTION_HOST, @@ -645,6 +653,8 @@ sub add_group_to_zone($$$$$) $zoneref->{interfaces}{$interface} = 1; + $options->{blacklist} ||= 0; + my @newnetworks; my @exclusions = (); my $new = \@newnetworks; @@ -909,6 +919,7 @@ sub process_interface( $$ ) { my %options; $options{port} = 1 if $port; + $options{blacklist} = 0; my $hostoptionsref = {}; @@ -920,7 +931,7 @@ sub process_interface( $$ ) { if ( $options ne '-' ) { - my %hostoptions = ( dynamic => 0 ); + my %hostoptions = ( blacklist => 0, dynamic => 0 ); for my $option (split_list1 $options, 'option' ) { next if $option eq '-'; @@ -952,8 +963,8 @@ sub process_interface( $$ ) { $options{$option} = $value; $hostoptions{$option} = $value if $hostopt; } elsif ( $type == ENUM_IF_OPTION ) { - fatal_error "The '$option' option may not be used with a wild-card interface name" if $wildcard; if ( $option eq 'arp_ignore' ) { + fatal_error q(The 'arp_ignore' option may not be used with a wild-card interface name) if $wildcard; if ( defined $value ) { if ( $value =~ /^[1-3,8]$/ ) { $options{arp_ignore} = $value; @@ -963,6 +974,11 @@ sub process_interface( $$ ) { } else { $options{arp_ignore} = 1; } + } elsif ( $option eq 'blacklist' ) { + $value = BL_IN unless ( defined $value && $value != '' ); + fatal_error "Invalid 'blacklist' value ( $value )" unless $value =~ /^[12]$/; + $options{blacklist} = $value eq 1 ? BL_IN | BL_OUT : BL_OUT; + $hostoptions{blacklist} = $options{blacklist} & BL_IN; } else { assert( 0 ); } @@ -1126,7 +1142,7 @@ sub validate_interfaces_file( $ ) { number => $nextinum , root => $interface , broadcasts => undef , - options => {} , + options => { blacklist => 0 } , zone => '', physical => 'lo', }; @@ -1665,11 +1681,11 @@ sub process_host( ) { } } - my $optionsref = { dynamic => 0 }; + my $optionsref = { blacklist => 0, dynamic => 0 }; if ( $options ne '-' ) { my @options = split_list $options, 'option'; - my %options = ( dynamic => 0 ); + my %options = ( blacklist => 0, dynamic => 0 ); for my $option ( @options ) { if ( $option eq 'ipsec' ) { @@ -1783,6 +1799,33 @@ sub find_hosts_by_option( $ ) { \@hosts; } +sub find_hosts_by_option1( $$ ) { + my ($option, $bit ) = @_; + my @hosts; + + for my $zone ( grep $zones{$_}{type} != FIREWALL , @zones ) { + while ( my ($type, $interfaceref) = each %{$zones{$zone}{hosts}} ) { + while ( my ( $interface, $arrayref) = ( each %{$interfaceref} ) ) { + for my $host ( @{$arrayref} ) { + if ( $host->{options}{$option} & $bit ) { + for my $net ( @{$host->{hosts}} ) { + push @hosts, [ $interface, $host->{ipsec} , $net , $host->{exclusions}]; + } + } + } + } + } + } + + for my $interface ( @interfaces ) { + if ( ! $interfaces{$interface}{zone} && $interfaces{$interface}{options}{$option} & $bit ) { + push @hosts, [ $interface, 'none', ALLIP , [] ]; + } + } + + \@hosts; +} + sub all_ipsets() { sort keys %ipsets; } diff --git a/Shorewall/changelog.txt b/Shorewall/changelog.txt index fcf7eea19..90a7a400d 100644 --- a/Shorewall/changelog.txt +++ b/Shorewall/changelog.txt @@ -12,6 +12,8 @@ Changes in Shorewall 4.4.13 6) Add secmark config file. +7) Split in and out blacklisting. + Changes in Shorewall 4.4.12 1) Fix IPv6 shorecap program. diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index cf690057a..a6c135b55 100644 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -175,6 +175,21 @@ VI. PROBLEMS CORRECTED AND NEW FEATURES IN PRIOR RELEASES As part of this change, the tcrules file now accepts chain designators 'I' and 'CI' for marking packets in the input chain. +4) The 'blacklist' interface option may now have one of 2 values: + + 1 - Inbound blacklisting + 2 - Outbond blacklisting + + Inbound blacklisting is targeted for use on Internet-facing + interfaces. Incoming packets are passed against the blacklist + entries with the 'from' option (either explicitly or defaulted). + Traffic originating on the firewall is passed against the blacklist + entries with the 'to' option. + + Outbound blacklisting is targeted for use on internal + interfaces. Packets arriving on these interfaces is passed against + the blacklist entries with the 'to' option. + ---------------------------------------------------------------------------- I V. R E L E A S E 4 . 4 H I G H L I G H T S ---------------------------------------------------------------------------- diff --git a/manpages/shorewall-blacklist.xml b/manpages/shorewall-blacklist.xml index d35c74a51..b856bc4ea 100644 --- a/manpages/shorewall-blacklist.xml +++ b/manpages/shorewall-blacklist.xml @@ -84,30 +84,50 @@ then this column has no effect on the generated rule. - Blacklisting is still restricted to traffic - arriving on an interface that has the + In Shorewall 4.4.12, blacklisting is still restricted to + traffic arriving on an interface that has the 'blacklist' option set. So to block traffic from your local network to an internet host, you must specify on your internal interface in shorewall-interfaces (5). + + + Beginning with Shorewall 4.4.13, entries specifying + to are applied based on the + blacklist setting in shorewall-interfaces(5): + + + + Input blacklisting (default if no value given). Traffic + entering this interface are passed against the entries in + shorewall-blacklist(5) + that have the from option + (specified or defaulted). Traffic originating on the firewall + and leaving by this interface is passed against the entries in + shorewall-blacklist(5) + that have the to + option. + + + + Output blacklisting. Traffic entering on this interface + is passed against the entries in shorewall-blacklist(5) + that have the to + option. + + + - When a packet arrives on an interface that has the blacklist option specified in shorewall-interfaces(5), its - source IP address and MAC address is checked against this file and - disposed of according to the BLACKLIST_DISPOSITION and BLACKLIST_LOGLEVEL variables in shorewall.conf(5). If PROTOCOL or PROTOCOL and PORTS - are supplied, only packets matching the protocol (and one of the ports if - PORTS supplied) are blocked. + diff --git a/manpages/shorewall-interfaces.xml b/manpages/shorewall-interfaces.xml index 86b0eec5c..adcab914c 100644 --- a/manpages/shorewall-interfaces.xml +++ b/manpages/shorewall-interfaces.xml @@ -223,13 +223,39 @@ loc eth2 - - blacklist + blacklist[=value] Check packets arriving on this interface against the shorewall-blacklist(5) - file. + file. The value may be specified when running Shorewall 4.4.13 + or later and can have a value in the range 1-2 + + + + Input blacklisting (default if no value given). + Traffic entering this interface are passed against the + entries in shorewall-blacklist(5) + that have the from option + (specified or defaulted). Traffic originating on the + firewall and leaving by this interface is passed against + the entries in shorewall-blacklist(5) + that have the to + option. + + + + Output blacklisting. Traffic entering on this + interface is passed against the entries in shorewall-blacklist(5) + that have the to + option. + + diff --git a/manpages6/shorewall6-blacklist.xml b/manpages6/shorewall6-blacklist.xml index 66c504e6b..30863ec55 100644 --- a/manpages6/shorewall6-blacklist.xml +++ b/manpages6/shorewall6-blacklist.xml @@ -94,6 +94,38 @@ url="shorewall6-interfaces.html">shorewall6-interfaces (5). + + + Beginning with Shorewall 4.4.13, entries specifying + to are applied to traffic based + on the blacklist setting in + shorewall6-interfaces(5). + + + + Input blacklisting (default if no value given). Traffic + entering this interface are passed against the entries in + shorewall6-blacklist(5) + that have the from option + (specified or defaulted). Traffic originating on the firewall + and leaving by this interface is passed against the entries in + shorewall6-blacklist(5) + that have the to + option. + + + + Output blacklisting. Traffic entering on this interface + is passed against the entries in shorewall6-blacklist(5) + that have the to + option. + + + diff --git a/manpages6/shorewall6-interfaces.xml b/manpages6/shorewall6-interfaces.xml index 78d9c21b4..b6fe648b8 100644 --- a/manpages6/shorewall6-interfaces.xml +++ b/manpages6/shorewall6-interfaces.xml @@ -115,13 +115,36 @@ loc eth2 - - blacklist + blacklist[=value] - Check packets arriving on this interface against the - shorewall6-blacklist(5) - file. + The value may be specified when running Shorewall 4.4.13 + or later and can have a value in the range 1-2 + + + + Input blacklisting (default if no value given). + Traffic entering this interface are passed against the + entries in shorewall6-blacklist(5) + that have the from option + (specified or defaulted). Traffic originating on the + firewall and leaving by this interface is passed against + the entries in shorewall6-blacklist(5) + that have the to + option. + + + + Output blacklisting. Traffic entering on this + interface is passed against the entries in shorewall6-blacklist(5) + that have the to + option. + +