#!/bin/sh # # Shorewall 4.0 -- /usr/share/shorewall/lib.dynamiczones # # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # # (c) 1999,2000,2001,2002,2003,2004,2005,2006,2007 - Tom Eastep (teastep@shorewall.net) # # Complete documentation is available at http://shorewall.net # # This program is free software; you can redistribute it and/or modify # it under the terms of Version 2 of the GNU General Public License # as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # This library is loaded by /usr/share/shorewall/firewall when processing # the 'add' and 'delete' commands. # # # 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 "$PHYSDEV_MATCH" ] && [ -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 "ERROR: Can't add $newhost to zone $zone" fi } DOING=Processing DONE=Processed # # Load $zones # determine_zones # # Validate Interfaces File # validate_interfaces_file # # Validate Hosts File # validate_hosts_file # # Validate IPSec File # f=$(find_file $IPSECFILE) [ -f $f ] && setup_ipsec $f # # Normalize host list # while [ $# -gt 1 ]; do interface=${1%%:*} host=${1#*:} [ "$host" = "$1" ] && host= # # 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 ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found" [ -f ${VARDIR}/zones ] || startup_error "${VARDIR}/zones -- file not found" # # Check for duplicates and create a new zone state file # > ${VARDIR}/zones_$$ while read z type hosts; do if [ "$z" = "$zone" ]; then case $type in bport4:*) rm -f ${VARDIR}/zones_$$ startup_error "Bridge Port zones may not be dynamically modified" ;; esac case "$hosts" in *exclude*) rm -f ${VARDIR}/zones_$$ startup_error "Modifying a zone that has an exclude list is not supported" ;; *) for h in $hostlist; do if ! list_search +$h $hosts; then if ! list_search $h $hosts; then newhostlist="$newhostlist +$h" else error_message "$h is already in zone $zone" fi else error_message "$h is already in zone $zone" fi done [ -z "$hosts" ] && hosts=$newhostlist || hosts="$hosts $newhostlist" ;; esac fi eval ${z}_hosts=\"$hosts\" echo "$z $type $hosts" >> ${VARDIR}/zones_$$ done < ${VARDIR}/zones mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones TERMINATOR=fatal_error # # Create a new Zone state file # for newhost in $newhostlist; do newhost=${newhost#+} # # 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 [ "$h" = exclude ] && break iface=${h%%:*} iface=${iface#+} 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 [ "$h" = exclude ] && break iface=${h%%:*} iface=${iface#+} 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 < ${VARDIR}/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= DOING=Processing DONE=Processed # # 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#*:} [ "$host" = "$1" ] && host= # # 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 ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found" [ -f ${VARDIR}/zones ] || startup_error "${VARDIR}/zones -- file not found" # # Delete the passed hosts from the zone state file # > ${VARDIR}/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 if [ "$h" = "$host" ]; then found=No break fi done [ -n "$found" ] || error_message "WARNING: $host does not appear to be in zone $zone" [ "$found" = No ] && startup_error "$host is a permanent member of 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" >> ${VARDIR}/zones_$$ done < ${VARDIR}/zones mv -f ${VARDIR}/zones_$$ ${VARDIR}/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 [ "$h" = exclude ] && break iface=${h%%:*} iface=${iface#+} 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 [ "$h" = exclude ] && break iface=${h%%:*} iface=${iface#+} 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 < ${VARDIR}/chains progress_message "$delhost removed from zone $zone" done rm -rf $TMP_DIR }