diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index b8dd3ee39..ece2915bd 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -8919,10 +8919,10 @@ sub ensure_ipsets( @ ) { my $set; my $counters = have_capability( 'IPSET_MATCH_COUNTERS' ) ? ' counters' : ''; - if ( $_[0] eq $globals{DBL_IPSET} ) { + if ( $_[0] eq $globals{DBL_IPSET_NAME} ) { shift; - emit( qq( if ! qt \$IPSET list $globals{DBL_IPSET}; then)); + emit( qq( if ! qt \$IPSET list $globals{DBL_IPSET_NAME}; then)); push_indent; @@ -8930,12 +8930,12 @@ sub ensure_ipsets( @ ) { emit( q( #), q( # Set the timeout for the dynamic blacklisting ipset), q( #), - qq( \$IPSET -exist create $globals{DBL_IPSET} hash:net family inet timeout 0${counters}) ); + qq( \$IPSET -exist create $globals{DBL_IPSET_NAME} hash:net family inet timeout 0${counters}) ); } else { emit( q( #), q( # Set the timeout for the dynamic blacklisting ipset), q( #), - qq( \$IPSET -exist create $globals{DBL_IPSET} hash:net family inet6 timeout 0${counters}) ); + qq( \$IPSET -exist create $globals{DBL_IPSET_NAME} hash:net family inet6 timeout 0${counters}) ); } pop_indent; @@ -9158,7 +9158,7 @@ sub create_load_ipsets() { if ( $config{SAVE_IPSETS} || @{$globals{SAVED_IPSETS}} ) { emit( ' if [ -f ${VARDIR}/ipsets.save ]; then' ); - if ( my $set = $globals{DBL_IPSET} ) { + if ( my $set = $globals{DBL_IPSET_NAME} ) { emit( ' #', ' # Update the dynamic blacklisting ipset timeout value', ' #', diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index d823ae997..66963acb7 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -313,6 +313,16 @@ our %EXPORT_TAGS = ( internal => [ qw( create_temp_script OPTIMIZE_POLICY_MASK OPTIMIZE_RULESET_MASK OPTIMIZE_ALL + + DBL_NONE + DBL_SRC + DBL_DST + DBL_SRC_DST + DBL_IPSET + DBL_CLASSIC + DBL_DISCONNECT + DBL_LOG + DBL_NOUPDATE ) , ] , protocols => [ qw ( TCP @@ -822,6 +832,19 @@ our %filecache; our $compiletime; our $test; +# +# Dynamic blacklisting values +# +use constant { DBL_NONE => 0, + DBL_SRC => 1, + DBL_DST => 2, + DBL_SRC_DST => 3, + DBL_CLASSIC => 4, + DBL_IPSET => 8, + DBL_DISCONNECT => 16, + DBL_LOG => 32, + DBL_NOUPDATE => 64 + }; sub process_shorewallrc($$); sub add_variables( \% ); @@ -894,8 +917,11 @@ sub initialize($;$$$$) { RPFILTER_LOG_TAG => '', INVALID_LOG_TAG => '', UNTRACKED_LOG_TAG => '', - DBL_IPSET => '', + DBL => DBL_NONE, + DBL_IPSET_NAME => '', DBL_TIMEOUT => 0, + DBL_TAG => '', + DBL_LEVEL => '', POSTROUTING => 'POSTROUTING', ); # @@ -5322,7 +5348,7 @@ sub determine_capabilities() { fatal_error 'Your kernel/iptables do not include state match support. No version of Shorewall will run on this system' unless qt1( "$iptables $iptablesw -A $sillyname -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") || - qt1( "$iptables $iptablesw -A $sillyname -m state --state ESTABLISHED,RELATED -j ACCEPT");; + qt1( "$iptables $iptablesw -A $sillyname -m state --state ESTABLISHED,RELATED -j ACCEPT"); $globals{KLUDGEFREE} = $capabilities{KLUDGEFREE} = detect_capability 'KLUDGEFREE'; } @@ -5742,7 +5768,7 @@ sub process_shorewall_conf( $$ ) { # Now update the config file if asked # if ( $update ) { - update_config_file( $annotate ); + update_config_file( $annotate ); # # Config file update requires that the option values not have # Shell variables expanded. We do that now. @@ -6712,26 +6738,38 @@ sub get_configuration( $$$ ) { if ( supplied( $val = $config{DYNAMIC_BLACKLIST} ) ) { if ( $val =~ /^ipset/ ) { - my %simple_options = ( 'src-dst' => 1, 'disconnect' => 1, 'log' => 1, 'noupdate' => 1, ); + my $setting = DBL_IPSET; + + $setting |= DBL_SRC; + $setting |= DBL_CLASSIC unless ( $val =~ /^ipset-only/ ); + $setting |= DBL_DST if ( $val =~ /,(src-)?dst[,:]/ ); + + my %simple_options = ( 'src-dst' => DBL_SRC_DST, + 'disconnect' => DBL_DISCONNECT, + 'log' => DBL_LOG, + 'noupdate' => DBL_NOUPDATE, + ); my ( $key, $set, $level, $tag, $rest ) = split( ':', $val , 5 ); ( $key , my @options ) = split_list( $key, 'option' ); - my $options = ''; - for ( @options ) { - if ( $simple_options{$_} ) { - $options = join( ',' , $options, $_ ); - } elsif ( $_ =~ s/^timeout=(\d+)$// ) { + my $tmp; + + if ( $_ =~ s/^timeout=(\d+)$// ) { $globals{DBL_TIMEOUT} = $1; + } elsif ( $tmp = $simple_options {$_} ) { + $setting |= $tmp; } else { - fatal_error "Invalid ipset option ($_)"; + if ( $_ =~ /^timeout=(.+)/ ) { + fatal_error( "Invalid Timeout ($1)" ) + } else { + fatal_error "Invalid ipset option ($_)"; + } } } - $globals{DBL_OPTIONS} = $options; - fatal_error "Invalid DYNAMIC_BLACKLIST setting ( $val )" if $key !~ /^ipset(?:-only)?$/ || defined $rest; if ( supplied( $set ) ) { @@ -6740,7 +6778,7 @@ sub get_configuration( $$$ ) { $set = 'SW_DBL' . $family; } - add_ipset( $globals{DBL_IPSET} = $set ); + add_ipset( $globals{DBL_IPSET_NAME} = $set ); $level = validate_level( $level ); @@ -6753,11 +6791,16 @@ sub get_configuration( $$$ ) { $variables{SW_DBL_IPSET} = $set; $variables{SW_DBL_TIMEOUT} = $globals{DBL_TIMEOUT}; + $globals{DBL} = $setting; + $globals{DBL_LEVEL} = $level; + $globals{DBL_TAG} = $tag; } else { default_yes_no( 'DYNAMIC_BLACKLIST', 'Yes' ); + $globals{DBL} = $config{DYNAMIC_BLACKLIST} ? DBL_CLASSIC : DBL_NONE; } } else { default_yes_no( 'DYNAMIC_BLACKLIST', 'Yes' ); + $globals{DBL} = $config{DYNAMIC_BLACKLIST} ? DBL_CLASSIC : DBL_NONE; } add_variables( %variables ); diff --git a/Shorewall/Perl/Shorewall/Misc.pm b/Shorewall/Perl/Shorewall/Misc.pm index 62bbbcba5..d5230977d 100644 --- a/Shorewall/Perl/Shorewall/Misc.pm +++ b/Shorewall/Perl/Shorewall/Misc.pm @@ -710,10 +710,10 @@ sub create_docker_rules() { sub setup_mss(); -sub add_ipset_dbl_jump( $$$@) { +sub add_ipset_dbl_ijump( $$$@) { my ( $chainref, $target, $ipset_dir ) = ( shift, shift, shift ); - add_ijump_extended( $chainref, j => $target, $origin{DYNAMIC_BLACKLIST}, @_, "--match-set $ipset_dir" ); + add_ijump_extended( $chainref, j => $target, $origin{DYNAMIC_BLACKLIST}, @_, "set --match-set" => $ipset_dir ); } # @@ -739,9 +739,9 @@ sub add_common_rules ( $ ) { my $dbl_ipset; my $dbl_level; my $dbl_tag; + my $dbl_timeout; my $dbl_src_target; my $dbl_dst_target; - my $dbl_options; if ( $config{REJECT_ACTION} ) { process_reject_action; @@ -791,13 +791,8 @@ sub add_common_rules ( $ ) { # create_docker_rules if $config{DOCKER}; - if ( my $val = $config{DYNAMIC_BLACKLIST} ) { - # - # $config{DYNAMIC_BLACKLIST} was normalized in Shorewall:Config:get_configuration - it is probably not what is specified in the shorewall[6].conf - # - ( $dbl_type, $dbl_ipset, $dbl_level, $dbl_tag ) = split( ':', $val ); - - unless ( $dbl_type =~ /^ipset-only/ ) { + if ( my $dbl = $globals{DBL} ) { + if ( $dbl & DBL_CLASSIC ) { # # Classic chain-based backlisting # @@ -807,13 +802,14 @@ sub add_common_rules ( $ ) { add_commands( $dynamicref, '[ -f ${VARDIR}/.dynamic ] && cat ${VARDIR}/.dynamic >&3' ); } - if ( $dbl_ipset ) { + if ( $dbl & DBL_IPSET ) { # # ipset-based blacklisting - not mutually exclusive with the classic type # - if ( $val = $globals{DBL_TIMEOUT} ) { - $dbl_options = $globals{DBL_OPTIONS}; - $dbl_src_target = $dbl_options =~ /src-dst/ ? 'dbl_src' : 'dbl_log'; + ( $dbl_ipset, $dbl_level, $dbl_tag , $dbl_timeout) = ( $globals{DBL_IPSET_NAME}, $globals{DBL_LEVEL}, $globals{DBL_TAG}, $globals{DBL_TIMEOUT} ); + + if ( $dbl_timeout ) { + $dbl_src_target = ( ($dbl & DBL_SRC_DST) == DBL_SRC_DST ) ? 'dbl_src' : 'dbl_log'; my $chainref = new_standard_chain( $dbl_src_target ); @@ -826,7 +822,7 @@ sub add_common_rules ( $ ) { 'add', '', $origin{DYNAMIC_BLACKLIST} ) if $dbl_level; - add_ijump_extended( $chainref, j => "SET --add-set $dbl_ipset src --exist --timeout $val", $origin{DYNAMIC_BLACKLIST} ) unless $dbl_options =~ /noupdate/; + add_ijump_extended( $chainref, j => "SET --add-set $dbl_ipset src --exist --timeout $globals{DBL_TIMEOUT}", $origin{DYNAMIC_BLACKLIST} ) unless $dbl & DBL_NOUPDATE;; add_ijump_extended( $chainref, j => 'DROP', $origin{DYNAMIC_BLACKLIST} ); if ( $dbl_src_target eq 'dbl_src' ) { @@ -841,7 +837,7 @@ sub add_common_rules ( $ ) { 'add', '', $origin{DYNAMIC_BLACKLIST} ) if $dbl_level; - add_ijump_extended( $chainref, j => "SET --add-set $dbl_ipset dst --exist --timeout $val", $origin{DYNAMIC_BLACKLIST} ); + add_ijump_extended( $chainref, j => "SET --add-set $dbl_ipset dst --exist --timeout $globals{DBL_TIMEOUT}", $origin{DYNAMIC_BLACKLIST} ); add_ijump_extended( $chainref, j => 'DROP', $origin{DYNAMIC_BLACKLIST} ); } else { $dbl_dst_target = $dbl_src_target; @@ -973,10 +969,22 @@ sub add_common_rules ( $ ) { # # Dynamic Blacklisting # - my ( $src_target, $dst_target, $classic_target ) = ( $dbl_src_target, $dbl_dst_target , $dynamicref->{name} ); + my ( $input_option_chainref, + $forward_option_chainref, + $output_option_chainref, + $classic_target_chain, + ) + = + ( $filter_table->{input_option_chain($interface)}, + $filter_table->{forward_option_chain($interface)}, + $filter_table->{output_option_chain($interface)}, + $dynamicref, + ); if ( ( my $setting = get_interface_option( $interface, 'dbl' ) ) != DBL_NONE ) { + my @matches = @state; + my ( @src_exclude, @dst_exclude, @classic_exclude ); my @nodbl = @{$interfaceref->{nodbl}}; @@ -990,27 +998,32 @@ sub add_common_rules ( $ ) { # We need to create an intermediate chain # if ( $dbl_ipset ) { - $chainref = new_standard_chain( $src_target = nodbl_src_chain( $interface )); + $chainref = new_standard_chain( nodbl_src_chain( $interface )); for (@nodbl) { add_ijump( $chainref, j => 'RETURN', s => $_ ); } - add_ijump( $chainref, j => $dbl_src_target ); + add_ijump( $input_option_chainref, j => $chainref->{name} ); + add_ijump( $forward_option_chainref, j => $chainref->{name} ); + + $input_option_chainref = $forward_option_chainref = $chainref; if ( $dbl_src_target ne $dbl_dst_target ) { - $chainref = new_standard_chain( $dst_target = nodbl_dst_chain( $interface )); + $chainref = new_standard_chain( nodbl_dst_chain( $interface )); for ( @nodbl ){ add_ijump( $chainref, j => 'RETURN', -d => $_ ); } - add_ijump( $chainref, j => $dbl_dst_target ); + add_ijump( $output_option_chainref, j => $chainref->{name} ); + + $output_option_chainref = $chainref, } } if ( $setting & DBL_CLASSIC ) { - $chainref = new_standard_chain( $classic_target = nodbl_classic_chain( $interface )); + $chainref = new_standard_chain( nodbl_classic_chain( $interface )); for (@nodbl) { add_ijump( $chainref, j => 'RETURN', s => $_ ); @@ -1018,13 +1031,15 @@ sub add_common_rules ( $ ) { } add_ijump( $chainref, j => $dynamicref->{name} ); + + $classic_target_chain = $chainref; } } else { # # Easy case # - @src_exclude = ( s => "! $nodbl[0]" ); - @dst_exclude = ( d => "! $nodbl[0]" ); + @matches = ( s => "! $nodbl[0]" , @matches ); + @matches = ( d => "! $nodbl[0]" , @matches ); } } @@ -1033,27 +1048,34 @@ sub add_common_rules ( $ ) { # # src or src-dst # - add_ipset_dbl_ijump( $filter_table->{input_option_chain($interface)}, $src_target, "$dbl_ipset src", @state ); - add_ipset_dbl_ijump( $filter_table->{forward_option_chain($interface)}, $src_target, "$dbl_ipset src", @state ); + add_ipset_dbl_ijump( $input_option_chainref, $dbl_src_target, "$dbl_ipset src", @matches ); + add_ipset_dbl_ijump( $forward_option_chainref, $dbl_src_target, "$dbl_ipset src", @matches ); } if ( $setting & DBL_DST ) { # # src-dst # - add_ipset_dbl_ijump( $filter_table->{forward_option_chain($interface)}, $dst_target, "$dbl_ipset dst", @state ); - add_ipset_dbl_ijump( $filter_table->{output_option_chain($interface)}, $dst_target, "$dbl_ipset dst", @state ); + add_ipset_dbl_ijump( $forward_option_chainref, $dbl_dst_target, "$dbl_ipset dst", @matches ); + add_ipset_dbl_ijump( $output_option_chainref, $dbl_dst_target, "$dbl_ipset dst", @matches ); } } + + if ( $setting & DBL_CLASSIC ) { + add_ijump_extended( $input_option_chainref, j => $classic_target_chain, $origin{DYNAMIC_BLACKLIST}, @state ); + add_ijump_extended( $forward_option_chainref, j => $classic_target_chain, $origin{DYNAMIC_BLACKLIST}, @state ); + add_ijump_extended( $output_option_chainref, j => $classic_target_chain, $origin{DYNAMIC_BLACKLIST}, @state ) if $setting & DBL_DST; + } } # - # Finish blacklisting and FASTACCEPT + # Finish FASTACCEPT # - for ( option_chains( $interface ) ) { - add_ijump_extended( $filter_table->{$_}, j => $classic_target, $origin{DYNAMIC_BLACKLIST}, @state ) if $dynamicref && ( get_interface_option( $interface, 'dbl' ) & DBL_CLASSIC ); - add_ijump_extended( $filter_table->{$_}, j => 'ACCEPT', $origin{FASTACCEPT}, state_imatch $faststate )->{comment} = '' if $config{FASTACCEPT}; + if ( $config{FASTACCEPT} ) { + for ( option_chains( $interface ) ) { + add_ijump_extended( $filter_table->{$_}, j => 'ACCEPT', $origin{FASTACCEPT}, state_imatch $faststate )->{comment} = ''; + } } - } + } #Not loopback interface } # # Delete 'sfilter' chains unless there are referenced to them diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm index 208379ccb..aa5c4c28d 100644 --- a/Shorewall/Perl/Shorewall/Rules.pm +++ b/Shorewall/Perl/Shorewall/Rules.pm @@ -748,7 +748,7 @@ sub process_a_policy1($$$$$$$) { fatal_error "NONE policy not allowed to/from firewall zone" if ( zone_type( $client ) == FIREWALL ) || ( zone_type( $server ) == FIREWALL ); } elsif ( $policy eq 'BLACKLIST' ) { - fatal_error 'BLACKLIST policies require ipset-based dynamic blacklisting' unless $config{DYNAMIC_BLACKLIST} =~ /^ipset/; + fatal_error 'BLACKLIST policies require ipset-based dynamic blacklisting' unless $globals{DBL} & DBL_IPSET; } unless ( $clientwild || $serverwild ) { @@ -1078,12 +1078,10 @@ sub add_policy_rules( $$$$$ ) { assert( $target ); if ( $target eq 'BLACKLIST' ) { - my ( $dbl_type, $dbl_ipset, $dbl_level, $dbl_tag ) = split( ':', $config{DYNAMIC_BLACKLIST} ); - if ( my $timeout = $globals{DBL_TIMEOUT} ) { - add_ijump( $chainref, j => "SET --add-set $dbl_ipset src --exist --timeout $timeout" ); + add_ijump( $chainref, j => "SET --add-set $globals{DBL_IPSET_NAME} src --exist --timeout $globals{DBL_TIMEOUT}" ); } else { - add_ijump( $chainref, j => "SET --add-set $dbl_ipset src --exist" ); + add_ijump( $chainref, j => "SET --add-set $globals{DBL_IPSET_NAME} src --exist" ); } $target = 'DROP'; diff --git a/Shorewall/Perl/Shorewall/Zones.pm b/Shorewall/Perl/Shorewall/Zones.pm index e7c37ab75..bd7be9729 100644 --- a/Shorewall/Perl/Shorewall/Zones.pm +++ b/Shorewall/Perl/Shorewall/Zones.pm @@ -49,11 +49,6 @@ our @EXPORT = ( qw( NOTHING GROUP NO_UPDOWN NO_SFILTER - DBL_NONE - DBL_SRC - DBL_DST - DBL_SRC_DST - DBL_CLASSIC determine_zones zone_report @@ -219,16 +214,6 @@ use constant { NOTHING => 'NOTHING', IPSECMODE => 'tunnel|transport' }; -# -# Dynamic blacklisting values -# -use constant { DBL_NONE => 0, - DBL_SRC => 1, - DBL_DST => 2, - DBL_SRC_DST => 3, - DBL_CLASSIC => 4, - }; - sub NETWORK() { $family == F_IPV4 ? '\d+.\d+.\d+.\d+(\/\d+)?' : '(?:[0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}(?:\/d+)?'; } @@ -1337,17 +1322,7 @@ sub process_interface( $$ ) { $options{port} = 1 if $port; - my $setting = DBL_NONE; - - if ( my $dbl = $config{DYNAMIC_BLACKLIST} ) { - unless ( $dbl =~ /^No/i ) { - $setting |= DBL_SRC; - $setting |= DBL_CLASSIC unless ( $dbl =~ /^ipset-only/ ); - $setting |= DBL_DST if ( $dbl =~ /,(src-)?dst[,:]/ ); - } - } - - $options{dbl} = $setting; + $options{dbl} = $globals{DBL}; my $hostoptionsref = {}; @@ -1419,7 +1394,7 @@ sub process_interface( $$ ) { if ( $value eq 'none' ) { $options{dbl} = DBL_NONE; } else { - fatal_error qq(Invalid setting ($value) for 'dbl') unless defined ( $setting = $values{$value} ); + fatal_error qq(Invalid setting ($value) for 'dbl') unless defined ( my $setting = $values{$value} ); $options{dbl} |= $setting; } } else {