Finish Vserver Implementation

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2010-06-30 20:35:46 -07:00
parent ac4349e930
commit e4afc15370
11 changed files with 354 additions and 40 deletions

View File

@ -1858,7 +1858,7 @@ sub set_mss( $$$ ) {
# Interate over non-firewall zones and interfaces with 'mss=' setting adding TCPMSS rules as appropriate.
#
sub setup_zone_mss() {
for my $zone ( all_zones ) {
for my $zone ( non_firewall_zones ) {
my $zoneref = find_zone( $zone );
set_mss( $zone, $zoneref->{options}{in_out}{mss}, '' ) if $zoneref->{options}{in_out}{mss};

View File

@ -34,7 +34,7 @@ use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw( validate_policy apply_policy_rules complete_standard_chain setup_syn_flood_chains save_policies optimize_policy_chains);
our @EXPORT_OK = qw( );
our $VERSION = '4.4_9';
our $VERSION = '4.4_11';
# @policy_chains is a list of references to policy chains in the filter table
@ -307,6 +307,7 @@ sub validate_policy()
NFQUEUE_DEFAULT => 'NFQUEUE' );
my $zone;
my $firewall = firewall_zone;
our @zonelist = $config{EXPAND_POLICIES} ? all_zones : ( all_zones, 'all' );
for my $option qw/DROP_DEFAULT REJECT_DEFAULT ACCEPT_DEFAULT QUEUE_DEFAULT NFQUEUE_DEFAULT/ {
@ -332,7 +333,9 @@ sub validate_policy()
push @policy_chains, ( new_policy_chain $zone, $zone, 'ACCEPT', PROVISIONAL );
push @policy_chains, ( new_policy_chain firewall_zone, $zone, 'NONE', PROVISIONAL ) if zone_type( $zone ) == BPORT;
if ( $config{IMPLICIT_CONTINUE} && ( @{find_zone( $zone )->{parents}} ) ) {
my $zoneref = find_zone( $zone );
if ( $config{IMPLICIT_CONTINUE} && ( @{$zoneref->{parents}} || $zoneref->{type} == VSERVER ) ) {
for my $zone1 ( all_zones ) {
unless( $zone eq $zone1 ) {
add_or_modify_policy_chain( $zone, $zone1 );

View File

@ -50,9 +50,9 @@ sub process_notrack_rule( $$$$$$ ) {
( my $zone, $source) = split /:/, $source, 2;
my $zoneref = find_zone $zone;
my $chainref = ensure_raw_chain( notrack_chain $zone );
my $restriction = $zone eq firewall_zone ? OUTPUT_RESTRICT : PREROUTE_RESTRICT;
my $restriction = $zoneref->{type} == FIREWALL || $zoneref->{type} == VSERVER ? OUTPUT_RESTRICT : PREROUTE_RESTRICT;
fatal_error 'USER/GROUP is not allowed unless the SOURCE zone is $FW' if $user ne '-' && $restriction != OUTPUT_RESTRICT;
fatal_error 'USER/GROUP is not allowed unless the SOURCE zone is $FW or a Vserver zone' if $user ne '-' && $restriction != OUTPUT_RESTRICT;
require_capability 'RAW_TABLE', 'Notrack rules', '';
my $rule = do_proto( $proto, $ports, $sports ) . do_user ( $user );

View File

@ -452,7 +452,7 @@ sub add_common_rules() {
add_rule( $filter_table->{$_} , "$globals{STATEMATCH} ESTABLISHED,RELATED -j ACCEPT" ) for qw( INPUT FORWARD OUTPUT );
}
for $interface ( all_interfaces ) {
for $interface ( grep $_ ne '%vserver%', all_interfaces ) {
ensure_chain( 'filter', $_ ) for first_chains( $interface ), output_chain( $interface );
}
@ -1133,10 +1133,10 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) {
my $restriction = NO_RESTRICT;
if ( $sourcezone eq firewall_zone ) {
$restriction = $destzone eq firewall_zone ? ALL_RESTRICT : OUTPUT_RESTRICT;
if ( $sourceref && ( $sourceref->{type} == FIREWALL || $sourceref->{type} == VSERVER ) ) {
$restriction = $destref && ( $destref->{type} == FIREWALL || $destref->{type} == VSERVER ) ? ALL_RESTRICT : OUTPUT_RESTRICT;
} else {
$restriction = INPUT_RESTRICT if $destzone eq firewall_zone;
$restriction = INPUT_RESTRICT if $destref && ( $destref->{type} == FIREWALL || $destref->{type} == VSERVER );
}
my ( $chain, $chainref, $policy );
@ -1589,7 +1589,7 @@ sub process_rule ( ) {
if ( $anydest ) {
@dest = ( all_parent_zones );
} else {
@dest = ( non_firewall_zones )
@dest = ( non_firewall_zones, vserver_zones )
}
unshift @dest, firewall_zone if $includedstfw;
@ -1668,6 +1668,121 @@ sub rules_target( $$ ) {
''; # CONTINUE policy
}
#
# Generate loopback rules for one destination zone
#
sub generate_loopback_rules1( $$$$ ) {
my ( $chainref, $chain, $z2, $match ) = @_;
my $z2ref = find_zone( $z2 );
my $type2 = $z2ref->{type};
if ( $type2 == VSERVER ) {
my $dest_hosts_ref = $z2ref->{hosts};
for my $typeref ( values %{$dest_hosts_ref} ) {
for my $hostref ( @{$typeref->{'%vserver%'}} ) {
my $ipsec_match = match_ipsec_out $z2 , $hostref;
my $exclusion = dest_exclusion( $hostref->{exclusions}, $chain);
for my $net ( @{$hostref->{hosts}} ) {
add_jump( $chainref,
$exclusion ,
0,
join('', $match, $ipsec_match,, match_dest_net( $net ) ) )
}
}
}
} else {
add_jump( $chainref, $chain, 0, $match );
}
}
#
# Generate loopback rules for one on-firewall source zone
#
sub generate_loopback_rules2( $$$$ ) {
my ( $outchainref, $z1, $z2, $match ) = @_;
my $chain = rules_target ( $z1, $z2 );
if ( $chain ) {
#
# Not a CONTINUE policy with no rules
#
my $source_hosts_ref = defined_zone( $z1 )->{hosts};
for my $typeref ( values %{$source_hosts_ref} ) {
for my $hostref ( @{$typeref->{'%vserver%'}} ) {
my $ipsec_match = match_ipsec_in $z1 , $hostref;
my $exclusion = source_exclusion( $hostref->{exclusions}, $chain);
for my $net ( @{$hostref->{hosts}} ) {
generate_loopback_rules1( $outchainref,
$exclusion,
$z2,
join('', match_source_net( $net ), $match , $ipsec_match )
);
}
}
}
}
}
#
# Loopback traffic -- this is where we assemble the intra-firewall traffic routing
#
sub handle_loopback_traffic() {
my @zones = ( vserver_zones, firewall_zone );
my $natout = $nat_table->{OUTPUT};
my $rulenum = 0;
my $outchainref;
my $rule = '';
if ( @zones > 1 ) {
$outchainref = new_standard_chain 'loopback';
add_jump $filter_table->{OUTPUT}, $outchainref, 0;
} else {
$outchainref = $filter_table->{OUTPUT};
$rule = '-o lo ';
}
for my $z1 ( @zones ) {
my $z1ref = find_zone( $z1 );
my $type1 = $z1ref->{type};
my $natref = $nat_table->{dnat_chain $z1};
if ( $type1 == FIREWALL ) {
for my $z2 ( @zones ) {
my $chain = rules_target( $z1, $z2 );
generate_loopback_rules1( $outchainref, $chain, $z2, $rule ) if $chain;
}
} else {
for my $z2 ( @zones ) {
generate_loopback_rules2( $outchainref, $z1, $z2, $rule );
}
}
if ( $natref && $natref->{referenced} ) {
my $source_hosts_ref = defined_zone( $z1 )->{hosts};
for my $typeref ( values %{$source_hosts_ref} ) {
for my $hostref ( @{$typeref->{'%vserver%'}} ) {
my $ipsec_match = match_ipsec_in $z1 , $hostref;
my $exclusion = source_exclusion( $hostref->{exclusions}, $natref);
for my $net ( @{$hostref->{hosts}} ) {
add_jump( $natout, $exclusion, 0, match_source_net( $net ), 0, $rulenum++ );
}
}
}
}
}
add_rule $filter_table->{INPUT} , '-i lo -j ACCEPT';
}
#
# Add jumps from the builtin chains to the interface-chains that are used by this configuration
#
@ -1686,7 +1801,7 @@ sub add_interface_jumps {
addnatjump 'POSTROUTING' , 'nat_out' , '';
addnatjump 'PREROUTING', 'dnat', '';
for my $interface ( @_ ) {
for my $interface ( grep $_ ne '%vserver%', @_ ) {
addnatjump 'PREROUTING' , input_chain( $interface ) , match_source_dev( $interface );
addnatjump 'POSTROUTING' , output_chain( $interface ) , match_dest_dev( $interface );
addnatjump 'POSTROUTING' , masq_chain( $interface ) , match_dest_dev( $interface );
@ -1694,7 +1809,7 @@ sub add_interface_jumps {
#
# Add the jumps to the interface chains from filter FORWARD, INPUT, OUTPUT
#
for my $interface ( @_ ) {
for my $interface ( grep $_ ne '%vserver%', @_ ) {
my $forwardref = $filter_table->{forward_chain $interface};
my $inputref = $filter_table->{input_chain $interface};
my $outputref = $filter_table->{output_chain $interface};
@ -1709,14 +1824,8 @@ sub add_interface_jumps {
add_jump $filter_table->{OUTPUT} , $outputref , 0, match_dest_dev( $interface ) unless get_interface_option( $interface, 'port' );
}
}
#
# Loopback
#
my $fw = firewall_zone;
my $chainref = $filter_table->{rules_chain( ${fw}, ${fw} )};
add_jump $filter_table->{OUTPUT} , ($chainref->{referenced} ? $chainref : 'ACCEPT' ), 0, '-o lo ';
add_rule $filter_table->{INPUT} , '-i lo -j ACCEPT';
handle_loopback_traffic;
}
# Generate the rules matrix.
@ -1734,6 +1843,7 @@ sub generate_matrix() {
my $fw = firewall_zone;
my $notrackref = $raw_table->{notrack_chain $fw};
my @zones = non_firewall_zones;
my @vservers = vserver_zones;
my $interface_jumps_added = 0;
our %input_jump_added = ();
our %output_jump_added = ();
@ -1802,7 +1912,6 @@ sub generate_matrix() {
my $source_hosts_ref = $zoneref->{hosts};
my $chain1 = rules_target firewall_zone , $zone;
my $chain2 = rules_target $zone, firewall_zone;
my $chain3 = rules_target $zone, $zone;
my $complex = $zoneref->{options}{complex} || 0;
my $type = $zoneref->{type};
my $frwd_ref = $filter_table->{zone_forward_chain $zone};
@ -1879,10 +1988,14 @@ sub generate_matrix() {
my $interfacematch = '';
my $use_output = 0;
if ( use_output_chain( $interface, $interfacechainref ) || ( @{$interfacechainref->{rules}} && ! $chain1ref ) ) {
if ( @vservers || use_output_chain( $interface, $interfacechainref ) || ( @{$interfacechainref->{rules}} && ! $chain1ref ) ) {
$outputref = $interfacechainref;
add_jump $filter_table->{OUTPUT}, $outputref, 0, match_dest_dev( $interface ) unless $output_jump_added{$interface}++;
$use_output = 1;
for my $vzone ( vserver_zones ) {
generate_loopback_rules2 ( $outputref, $vzone, $zone, $dest );
}
} else {
$outputref = $filter_table->{OUTPUT};
$interfacematch = match_dest_dev $interface;
@ -1934,10 +2047,15 @@ sub generate_matrix() {
my $interfacematch = '';
my $use_input;
if ( use_input_chain( $interface, $interfacechainref ) || ! $chain2 || ( @{$interfacechainref->{rules}} && ! $chain2ref ) ) {
if ( @vservers || use_input_chain( $interface, $interfacechainref ) || ! $chain2 || ( @{$interfacechainref->{rules}} && ! $chain2ref ) ) {
$inputchainref = $interfacechainref;
add_jump $filter_table->{INPUT}, $inputchainref, 0, match_source_dev($interface) unless $input_jump_added{$interface}++;
$use_input = 1;
for my $vzone ( @vservers ) {
my $target = rules_target( $zone, $vzone );
generate_loopback_rules1( $inputchainref, $target, $vzone, $source . $ipsec_in_match ) if $target;
}
} else {
$inputchainref = $filter_table->{INPUT};
$interfacematch = match_source_dev $interface;

View File

@ -37,6 +37,7 @@ our @EXPORT = qw( NOTHING
IPSECPROTO
IPSECMODE
FIREWALL
VSERVER
IP
BPORT
IPSEC
@ -52,6 +53,7 @@ our @EXPORT = qw( NOTHING
all_zones
all_parent_zones
complex_zones
vserver_zones
non_firewall_zones
single_interface
chain_base
@ -80,7 +82,7 @@ our @EXPORT = qw( NOTHING
);
our @EXPORT_OK = qw( initialize );
our $VERSION = '4.4_10';
our $VERSION = '4.4_11';
#
# IPSEC Option types
@ -167,7 +169,7 @@ use constant { FIREWALL => 1,
IP => 2,
BPORT => 3,
IPSEC => 4,
VSERVER => 8 };
VSERVER => 5 };
use constant { SIMPLE_IF_OPTION => 1,
BINARY_IF_OPTION => 2,
@ -378,6 +380,7 @@ sub process_zone( \$ ) {
fatal_error "Invalid Parent List ($2)" unless $p;
fatal_error "Unknown parent zone ($p)" unless $zones{$p};
fatal_error 'Subzones of firewall zone not allowed' if $zones{$p}{type} == FIREWALL;
fatal_error 'Subzones of a Vserver zone not allowed' if $zones{$p}{type} == VSERVER;
push @{$zones{$p}{children}}, $zone;
}
}
@ -500,9 +503,9 @@ sub zone_report()
my @translate;
if ( $family == F_IPV4 ) {
@translate = ( undef, 'firewall', 'ipv4', 'bport4', 'ipsec4' );
@translate = ( undef, 'firewall', 'ipv4', 'bport4', 'ipsec4', 'vserver' );
} else {
@translate = ( undef, 'firewall', 'ipv6', 'bport6', 'ipsec6' );
@translate = ( undef, 'firewall', 'ipv6', 'bport6', 'ipsec6', 'vserver' );
}
for my $zone ( @zones )
@ -559,9 +562,9 @@ sub dump_zone_contents()
my @xlate;
if ( $family == F_IPV4 ) {
@xlate = ( undef, 'firewall', 'ipv4', 'bport4', 'ipsec4' );
@xlate = ( undef, 'firewall', 'ipv4', 'bport4', 'ipsec4', 'vserver' );
} else {
@xlate = ( undef, 'firewall', 'ipv6', 'bport6', 'ipsec6' );
@xlate = ( undef, 'firewall', 'ipv6', 'bport6', 'ipsec6', 'vserver' );
}
for my $zone ( @zones )
@ -719,7 +722,7 @@ sub all_zones() {
}
sub non_firewall_zones() {
grep ( $zones{$_}{type} != FIREWALL , @zones );
grep ( ! ( $zones{$_}{type} == FIREWALL || $zones{$_}{type} == VSERVER ) , @zones );
}
sub all_parent_zones() {
@ -730,6 +733,10 @@ sub complex_zones() {
grep( $zones{$_}{options}{complex} , @zones );
}
sub vserver_zones() {
grep ( $zones{$_}{type} == VSERVER, @zones );
}
sub firewall_zone() {
$firewall_zone;
}
@ -802,6 +809,8 @@ sub process_interface( $$ ) {
} else {
fatal_error "Duplicate Interface ($interface)" if $interfaces{$interface};
fatal_error "Zones of type 'bport' may only be associated with bridge ports" if $zone && $zoneref->{type} == BPORT;
fatal_error "Vserver zones may not be associated with interfaces" if $zone && $zoneref->{type} == VSERVER;
$bridge = $interface;
}
@ -1041,6 +1050,27 @@ sub validate_interfaces_file( $ ) {
# Be sure that we have at least one interface
#
fatal_error "No network interfaces defined" unless @interfaces;
if ( vserver_zones ) {
#
# While the user thinks that vservers are associated with a particular interface, they really are not.
# We create an interface to associated them with.
#
my $interface = '%vserver%';
$interfaces{$interface} = { name => $interface ,
bridge => $interface ,
nets => 0 ,
number => $nextinum ,
root => $interface ,
broadcasts => undef ,
options => {} ,
zone => '',
physical => 'lo',
};
push @interfaces, $interface;
}
}
#
@ -1566,6 +1596,7 @@ sub process_host( ) {
$hosts = join( '', ALLIP , $hosts ) if substr($hosts, 0, 2 ) eq ',!';
if ( $hosts eq 'dynamic' ) {
fatal_error "Vserver zones may not be dynamic" if $type == VSERVER;
require_capability( 'IPSET_MATCH', 'Dynamic nets', '');
my $physical = physical_name $interface;
$hosts = "+${zone}_${physical}";
@ -1573,6 +1604,10 @@ sub process_host( ) {
$ipsets{"${zone}_${physical}"} = 1;
}
#
# We ignore the user's notion of what interface vserver addresses are on and simply invent one for all of the vservers.
#
$interface = '%vserver%' if $type == VSERVER;
add_group_to_zone( $zone, $type , $interface, [ split_list( $hosts, 'host' ) ] , $optionsref);

View File

@ -10,7 +10,10 @@ Changes in Shorewall 4.4.11
5) Add REQUIRE_INTERFACE to shorewall*.conf
6) Avoid run-time warnings when options are not listed in shorewall.conf.
6) Avoid run-time warnings when options are not listed in
shorewall.conf.
7) Implement Vserver zones.
Changes in Shorewall 4.4.10

View File

@ -1,6 +1,6 @@
----------------------------------------------------------------------------
S H O R E W A L L 4 . 4 . 1 1
B E T A I
B E T A I I
----------------------------------------------------------------------------
I. RELEASE 4.4 HIGHLIGHTS
@ -258,7 +258,12 @@ None.
V. N E W F E A T U R E S I N T H I S R E L E A S E
----------------------------------------------------------------------------
None.
1) Beginning with this release, Shorewall supports a 'vserver'
zone type. This zone type is used with Shorewall running on a
Linux-vserver host system and allows you to define zones that
represent a set of Linux-vserver hosts.
See http://www.shorewall.net/Vserver.html for details.
----------------------------------------------------------------------------
V I. P R O B L E M S C O R R E C T E D A N D N E W F E A T U R E S

View File

@ -57,11 +57,9 @@
<row>
<entry></entry>
<entry><ulink url="KVM.html">KVM (Kernel-mode Virtual
Machine)</ulink></entry>
<entry><ulink url="Vserver.html">Linux-vserver</ulink></entry>
<entry><ulink url="Laptop.html">Shorewall on a
Laptop</ulink></entry>
<entry></entry>
</row>
<row>
@ -386,6 +384,16 @@
<entry></entry>
</row>
<row>
<entry><ulink url="KVM.html">KVM (Kernel-mode Virtual
Machine)</ulink></entry>
<entry><ulink url="Laptop.html">Shorewall on a
Laptop</ulink></entry>
<entry></entry>
</row>
</tbody>
</tgroup>
</informaltable>

120
docs/Vserver.xml Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<article>
<!--$Id$-->
<articleinfo>
<title>Shorewall and Linux-vserver</title>
<authorgroup>
<author>
<firstname>Tom</firstname>
<surname>Eastep</surname>
</author>
</authorgroup>
<pubdate><?dbtimestamp format="Y/m/d"?></pubdate>
<copyright>
<year>2010</year>
<holder>Thomas M. Eastep</holder>
</copyright>
<legalnotice>
<para>Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License, Version
1.2 or any later version published by the Free Software Foundation; with
no Invariant Sections, with no Front-Cover, and with no Back-Cover
Texts. A copy of the license is included in the section entitled
<quote><ulink url="GnuCopyright.htm">GNU Free Documentation
License</ulink></quote>.</para>
</legalnotice>
</articleinfo>
<section>
<title>Introduction</title>
<para>Formal support for Linux-vserver was added in Shorewall 4.4.11
Beta2. The centerpiece of that support is the
<firstterm>vserver</firstterm> zone type. Vserver zones have the following
characteristics:</para>
<itemizedlist>
<listitem>
<para>They are defined on the Linux-vserver host.</para>
</listitem>
<listitem>
<para>The $FW zone is their implicit parent.</para>
</listitem>
<listitem>
<para>Their contents must be defined use the <ulink
url="manpages/shorewall-hosts.html">shorewall-hosts </ulink>(5)
file.</para>
</listitem>
<listitem>
<para>They may not appear in the ZONE column of the <ulink
url="manpages/shorewall-interfaces.html">shorewall-interfaces</ulink>
(5) file.</para>
</listitem>
</itemizedlist>
<para>If you use these zones, keep in mind that Linux-vserver implements a
very weak form of network virtualization:</para>
<itemizedlist>
<listitem>
<para>From a networking point of view, vservers live on the host
system. So if you don't use care, Vserver traffic to/from zone z will
be controlled by the fw-&gt;z and z-&gt;fw rules and policies rather
than by vserver-&gt;z and z-&gt;vserver rules and policies.</para>
</listitem>
<listitem>
<para>Outgoing connections from a vserver will not use the Vserver's
address as the SOURCE IP address unless you configure applications
running in the Vserver properly. This is especially true for IPv6
applications. Such connections will appear to come from the $FW zone
rather than the intended Vserver zone.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Vserver Zones</title>
<para>You create a zone that includes one or more Linux-vserver instances
as follow.</para>
<para><filename>/etc/shorewall/zones</filename>:</para>
<programlisting>#ZONE TYPE OPTIONS ...
fw firewall
loc ip #Local Zone
drct:loc ipv4 #Direct internet access
net ipv4 #Internet
vpn ipv4 #OpenVPN clients
<emphasis role="bold">dmz vserver #Vservers</emphasis></programlisting>
<para><filename>/etc/shorewall/hosts</filename>:</para>
<programlisting>#ZONE HOST(S) OPTIONS
drct eth3:dynamic
<emphasis role="bold">dmz eth1:70.90.191.124/31</emphasis></programlisting>
<para>While the IP addresses 70.90.191.124 and 70.90.191.125 are
configured on eth1, the actual interface name is irrelevate so long as the
interface is defined in <ulink
url="manpages/shorewall-interfaces.html">shorewall-interfaces</ulink> (5).
Shorewall will consider all vserver zones to be associated with the
loopback interface (usually <emphasis role="bold">lo</emphasis>).</para>
<para>Once a vserver zone is defined, it can be used as any other zone
type.</para>
</section>
</article>

View File

@ -169,6 +169,17 @@ c:a,b ipv4</programlisting>
single bridge.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">vserver</emphasis></term>
<listitem>
<para>Added in Shorewall 4.4.11 Beta 2 - A zone composed of
Linux-vserver guests. The zone contents must be defined in
<ulink url="shorewall-hosts.html">shorewall-hosts</ulink>
(5).</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
</varlistentry>

View File

@ -169,6 +169,17 @@ c:a,b ipv6</programlisting>
single bridge.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">vserver</emphasis></term>
<listitem>
<para>Added in Shorewall 4.4.11 Beta 2 - A zone composed of
Linux-vserver guests. The zone contents must be defined in
<ulink url="shorewall-hosts.html">shorewall-hosts</ulink>
(5).</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
</varlistentry>