From a2cd4bd1f466b97cb770517002f8db1ad73612c2 Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Wed, 25 Nov 2009 09:42:28 -0800 Subject: [PATCH] Add 'virtual' zone support --- Shorewall/Perl/Shorewall/Chains.pm | 48 +++++++++++++++++++++- Shorewall/Perl/Shorewall/Config.pm | 2 +- Shorewall/Perl/Shorewall/Policy.pm | 27 +++++++++++-- Shorewall/Perl/Shorewall/Rules.pm | 37 +---------------- Shorewall/Perl/Shorewall/Zones.pm | 64 ++++++++++++++++++++---------- Shorewall/changelog.txt | 2 + Shorewall/releasenotes.txt | 33 +++++++++++++++ manpages/shorewall-nesting.xml | 60 ++++++++++++++++++++++++++-- manpages6/shorewall6-nesting.xml | 54 +++++++++++++++++++++++++ 9 files changed, 261 insertions(+), 66 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index cbc8fbc09..0a624b803 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -145,6 +145,8 @@ our %EXPORT_TAGS = ( log_rule expand_rule addnatjump + rules_target + continuation_target set_chain_variables mark_firewall_not_started mark_firewall6_not_started @@ -167,7 +169,7 @@ our %EXPORT_TAGS = ( Exporter::export_ok_tags('internal'); -our $VERSION = '4.4_4'; +our $VERSION = '4.4_5'; # # Chain Table @@ -2017,6 +2019,50 @@ sub addnatjump( $$$ ) { } } +# +# Return the target for rules from $zone to $zone1. +# +sub continuation_target( $$ ); + +sub rules_target( $$ ) { + my ( $zone, $zone1 ) = @_; + my $chain = rules_chain( ${zone}, ${zone1} ); + my $chainref = $filter_table->{$chain}; + + return $chain if $chainref && $chainref->{referenced}; + return 'ACCEPT' if $zone eq $zone1; + + assert( $chainref ); + + if ( $chainref->{policy} ne 'CONTINUE' ) { + my $policyref = $filter_table->{$chainref->{policychain}}; + assert( $policyref ); + return $policyref->{name}; + } + + continuation_target $zone, $zone1 +} + +sub continuation_target( $$ ) { + my ( $zone, $zone1 ) = @_; + + my $zoneref = defined_zone( $zone ); + + my $chain; + + for ( @{$zoneref->{parents}} ) { + return $chain if virtual_zone( $_ ) && ( $chain = rules_target( $_, $zone1 ) ); + } + + $zoneref = defined_zone( $zone1 ); + + for ( @{$zoneref->{parents}} ) { + return $chain if virtual_zone( $_) && ( $chain = rules_target( $zone, $_ ) ); + } + + ''; +} + # # Split a comma-separated source or destination host list but keep [...] together. Used for spliting address lists # where an element of the list might be +ipset[binding]. diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index 6aa6264f8..9f8689615 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -327,7 +327,7 @@ sub initialize( $ ) { TC_SCRIPT => '', EXPORT => 0, UNTRACKED => 0, - VERSION => "4.5.0", + VERSION => "4.4.5", CAPVERSION => 40402 , ); diff --git a/Shorewall/Perl/Shorewall/Policy.pm b/Shorewall/Perl/Shorewall/Policy.pm index 99d34da14..625f8f152 100644 --- a/Shorewall/Perl/Shorewall/Policy.pm +++ b/Shorewall/Perl/Shorewall/Policy.pm @@ -341,13 +341,22 @@ sub validate_policy() for $zone ( all_zones ) { push @policy_chains, ( new_policy_chain $zone, $zone, 'ACCEPT', OPTIONAL ); - if ( $config{IMPLICIT_CONTINUE} && ( @{find_zone( $zone )->{parents}} ) ) { + if ( $config{IMPLICIT_CONTINUE} && ( @{defined_zone( $zone )->{parents}} ) ) { for my $zone1 ( all_zones ) { unless( $zone eq $zone1 ) { add_or_modify_policy_chain( $zone, $zone1 ); add_or_modify_policy_chain( $zone1, $zone ); } } + } elsif ( virtual_zone( $zone ) ) { + for my $zone1 ( @{defined_zone( $zone )->{children}} ) { + for my $zone2 ( all_zones ) { + unless ( $zone1 eq $zone2 ) { + add_or_modify_policy_chain( $zone1, $zone2 ); + add_or_modify_policy_chain( $zone2, $zone1 ); + } + } + } } } @@ -385,7 +394,7 @@ sub report_syn_flood_protection() { } sub default_policy( $$$ ) { - my $chainref = $_[0]; + my ( $chainref, $zone, $zone1 ) = @_; my $policyref = $filter_table->{$chainref->{policychain}}; my $synparams = $policyref->{synparams}; my $default = $policyref->{default}; @@ -395,7 +404,13 @@ sub default_policy( $$$ ) { assert( $policyref ); if ( $chainref eq $policyref ) { - policy_rules $chainref , $policy, $loglevel , $default, $config{MULTICAST}; + policy_rules $chainref, $policy, $loglevel, $default, $config{MULTICAST}; + if ( $policy eq 'CONTINUE' ) { + if ( my $continuation = continuation_target( $zone, $zone1 ) ) { + add_jump( $chainref, $continuation, 1 ); + $chainref = $filter_table->{$continuation}; + } + } } else { if ( $policy eq 'ACCEPT' || $policy eq 'QUEUE' || $policy =~ /^NFQUEUE/ ) { if ( $synparams ) { @@ -408,6 +423,10 @@ sub default_policy( $$$ ) { } elsif ( $policy eq 'CONTINUE' ) { report_syn_flood_protection if $synparams; policy_rules $chainref , $policy , $loglevel , $default, $config{MULTICAST}; + if ( my $continuation = continuation_target( $zone, $zone1 ) ) { + add_jump( $chainref, $continuation, 1 ); + $chainref = $filter_table->{$continuation}; + } } else { report_syn_flood_protection if $synparams; add_jump $chainref , $policyref, 1; @@ -415,7 +434,7 @@ sub default_policy( $$$ ) { } } - progress_message_nocompress " Policy $policy from $_[1] to $_[2] using chain $chainref->{name}"; + progress_message_nocompress " Policy $policy from $zone to $zone1 using chain $chainref->{name}"; } diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm index ad85061b1..8f38dca53 100644 --- a/Shorewall/Perl/Shorewall/Rules.pm +++ b/Shorewall/Perl/Shorewall/Rules.pm @@ -1639,42 +1639,7 @@ sub add_interface_jumps { # The function traverses the full "source-zone by destination-zone" matrix and generates the rules necessary to direct traffic through the right set of filter-table rules. # sub generate_matrix() { - # - # Helper functions for generate_matrix() - #----------------------------------------- - # - # Return the target for rules from $zone to $zone1. - # - sub rules_target( $$ ) { - my ( $zone, $zone1 ) = @_; - my $chain = rules_chain( ${zone}, ${zone1} ); - my $chainref = $filter_table->{$chain}; - - return $chain if $chainref && $chainref->{referenced}; - return 'ACCEPT' if $zone eq $zone1; - - assert( $chainref ); - - if ( $chainref->{policy} ne 'CONTINUE' ) { - my $policyref = $filter_table->{$chainref->{policychain}}; - assert( $policyref ); - return $policyref->{name}; - } - - ''; # CONTINUE policy - } - - # - # Set a breakpoint in this function if you want to step through generate_matrix(). - # - sub start_matrix() { - progress_message2 'Generating Rule Matrix...'; - } - - # - # G e n e r a t e _ M a t r i x ( ) S t a r t s H e r e - # - start_matrix; + progress_message2 'Generating Rule Matrix...'; my @interfaces = ( all_interfaces ); my $preroutingref = ensure_chain 'nat', 'dnat'; diff --git a/Shorewall/Perl/Shorewall/Zones.pm b/Shorewall/Perl/Shorewall/Zones.pm index 7496b0cec..cc68f1c32 100644 --- a/Shorewall/Perl/Shorewall/Zones.pm +++ b/Shorewall/Perl/Shorewall/Zones.pm @@ -53,6 +53,7 @@ our @EXPORT = qw( NOTHING all_parent_zones complex_zones non_firewall_zones + virtual_zone single_interface validate_interfaces_file all_interfaces @@ -96,9 +97,11 @@ use constant { NOTHING => 'NOTHING', # options => { complex => 0|1 # nested => 0|1 # super => 0|1 -# in_out => < policy match string > -# in => < policy match string > -# out => < policy match string > +# in_out => { ipsec => < policy match string > , +# mss => +# virtual => 0|1 } +# in => ... +# out => ... # } # parents => [ ] Parents, Children and interfaces are listed by name # children => [ ] @@ -269,6 +272,7 @@ sub initialize( $ ) { sub parse_zone_option_list($$) { my %validoptions = ( mss => NUMERIC, + virtual => NOTHING, strict => NOTHING, next => NOTHING, reqid => NUMERIC, @@ -281,7 +285,7 @@ sub parse_zone_option_list($$) # # Hash of options that have their own key in the returned hash. # - my %key = ( mss => 'mss' ); + my %key = ( mss => 1 , virtual => 1 ); my ( $list, $zonetype ) = @_; my %h; @@ -314,7 +318,7 @@ sub parse_zone_option_list($$) } if ( $key{$e} ) { - $h{$e} = $val; + $h{$e} = $val || 1; } else { fatal_error "The \"$e\" option may only be specified for ipsec zones" unless $zonetype == IPSEC; $options .= $invert; @@ -406,20 +410,27 @@ sub process_zone( \$ ) { $_ = '' if $_ eq '-'; } - $zones{$zone} = { type => $type, - parents => \@parents, - bridge => '', - options => { in_out => parse_zone_option_list( $options || '', $type ) , - in => parse_zone_option_list( $in_options || '', $type ) , - out => parse_zone_option_list( $out_options || '', $type ) , - complex => ($type == IPSEC || $options || $in_options || $out_options ? 1 : 0) , - nested => @parents > 0 , - super => 0 , - } , - interfaces => {} , - children => [] , - hosts => {} - }; + my $zoneref = $zones{$zone} = { type => $type, + parents => \@parents, + bridge => '', + options => $options = { in_out => parse_zone_option_list( $options, $type ) , + in => parse_zone_option_list( $in_options, $type ) , + out => parse_zone_option_list( $out_options, $type ) , + complex => ($type == IPSEC || $options || $in_options || $out_options ? 1 : 0) , + nested => @parents > 0 , + super => 0 , + } , + interfaces => {} , + children => [] , + hosts => {} + }; + + fatal_error "'virtual' is not permitted in IN OPTIONS or in OUT OPTIONS" if $options->{in}{virtual} || $options->{out}{virtual}; + + if ( $options->{in_out}{virtual} ) { + fatal_error "Nested virtual Zones are not supported" if $options->{nested}; + fatal_error "Only IP zones are allowed to be virtual" unless $type == IP; + } return $zone; @@ -523,14 +534,13 @@ sub zone_report() $printed = 1; } } - } } } unless ( $printed ) { fatal_error "No bridge has been associated with zone $zone" if $type == BPORT && ! $zoneref->{bridge}; - warning_message "*** $zone is an EMPTY ZONE ***" unless $type == FIREWALL; + warning_message "*** $zone is an EMPTY ZONE ***" unless $type == FIREWALL || ( $optionref->{in_out}{virtual} && @{$zoneref->{children}} ); } } @@ -587,6 +597,12 @@ sub dump_zone_contents() } } + if ( $zoneref->{options}{in_out}{virtual} && @{$zoneref->{children}} ) { + $entry .= " ("; + $entry .= "$_," for @{$zoneref->{children}}; + $entry =~ s/,$/) /; + } + emit_unindented $entry; } } @@ -614,6 +630,8 @@ sub add_group_to_zone($$$$$) my $zoneref = $zones{$zone}; my $zonetype = $zoneref->{type}; + fatal_error "Zone $zone is virtual and may not be defined in the interfaces and hosts files" if $zoneref->{options}{in_out}{virtual}; + $zoneref->{interfaces}{$interface} = 1; my @newnetworks; @@ -719,6 +737,10 @@ sub firewall_zone() { $firewall_zone; } +sub virtual_zone( $ ) { + $zones{$_[0]}{options}{in_out}{virtual}; +} + # # Process a record in the interfaces file # diff --git a/Shorewall/changelog.txt b/Shorewall/changelog.txt index 043074171..1bb5c4392 100644 --- a/Shorewall/changelog.txt +++ b/Shorewall/changelog.txt @@ -12,6 +12,8 @@ Changes in Shorewall 4.4.5 6) Fix 'show policies' in Shorewall6. +7) Implement 'virtual' zones. + Changes in Shorewall 4.4.4 1) Change STARTUP_LOG and LOG_VERBOSITY in default shorewall6.conf. diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index 8702f35cf..347e8460a 100644 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -230,6 +230,39 @@ None. $FW dmz REJECT info $FW all ACCEPT +3) Shorewall 4.4.5 introduces 'virtual' zones. A virtual zone is used + to group together a set of sub-zones. A virtual zone must by an + ipv4 zone (Shorewall) or an ipv6 zone (Shorewall6) and is declared + with the 'virtual' OPTION in /etc/shorewall/zones. + + Example: + + virt ipv4 virtual + + The virtual zone must have no definition in + /etc/shorewall/interfaces or /etc/shorewall/hosts. In this first + release, virtual zones cannot themselves be nested. + + Virtual zones are use as parent zones for other zones using the + : syntax in /etc/shorewall/zones: + + Example: + + virt ipv4 virtual + loc:virt ipv4 + vpn:virt ipsec + + As shown in that example, a virtual zone may be a parent for + multiple zone types. + + Virtual zones are intended to be used with + IMPLICIT_CONTINUE=No. They provide semantic behavior similar to + IMPLICIT_CONTINUE=Yes in that connections that do not match rules + for the sub-zone are applied to the parent zone. + + For more information, see + http://www.shorewall.net/manpages/shorewall-nesting.html + ---------------------------------------------------------------------------- N E W F E A T U R E S I N 4 . 4 . 0 ---------------------------------------------------------------------------- diff --git a/manpages/shorewall-nesting.xml b/manpages/shorewall-nesting.xml index 1884696e0..534a83cf6 100644 --- a/manpages/shorewall-nesting.xml +++ b/manpages/shorewall-nesting.xml @@ -154,14 +154,14 @@ to change the 'net' interface to something other than ppp0. That way, it won't match ppp+. - If you are running Shorewall version 4.1.4 or later, a second way is - to simply make the nested zones explicit: #ZONE TYPE OPTION + A second way is to simply make the nested zones + explicit: #ZONE TYPE OPTION fw firewall loc ipv4 net:loc ipv4 dmz ipv4 - If you take this approach, be sure to set IMPLICIT_CONTINUE=No in + If you take this approach, be sure to set IMPLICIT_CONTINUE=Yes in shorewall.conf. When using other Shorewall versions, another way is to rewrite the @@ -183,6 +183,60 @@ loc ppp+:192.168.2.0/23 + + Virtual Zones + + Beginning with Shorewall 4.4.5, Shorewall allows the declaration of + virtual zones. A virtual zone has no definition in + /etc/shorewall/interfaces or in + /etc/shorewall/hosts. Rather, it is used as a parent + zone for other zones in /etc/shorewall/zones. + + Example: + + /etc/shorewall/zones: + + #ZONE TYPE OPTIONS + fw firewall + net ipv4 + loc ipv4 virtual + loc1:loc ipv4 + loc2:loc ipv4 + + /etc/shorewall/interfaces: + + #ZONE INTERFACE BROADCAST OPTIONS + net eth0 detect dhcp,tcpflags,nosmurfs,routefilter,logmartians + - eth1 detect tcpflags,nosmurfs,routefilter,logmartians + + /etc/shorewall/hosts: + + #ZONE HOST(S) OPTIONS + loc1 eth1:192.168.1.0/24 + loc2 eth1:192.168.2.0/24 + + There are several restrictions on virtual zones: + + + + They must have type . + + + + They may not themselves be nested. + + + + They should not be used with IMPLICIT_CONTINUE=Yes in shorewall.conf(5). + + + + When a connection request to/from a sub-zone of a virtual zone does + not match the rules for the sub-zone, the connection is compared against + the rules (and policies) for the parent virtual zone. + + FILES diff --git a/manpages6/shorewall6-nesting.xml b/manpages6/shorewall6-nesting.xml index 7edbea347..36694e9ef 100644 --- a/manpages6/shorewall6-nesting.xml +++ b/manpages6/shorewall6-nesting.xml @@ -87,6 +87,60 @@ significant. + + Virtual Zones + + Beginning with Shorewall 4.4.5, Shorewall allows the declaration of + virtual zones. A virtual zone has no definition in + /etc/shorewall6/interfaces or in + /etc/shorewall6/hosts. Rather, it is used as a parent + zone for other zones in /etc/shorewall6/zones. + + Example: + + /etc/shorewall6/zones: + + #ZONE TYPE OPTIONS + fw firewall + net ipv6 + loc ipv6 virtual + loc1:loc ipv6 + loc2:loc ipv6 + + /etc/shorewall/interfaces: + + #ZONE INTERFACE BROADCAST OPTIONS + net eth0 detect dhcp,tcpflags + - eth1 detect tcpflags + + /etc/shorewall/hosts: + + #ZONE HOST(S) OPTIONS + loc1 eth1:2001:19f0:feee:1::/48 + loc2 eth1:2001:19f0:feee:2::/48 + + There are several restrictions on virtual zones: + + + + They must have type . + + + + They may not themselves be nested. + + + + They should not be used with IMPLICIT_CONTINUE=Yes in shorewall6.conf(5). + + + + When a connection request to/from a sub-zone of a virtual zone does + not match the rules for the sub-zone, the connection is compared against + the rules (and policies) for the parent virtual zone. + + FILES