From 248b26a7d821c85f56d09988b06c5469b9e392d3 Mon Sep 17 00:00:00 2001 From: teastep Date: Wed, 11 Jan 2006 23:30:33 +0000 Subject: [PATCH] Re-add dynamic zone capability git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@3264 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- Shorewall/changelog.txt | 2 + Shorewall/firewall | 474 ++++++++++++++++++++++++++++++++++++- Shorewall/help | 44 ++++ Shorewall/releasenotes.txt | 4 +- Shorewall/shorewall | 6 + Shorewall/shorewall.conf | 8 + 6 files changed, 534 insertions(+), 4 deletions(-) diff --git a/Shorewall/changelog.txt b/Shorewall/changelog.txt index 1e5f8bd23..23e6f8c6a 100755 --- a/Shorewall/changelog.txt +++ b/Shorewall/changelog.txt @@ -5,3 +5,5 @@ Changes in 3.1.0 2) Implement 'generate' command. 3) Implement 'super-quiet' mode using multiple -q options (e.g., -qq). + +4) Add back dynamic zones. diff --git a/Shorewall/firewall b/Shorewall/firewall index 33990a1c0..2012768ea 100755 --- a/Shorewall/firewall +++ b/Shorewall/firewall @@ -206,6 +206,19 @@ run_iptables2() { } +# +# Quietly run iptables +# +qt_iptables() { + # + # Purge the temporary files that we use to prevent duplicate '-m' specifications + # + [ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev + [ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange + + qt $IPTABLES $@ +} + # # Run ip and if an error occurs, stop the firewall and quit # @@ -390,6 +403,14 @@ addrule2() # $1 = chain name, remainder of arguments specify the rule run_iptables2 -A $@ } +# +# Query NetFilter about the existence of a filter chain +# +chain_exists() # $1 = chain name +{ + qt $IPTABLES -L $1 -n +} + # # Create a mangle chain # @@ -685,6 +706,31 @@ macrecent_target() # $1 - interface [ -n "$MACLIST_TTL" ] && echo $(chain_base $1)_rec || echo RETURN } +# +# Functions for creating dynamic zone rules +# +dynamic_fwd() # $1 = interface +{ + echo $(chain_base $1)_dynf +} + +dynamic_in() # $1 = interface +{ + echo $(chain_base $1)_dyni +} + +dynamic_out() # $1 = interface +{ + echo $(chain_base $1)_dyno +} + +dynamic_chains() #$1 = interface +{ + local c=$(chain_base $1) + + echo ${c}_dyni ${c}_dynf ${c}_dyno +} + # # DNAT Chain from a zone # @@ -8344,6 +8390,22 @@ add_common_rules() { done fi + if [ -n "$DYNAMIC_ZONES" ]; then + echo "Setting up Dynamic Zone Chains..." + + for interface in $ALL_INTERFACES; do + for chain in $(dynamic_chains $interface); do + createchain $chain no + done + + chain=$(dynamic_in $interface) + createnatchain $chain + + run_iptables -A $(input_chain $interface) -j $chain + run_iptables -A $(forward_chain $interface) -j $(dynamic_fwd $interface) + run_iptables -A OUTPUT -o $interface -j $(dynamic_out $interface) + done + fi # # UPnP # @@ -8474,6 +8536,12 @@ activate_rules() addnatjump POSTROUTING $(snat_chain $interface) -o $interface done # + # Add jumps for dynamic nat chains + # + [ -n "$DYNAMIC_ZONES" ] && for interface in $ALL_INTERFACES ; do + addrulejump PREROUTING $(dynamic_in $interface) -i $interface + done + # # Add jumps from the builtin chains to the nat chains # addnatjump PREROUTING nat_in @@ -8484,6 +8552,7 @@ activate_rules() addnatjump POSTROUTING $(output_chain $interface) -o $interface done + > /var/lib/shorewall/chains echo "$FW firewall" > $STATEDIR/zones # # Create forwarding chains for complex zones and generate jumps for IPSEC source hosts to that chain. @@ -8503,8 +8572,10 @@ activate_rules() if [ -n "$is_ipsec" ]; then eval source_hosts=\$${zone}_hosts + [ -n "$DYNAMIC_ZONES" ] && create_zone_dyn_chain $zone $frwd_chain else eval source_hosts=\$${zone}_ipsec_hosts + [ -n "$DYNAMIC_ZONES" -a -n "$source_hosts" ] && create_zone_dyn_chain $zone $frwd_chain fi for host in $source_hosts; do @@ -8530,6 +8601,11 @@ activate_rules() echo $zone $type $source_hosts >> $STATEDIR/zones + if [ -n "$DYNAMIC_ZONES" ]; then + echo "$FW $zone $chain1" >> /var/lib/shorewall/chains + echo "$zone $FW $chain2" >> /var/lib/shorewall/chains + fi + need_broadcast= for host in $source_hosts; do @@ -8579,6 +8655,8 @@ activate_rules() [ -z "$chain" ] && continue # CONTINUE policy and there is no canonical chain. + [ -n "$DYNAMIC_ZONES" ] && echo "$zone $zone1 $chain" >> /var/lib/shorewall/chains + if [ $zone = $zone1 ]; then # # Try not to generate superfluous intra-zone rules @@ -9128,6 +9206,368 @@ refresh_firewall() rm -rf $TMP_DIR } +# +# Add a host or networks to a zone +# +add_to_zone() # $1...${n-1} = [:] $n = zone +{ + local interface host zone z h z1 z2 chain + local dhcp_interfaces blacklist_interfaces maclist_interfaces + local tcpflags_interfaces newhostlist= + local rulenum source_chain dest_hosts iface hosts hostlist= + + nat_chain_exists() # $1 = chain name + { + qt $IPTABLES -t nat -L $1 -n + } + + do_iptables() # $@ = command + { + [ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev + [ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange + + if ! $IPTABLES $@ ; then + error_message "Can't add $newhost to zone $zone" + fi + } + + # + # Load $zones + # + determine_zones + # + # Validate Interfaces File + # + validate_interfaces_file + # + # Validate Hosts File + # + validate_hosts_file + # + # Validate IPSec File + # + f=$(find_file ipsec) + + [ -f $f ] && setup_ipsec $f + # + # Normalize host list + # + while [ $# -gt 1 ]; do + interface=${1%%:*} + host=${1#*:} + # + # Be sure that the interface was dynamic at last [re]start + # + if ! chain_exists $(input_chain $interface) ; then + startup_error "Unknown interface $interface" + fi + + if ! chain_exists $(dynamic_in $interface) ; then + startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" + fi + + if [ -z "$host" ]; then + hostlist="$hostlist $interface:0.0.0.0/0" + else + for h in $(separate_list $host); do + hostlist="$hostlist $interface:$h" + done + fi + + shift + done + # + # Validate Zone + # + zone=$1 + + validate_zone $zone || startup_error "Unknown zone: $zone" + + [ "$zone" = $FW ] && startup_error "Can't add $1 to firewall zone" + + # + # Be sure that Shorewall has been restarted using a DZ-aware version of the code + # + [ -f /var/lib/shorewall/chains ] || startup_error "/var/lib/shorewall/chains -- file not found" + [ -f /var/lib/shorewall/zones ] || startup_error "/var/lib/shorewall/zones -- file not found" + # + # Check for duplicates and create a new zone state file + # + > /var/lib/shorewall/zones_$$ + + while read z type hosts; do + if [ "$z" = "$zone" ]; then + for h in $hostlist; do + list_search $h $hosts + if [ "$?" -gt 0 ]; then + newhostlist="$newhostlist $h" + else + error_message "$h already in zone $zone" + fi + done + + [ -z "$hosts" ] && hosts=$newhostlist || hosts="$hosts $newhostlist" + fi + + eval ${z}_hosts=\"$hosts\" + + echo "$z $type $hosts" >> /var/lib/shorewall/zones_$$ + done < /var/lib/shorewall/zones + + mv -f /var/lib/shorewall/zones_$$ /var/lib/shorewall/zones + + TERMINATOR=fatal_error + # + # Create a new Zone state file + # + for newhost in $newhostlist; do + # + # Isolate interface and host parts + # + interface=${newhost%%:*} + host=${newhost#*:} + # + # If the zone passed in the command has a dnat chain then insert a rule in + # the nat table PREROUTING chain to jump to that chain when the source + # matches the new host(s)# + # + chain=${zone}_dnat + + if nat_chain_exists $chain; then + do_iptables -t nat -A $(dynamic_in $interface) $(source_ip_range $host) $(match_ipsec_in $zone $newhost) -j $chain + fi + # + # Insert new rules into the filter table for the passed interface + # + while read z1 z2 chain; do + [ "$z1" = "$z2" ] && op="-I" || op="-A" + if [ "$z1" = "$zone" ]; then + if [ "$z2" = "$FW" ]; then + do_iptables $op $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j $chain + else + source_chain=$(dynamic_fwd $interface) + if is_ipsec_host $z1 $newhost ; then + do_iptables $op $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd + else + eval dest_hosts=\"\$${z2}_hosts\" + + for h in $dest_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + do_iptables $op $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain + fi + done + fi + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + # + # Add a rule to the dynamic out chain for the interface + # + do_iptables $op $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + else + eval source_hosts=\"\$${z1}_hosts\" + + for h in $source_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + if is_ipsec_host $z1 $h; then + do_iptables $op ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + else + do_iptables $op $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + fi + fi + done + fi + fi + done < /var/lib/shorewall/chains + + progress_message "$newhost added to zone $zone" + + done + + rm -rf $TMP_DIR +} + +# +# Delete a host or networks from a zone +# +delete_from_zone() # $1 = [:] $2 = zone +{ + local interface host zone z h z1 z2 chain delhost + local dhcp_interfaces blacklist_interfaces maclist_interfaces tcpflags_interfaces + local rulenum source_chain dest_hosts iface hosts hostlist= + + # + # Load $zones + # + determine_zones + # + # Validate Interfaces File + # + validate_interfaces_file + # + # Validate Hosts File + # + validate_hosts_file + # + # Validate IPSec File + # + f=$(find_file ipsec) + + [ -f $f ] && setup_ipsec $f + + # + # Normalize host list + # + while [ $# -gt 1 ]; do + interface=${1%%:*} + host=${1#*:} + # + # Be sure that the interface was dynamic at last [re]start + # + if ! chain_exists $(input_chain $interface) ; then + startup_error "Unknown interface $interface" + fi + + if ! chain_exists $(dynamic_in $interface) ; then + startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" + fi + + if [ -z "$host" ]; then + hostlist="$hostlist $interface:0.0.0.0/0" + else + for h in $(separate_list $host); do + hostlist="$hostlist $interface:$h" + done + fi + + shift + done + # + # Validate Zone + # + zone=$1 + + validate_zone $zone || startup_error "Unknown zone: $zone" + + [ "$zone" = $FW ] && startup_error "Can't delete from the firewall zone" + + # + # Be sure that Shorewall has been restarted using a DZ-aware version of the code + # + [ -f /var/lib/shorewall/chains ] || startup_error "/var/lib/shorewall/chains -- file not found" + [ -f /var/lib/shorewall/zones ] || startup_error "/var/lib/shorewall/zones -- file not found" + # + # Delete the passed hosts from the zone state file + # + > /var/lib/shorewall/zones_$$ + + while read z hosts; do + if [ "$z" = "$zone" ]; then + temp=$hosts + hosts= + + for host in $hostlist; do + found= + for h in $temp; do + if [ "$h" = "$host" ]; then + found=Yes + break + fi + done + + [ -n "$found" ] || error_message "Warning: $host does not appear to be in zone $zone" + done + + for h in $temp; do + found= + for host in $hostlist; do + if [ "$h" = "$host" ]; then + found=Yes + break + fi + done + + [ -n "$found" ] || hosts="$hosts $h" + done + fi + + eval ${z}_hosts=\"$hosts\" + + echo "$z $hosts" >> /var/lib/shorewall/zones_$$ + done < /var/lib/shorewall/zones + + mv -f /var/lib/shorewall/zones_$$ /var/lib/shorewall/zones + + TERMINATOR=fatal_error + + for delhost in $hostlist; do + interface=${delhost%%:*} + host=${delhost#*:} + # + # Delete any nat table entries for the host(s) + # + qt_iptables -t nat -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $zone $delhost) -j ${zone}_dnat + # + # Delete rules rules the input chains for the passed interface + # + while read z1 z2 chain; do + if [ "$z1" = "$zone" ]; then + if [ "$z2" = "$FW" ]; then + qt_iptables -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $delhost) -j $chain + else + source_chain=$(dynamic_fwd $interface) + if is_ipsec_host $z1 $delhost ; then + qt_iptables -D $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd + else + eval dest_hosts=\"\$${z2}_hosts\" + + [ "$z2" = "$zone" ] && dest_hosts="$dest_hosts $hostlist" + + for h in $dest_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + qt_iptables -D $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain + fi + done + fi + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + qt_iptables -D $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + else + eval source_hosts=\"\$${z1}_hosts\" + + for h in $source_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + if is_ipsec_host $z1 $h; then + qt_iptables -D ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + else + qt_iptables -D $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + fi + fi + done + fi + fi + done < /var/lib/shorewall/chains + + progress_message "$delhost removed from zone $zone" + + done + + rm -rf $TMP_DIR +} + # # Determine the value for a parameter that defaults to Yes # @@ -9235,6 +9675,7 @@ do_initialize() { SMURF_LOG_LEVEL= DISABLE_IPV6= BRIDGING= + DYNAMIC_ZONES= PKTTYPE= USEPKTYPE= RETAIN_ALIASES= @@ -9427,6 +9868,7 @@ do_initialize() { BLACKLISTNEWONLY=$(added_param_value_no BLACKLISTNEWONLY $BLACKLISTNEWONLY) DISABLE_IPV6=$(added_param_value_no DISABLE_IPV6 $DISABLE_IPV6) BRIDGING=$(added_param_value_no BRIDGING $BRIDGING) + DYNAMIC_ZONES=$(added_param_value_no DYNAMIC_ZONES $DYNAMIC_ZONES) STARTUP_ENABLED=$(added_param_value_yes STARTUP_ENABLED $STARTUP_ENABLED) RETAIN_ALIASES=$(added_param_value_no RETAIN_ALIASES $RETAIN_ALIASES) [ -n "${ADD_IP_ALIASES}${ADD_SNAT_ALIASES}" ] || RETAIN_ALIASES= @@ -9626,7 +10068,37 @@ case "$COMMAND" in compile_firewall $2 ;; - call) + add) + [ $# -lt 3 ] && usage + do_initialize + my_mutex_on + if ! shorewall_is_started ; then + echo "Shorewall Not Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2; + fi + shift + add_to_zone $@ + my_mutex_off + ;; + + delete) + [ $# -lt 3 ] && usage + do_initialize + my_mutex_on + if ! shorewall_is_started ; then + echo "Shorewall Not Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2; + fi + shift + delete_from_zone $@ + my_mutex_off + ;; + + call) # # Undocumented way to call functions in /usr/share/shorewall/firewall directly # diff --git a/Shorewall/help b/Shorewall/help index 8c79c47ae..77f13e183 100755 --- a/Shorewall/help +++ b/Shorewall/help @@ -28,6 +28,28 @@ case $1 in +add) + echo "add: add [:] ... + Adds a list of hosts or subnets to a dynamic zone usually used with VPN's. + + shorewall add interface:host-list ... zone - Adds the specified interface + (and host-list if included) to the specified zone. + + A host-list is a comma-separated list whose elements are: + + A host or network address + The name of a bridge port + The name of a bridge port followed by a colon (":") and a host or + network address. + + Example: + + shorewall add ipsec0:192.0.2.24 vpn1 -- adds the address 192.0.2.24 + from interface ipsec0 to the zone vpn1. + + See also \"help host\"" + ;; + address|host) echo "<$1>: May be either a host IP address such as 192.168.1.4 or a network address in @@ -90,6 +112,28 @@ debug) The word 'trace' is a synonym for 'debug'." ;; +delete) + echo "delete: delete [:] ... + Deletes a list of hosts or networks from a dynamic zone usually used with VPN's. + + shorewall delete interface[:host-list] ... zone - Deletes the specified + interfaces (and host list if included) from the specified zone. + + A host-list is a comma-separated list whose elements are: + + A host or network address + The name of a bridge port + The name of a bridge port followed by a colon (":") and a host or + network address. + + Example: + + shorewall delete ipsec0:192.0.2.24 vpn1 -- deletes the address + 192.0.2.24 from interface ipsec0 from zone vpn1 + + See also \"help host\"" + ;; + drop) echo "$1: $1
... Causes packets from the specified
to be ignored diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index ba0e751a3..07eac8382 100755 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -33,9 +33,7 @@ Problems corrected in 3.1.0 Migration Considerations: -1) The dynamic zone capability has been removed from Shorewall. Based on when - ipsets are made a standard part of the Linux kernels from kernel.org, dynamic - zones may be restored prior to the release of Shorewall 3.2. +None. New Features: diff --git a/Shorewall/shorewall b/Shorewall/shorewall index 04c23685e..ab299872a 100755 --- a/Shorewall/shorewall +++ b/Shorewall/shorewall @@ -31,6 +31,8 @@ # # Commands are: # +# shorewall add [:] zone Adds a host or subnet to a zone +# shorewall delete [:] zone Deletes a host or subnet from a zone # shorewall dump Dumps all Shorewall-related information # for problem analysis # shorewall start Starts the firewall @@ -921,6 +923,10 @@ case "$1" in $0 $(make_quiet) restore .reload fi ;; + add|delete) + [ $# -lt 3 ] && usage 1 + exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock $@ + ;; show|list) [ -n "$debugging" ] && set -x case "$2" in diff --git a/Shorewall/shorewall.conf b/Shorewall/shorewall.conf index 0af560a9a..786881127 100755 --- a/Shorewall/shorewall.conf +++ b/Shorewall/shorewall.conf @@ -698,6 +698,14 @@ DISABLE_IPV6=Yes BRIDGING=No +# +# DYNAMIC ZONES +# +# If you need to be able to add and delete hosts from zones dynamically then +# set DYNAMIC_ZONES=Yes. Otherwise, set DYNAMIC_ZONES=No. + +DYNAMIC_ZONES=No + # # USE PKTTYPE MATCH #