Add 'virtual' zone support

This commit is contained in:
Tom Eastep 2009-11-25 09:42:28 -08:00
parent 4c40b205f8
commit a2cd4bd1f4
9 changed files with 261 additions and 66 deletions

View File

@ -145,6 +145,8 @@ our %EXPORT_TAGS = (
log_rule log_rule
expand_rule expand_rule
addnatjump addnatjump
rules_target
continuation_target
set_chain_variables set_chain_variables
mark_firewall_not_started mark_firewall_not_started
mark_firewall6_not_started mark_firewall6_not_started
@ -167,7 +169,7 @@ our %EXPORT_TAGS = (
Exporter::export_ok_tags('internal'); Exporter::export_ok_tags('internal');
our $VERSION = '4.4_4'; our $VERSION = '4.4_5';
# #
# Chain Table # 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 # 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]. # where an element of the list might be +ipset[binding].

View File

@ -327,7 +327,7 @@ sub initialize( $ ) {
TC_SCRIPT => '', TC_SCRIPT => '',
EXPORT => 0, EXPORT => 0,
UNTRACKED => 0, UNTRACKED => 0,
VERSION => "4.5.0", VERSION => "4.4.5",
CAPVERSION => 40402 , CAPVERSION => 40402 ,
); );

View File

@ -341,13 +341,22 @@ sub validate_policy()
for $zone ( all_zones ) { for $zone ( all_zones ) {
push @policy_chains, ( new_policy_chain $zone, $zone, 'ACCEPT', OPTIONAL ); 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 ) { for my $zone1 ( all_zones ) {
unless( $zone eq $zone1 ) { unless( $zone eq $zone1 ) {
add_or_modify_policy_chain( $zone, $zone1 ); add_or_modify_policy_chain( $zone, $zone1 );
add_or_modify_policy_chain( $zone1, $zone ); 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( $$$ ) { sub default_policy( $$$ ) {
my $chainref = $_[0]; my ( $chainref, $zone, $zone1 ) = @_;
my $policyref = $filter_table->{$chainref->{policychain}}; my $policyref = $filter_table->{$chainref->{policychain}};
my $synparams = $policyref->{synparams}; my $synparams = $policyref->{synparams};
my $default = $policyref->{default}; my $default = $policyref->{default};
@ -396,6 +405,12 @@ sub default_policy( $$$ ) {
if ( $chainref eq $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 { } else {
if ( $policy eq 'ACCEPT' || $policy eq 'QUEUE' || $policy =~ /^NFQUEUE/ ) { if ( $policy eq 'ACCEPT' || $policy eq 'QUEUE' || $policy =~ /^NFQUEUE/ ) {
if ( $synparams ) { if ( $synparams ) {
@ -408,6 +423,10 @@ sub default_policy( $$$ ) {
} elsif ( $policy eq 'CONTINUE' ) { } elsif ( $policy eq 'CONTINUE' ) {
report_syn_flood_protection if $synparams; report_syn_flood_protection if $synparams;
policy_rules $chainref , $policy , $loglevel , $default, $config{MULTICAST}; 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 { } else {
report_syn_flood_protection if $synparams; report_syn_flood_protection if $synparams;
add_jump $chainref , $policyref, 1; 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}";
} }

View File

@ -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. # 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() { 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...'; 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;
my @interfaces = ( all_interfaces ); my @interfaces = ( all_interfaces );
my $preroutingref = ensure_chain 'nat', 'dnat'; my $preroutingref = ensure_chain 'nat', 'dnat';

View File

@ -53,6 +53,7 @@ our @EXPORT = qw( NOTHING
all_parent_zones all_parent_zones
complex_zones complex_zones
non_firewall_zones non_firewall_zones
virtual_zone
single_interface single_interface
validate_interfaces_file validate_interfaces_file
all_interfaces all_interfaces
@ -96,9 +97,11 @@ use constant { NOTHING => 'NOTHING',
# options => { complex => 0|1 # options => { complex => 0|1
# nested => 0|1 # nested => 0|1
# super => 0|1 # super => 0|1
# in_out => < policy match string > # in_out => { ipsec => < policy match string > ,
# in => < policy match string > # mss => <mss>
# out => < policy match string > # virtual => 0|1 }
# in => ...
# out => ...
# } # }
# parents => [ <parents> ] Parents, Children and interfaces are listed by name # parents => [ <parents> ] Parents, Children and interfaces are listed by name
# children => [ <children> ] # children => [ <children> ]
@ -269,6 +272,7 @@ sub initialize( $ ) {
sub parse_zone_option_list($$) sub parse_zone_option_list($$)
{ {
my %validoptions = ( mss => NUMERIC, my %validoptions = ( mss => NUMERIC,
virtual => NOTHING,
strict => NOTHING, strict => NOTHING,
next => NOTHING, next => NOTHING,
reqid => NUMERIC, reqid => NUMERIC,
@ -281,7 +285,7 @@ sub parse_zone_option_list($$)
# #
# Hash of options that have their own key in the returned hash. # 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 ( $list, $zonetype ) = @_;
my %h; my %h;
@ -314,7 +318,7 @@ sub parse_zone_option_list($$)
} }
if ( $key{$e} ) { if ( $key{$e} ) {
$h{$e} = $val; $h{$e} = $val || 1;
} else { } else {
fatal_error "The \"$e\" option may only be specified for ipsec zones" unless $zonetype == IPSEC; fatal_error "The \"$e\" option may only be specified for ipsec zones" unless $zonetype == IPSEC;
$options .= $invert; $options .= $invert;
@ -406,12 +410,12 @@ sub process_zone( \$ ) {
$_ = '' if $_ eq '-'; $_ = '' if $_ eq '-';
} }
$zones{$zone} = { type => $type, my $zoneref = $zones{$zone} = { type => $type,
parents => \@parents, parents => \@parents,
bridge => '', bridge => '',
options => { in_out => parse_zone_option_list( $options || '', $type ) , options => $options = { in_out => parse_zone_option_list( $options, $type ) ,
in => parse_zone_option_list( $in_options || '', $type ) , in => parse_zone_option_list( $in_options, $type ) ,
out => parse_zone_option_list( $out_options || '', $type ) , out => parse_zone_option_list( $out_options, $type ) ,
complex => ($type == IPSEC || $options || $in_options || $out_options ? 1 : 0) , complex => ($type == IPSEC || $options || $in_options || $out_options ? 1 : 0) ,
nested => @parents > 0 , nested => @parents > 0 ,
super => 0 , super => 0 ,
@ -421,6 +425,13 @@ sub process_zone( \$ ) {
hosts => {} 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; return $zone;
} }
@ -523,14 +534,13 @@ sub zone_report()
$printed = 1; $printed = 1;
} }
} }
} }
} }
} }
unless ( $printed ) { unless ( $printed ) {
fatal_error "No bridge has been associated with zone $zone" if $type == BPORT && ! $zoneref->{bridge}; 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; emit_unindented $entry;
} }
} }
@ -614,6 +630,8 @@ sub add_group_to_zone($$$$$)
my $zoneref = $zones{$zone}; my $zoneref = $zones{$zone};
my $zonetype = $zoneref->{type}; 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; $zoneref->{interfaces}{$interface} = 1;
my @newnetworks; my @newnetworks;
@ -719,6 +737,10 @@ sub firewall_zone() {
$firewall_zone; $firewall_zone;
} }
sub virtual_zone( $ ) {
$zones{$_[0]}{options}{in_out}{virtual};
}
# #
# Process a record in the interfaces file # Process a record in the interfaces file
# #

View File

@ -12,6 +12,8 @@ Changes in Shorewall 4.4.5
6) Fix 'show policies' in Shorewall6. 6) Fix 'show policies' in Shorewall6.
7) Implement 'virtual' zones.
Changes in Shorewall 4.4.4 Changes in Shorewall 4.4.4
1) Change STARTUP_LOG and LOG_VERBOSITY in default shorewall6.conf. 1) Change STARTUP_LOG and LOG_VERBOSITY in default shorewall6.conf.

View File

@ -230,6 +230,39 @@ None.
$FW dmz REJECT info $FW dmz REJECT info
$FW all ACCEPT $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
<zone>:<parent> 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 N E W F E A T U R E S I N 4 . 4 . 0
---------------------------------------------------------------------------- ----------------------------------------------------------------------------

View File

@ -154,14 +154,14 @@
to change the 'net' interface to something other than ppp0. That way, it to change the 'net' interface to something other than ppp0. That way, it
won't match ppp+.</para> won't match ppp+.</para>
<para>If you are running Shorewall version 4.1.4 or later, a second way is <para>A second way is to simply make the nested zones
to simply make the nested zones explicit:<programlisting> #ZONE TYPE OPTION explicit:<programlisting> #ZONE TYPE OPTION
fw firewall fw firewall
loc ipv4 loc ipv4
net:loc ipv4 net:loc ipv4
dmz ipv4</programlisting></para> dmz ipv4</programlisting></para>
<para>If you take this approach, be sure to set IMPLICIT_CONTINUE=No in <para>If you take this approach, be sure to set IMPLICIT_CONTINUE=Yes in
<filename>shorewall.conf</filename>.</para> <filename>shorewall.conf</filename>.</para>
<para>When using other Shorewall versions, another way is to rewrite the <para>When using other Shorewall versions, another way is to rewrite the
@ -183,6 +183,60 @@
loc ppp+:192.168.2.0/23</programlisting></para> loc ppp+:192.168.2.0/23</programlisting></para>
</refsect1> </refsect1>
<refsect1 id="Virtual">
<title>Virtual Zones</title>
<para>Beginning with Shorewall 4.4.5, Shorewall allows the declaration of
<firstterm>virtual</firstterm> zones. A virtual zone has no definition in
<filename>/etc/shorewall/interfaces</filename> or in
<filename>/etc/shorewall/hosts</filename>. Rather, it is used as a parent
zone for other zones in <filename>/etc/shorewall/zones</filename>.</para>
<para>Example:</para>
<para><filename>/etc/shorewall/zones</filename>:</para>
<programlisting> #ZONE TYPE OPTIONS
fw firewall
net ipv4
loc ipv4 virtual
loc1:loc ipv4
loc2:loc ipv4</programlisting>
<para><filename>/etc/shorewall/interfaces</filename>:</para>
<programlisting> #ZONE INTERFACE BROADCAST OPTIONS
net eth0 detect dhcp,tcpflags,nosmurfs,routefilter,logmartians
- eth1 detect tcpflags,nosmurfs,routefilter,logmartians</programlisting>
<para><filename>/etc/shorewall/hosts</filename>:</para>
<programlisting> #ZONE HOST(S) OPTIONS
loc1 eth1:192.168.1.0/24
loc2 eth1:192.168.2.0/24</programlisting>
<para>There are several restrictions on virtual zones:</para>
<itemizedlist>
<listitem>
<para>They must have type <option>ipv4</option>.</para>
</listitem>
<listitem>
<para>They may not themselves be nested.</para>
</listitem>
<listitem>
<para>They should not be used with IMPLICIT_CONTINUE=Yes in <ulink
url="shorewall.conf.html">shorewall.conf</ulink>(5).</para>
</listitem>
</itemizedlist>
<para>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.</para>
</refsect1>
<refsect1> <refsect1>
<title>FILES</title> <title>FILES</title>

View File

@ -87,6 +87,60 @@
significant.</para> significant.</para>
</refsect1> </refsect1>
<refsect1 id="Virtual">
<title>Virtual Zones</title>
<para>Beginning with Shorewall 4.4.5, Shorewall allows the declaration of
<firstterm>virtual</firstterm> zones. A virtual zone has no definition in
<filename>/etc/shorewall6/interfaces</filename> or in
<filename>/etc/shorewall6/hosts</filename>. Rather, it is used as a parent
zone for other zones in <filename>/etc/shorewall6/zones</filename>.</para>
<para>Example:</para>
<para><filename>/etc/shorewall6/zones</filename>:</para>
<programlisting> #ZONE TYPE OPTIONS
fw firewall
net ipv6
loc ipv6 virtual
loc1:loc ipv6
loc2:loc ipv6</programlisting>
<para><filename>/etc/shorewall/interfaces</filename>:</para>
<programlisting> #ZONE INTERFACE BROADCAST OPTIONS
net eth0 detect dhcp,tcpflags
- eth1 detect tcpflags</programlisting>
<para><filename>/etc/shorewall/hosts</filename>:</para>
<programlisting> #ZONE HOST(S) OPTIONS
loc1 eth1:2001:19f0:feee:1::/48
loc2 eth1:2001:19f0:feee:2::/48</programlisting>
<para>There are several restrictions on virtual zones:</para>
<itemizedlist>
<listitem>
<para>They must have type <option>ipv6</option>.</para>
</listitem>
<listitem>
<para>They may not themselves be nested.</para>
</listitem>
<listitem>
<para>They should not be used with IMPLICIT_CONTINUE=Yes in <ulink
url="shorewall6.conf.html">shorewall6.conf</ulink>(5).</para>
</listitem>
</itemizedlist>
<para>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.</para>
</refsect1>
<refsect1> <refsect1>
<title>FILES</title> <title>FILES</title>