Add Policy module with nascent Actions module

git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@5538 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
This commit is contained in:
teastep 2007-03-15 02:18:29 +00:00
parent 9f002acb20
commit 7bbb015e8a
3 changed files with 358 additions and 327 deletions

27
New/Shorewall/Actions.pm Normal file
View File

@ -0,0 +1,27 @@
package Shorewall::Actions;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Zones;
use Shorewall::Chains;
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw( %usedactions %default_actions );
our @EXPORT_OK = qw( );
our @VERSION = 1.00;
#
# Used Actions. Each action that is actually used has an entry with value 1.
#
our %usedactions;
#
# Default actions for each policy.
#
our %default_actions = ( DROP => 'none' ,
REJECT => 'none' ,
ACCEPT => 'none' ,
QUEUE => 'none' );
1;

323
New/Shorewall/Policy.pm Normal file
View File

@ -0,0 +1,323 @@
package Shorewall::Policy;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Zones;
use Shorewall::Chains;
use Shorewall::Actions;
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw( validate_policy apply_policy_rules complete_standard_chain );
our @EXPORT_OK = qw( );
our @VERSION = 1.00;
#
# Create a new policy chain and return a reference to it.
#
sub new_policy_chain($$$)
{
my ($chain, $policy, $optional) = @_;
my $chainref = new_chain 'filter', $chain;
$chainref->{is_policy} = 1;
$chainref->{policy} = $policy;
$chainref->{is_optional} = $optional;
$chainref->{policychain} = $chainref;
}
#
# Set the passed chain's policychain and policy to the passed values.
#
sub set_policy_chain($$$)
{
my ($chain1, $chainref, $policy) = @_;
my $chainref1 = $filter_table->{$chain1};
$chainref1 = new_chain 'filter', $chain1 unless $chainref1;
unless ( $chainref1->{policychain} ) {
$chainref1->{policychain} = $chainref;
$chainref1->{policy} = $policy;
}
}
#
# Process the policy file
#
sub validate_policy()
{
sub print_policy($$$$)
{
my ( $source, $dest, $policy , $chain ) = @_;
progress_message " Policy for $source to $dest is $policy using chain $chain"
unless ( $source eq $dest ) || ( $source eq 'all' ) || ( $dest eq 'all' );
}
my %validpolicies = (
ACCEPT => undef,
REJECT => undef,
DROP => undef,
CONTINUE => undef,
QUEUE => undef,
NONE => undef
);
my %map = ( DROP_DEFAULT => 'DROP' ,
REJECT_DEFAULT => 'REJECT' ,
ACCEPT_DEFAULT => 'ACCEPT' ,
QUEUE_DEFAULT => 'QUEUE' );
my $zone;
use constant { OPTIONAL => 1 };
for my $option qw/DROP_DEFAULT REJECT_DEFAULT ACCEPT_DEFAULT QUEUE_DEFAULT/ {
my $action = $config{$option};
next if $action eq 'none';
my $actiontype = $targets{$action};
if ( defined $actiontype ) {
fatal_error "Invalid setting ($action) for $option" unless $actiontype & ACTION;
} else {
fatal_error "Default Action $option=$action not found";
}
unless ( $usedactions{$action} ) {
$usedactions{$action} = 1;
createactionchain $action;
}
$default_actions{$map{$option}} = $action;
}
for $zone ( @zones ) {
push @policy_chains, ( new_policy_chain "${zone}2${zone}", 'ACCEPT', OPTIONAL );
if ( $config{IMPLICIT_CONTINUE} && ( @{$zones{$zone}{parents}} ) ) {
for my $zone1 ( @zones ) {
next if $zone eq $zone1;
push @policy_chains, ( new_policy_chain "${zone}2${zone1}", 'CONTINUE', OPTIONAL );
push @policy_chains, ( new_policy_chain "${zone1}2${zone}", 'CONTINUE', OPTIONAL );
}
}
}
open POLICY, "$ENV{TMP_DIR}/policy" or fatal_error "Unable to open stripped policy file: $!";
while ( $line = <POLICY> ) {
chomp $line;
$line =~ s/\s+/ /g;
my ( $client, $server, $policy, $loglevel, $synparams , $extra ) = split /\s+/, $line;
fatal_error "Invalid policy file entry: $line" if $extra;
$loglevel = '' unless defined $loglevel;
$synparams = '' unless defined $synparams;
$loglevel = '' if $loglevel eq '-';
$synparams = '' if $synparams eq '-';
my $clientwild = ( "\L$client" eq 'all' );
fatal_error "Undefined zone $client" unless $clientwild || $zones{$client};
my $serverwild = ( "\L$server" eq 'all' );
fatal_error "Undefined zone $server" unless $serverwild || $zones{$server};
( $policy , my $default ) = split /:/, $policy;
if ( "\L$policy" eq 'none' ) {
$default = 'none';
} elsif ( $default ) {
my $defaulttype = $targets{$default};
if ( $defaulttype & ACTION ) {
unless ( $usedactions{$default} ) {
$usedactions{$default} = 1;
createactionchain $default;
}
} else {
fatal_error "Unknown Default Action ($default) in policy \"$line\"";
}
} else {
$default = $default_actions{$policy} || '';
}
fatal_error "Invalid policy $policy" unless exists $validpolicies{$policy};
if ( $policy eq 'NONE' ) {
fatal_error "$client, $server, $policy, $loglevel, $synparams: NONE policy not allowed to/from firewall zone"
if ( $zones{$client}{type} eq 'firewall' ) || ( $zones{$server}{type} eq 'firewall' );
fatal_error "$client $server $policy $loglevel $synparams: NONE policy not allowed with \"all\""
if $clientwild || $serverwild;
}
my $chain = "${client}2${server}";
my $chainref;
if ( defined $filter_table->{$chain} ) {
$chainref = $filter_table->{$chain};
if ( $chainref->{is_policy} ) {
if ( $chainref->{is_optional} ) {
$chainref->{is_optional} = 0;
} else {
fatal_error "Duplicate policy: $client $server $policy";
}
} else {
$chainref->{is_policy} = 1;
$chainref->{policy} = $policy;
$chainref->{policy_chain} = $chainref;
push @policy_chains, ( $chainref );
}
} else {
$chainref = new_policy_chain $chain, $policy, 0;
push @policy_chains, ( $chainref );
}
$chainref->{loglevel} = $loglevel if $loglevel;
$chainref->{synparams} = $synparams if $synparams;
$chainref->{default} = $default if $default;
if ( $clientwild ) {
if ( $serverwild ) {
for my $zone ( @zones , 'all' ) {
for my $zone1 ( @zones , 'all' ) {
set_policy_chain "${zone}2${zone1}", $chainref, $policy;
print_policy $zone, $zone1, $policy, $chain;
}
}
} else {
for my $zone ( @zones ) {
set_policy_chain "${zone}2${server}", $chainref, $policy;
print_policy $zone, $server, $policy, $chain;
}
}
} elsif ( $serverwild ) {
for my $zone ( @zones , 'all' ) {
set_policy_chain "${client}2${zone}", $chainref, $policy;
print_policy $client, $zone, $policy, $chain;
}
} else {
print_policy $client, $server, $policy, $chain;
}
}
close POLICY;
}
#
# Policy Rule application
#
sub policy_rules( $$$$ ) {
my ( $chainref , $target, $loglevel, $default ) = @_;
add_rule $chainref, "-j $default" if $default && $default ne 'none';
log_rule $loglevel , $chainref , $target , '' if $loglevel;
fatal_error "Null target in policy_rules()" unless $target;
add_rule $chainref , ( '-j ' . ( $target eq 'REJECT' ? 'reject' : $target ) ) unless $target eq 'CONTINUE';
}
sub report_syn_flood_protection() {
progress_message ' Enabled SYN flood protection';
}
sub default_policy( $$$ ) {
my $chainref = $_[0];
my $policyref = $chainref->{policychain};
my $synparams = $policyref->{synparams};
my $default = $policyref->{default};
my $policy = $policyref->{policy};
my $loglevel = $policyref->{loglevel};
fatal_error "No default policy for $_[1] to zone $_[2]" unless $policyref;
if ( $chainref eq $policyref ) {
policy_rules $chainref , $policy, $loglevel , $default;
} else {
if ( $policy eq 'ACCEPT' || $policy eq 'QUEUE' ) {
if ( $synparams ) {
report_syn_flood_protection;
policy_rules $chainref , $policy , $loglevel , $default;
} else {
add_rule $chainref, "-j $policyref->{name}";
$chainref = $policyref;
}
} elsif ( $policy eq 'CONTINUE' ) {
report_syn_flood_protection if $synparams;
policy_rules $chainref , $policy , $loglevel , $default;
} else {
report_syn_flood_protection if $synparams;
add_rule $chainref , "-j $policyref->{name}";
$chainref = $policyref;
}
}
progress_message " Policy $policy from $_[1] to $_[2] using chain $chainref->{name}";
}
sub apply_policy_rules() {
for my $chainref ( @policy_chains ) {
my $policy = $chainref->{policy};
my $loglevel = $chainref->{loglevel};
my $optional = $chainref->{is_optional};
my $default = $chainref->{default};
my $name = $chainref->{name};
if ( $policy ne 'NONE' ) {
if ( ! $chainref->{referenced} && ( ! $optional && $policy ne 'CONTINUE' ) ) {
ensure_filter_chain $name, 1;
}
if ( $name =~ /^all2|2all$/ ) {
policy_rules $chainref , $policy, $loglevel , $default;
}
}
}
for my $zone ( @zones ) {
for my $zone1 ( @zones ) {
my $chainref = $filter_table->{"${zone}2${zone1}"};
default_policy $chainref, $zone, $zone1 if $chainref->{referenced};
}
}
}
#
# Complete a standard chain
#
# - run any supplied user exit
# - search the policy file for an applicable policy and add rules as
# appropriate
# - If no applicable policy is found, add rules for an assummed
# policy of DROP INFO
#
sub complete_standard_chain ( $$$ ) {
my ( $stdchainref, $zone, $zone2 ) = @_;
my $ruleschainref = $filter_table->{"${zone}2${zone2}"};
my ( $policy, $loglevel, $default ) = ( 'DROP', 'info', $config{DROP_DEFAULT} );
my $policychainref;
$policychainref = $ruleschainref->{policychain} if $ruleschainref;
if ( $policychainref ) {
$policy = $policychainref->{policy};
$loglevel = $policychainref->{loglevel};
$default = $policychainref->{default};
}
policy_rules $stdchainref , $policy , $loglevel, $default;
}
1;

View File

@ -12,6 +12,8 @@ use Shorewall::Nat;
use Shorewall::Tc;
use Shorewall::Tunnels;
use Shorewall::Providers;
use Shorewall::Policy;
use Shorewall::Actions;
#
# Set to one if we find a SECTION
@ -28,10 +30,6 @@ my $sectioned = 0;
#
my %actions;
#
# Used Actions. Each action that is actually used has an entry with value 1.
#
my %usedactions;
#
# Contains an entry for each used <action>:<level>[:<tag>] that maps to the associated chain.
#
my %logactionchains;
@ -39,14 +37,6 @@ my %logactionchains;
# Maps each used macro to it's 'macro. ...' file.
#
my %macros;
#
# Default actions for each policy.
#
my %default_actions = ( DROP => 'none' ,
REJECT => 'none' ,
ACCEPT => 'none' ,
QUEUE => 'none' );
#
# This function determines the logging for a subordinate action or a rule within a subordinate action
@ -94,7 +84,7 @@ sub split_action ( $ ) {
#
# Get Macro Name
#
sub isolate_action( $ ) {
sub isolate_basic_target( $ ) {
( split '/' , $_[0] )[0];
}
@ -240,46 +230,6 @@ sub merge_macro_column( $$ ) {
}
}
#
# Create a new policy chain and return a reference to it.
#
sub new_policy_chain($$$)
{
my ($chain, $policy, $optional) = @_;
my $chainref = new_chain 'filter', $chain;
$chainref->{is_policy} = 1;
$chainref->{policy} = $policy;
$chainref->{is_optional} = $optional;
$chainref->{policychain} = $chainref;
}
#
# Set the passed chain's policychain and policy to the passed values.
#
sub set_policy_chain($$$)
{
my ($chain1, $chainref, $policy) = @_;
my $chainref1 = $filter_table->{$chain1};
$chainref1 = new_chain 'filter', $chain1 unless $chainref1;
unless ( $chainref1->{policychain} ) {
$chainref1->{policychain} = $chainref;
$chainref1->{policy} = $policy;
}
}
#
# Display a policy
#
sub print_policy($$$$)
{
my ( $source, $dest, $policy , $chain ) = @_;
progress_message " Policy for $source to $dest is $policy using chain $chain"
unless ( $source eq $dest ) || ( $source eq 'all' ) || ( $dest eq 'all' );
}
#
# Try to find a macro file -- RETURNS false if the file doesn't exist or MACRO if it does.
# If the file exists, the macro is entered into the 'targets' table and the fully-qualified
@ -296,166 +246,6 @@ sub find_macro( $ )
}
}
#
# Process the policy file
#
sub validate_policy()
{
my %validpolicies = (
ACCEPT => undef,
REJECT => undef,
DROP => undef,
CONTINUE => undef,
QUEUE => undef,
NONE => undef
);
my %map = ( DROP_DEFAULT => 'DROP' ,
REJECT_DEFAULT => 'REJECT' ,
ACCEPT_DEFAULT => 'ACCEPT' ,
QUEUE_DEFAULT => 'QUEUE' );
my $zone;
use constant { OPTIONAL => 1 };
for my $option qw/DROP_DEFAULT REJECT_DEFAULT ACCEPT_DEFAULT QUEUE_DEFAULT/ {
my $action = $config{$option};
next if $action eq 'none';
my $actiontype = $targets{$action};
if ( defined $actiontype ) {
fatal_error "Invalid setting ($action) for $option" unless $actiontype & ACTION;
} else {
fatal_error "Default Action/Macro $option=$action not found";
}
unless ( $usedactions{$action} ) {
$usedactions{$action} = 1;
createactionchain $action;
}
$default_actions{$map{$option}} = $action;
}
for $zone ( @zones ) {
push @policy_chains, ( new_policy_chain "${zone}2${zone}", 'ACCEPT', OPTIONAL );
if ( $config{IMPLICIT_CONTINUE} && ( @{$zones{$zone}{parents}} ) ) {
for my $zone1 ( @zones ) {
next if $zone eq $zone1;
push @policy_chains, ( new_policy_chain "${zone}2${zone1}", 'CONTINUE', OPTIONAL );
push @policy_chains, ( new_policy_chain "${zone1}2${zone}", 'CONTINUE', OPTIONAL );
}
}
}
open POLICY, "$ENV{TMP_DIR}/policy" or fatal_error "Unable to open stripped policy file: $!";
while ( $line = <POLICY> ) {
chomp $line;
$line =~ s/\s+/ /g;
my ( $client, $server, $policy, $loglevel, $synparams , $extra ) = split /\s+/, $line;
fatal_error "Invalid policy file entry: $line" if $extra;
$loglevel = '' unless defined $loglevel;
$synparams = '' unless defined $synparams;
$loglevel = '' if $loglevel eq '-';
$synparams = '' if $synparams eq '-';
my $clientwild = ( "\L$client" eq 'all' );
fatal_error "Undefined zone $client" unless $clientwild || $zones{$client};
my $serverwild = ( "\L$server" eq 'all' );
fatal_error "Undefined zone $server" unless $serverwild || $zones{$server};
( $policy , my $default ) = split /:/, $policy;
if ( "\L$policy" eq 'none' ) {
$default = 'none';
} elsif ( $default ) {
my $defaulttype = $targets{$default};
if ( $defaulttype & ACTION ) {
unless ( $usedactions{$default} ) {
$usedactions{$default} = 1;
createactionchain $default;
}
} else {
fatal_error "Unknown Default Action ($default) in policy \"$line\"";
}
} else {
$default = $default_actions{$policy} || '';
}
fatal_error "Invalid policy $policy" unless exists $validpolicies{$policy};
if ( $policy eq 'NONE' ) {
fatal_error "$client, $server, $policy, $loglevel, $synparams: NONE policy not allowed to/from firewall zone"
if ( $zones{$client}{type} eq 'firewall' ) || ( $zones{$server}{type} eq 'firewall' );
fatal_error "$client $server $policy $loglevel $synparams: NONE policy not allowed with \"all\""
if $clientwild || $serverwild;
}
my $chain = "${client}2${server}";
my $chainref;
if ( defined $filter_table->{$chain} ) {
$chainref = $filter_table->{$chain};
if ( $chainref->{is_policy} ) {
if ( $chainref->{is_optional} ) {
$chainref->{is_optional} = 0;
} else {
fatal_error "Duplicate policy: $client $server $policy";
}
} else {
$chainref->{is_policy} = 1;
$chainref->{policy} = $policy;
$chainref->{policy_chain} = $chainref;
push @policy_chains, ( $chainref );
}
} else {
$chainref = new_policy_chain $chain, $policy, 0;
push @policy_chains, ( $chainref );
}
$chainref->{loglevel} = $loglevel if $loglevel;
$chainref->{synparams} = $synparams if $synparams;
$chainref->{default} = $default if $default;
if ( $clientwild ) {
if ( $serverwild ) {
for my $zone ( @zones , 'all' ) {
for my $zone1 ( @zones , 'all' ) {
set_policy_chain "${zone}2${zone1}", $chainref, $policy;
print_policy $zone, $zone1, $policy, $chain;
}
}
} else {
for my $zone ( @zones ) {
set_policy_chain "${zone}2${server}", $chainref, $policy;
print_policy $zone, $server, $policy, $chain;
}
}
} elsif ( $serverwild ) {
for my $zone ( @zones , 'all' ) {
set_policy_chain "${client}2${zone}", $chainref, $policy;
print_policy $client, $zone, $policy, $chain;
}
} else {
print_policy $client, $server, $policy, $chain;
}
}
close POLICY;
}
sub process_tos() {
my $chain = 'pretos';
my $stdchain = 'PREROUTING';
@ -794,115 +584,6 @@ sub add_common_rules() {
setup_forwarding;
}
#
# Policy Rule application
#
sub policy_rules( $$$$ ) {
my ( $chainref , $target, $loglevel, $default ) = @_;
add_rule $chainref, "-j $default" if $default && $default ne 'none';
log_rule $loglevel , $chainref , $target , '' if $loglevel;
fatal_error "Null target in policy_rules()" unless $target;
add_rule $chainref , ( '-j ' . ( $target eq 'REJECT' ? 'reject' : $target ) ) unless $target eq 'CONTINUE';
}
sub report_syn_flood_protection() {
progress_message ' Enabled SYN flood protection';
}
sub default_policy( $$$ ) {
my $chainref = $_[0];
my $policyref = $chainref->{policychain};
my $synparams = $policyref->{synparams};
my $default = $policyref->{default};
my $policy = $policyref->{policy};
my $loglevel = $policyref->{loglevel};
fatal_error "No default policy for $_[1] to zone $_[2]" unless $policyref;
if ( $chainref eq $policyref ) {
policy_rules $chainref , $policy, $loglevel , $default;
} else {
if ( $policy eq 'ACCEPT' || $policy eq 'QUEUE' ) {
if ( $synparams ) {
report_syn_flood_protection;
policy_rules $chainref , $policy , $loglevel , $default;
} else {
add_rule $chainref, "-j $policyref->{name}";
$chainref = $policyref;
}
} elsif ( $policy eq 'CONTINUE' ) {
report_syn_flood_protection if $synparams;
policy_rules $chainref , $policy , $loglevel , $default;
} else {
report_syn_flood_protection if $synparams;
add_rule $chainref , "-j $policyref->{name}";
$chainref = $policyref;
}
}
progress_message " Policy $policy from $_[1] to $_[2] using chain $chainref->{name}";
}
sub apply_policy_rules() {
for my $chainref ( @policy_chains ) {
my $policy = $chainref->{policy};
my $loglevel = $chainref->{loglevel};
my $optional = $chainref->{is_optional};
my $default = $chainref->{default};
my $name = $chainref->{name};
if ( $policy ne 'NONE' ) {
if ( ! $chainref->{referenced} && ( ! $optional && $policy ne 'CONTINUE' ) ) {
ensure_filter_chain $name, 1;
}
if ( $name =~ /^all2|2all$/ ) {
policy_rules $chainref , $policy, $loglevel , $default;
}
}
}
for my $zone ( @zones ) {
for my $zone1 ( @zones ) {
my $chainref = $filter_table->{"${zone}2${zone1}"};
default_policy $chainref, $zone, $zone1 if $chainref->{referenced};
}
}
}
#
# Complete a standard chain
#
# - run any supplied user exit
# - search the policy file for an applicable policy and add rules as
# appropriate
# - If no applicable policy is found, add rules for an assummed
# policy of DROP INFO
#
sub complete_standard_chain ( $$$ ) {
my ( $stdchainref, $zone, $zone2 ) = @_;
my $ruleschainref = $filter_table->{"${zone}2${zone2}"};
my ( $policy, $loglevel, $default ) = ( 'DROP', 'info', $config{DROP_DEFAULT} );
my $policychainref;
$policychainref = $ruleschainref->{policychain} if $ruleschainref;
if ( $policychainref ) {
$policy = $policychainref->{policy};
$loglevel = $policychainref->{loglevel};
$default = $policychainref->{default};
}
policy_rules $stdchainref , $policy , $loglevel, $default;
}
my %maclist_targets = ( ACCEPT => { target => 'RETURN' , mangle => 1 } ,
REJECT => { target => 'reject' , mangle => 0 } ,
DROP => { target => 'DROP' , mangle => 1 } );
@ -1068,7 +749,7 @@ sub process_macro ( $$$$$$$$$$$ ) {
$mtarget = substitute_action $param, $mtarget;
}
my $action = isolate_action $mtarget;
my $action = isolate_basic_target $mtarget;
my $actiontype = $targets{$action};
if ( $actiontype & ACTION ) {
@ -1143,13 +824,13 @@ sub process_rule1 ( $$$$$$$$$ ) {
#
# Determine the validity of the action
#
my $actiontype = $targets{$action} || find_macro( isolate_action $action );
my $actiontype = $targets{$action} || find_macro( isolate_basic_target $action );
fatal_error "Unknown action ($action) in rule \"$line\"" unless $actiontype;
if ( $actiontype == MACRO ) {
process_macro
$macros{isolate_action $action}, $
$macros{isolate_basic_target $action}, $
target ,
(split '/', $action)[1] ,
$source,
@ -1385,7 +1066,7 @@ sub process_rule ( $$$$$$$$$ ) {
}
}
my $action = isolate_action $target;
my $action = isolate_basic_target $target;
$optimize = 0 if $action =~ /!^/;
@ -1527,7 +1208,7 @@ sub process_action3( $$$$$ ) {
my ( $action2 , $level2 ) = split_action $target2;
my $action2type = $targets{isolate_action $action2};
my $action2type = $targets{isolate_basic_target $action2};
unless ( $action2type == STANDARD ) {
if ( $target eq 'COMMENT' ) {