From ed809afe96286da617b93091898ea22bacde74c9 Mon Sep 17 00:00:00 2001 From: teastep Date: Wed, 8 Dec 2004 20:00:03 +0000 Subject: [PATCH] Add/Delete multiple hosts in one command; fix Add/Delete IPSEC problems git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@1811 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- Shorewall2/changelog.txt | 5 +- Shorewall2/firewall | 521 +++++++++++++++++++----------------- Shorewall2/help | 12 +- Shorewall2/releasenotes.txt | 12 + Shorewall2/shorewall | 4 +- 5 files changed, 298 insertions(+), 256 deletions(-) diff --git a/Shorewall2/changelog.txt b/Shorewall2/changelog.txt index 5dc90c512..6a23c4029 100644 --- a/Shorewall2/changelog.txt +++ b/Shorewall2/changelog.txt @@ -175,4 +175,7 @@ Changes since 2.0.3 86) Corrected typo in interfaces file. -97) Add DROPINVALID option. +87) Add DROPINVALID option. + +88) Allow list of hosts in add and delete commands. Fix ipsec problem + with "add" and "delete" diff --git a/Shorewall2/firewall b/Shorewall2/firewall index 98adc3403..d7db13a62 100755 --- a/Shorewall2/firewall +++ b/Shorewall2/firewall @@ -6637,11 +6637,11 @@ refresh_firewall() # # Add a host or networks to a zone # -add_to_zone() # $1 = [:] $2 = zone +add_to_zone() # $1...${n-1} = [:] $n = zone { - local base interface host newhost zone z h z1 z2 chain terminator + local interface host zone z h z1 z2 chain local dhcp_interfaces blacklist_interfaces maclist_interfaces tcpflags_interfaces - local rulenum source_chain dest_hosts iface hosts is_ipsec policyin= policyout= + local rulenum source_chain dest_hosts iface hosts hostlist= nat_chain_exists() # $1 = chain name { @@ -6654,17 +6654,10 @@ add_to_zone() # $1 = [:] $2 = zone [ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange if ! $IPTABLES $@ ; then - startup_error "Can't add $1 to zone $2" + error_message "Can't add $newhost to zone $zone" fi } - # - # Isolate interface and host parts - # - interface=${1%%:*} - host=${1#*:} - - [ -z "$host" ] && host="0.0.0.0/0" # # Load $zones # @@ -6674,74 +6667,252 @@ add_to_zone() # $1 = [:] $2 = zone # validate_interfaces_file # + # Validate Hosts File + # + validate_hosts_file + # # Validate IPSec File # f=$(find_file ipsec) - if [ -f $f ]; then - progress_message "Processing $f..." - setup_ipsec $f - fi + [ -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=$2 + zone=$1 validate_zone $zone || startup_error "Unknown zone: $zone" [ "$zone" = $FW ] && startup_error "Can't add $1 to firewall zone" - eval is_ipsec=\$${zone}_is_ipsec - eval options=\"\$${zone}_ipsec_options\" - eval in_options=\"\$${zone}_ipsec_in_options\" - eval out_options=\"\$${zone}_ipsec_out_options\" - - if [ -n "$is_ipsec" ]; then - [ -n "$POLICY_MATCH" ] || startup_error "Your kernel and/or iptables lacks policy match support" - policyin="-m policy --pol ipsec --dir in $options $in_options" - policyout="-m policy --pol ipsec --dir out $options $out_options" - elif [ -n "$POLICY_MATCH" ]; then - policyin="-m policy --pol none --dir in" - policyout="-m policy --pol none --dir out" - fi # # Be sure that Shorewall has been restarted using a DZ-aware version of the code # [ -f ${STATEDIR}/chains ] || startup_error "${STATEDIR}/chains -- file not found" [ -f ${STATEDIR}/zones ] || startup_error "${STATEDIR}/zones -- file not found" # - # Be sure that the interface was dynamic at last [re]start + # Check for duplicates and create a new zone state file # - if ! chain_exists $(input_chain $interface) ; then - startup_error "Unknown interface $interface" - fi + > ${STATEDIR}/zones_$$ - if ! chain_exists $(dynamic_in $interface) ; then - startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" - fi - # - # Normalize the first argument to this function - # - newhost="$interface:$host" + while read z hosts; do + if [ "$z" = "$zone" ]; then + for h in $hosts; do + for host in $hostlist; do + if [ "$h" = "$host" ]; then + rm -f ${STATEDIR}/zones_$$ + startup_error "$host already in zone $zone" + fi + done + done + + [ -z "$hosts" ] && hosts=$hostlist || hosts="$hosts $hostlist" + fi + + eval ${z}_hosts=\"$hosts\" + + echo "$z $hosts" >> ${STATEDIR}/zones_$$ + done < ${STATEDIR}/zones + + mv -f ${STATEDIR}/zones_$$ ${STATEDIR}/zones terminator=fatal_error # # Create a new Zone state file # + for newhost in $hostlist; 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 + if [ "$z1" = "$zone" ]; then + if [ "$z2" = "$FW" ]; then + do_iptables -A $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j $chain + else + source_chain=$(dynamic_fwd $interface) + eval dest_hosts=\"\$${z2}_hosts\" + + for h in $dest_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + do_iptables -A $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain + fi + done + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + # + # Add a rule to the dynamic out chain for the interface + # + do_iptables -A $(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 + do_iptables -A $(dynamic_fwd $iface) $rulenum $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + fi + done + fi + fi + done < ${STATEDIR}/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 ${STATEDIR}/chains ] || startup_error "${STATEDIR}/chains -- file not found" + [ -f ${STATEDIR}/zones ] || startup_error "${STATEDIR}/zones -- file not found" + # + # Delete the passed hosts from the zone state file + # > ${STATEDIR}/zones_$$ - # - # Add $1 to the Zone state file - # + while read z hosts; do if [ "$z" = "$zone" ]; then - for h in $hosts; do - if [ "$h" = "$newhost" ]; then - rm -f ${STATEDIR}/zones_$$ - startup_error "$1 already in zone $zone" - fi + 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: $1 does not appear to be in zone $2" done - [ -z "$hosts" ] && hosts=$newhost || hosts="$hosts $newhost" + 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\" @@ -6750,207 +6921,61 @@ add_to_zone() # $1 = [:] $2 = zone done < ${STATEDIR}/zones mv -f ${STATEDIR}/zones_$$ ${STATEDIR}/zones - # - # 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) $policyin -j $chain - fi - # - # Insert new rules into the filter table for the passed interface - # - while read z1 z2 chain; do - if [ "$z1" = "$zone" ]; then - if [ "$z2" = "$FW" ]; then - do_iptables -A $(dynamic_in $interface) $(match_source_hosts $host) $policyin -j $chain - else - source_chain=$(dynamic_fwd $interface) - eval dest_hosts=\"\$${z2}_hosts\" - - for h in $dest_hosts; do - iface=${h%%:*} - hosts=${h#*:} - - if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then - do_iptables -A $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $policyout -j $chain - fi - done - fi - elif [ "$z2" = "$zone" ]; then - if [ "$z1" = "$FW" ]; then - # - # Add a rule to the dynamic out chain for the interface - # - do_iptables -A $(dynamic_out $interface) $(match_dest_hosts $host) $policyout -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 - do_iptables -A $(dynamic_fwd $iface) $rulenum $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $policyout -j $chain - fi - done - fi - fi - done < ${STATEDIR}/chains - - rm -rf $TMP_DIR - - progress_message "$1 added to zone $2" -} - -# -# Delete a host or networks from a zone -# -delete_from_zone() # $1 = [:] $2 = zone -{ - # - # Delete the subject host(s) from the zone state file - # - delete_from_zones_file() - { - > ${STATEDIR}/zones_$$ - - while read z hosts; do - if [ "$z" = "$zone" ]; then - temp=$hosts - hosts= - - for h in $temp; do - if [ "$h" = "$delhost" ]; then - echo Yes - else - hosts="$hosts $h" - fi - done - fi - - echo "$z $hosts" >> ${STATEDIR}/zones_$$ - done < ${STATEDIR}/zones - - mv -f ${STATEDIR}/zones_$$ ${STATEDIR}/zones - } - # - # Isolate interface and host parts - # - interface=${1%%:*} - host=${1#*:} - - [ -z "$host" ] && host="0.0.0.0/0" - # - # Load $zones - # - determine_zones - - f=$(find_file ipsec) - if [ -f $f ]; then - progress_message "Processing $f..." - setup_ipsec $f - fi - - zone=$2 - - validate_zone $zone || startup_error "Unknown zone: $zone" - - [ "$zone" = $FW ] && startup_error "Can't remove $1 from firewall zone" - - eval is_ipsec=\$${zone}_is_ipsec - eval options=\"\$${zone}_ipsec_options\" - eval in_options=\"\$${zone}_ipsec_in_options\" - eval out_options=\"\$${zone}_ipsec_out_options\" - - if [ -n "$is_ipsec" ]; then - [ -n "$POLICY_MATCH" ] || startup_error "Your kernel and/or iptables lacks policy match support" - policyin="-m policy --pol ipsec --dir in $options $in_options" - policyout="-m policy --pol ipsec --dir out $options $out_options" - elif [ -n "$POLICY_MATCH" ]; then - policyin="-m policy --pol none --dir in" - policyout="-m policy --pol none --dir out" - fi - # - # Be sure that Shorewall has been restarted using a DZ-aware version of the code - # - [ -f ${STATEDIR}/chains ] || startup_error "${STATEDIR}/chains -- file not found" - [ -f ${STATEDIR}/zones ] || startup_error "${STATEDIR}/zones -- file not found" - # - # Be sure that the interface was present 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 "Interface $interface is not dynamic" - fi - # - # Normalize the first argument to this function - # - delhost="$interface:$host" - # - # Delete the passed hosts from the zone state file - # - [ -z "$(delete_from_zones_file)" ] && \ - error_message "Warning: $1 does not appear to be in zone $2" - # - # Construct the zone host maps - # - while read z hosts; do - eval ${z}_hosts=\"$hosts\" - done < ${STATEDIR}/zones terminator=fatal_error - # - # Delete any nat table entries for the host(s) - # - qt_iptables -t nat -D $(dynamic_in $interface) $(match_source_hosts $host) $policyin -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) $policyin -j $chain - else - source_chain=$(dynamic_fwd $interface) - eval dest_hosts=\"\$${z2}_hosts\" - for h in $dest_hosts $delhost; do - iface=${h%%:*} - hosts=${h#*:} + 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 $newhost) -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 $newhost) -j $chain + else + source_chain=$(dynamic_fwd $interface) + eval dest_hosts=\"\$${z2}_hosts\" - if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then - qt_iptables -D $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $policyout -j $chain - fi - done + [ "$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 + 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 + qt_iptables -D $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + fi + done + fi fi - elif [ "$z2" = "$zone" ]; then - if [ "$z1" = "$FW" ]; then - qt_iptables -D $(dynamic_out $interface) $(match_dest_hosts $host) $policyout -j $chain - else - eval source_hosts=\"\$${z1}_hosts\" - - for h in $source_hosts; do - iface=${h%%:*} - hosts=${h#*:} + done < ${STATEDIR}/chains - if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then - qt_iptables -D $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $policyout -j $chain - fi - done - fi - fi - done < ${STATEDIR}/chains + progress_message "$delhost removed from zone $zone" + done + rm -rf $TMP_DIR - - progress_message "$1 removed from zone $2" } # @@ -7123,7 +7148,7 @@ do_initialize() { ensure_config_path # # Determine the capabilities of the installed iptables/netfilter - # We load the kernel modules here to acurately determine + # We load the kernel modules here to acuray determine # capabilities when module autoloading isn't enabled. # @@ -7409,7 +7434,7 @@ case "$COMMAND" in ;; add) - [ $# -ne 3 ] && usage + [ $# -lt 3 ] && usage do_initialize my_mutex_on if ! qt $IPTABLES -L shorewall -n ; then @@ -7418,12 +7443,13 @@ case "$COMMAND" in my_mutex_off exit 2; fi - add_to_zone $2 $3 + shift + add_to_zone $@ my_mutex_off ;; delete) - [ $# -ne 3 ] && usage + [ $# -lt 3 ] && usage do_initialize my_mutex_on if ! qt $IPTABLES -L shorewall -n ; then @@ -7432,7 +7458,8 @@ case "$COMMAND" in my_mutex_off exit 2; fi - delete_from_zone $2 $3 + shift + delete_from_zone $@ my_mutex_off ;; diff --git a/Shorewall2/help b/Shorewall2/help index 61551ab34..63b87f2e6 100644 --- a/Shorewall2/help +++ b/Shorewall2/help @@ -29,10 +29,10 @@ case $1 in add) - echo "add: add [:][:] - Adds a host or subnet to a dynamic zone usually used with VPN's. + echo "add: add [:][:] ... + Adds a list of hosts or subnets to a dynamic zone usually used with VPN's. - shorewall add interface[:host] zone - Adds the specified interface + shorewall add interface[:][:host-list] ... zone - Adds the specified interface (and bridge port/host if included) to the specified zone. Example: @@ -95,11 +95,11 @@ debug) ;; delete) - echo "delete: delete [:][:] + echo "delete: delete [:][:] ... Deletes a host or subnet from a dynamic zone usually used with VPN's. - shorewall delete interface[:port][:host] zone - Deletes the specified - interface (and bridge port/host if included) from the specified zone. + shorewall delete interface[:port][:host-list] ... zone - Deletes the specified + interfaces (and bridge ports/hosts if included) from the specified zone. Example: diff --git a/Shorewall2/releasenotes.txt b/Shorewall2/releasenotes.txt index 620326bdd..040c8da99 100755 --- a/Shorewall2/releasenotes.txt +++ b/Shorewall2/releasenotes.txt @@ -184,6 +184,9 @@ Problems corrected since 2.2.0 Beta 7: 1) A typo in the /etc/shorewall/interfaces file has been corrected. +2) The "add" and "delete" commands were generating incorrect policy + matches when policy match support was available. + ----------------------------------------------------------------------- Issues when migrating from Shorewall 2.0 to Shorewall 2.1: @@ -806,3 +809,12 @@ New Features: If not specified or if specified as empty (e.g., DROPINVALID="") then DROPINVALID=Yes is assumed. + +36) The "shorewall add" and "shorewall delete" commands now accept a + list of hosts to add or delete. + + Examples: + + shorewall add eth1:1.2.3.4 eth1:2.3.4.5 z12 + shorewall delete eth1:1.2.3.4 eth1:2.3.4.5 z12 + diff --git a/Shorewall2/shorewall b/Shorewall2/shorewall index ebebb2fd7..2043cd9d2 100755 --- a/Shorewall2/shorewall +++ b/Shorewall2/shorewall @@ -867,8 +867,8 @@ case "$1" in exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock $1 ;; add|delete) - [ $# -ne 3 ] && usage 1 - exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock $1 $2 $3 + [ $# -lt 3 ] && usage 1 + exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock $@ ;; show|list) [ -n "$debugging" ] && set -x