mkrules.sh
#!/bin/bash # # Firewall/Traffic script for Corporation FictCorp for Site A. # # The script implements: # # - packet filtering rules via iptables for all nets # - address table lookup using symbolic host names # - separate filter chains for each attached network # - 1:many NAT maps, routable IPs -> RFC1918 addresses # - 1:1 NAT maps, used for inbound translations (holes) # - configurable logging based on either ulog or syslog # - priority queuing of traffic using linux classifier # - three flow bands: voice, video, and other data # - multicast routing -> GRE -> IP -> ESP -> IP # # The hypothetical system: # # - 5 directly attached LAN segments # - 2 WAN links, (1) Internet and (2) branch office # - /30 connections to edge router with ISP uplink # - PIM-SM router using Xorp # - multicast routing to peers across IPSEC VPN # - 5 different VPN tunnels # # How to use safely on a live firewall when you are remote: # # bash mkrules.sh iptables > test-iptables.sh # bash mkrules.sh traffic > test-traffic.sh # bash mkrules.sh multicast > test-multicast.sh # atnum=$(echo reboot -f | at now + 5 minutes | awk '{print $2}') # bash test-whatever.sh # # If the rules didn't work, wait for the reboot and log back # in; if it worked: # # atrm $atnum # mv test-whatever.sh rules-whatever.sh # # Make sure rules-*.sh are executed on boot. # # Further comments describing the site go here. Note: this # script has been sanitized so as not to leak information # about any of the companies it was used for. It is, # however, based on a real deployment script. Fictional # networks, systems, and IPs have been created. # # * THIS SCRIPT MUST ECHO COMMANDS ONLY, NOT EXECUTE THEM. # * Its output will be executed directly. This build script # * does not do ANYTHING AT ALL other than write another # * script to stdout. # # All interfaces should be plumbed before using this script. # ############################################################################## function setenv () { __vnm__=$1; shift; IFS= eval $__vnm__='"$*"';} function esetenv () { setenv "$@"; export $1; } ############################################################################## # VARIABLES ############################################################################## # use which add_log_rule_$LOGTYPE function to log events? setenv LOGTYPE syslog setenv LOGRATELIM yes setenv LOGRATEPERSEC 5 setenv LOGRATEBURST 60 # this controls the number of messages that will be buffered # before dumping to userspace, if LOGTYPE is set to "ulog" setenv LOGULOGQDEPTH 32 # we use this arg to REJECT module for ICMP error when # responding to packets that are oriented on the wrong side # of the RFC1918 horizon setenv REJECT_HORIZ "--reject-with icmp-net-prohibited" # for services where we restrict how frequently new # connections can be made (per source), what is the number # of seconds that will elapse before counter is reset and # connections are again allowed from that IP? setenv CONNLIMIT_ONLYEVERY 60 # physical interfaces setenv LAN_GROUPWARE_IF eth0 setenv LAN_USERPCS_IF eth3 setenv LAN_DMZ_IF eth2 setenv LAN_SALESDEMO_IF eth3 setenv LAN_PROTOTYPE_IF eth4 setenv WAN_BRANCHXYZ_IF eth1 setenv WAN_INET_IF eth5 # we need to stream multicast packets to BRANCHXYZ over a # GRE tunnel so they can be routed over IPSEC setenv MCASTIF mtun0 setenv MCASTPEER 10.70.4.1 setenv MCASTLOCAL 10.60.8.1 setenv MCASTTUNADDR 192.168.150.2/30 # routable address space used for NAT mappings by branch office setenv XYZ_ADDR_SPACE_ROUTABLE 123.45.6.128/26 setenv XYZ_ADDR_SPACE_RFC1918 10.70.0.0/16 # hide internal hosts behind this address when they make new # connections setenv MASQIP 99.22.47.1 # we need also to be aware of what our WAN partner uses for # that purpose setenv BRANCHXYZ_MASQIP 108.2.147.177 # sales demo network routable range setenv SALESDEMO_NET 99.22.47.32/28 # locally attached networks this system arbitrates access to # and from. setenv PROTOTYPE_INTRANET 10.60.4.0/22 setenv THISBRANCH_INTRANET 10.60.8.0/22 setenv GROUPWARE_INTRANET 10.60.12.0/22 setenv BRANCHXYZ_INTRANET 10.70.4.0/22 setenv XYZ_GWARE_INTRANET 10.70.12.0/22 setenv ADMIN_INTRANET 10.70.8.0/22 # encryption domain for VPNs with other corporations setenv CORPABC_INTRANET 192.168.101.0/24 setenv CORPDEF_INTRANET 192.168.80.0/24 ############################################################################## # PROGRAMS ############################################################################## # use our local patched versions of the software that # implements these rules setenv _tc 'echo /usr/local/sbin/tc' setenv _mrt 'echo /usr/local/sbin/smcroute' setenv _iptpath '/usr/local/sbin/iptables' setenv _ipt "echo $_iptpath" setenv _iptnataddpre "$_ipt " \ '-t nat ' \ '-A PREROUTING ' setenv _iptnataddpost "$_ipt " \ '-t nat ' \ '-A POSTROUTING ' # this is used to start the PIM-SM routing daemon. It # doesn't seem to have the ability to daemonize on its own # so we do it ourself using the shell setenv _xorp 'echo ' \ 'nohup ' \ 'nice -n 19 ' \ 'setsid ' \ '/usr/local/sbin/xorp ' \ '</dev/null ' \ '&>/dev/null &' ############################################################################## # CHAINS ############################################################################## # # here we classify and enumerate all the chains we will use, # for later reference (particularly as iterators). # # will ultimately terminate user_chains_end=( accept drop invalid to_vpn reject_horiz ) # nonterminating user_chains_return=( to_dmz to_userlan to_demolan to_prototype to_groupware to_userlan_from_abc ) user_chains=( ${user_chains_end[@]} ${user_chains_return[@]} ) builtin_chains=( INPUT OUTPUT FORWARD ) all_chains=( ${user_chains[@]} ${builtin_chains[@]} ) ############################################################################## # ADDRESS MAP ############################################################################## # # key routable rfc1918 # ADDRS=( \ dmz_ns1 99.22.47.2 no_internal \ dmz_ns2 99.22.47.3 no_internal \ \ demo_zxx 99.22.47.40 no_internal \ demo_zxx_pa 99.22.47.41 no_internal \ demo_zxx_pb 99.22.47.42 no_internal \ demo_custb 99.22.47.43 no_internal \ demo_custc 99.22.47.44 no_internal \ \ support 88.192.63.200 no_internal \ zxx_app2 88.192.63.105 no_internal \ zxx_db2 88.192.63.104 no_internal \ \ sfo_mailsrv 99.22.47.50 10.60.8.25 \ sfo_vpn 99.22.47.52 10.60.9.223 \ sfo_imap 99.22.47.53 10.60.8.19 \ sfo_ldap 99.22.47.54 10.60.8.29 \ sfo_smtp 99.22.47.55 10.60.8.31 \ web_directory 99.22.47.56 10.60.9.15 \ web_mail 99.22.47.57 10.60.9.13 \ web_info 99.22.47.58 10.60.9.22 \ sfo_ddns_app_ps_app48 99.22.47.59 10.60.10.95 \ sfo_mail 99.22.47.61 10.60.12.71 \ \ web_bug no_external 10.60.9.7 \ web_test no_external 10.60.9.19 \ web_timesheet no_external 10.60.9.20 \ \ sfo_app_sol8_web_pa no_external 10.60.8.194 \ sfo_app_sol8_web_pb no_external 10.60.8.195 \ sfo_app_sol8_web no_external 10.60.8.196 \ sfo_app_sol8_db no_external 10.60.8.197 \ sfo_app_sol8_app no_external 10.60.8.198 \ sfo_app_sol8_app2 no_external 10.60.8.200 \ sfo_app_sol9_web_pa no_external 10.60.8.204 \ sfo_app_sol9_web_pb no_external 10.60.8.205 \ sfo_app_sol9_web no_external 10.60.8.206 \ sfo_app_sol9_db no_external 10.60.8.210 \ sfo_app_sol9_db0 no_external 10.60.8.207 \ sfo_app_sol9_db1 no_external 10.60.8.208 \ sfo_app_sol9_app no_external 10.60.8.208 \ sfo_app_sol9_analytics no_external 10.60.8.209 \ \ sfo_qa_sol9_analytics no_external 10.60.8.199 \ sfo_qa_sol9_app0 no_external 10.60.8.217 \ sfo_qa_sol9_app1 no_external 10.60.8.218 \ \ sfo_hpux1 no_external 10.60.8.186 \ sfo_hpux1_pa no_external 10.60.8.182 \ sfo_hpux1_pb no_external 10.60.8.183 \ sfo_hpux2 no_external 10.60.8.187 \ \ sfo_app_db0 no_external 10.60.8.140 \ sfo_app_db1 no_external 10.60.8.135 \ sfo_app_staging no_external 10.60.8.138 \ sfo_app_apps no_external 10.60.8.170 \ sfo_app_apps0 no_external 10.60.8.171 \ sfo_aix51 no_external 10.60.8.188 \ sfo_aix43 no_external 10.60.8.189 \ sfo_printer3 no_external 10.60.8.43 \ sfo_printer2 no_external 10.60.8.40 \ sfo_persona no_external 10.60.8.121 \ sfo_file0 99.22.47.51 10.60.8.30 \ sfo_printer1 no_external 10.60.8.44 \ sfo_phone no_external 10.60.8.4 \ \ sfo_app_perf4 no_external 10.60.8.144 \ \ sfo_ddns_win2k3_test no_external 10.60.10.250 \ \ sfo_app_redhat71 no_external 10.60.8.136 \ sfo_app_redhat9 no_external 10.60.8.145 \ ) ############################################################################## # LIBRARY ############################################################################## # # We need this error routine because stdout is being # captured, so the user will not know about errors reported # via stdout unless he inspects the build output. It's a # little weird to make functions like this because of the # indirection introduced by the fact that it won't actually # be run until the rules script is executed. But each # occurence in the rules script will have hardcoded the # expansion of the arguments to the wrapper func, so it # doesn't actually need to be passed arguments when it's # invoked at runtime. See also the implementation of # wait_for_death() for another example. # # Note, we want this routine to be available to the build # script too. To support this, the function is defined in # the build script, and its definition via "type" builtin is # embedded in the rules output. Then a helper function is # provided to the build script which will create a callsite # for the routine in rules output. So it can be invoked for # the build script with a simple "error" invocation, while # "make_error_callsite" will produce the text of an "error" # call for dumping into the output script. # function error () { echo "${@}" > /dev/stderr; } function \ make_error_callsite () { # # We overwrite any previous definition because the # function will contain expanded arguments embedded # right in the declaration. Since these change each # time we call it from the build script, the only # way to get different output as desired is to # redefine the entire function each time it is # called, overwriting the previous definition. # cat <<- HERE unset error; $(type error | grep -v "is a function"); error ${@}; HERE } # # This procedure outputs a single line that contains an # iptables command, along with a branch clause linked to the # return code of iptables. At runtime, a failure # code causes the branch to execute; error() is redefined # inline, along with its already-expanded arguments that # were put into the rules file at build-time (but the # arguments should still be correct, as they are known # before interpretation). The newly hardcoded copy of # error() is called, writes its diagnostics, the program is # exits. # # XXX TODO for a firewall script such as this, it might be # better to try to continue and basically never exit, so we # get the chance for at least some of the rules to # take...perhaps even the essential ones required to login # and fix the problem # function \ exec_ipt_bomb_on_failure () { local numiptargs=$1; shift exec_string=("$_iptpath $( for ((n = 1; n <= $numiptargs; n++)); do echo $1 shift done ) || { $(make_error_callsite ${@}) exit 1 }") echo $exec_string } function \ toupper () { echo -n $1 | tr '[:lower:]' '[:upper:]' } # extract IP from table function \ getip () { local addrtype=$1 local keyname=$2 local thiskey external internal for ((n = 0; $n < ${#ADDRS[@]}; n++)); do thiskey=${ADDRS[$n]} external=${ADDRS[$((++n))]} internal=${ADDRS[$((++n))]} if test $thiskey == $keyname; then echo ${!addrtype} return fi done error $0: NOTHING FOUND FOR HOST $keyname exit 1 } function gip () { getip internal $1; } function gep () { getip external $1; } function \ conditional_print_ratelimit_argset () { # if this variable is not set then this function is a no-op test -z "$LOGRATELIMIT" && return # and same if it's set to a discernable "no" value if test \ $LOGRATELIMIT == no -o \ $LOGRATELIMIT == NO -o \ $LOGRATELIMIT == No -o \ $LOGRATELIMIT == off -o \ $LOGRATELIMIT == Off -o \ $LOGRATELIMIT == OFF -o \ $LOGRATELIMIT == 0 then return fi # otherwise we assume "yes" and we have set the # related variables that will specify rates to the # limit match module printf -- \ -m limit \ --limit ${LOGRATEPERSEC:-5}/sec \ --limit-burst ${LOGRATEBURST:-60} } function \ make_log_args_ulog () { local log_prefix="$1" printf -- \ -j LOG \ --ulog-qthreshold ${LOGULOGQDEPTH:-32} \ --ulog-prefix \""$log_prefix"\" } function \ make_log_args_syslog () { local log_prefix="$1" printf -- \ `conditional_print_ratelimit_argset` \ -j LOG \ --log-tcp-options \ --log-ip-options \ --log-prefix \""$log_prefix"\" } function \ add_log_rule () { local chain_name=$1 local event_name=$2 $_ipt -A $chain_name $(make_log_args $chain_name $event_name) } # this is where we control which log backend we are using function \ make_log_args () { local chain_name=$1 local event_name=$2 local log_prefix=$( \ printf "chain %s: %s: " \ $chain_name \ "$(toupper $event_name)" \ ) make_log_args_$LOGTYPE "$log_prefix" } # Add new hole for host on current $CHAIN with TCP or UDP # use CHAIN when entering a chain to set the current chain # these functions will manipulate. I like using a global # better than an argument here because it's sort of a # system-wide state (i.e. which chain we're in) and we do # not often add rules to multiple chains in proximity # (except builtin ones and those won't use these convenience # functions anyways). Plus it makes the invocation shorter. # NEW: optional fourth argument can be used to narrow the # match by IP source. This is actuallythe third argument as # the user would invoke it (using an ipta* wrapper function) function \ ipta () { local dest=$1 local port=$2 local proto=$3 local src=$4 test -z "$CHAIN" && { error $0: CHAIN must be set exit 1 } iptargs=( -A $CHAIN -p $proto --dport $port $(test -n "$4" && printf -- -s $4) -d $dest -m state --state NEW -j accept ) exec_ipt_bomb_on_failure \ ${#iptargs[@]} \ ${iptargs[@]} \ $0: \ dest $dest \ proto $proto \ chain $CHAIN \ port $port } function iptatcp () { ipta $1 $2 tcp "$3"; } function iptaudp () { ipta $1 $2 udp "$3"; } function iptaboth () { ipta $1 $2 tcp "$3"; ipta $1 $2 udp "$3"; } # # adds a new hole on the given chain for a service that will # restrict connection rates, avoiding things like ssh # vulnerability scans from executing too fast. Optional # destination argument, but chain is required (note: unlike # semantics of ipta* functions, we do not use the global # $CHAIN here because we are used with INPUT which doesn't # set CHAIN. If INPUT is used, the destination argument # will not be meangingful. # function \ limconn () { local chain=$1 # we want the parameter sequence to be the same as # for ipta* functions but since we don't always have # a destination (as we are used for input) we will # have to have different calling conventions for the # two cases with/without destination if test $# -eq 4; then local dest=$2 local proto=$3 local port=$4 else unset dest local proto=$2 local port=$3 fi # This is basically a hash function that uses all # the inputs as perturbations; should be a good key # unique to a chain/dest/port/proto combo but # reproducibly generated given that same combo. # This is used to tag the "recent" counter for a # particular packet and we definitely don't want # collisions or we might inadvertantly prevent # logins. # local name=$( echo "${chain}${dest:-INPUT}${port}${proto}" | md5sum | awk '{print $1}' ) # Three different rules are needed to implement # connection limiting. Each rule is somewhat # different, but also shares arguments with the # other rules. An array of args for each of the # three required iterations is defined here; to be # expanded using variable name indirection. Thus we # can implement a "fake" two dimentional array with # one dimension as iteration number and the other # dimension as the arguments therefor. # recent_args=( -m recent --name $name ) update_args=( --update --seconds ${CONNLIMIT_ONLYEVERY:-60} ) set_args=( --set ) accept_args=( -j accept ) drop_args=( -j drop ) ruleargs0=(${recent_args[@]} ${update_args[@]} ${drop_args[@]} ) ruleargs1=(${recent_args[@]} ${set_args[@]} ) ruleargs2=(${accept_args[@]} ) for ((n = 0; n < 3; n++)); do arg_array=ruleargs$n iptargs=( -A $chain -p $proto --dport $port $(test -n "$dest" && printf -- -d $dest) -m state --state NEW ${!arg_array[@]} ) exec_ipt_bomb_on_failure \ ${#iptargs[@]} \ ${iptargs[@]} \ $0: \ proto $proto \ chain $chain \ port $port done } function limtcp () { limconn $1 $2 tcp "$3"; } function limudp () { limconn $1 $2 udp "$3"; } ############################################################################## # TRAFFIC CONTROL ############################################################################## # Note: end to end latency should not exceed 100ms for voice # or 250 or so ms for video. For video with synchronized # audio, it should not exceed 100ms. # It is essential that video, voice, and data are # prioritized correctly LAN-wide (done by enabling # ToS-determined switching in our LAN meshes) and WAN-wide # (done by prioritizing traffic with the Linux classifier). # To support this all the routers along the way need to # maintain the ToS bits, *including those of the ISP*. In # fact, to properly implement queueing, traffic policies # must be implemented on *all* egress points. This means # the ISP needs to have bandwidth classes and allocations # based on the ToS bits we set. Fortunately, the Linux ESP # code copies the ToS bits from the encapsulated packets to # the outer tunnel packets. Doubly fortunately, our ISP has # just begun offering a service whereby they actually use # priority queues in the Cisco edge equipment. I basically # sent them IOS snippets from our own edge equipment and # they duplicated in theirs. # # Remember: it is the part that can fill the pipe, that # needs to do so intelligently. The ISP has full control of # how much data it will stuff in our receive queues at the # border, so it is the party that makes sure our important # classes have enough bandwidth. After that, our own edge # equipment at the VPN endpoints (i.e. this system) # determines how much data is sent to the remote system. # Since we control both VPN endpoints, and the ISP is # guaranteeing us enough bandwidth for our classes, it # becomes our job to make sure that we don't waste our # egress pipes with data that is just going to be dropped by # sprint. We also have to send according to policy because # *we* control site-to-site traffic. ISP controls # everyone-else-to-us, and we control us-to-us. # # Remember: egress queue is the one that has control! For a # given IP flow the egress queue at the sender *must* be # prioritized or competing traffic will not leave enough in # the way of slots for our important data. For each side, # ISP's border router is the "sender" that can decide what # packets to drop. But we also have to prioritize packets # we give to ISP because *we* control egress queues for # site-to-site! ISP will just start dropping packets to # guarantee bandwidth, so we need to make sure we aren't # overlimit when sending to the remote site. # # This actually works! Without it voice and video get drops # when we fill the interoffice pipe with FTP or HTTP data. # It took experimentation to find the right levels...it # would work for a while but when too many people got on the # phone, voice would start getting choppy if # videoconferencing was going. # # Note: we are using PPP multilink to aggregate DS1s to our # upstream. This is preferred to Cisco Express Forwarding # because we can guarantee packet delivery *in order* !! # This is important for streaming media like voice and # video. Out of order is basically the same as dropped for # realtime digital media streams. Research reveals that # phone do simply drop packets with sequence numbers they # aren't expecting. # # Note that these numbers are specifically designed for a # 2xDS1 pipe, aggregate 3072 kilobits. Fortunately this is # just enough for two 1152 kilobit streams to be guaranteed # (one for voice, the other for video) with enough room for # other data. # # Note that idle higher-priority bandwidth is (obviously) # borrowed by other applications if it isn't needed. # function \ do_htb_traffic_queue () { local iface=$1 local maxkbit=$2 local fixedvoicekbit=1152 local fixedvideokbit=1152 # remove all existing queues and classifiers on the # interface $_tc qdisc del dev $iface root '2>/dev/null' # for first run # the root queueing discipline will use only the # maximum given bandwidth of the interface; all # traffic will flow through this queue $_tc qdisc add dev $iface root \ handle 1: \ htb \ default 30 # we leave it intentionally short of the # true link bandwidth (this is done by the # caller passing in the $maxkbit parameter) # so we don't fill any upstream queues and # can guarantee that we have full control to # shape the traffic $_tc class add dev $iface \ parent 1: \ classid 1:1 \ htb \ rate ${maxkbit}kbit # VOIP traffic gets highest priority $_tc class add dev $iface \ parent 1:1 \ classid 1:10 \ htb \ rate ${fixedvoicekbit}kbit \ ceil ${maxkbit}kbit \ prio 0 $_tc qdisc add dev $iface \ parent 1:10 \ pfifo # videoconferencing gets next best priority $_tc class add dev $iface \ parent 1:1 \ classid 1:20 \ htb \ rate ${fixedvideokbit}kbit \ ceil ${maxkbit}kbit \ prio 1 $_tc qdisc add dev $iface \ parent 1:20 \ pfifo # half of remaining bandwidth for # anything sent from remote branch # office net $_tc class add dev $iface \ parent 1:1 \ classid 1:25 \ htb \ rate $(( $(( $maxkbit - $fixedvoicekbit - $fixedvideokbit )) / 2 ))kbit \ ceil ${maxkbit}kbit \ prio 2 $_tc qdisc add dev $iface \ parent 1:25 \ pfifo # all remaining traffic can consume # the rest of the bandwidth, ending # up in a stochastic fairness queue $_tc class add dev $iface \ parent 1:1 \ classid 1:30 \ htb \ rate $(( $(( $maxkbit - $fixedvoicekbit - $fixedvideokbit )) / 2 ))kbit \ ceil ${maxkbit}kbit \ prio 3 $_tc qdisc add dev $iface \ parent 1:30 \ sfq # shunt traffic classified as voice (IP Precedence 5) # to queue 1 $_tc filter add dev $iface parent 1: protocol ip pref 1 u32 \ match ip tos 0xa0 0xe0 flowid 1:10 # shunt traffic classified as video (IP Precedence 4) # to queue 2 $_tc filter add dev $iface parent 1: protocol ip pref 2 u32 \ match ip tos 0x80 0xe0 flowid 1:20 # traffic from remote branch office network gets put # into its own class; note that this includes all # tunneled traffic since we match on the gateway # address $_tc filter add dev $iface parent 1: protocol ip pref 3 u32 \ match ip src $XYZ_ADDR_SPACE_ROUTABLE flowid 1:25 $_tc filter add dev $iface parent 1: protocol ip pref 3 u32 \ match ip src $XYZ_ADDR_SPACE_RFC1918 flowid 1:25 } # # This alternative implementation does work, but has # problems like utter starvation and the possibility for # someone crafting packets to shut down the whole network. # Using a uid=0 ping with flood and manually set ToS bits, I # was able to completely block remote SSH logins to the # firewall. # # Note that in any case, since we don't filter out # higher-precedence ToS in headers and just assume the # validity of any client to set those, the network is # vulnerable at least to being slowed down. But that would # soon be detected by the SNMP monitors as excessive traffic # on some switch ports that shouldn't generate that. At # least we can log into machines and do stuff :) # function \ do_prio_traffic_queue () { local iface=$1 # remove all existing queues and classifiers $_tc qdisc del dev $iface root '2>/dev/null' # for first run # make three queues for priority scheduling $_tc qdisc add dev $iface root handle 1: prio $_tc qdisc add dev $iface parent 1:1 handle 10: sfq $_tc qdisc add dev $iface parent 1:2 handle 20: sfq $_tc qdisc add dev $iface parent 1:3 handle 30: sfq # shunt traffic classified as voice (IP Precedence 5) to queue 1 $_tc filter add dev $iface parent 1:0 protocol ip pref 1 u32 \ match ip tos 0xa0 0xe0 flowid 1:1 # shunt traffic classified as video (IP Precedence 4) to queue 2 $_tc filter add dev $iface parent 1:0 protocol ip pref 1 u32 \ match ip tos 0x80 0xe0 flowid 1:2 # shunt all other traffic to queue three $_tc filter add dev $iface parent 1:0 protocol ip pref 2 u32 \ match ip src 0.0.0.0/0 flowid 1:3 } function \ configure_traffic_queues () { do_htb_traffic_queue $LAN_USERPCS_IF $((95 * 1024)) do_htb_traffic_queue $LAN_GROUPWARE_IF $((95 * 1024)) do_htb_traffic_queue $LAN_DMZ_IF $((95 * 1024)) do_htb_traffic_queue $WAN_INET_IF 2850 do_htb_traffic_queue $MCASTIF 2700 # allow some room for GRE # overhead - XXX TODO how # much is actually # required? # legacy link uses strict priority queuing with no shaping do_prio_traffic_queue $WAN_BRANCHXYZ_IF } ############################################################################## # MULTICAST ############################################################################## # # Remember that our output is shell script; if we have a # function to wait for process death, we can't just call it # from the build script; we have to embed it in the rules # file that we are generating in such a way that it will be # available to the script for execution at run-time. # We do this by introducing a level of indirection: the # function, instead of containing real code, outputs shell # commands that define the desired function, and finally the # command that will invoke it. We are basically creating a # string buffer with (1) the definition of a function (i.e. # it would be suitable for "eval"uation); (2) the arguments # expanded already at build time; (3) the invocation text # that, when evaluated, will execute the procedure. # function \ wait_for_death () { cat <<- HERE function \ do_wait_for_death () { for ((n = 0; n <= 25; n++)); do if pgrep -lf $1 &>/dev/null; then sleep 1 else return fi done echo killing xorp failed, aborting exit } do_wait_for_death HERE } # # This is one alternative implementation to get multicast # across the IPSEC WAN, which uses the xorp daemon to # participate in PIM-SM. This should allow clients on one # side of our IPSEC tunnel to advertise Multicast groups, # and clients on the other side will get those # advertisements and potentially join. Multicast packets # are forwarded across a GRE tunnel, itself tunneled in ESP. # When they come out the other side they are available to # the xorp daemon and arrive in the INPUT chain. # # See the shell script entry code to see how to use this # particular alternative. The other implementation sets up # static multicast group memberships and static routes. It # didn't work very well. It became clear that a real PIM-SM # implementation was necessary and would be less headache # because the actual devices already know what groups they # need. This just lets them do their thing. # # Multicast is used by the IP phones for (1) conferencing; # (2) broadcast pages; (3) to light certain LEDs; (4) to # play "music on hold." It may also be used by multipoint # videoconferencing, but we are strictly point to point. # function \ configure_multicast_tunnel () { echo pkill smcroute '&>/dev/null' echo pkill -f xorp '&>/dev/null' # is there such thing as a C++ application that # starts and stops quickly? wait_for_death xorp echo ip link set dev $MCASTIF down '&>/dev/null' echo ip tunnel del $MCASTIF '&>/dev/null' echo ip tunnel add $MCASTIF \ mode gre \ remote $MCASTPEER \ local $MCASTLOCAL echo ip addr add $MCASTTUNADDR dev $MCASTIF echo ip link set dev $MCASTIF up echo ip link set dev $MCASTIF multicast on $_xorp } function \ configure_multicast_routing () { # purge existing routes echo pkill smcroute '&>/dev/null' echo pkill -lf xorp '&>/dev/null' # start new static mcast routing daemon $_mrt -d # these are the Multicast groups I've identified so # far -- by sniffing -- that are used by the VOIP # PBX. XXX TODO try Asterisk, then we don't have to # use trial and error all the time to figure out how # things work. groups=( \ 224.0.1.55 \ 224.0.1.22 \ 224.0.1.18 \ 224.0.1.26 \ 224.0.1.30 \ 224.0.1.40 \ 224.0.1.44 \ 224.0.1.36 \ 224.0.1.59 ) # signal to the LANs in this branch office that we # want their Multicast messages for group in ${groups[@]}; do $_mrt -j $MCASTIF $group done # tell the kernel to forward the packets we get from them for group in ${groups[@]}; do $_mrt -a $MCASTIF `gip sfo_phone` $group $LAN_USERPCS_IF done } ############################################################################# # ENTRY ############################################################################## # this script generates rules for one of: iptables, tc, and # multicast tunneling, depending on how we were invoked case "$1" in (traffic) configure_traffic_queues; exit $?;; (multicast-tunnel) configure_multicast_tunnel; exit $?;; (multicast-static) configure_multicast_routing; exit $?;; (multicast) configure_multicast_tunnel; exit $?;; (iptables) :;; # continue on (*) echo unsupported invocation \ type or no type \ specified; exit 1;; esac # the first thing we do is stop routing while we make changes echo 'echo 0 >/proc/sys/net/ipv4/ip_forward' # we'll need the FTP tracking module installed forcefully # XXX do we need this anymore? #echo modprobe -a ip_conntrack_ftp #echo modprobe -a ip_nat_ftp # start with all builtin tables and chains having a default # DROP policy so as to catch any mistakes $_ipt -t filter -P INPUT DROP $_ipt -t filter -P FORWARD DROP $_ipt -t filter -P OUTPUT DROP $_ipt -t nat -P PREROUTING DROP $_ipt -t nat -P POSTROUTING DROP $_ipt -t nat -P OUTPUT DROP $_ipt -t mangle -P PREROUTING DROP $_ipt -t mangle -P OUTPUT DROP $_ipt -t mangle -P INPUT DROP $_ipt -t mangle -P FORWARD DROP $_ipt -t mangle -P POSTROUTING DROP # ...but don't let any tables besides filter actually # control the default policy. We want the routing engine to # let everything through, leaving the filter code to make # decisions on what to allow. $_ipt -t nat -P PREROUTING ACCEPT $_ipt -t nat -P POSTROUTING ACCEPT $_ipt -t nat -P OUTPUT ACCEPT $_ipt -t mangle -P PREROUTING ACCEPT $_ipt -t mangle -P OUTPUT ACCEPT $_ipt -t mangle -P INPUT ACCEPT $_ipt -t mangle -P FORWARD ACCEPT $_ipt -t mangle -P POSTROUTING ACCEPT # now flush any and all existing rules for all default # chains $_ipt -t filter -F '2>/dev/null' $_ipt -t nat -F '2>/dev/null' $_ipt -t mangle -F '2>/dev/null' # flush all user chains; these won't exist at bootup so # stifle stderr for chain in ${user_chains[@]}; do $_ipt -F $chain '2>/dev/null' done # now delete all non builtin chains; they must be flushed # first or deletion routines will balk $_ipt -X # we must define chains before adding rules to them # note: builtin chains don't require this step because they # are always defined for chain in ${user_chains[@]}; do $_ipt -N $chain done # first rule when any chain is hit is to log that fact so we # can trace packets' traversal of the ruleset. for chain in \ ${user_chains[@]} \ ${builtin_chains[@]} do add_log_rule $chain entered done ############################################################################## # ADDRESS TRANSLATION ############################################################################## # 1:1 (static) NAT holes; for this to work we need NAT in both directions. for ((n = 0; n < ${#ADDRS[@]}; n++)); do ext=${ADDRS[$((++n))]} int=${ADDRS[$((++n))]} test $ext == no_external && continue test $int == no_internal && continue $_iptnataddpre -i $WAN_INET_IF -d $ext -j DNAT --to $int $_iptnataddpre -i $LAN_DMZ_IF -d $ext -j DNAT --to $int $_iptnataddpost -o $WAN_INET_IF -s $int -j SNAT --to $ext $_iptnataddpost -o $LAN_DMZ_IF -s $int -j SNAT --to $ext done # 1:N (dynamic) NAT hole; everyone from the inside through # the masquerade IP. Note that it is necessary to use IP # layer source address as match clause since we can't use -i # with POSTROUTING and -j SNAT isn't available with # PREROUTING. And if we just use -o we end up getting # Internet sources translated to our MASQIP, which works but # is very broken. $_iptnataddpost -o $WAN_INET_IF -s $THISBRANCH_INTRANET -j SNAT --to $MASQIP $_iptnataddpost -o $LAN_DMZ_IF -s $THISBRANCH_INTRANET -j SNAT --to $MASQIP # Groupware and Prototype nets will only need to reach # Internet, (mostly for updates) not DMZ or DEMO networks. $_iptnataddpost -o $WAN_INET_IF -s $GROUPWARE_INTRANET -j SNAT --to $MASQIP $_iptnataddpost -o $WAN_INET_IF -s $PROTOTYPE_INTRANET -j SNAT --to $MASQIP $_iptnataddpost -o $WAN_INET_IF -s $XYZ_GWARE_INTRANET -j SNAT --to $MASQIP ############################################################################## # SETUP ############################################################################## # we are a multicast router for the LAN and as such need to # get those packets...the do NOT arrive on INPUT chain. for chain in FORWARD INPUT; do $_ipt -A $chain \ -i $LAN_USERPCS_IF \ -m pkttype --pkt-type multicast \ -j accept done # or to arrive for processing (e.g. IGMP) if they arrived # from the local subnet $_ipt -A INPUT -i $LAN_USERPCS_IF -p igmp -j accept # multicast packets (for PIM) arrive to us from RWC out of a # GRE tunnel $_ipt -A INPUT -s $MCASTPEER -p gre -j accept $_ipt -A INPUT -i $MCASTIF -j accept $_ipt -A FORWARD -i $MCASTIF -j accept # force packets to NAT routables from their mapped # unroutable source IPs to be rejected: they shouldn't be # using those IPs, they are using the wrong DNS server # XXX TODO this might not actually work as it's supposed to; # needs to be traced and verified $_ipt -A FORWARD -i $LAN_USERPCS_IF -d $MAPPED_NET -j reject_horiz # disallow all invalid traffic $_ipt -A INPUT -m state --state INVALID -j invalid $_ipt -A FORWARD -m state --state INVALID -j invalid # allow valid, statefully tracked return traffic $_ipt -A INPUT -m state --state RELATED,ESTABLISHED -j accept $_ipt -A FORWARD -m state --state RELATED,ESTABLISHED -j accept # handle ping here since we want it for everyone, note we # already need states and have dumped bogus ones so it # shouldn't be a security hole, but this should be verified $_ipt -A INPUT -p icmp -m state --state NEW -j accept $_ipt -A FORWARD -p icmp -m state --state NEW -j accept ############################################################################## CHAIN=forward ############################################################################## # let users from inside our LANs do anything they want, # without exception # # ---> XXX TODO LOOKHERE FIXME WARNING XXX <--- # # NOTE: BIG GAPING SECURITY HOLE. This block was # implemented while doing a network cutover. Must be # removed! Note, does not expose to outside attack, but # makes all nets in our entire WAN bypass the firewall rules # of the destination network! # #$_ipt -A FORWARD -i $LAN_USERPCS_IF -j accept #$_ipt -A FORWARD -i $LAN_PROTOTYPE_IF -j accept #$_ipt -A FORWARD -i $LAN_GROUPWARE_IF -j accept #$_ipt -A FORWARD -i $WAN_BRANCHXYZ_IF -j accept #$_ipt -A FORWARD -o $WAN_BRANCHXYZ_IF -j accept #$_ipt -A FORWARD -s $BRANCHXYZ_INTRANET -j accept #$_ipt -A FORWARD -s $XYZ_GWARE_INTRANET -j accept # let DMZ traffic outbound to the Internet proceed without # restriction as well $_ipt -A FORWARD -i $LAN_DMZ_IF -o $WAN_INET_IF -j accept # vpn server is the only thing that should be getting IP/GRE or TCP/PPTP $_ipt -A FORWARD -i $WAN_INET_IF -d `gip sfo_vpn` \ -p gre -j accept $_ipt -A FORWARD -i $WAN_INET_IF -d `gip sfo_vpn` \ -p tcp --dport 1723 -j accept # Send traffic for each logical network to its own rule # chain. At this point any RELATED, ESTABLISHED or INVALID # packets have already been dealt with so we can add the NEW # state as a condition for jumping. This means we don't # have to match on the NEW state in the individual chains # because it's assumed. $_ipt -A FORWARD -d $SALESDEMO_NET -m state --state NEW -j to_demolan $_ipt -A FORWARD -o $LAN_USERPCS_IF -m state --state NEW -j to_userlan $_ipt -A FORWARD -o $LAN_PROTOTYPE_IF -m state --state NEW -j to_prototype $_ipt -A FORWARD -o $LAN_GROUPWARE_IF -m state --state NEW -j to_groupware $_ipt -A FORWARD -o $LAN_DMZ_IF -m state --state NEW -j to_dmz ############################################################################## # LOCAL CHAINS ############################################################################## # detect accidental use of ipta* functions unset CHAIN # let us go outbound for everything $_ipt -A OUTPUT -j accept # all loopback traffic allowed (note rp_filter should take # care of spoofing) $_ipt -A INPUT -i lo -j accept # we want ping, mroute daemons, etc from BRANCHXYZ to work # without effort after they emerge from the tunnel $_ipt -A INPUT -s $BRANCHXYZ_INTRANET -j accept $_ipt -A INPUT -s $XYZ_GWARE_INTRANET -j accept # handle the fact that we are an IPSec gateway. # note: protocol 50 (esp) not in /etc/services $_ipt -A INPUT -p tcp --dport isakmp -m state --state NEW -j accept $_ipt -A INPUT -p udp --dport isakmp -m state --state NEW -j accept $_ipt -A INPUT -p 50 -j accept $_ipt -A OUTPUT -p tcp --dport isakmp -m state --state NEW -j accept $_ipt -A OUTPUT -p udp --dport isakmp -m state --state NEW -j accept $_ipt -A OUTPUT -p 50 -j accept # allow new incoming ssh connections to the firewal's ssh # port from anywhere. Note that we limit connections # because of widespread distributed ssh vulnerability # scanning. I was getting very annoyed at the thousands # plus failed connect attempts per day in the logs, many # times just trying easy credentials like "root/password" limtcp INPUT ssh # allow intranet to get SNMP monitor values. I don't know # why I can't just use a single UDP NEW accept because it # should hit the RELATED,ESTABLISHED rule at the top of the # rules, but it doesn't seem to be working with UDP for some # reason. # # XXX TODO see comments about NTP conntracking. UDP # conntrack apparently is not getting the information it # needs. Since UDP is connectionless I'm not sure how they # do it anwyays...must just be am heuristic. # $_ipt -A INPUT -p udp -s $THISBRANCH_INTRANET --dport snmp -j accept $_ipt -A INPUT -p udp -s $BRANCHXYZ_INTRANET --dport snmp -j accept $_ipt -A OUTPUT -p udp -d $THISBRANCH_INTRANET --sport snmp -j accept $_ipt -A OUTPUT -p udp -d $BRANCHXYZ_INTRANET --sport snmp -j accept ############################################################################## CHAIN=to_dmz ############################################################################## # # A NOTE ABOUT "DMZ": most DMZs are a bastion LAN between # two firewalls, one on the edge side and one closer to the # core. Servers that run only the front-end parts of the # services they offer are put in this net and access their # backends through the inner firewall, which allows only # these bastion hosts to access the backend services, and only # those services. The second firewall is not necessary in # our topology because logically, we can implement the exact # same thing with different rulesets for each pair of # networks that's talking. E.g. outside -> bastion has one # set of access rules and bastion -> inside has another, # while outside -> inside has no possible communications # path. # # This accomplishes the exact same thing as having a second # physical firewall; the only difference is that the second # firewall is on the same host as the first one. Since two # firewalls are likely to be running the exact same firewall # software anyways, they are both vulnerable to the same # attack that can take over the edge firewall, so I don't # see any advantage to the two firewall deployment. Once # the edge firewall is cracked, the LAN allows communication # with the inner firewall, which presumably can be cracked # in the same fasion. # # For our topology, the firewall acts as the "switch" at the # nexus of all the networks we want access to allows us to # have a fully self-contained ruleset for any (fromnet, # tonet) pair. # # # Name service; for external queries ONLY. These do NOT # have any RFC1918 data and should never be queried by any # machines from inside. In particular, the caching DNS # servers on the LAN are recursive and maintain their own # caches, they do NOT rely on the DMZ name servers. The # sole purpose of those is to (1) do recursive lookup for # other clients in the DMZ and (2) maintain authoritative # name data for ourcompany.com and the in-addr.arpa PTRs. # iptaboth `gep dmz_ns1` domain iptaboth `gep dmz_ns2` domain # ssh logins (rate limited) limtcp to_dmz `gep dmz_ns1` ssh limtcp to_dmz `gep dmz_ns2` ssh # MX hosts and ftp.mycompany.com are also in the DMZ iptatcp `gep dmz_ns1` smtp iptatcp `gep dmz_ns2` smtp iptatcp `gep dmz_ns2` ftp # colo uses us for NTP synchronization # # XXX TODO iptables still is not maintaining state # consistently for NTP sessions, and currently whether two # peers will sync through the firewall is a crapshoot. I # think it needs a helper module to be written because # standard udp conntrack does NOT work reliably. I have to # keep restarting over and over and eventually, and # seemingly at random, it will get conntracked and allow the # peer synchronization messages through. But wait, you're # not done there: next you have to cross your fingers and # hope it will get into 1024s poll mode (i.e. steady state) # before conntrack decides it isn't RELATED anymore. This # *should* work but doesn't, and I don't know why. # # Note: if we could somehow use TCP for NTP, that would # solve the problem. To my knowledge, NTPv4 does not ever # use NTP. I know that I've never seen tcp packets sent # while listening to the excahnge with tcpdump (and I've # done that a *number* of times trying to figure out what # varies to cause udp conntrack not to recognize the packets # anymore) # iptaboth `gep dmz_ns1` ntp iptaboth `gep dmz_ns2` ntp ############################################################################## CHAIN=to_demolan ############################################################################## # # these machine are in use by sales or services teams and # must be accessible remotely. Since they aren't patched # and IT has no jurisdiction we cordon it off in a separate # network. It is expected that these machines will be # cracked. At least we can limit the exposure to those # ports needed for the demonstration purposes. # everything on this chain is TCP $_ipt -A to_demolan -p ! tcp -j drop iptatcp `gep demo_zxx` http iptatcp `gep demo_zxx` webcache iptatcp `gep demo_zxx` 8180 iptatcp `gep demo_zxx` 4443 iptatcp `gep demo_zxx` https iptatcp `gep demo_custb` https iptatcp `gep demo_zxx_pa` https iptatcp `gep demo_zxx_pb` https ############################################################################## CHAIN=to_prototype ############################################################################## # # This network is used as a prototyping/test network for IT # systems. It needs to have more restriction, possibly only # allowing access from designated admin station IPs, or # requiring authentication first or something of that # nature. $_ipt -A to_prototype -s $ADMIN_INTRANET -j accept $_ipt -A to_prototype -s $THISBRANCH_INTRANET -j accept $_ipt -A to_prototype -s $BRANCHXYZ_INTRANET -j accept ############################################################################## CHAIN=to_groupware ############################################################################## # # After the MX hosts get the mail and it is transferred to # the mail hub for canonicalization via LDAP. The LDAP # schema used by the hub MTA can specify reception # addresses, SMTP routing, and delivery methods if local, on # a per-user basis. It also explodes ou=groups entries into # its members, for groupOfUniqueNames object types that have # mail address attributes. This implements one level of # distrubution list. # # For most users, their LDAP entry tells the MTA to route # the mail to their branch office MTA if this isn't it. # Once in hand, that MTA then will typically route it into # the big honking groupware system that has its own LAN and # 4 servers per office just to make it work. The groupware # handles the mail from there; typically it is fetched with # some strange non-SMTP protocol by Outlook, and cached on # the client. # # The groupware system is partitioned from everything else # and it is assumed that it will be cracked. A frontend # server in the DMZ proxies HTTP to a web access client in # the backend. This puts one more HTTP stack in front of a # potential attacker to deal with. # # The groupware LANs in each branch office are fairly # dependent on communication with one another. For # debugging purposes all traffic is being allowed right now. # # XXX TODO restrict to only what's required # $_ipt -A to_groupware -s $ADMIN_INTRANET -j accept $_ipt -A to_groupware -s $THISBRANCH_INTRANET -j accept $_ipt -A to_groupware -s $CORPABC_INTRANET -j accept $_ipt -A to_groupware -s $CORPDEF_INTRANET -j accept $_ipt -A to_groupware -s $BRANCHXYZ_INTRANET -j accept # webmail from Internet to sfo-based users iptatcp `gip sfo_mail` https ############################################################################## CHAIN=to_userlan ############################################################################## # # The user LAN also is home to servers at the present time. # Some services in the user LAN are available externally via # firewall holes on the corresponding service port. A # separate server network is planned but resources are not # available to implement this. In the meantime there are a # lot of holes in this chain. # Let admin network (which is me over VPN :) come in # unrestricted. Note that this adds another border to the # company LANs which must be protected with the same # diligence as the corporate network itself. $_ipt -A to_userlan -s $ADMIN_INTRANET -j accept # Since there are so many holes for the offshore developers, # we move them to their own chain; this way there's not so # many match clauses that have to be given for every rule. # Note that they typically have the same rule specified for # sources in both their "dmz" and their "client lan" and in # any case we may have to accept from either on the abc # chain, so we need to match on both of their networks. for sourcenet in \ $CORPABC_INTRANET \ $CORPDEF_INTRANET do $_ipt -A to_userlan \ -s $sourcenet \ -m state --state NEW \ -j userlan_from_abc done # demo network needs LDAP records because some machines # there have our software configured to do LDAP auth and our # test DIT for the application is in the company-wide DIT iptatcp `gip sfo_ldap` ldaps $SALESDEMO_NET # logins to the support portal auth against DNs with # userType=customer in ou=people iptatcp `gip sfo_ldap` ldaps `gep support` # also for a test of db cluster RAC node operational I need # to get at the colo iptatcp `gip sfo_app_sol9_db0` 1521 `gep zxx_db2` iptatcp `gip sfo_app_sol9_db1` 1521 `gep zxx_db2` # let customer B demo use internal SQL instance for its data iptatcp `gip sfo_app_db0` ms-sql-s `gep demo_custb` # internal .web hosts get HTTP and HTTPS, although HTTP is # just a jump page for webhost in \ mail \ directory \ info do iptatcp `gip web_$webhost` https done # MX hosts (which run on the name servers) can relay to # either branch SMTP hub. allow unencrypted connections for # debugging; we control whether it's used at any given time. iptatcp `gip sfo_smtp` smtp `gep dmz_ns1` iptatcp `gip sfo_smtp` smtps `gep dmz_ns1` iptatcp `gip sfo_smtp` smtp `gep dmz_ns2` iptatcp `gip sfo_smtp` smtps `gep dmz_ns2` # NTP stratum 3 hosts to internal stratum 4 distribution nodes # XXX TODO conntrack doesn't seem to work for UDP NTP, see # comments elsewhere in script iptaboth `gip sfo_mailsrv` ntp `gep dmz_ns1` iptaboth `gip sfo_mailsrv` ntp `gep dmz_ns2` ############################################################################## CHAIN=to_userlan_from_abc ############################################################################## # # This chain is arrived at if source is any of the two # offshore devel nets. Initially protocol is not matched # but we RETURN early in this chain (after inserting rules # for all non-TCP matches we want to deal with) if the # packet isn't TCP. There's so many holes for these guys it # makes it a lot simpler to have a special subchain without # having to have so many matches in each individual rule # when we're just on the to_intranet chain. # # Offshore developers coming in over VPN are using our DNS. iptaboth `gip sfo_mailsrv` domain # We sent them one of our IP phones so we could have free # calls to India over our VPN...this actually worked before # they changed ISPs and we ended up having totally # asymmetric routes 800ms one way and 250ms the other. iptaudp `gip sfo_phone` 2093:2096 iptatcp `gip sfo_phone` 1040:1044 iptatcp `gip sfo_phone` http iptatcp `gip sfo_phone` imap # The phones establish direct connections once the PBX sets # the call up. The phones get addresses from our DHCP # server in pools specifically for phones, based on their # MAC. The below allows the ABC nets to access these ranges # (which are discontiguous) on the UDP ports used by the # system to implement point to point calls. $_ipt -A userlan_fromabc \ -p udp --dport 2093:2096 \ -m iprange --dst-range 10.60.7.2-10.60.7.100 \ --dst-range 10.60.9.137-10.60.9.222 \ -j accept # Ensure all further matches on this chain are TCP # protocols. Nothing else should be coming from them on this # chain so we already know this packet should not be # forwarded. Therefore we drop now rather than RETURN. $_ipt -A userlan_fromabc -p ! tcp -j drop # staging host which runs the current devel build. iptatcp `gip sfo_app_staging` http iptatcp `gip sfo_app_staging` https # they of course source code repository access iptatcp `gip sfo_file0` cvspserver # login to build servers to test fresh builds for QA purposes iptatcp `gip sfo_aix43` ssh iptatcp `gip sfo_aix51` ssh iptatcp `gip sfo_aix51` telnet # ick iptatcp `gip sfo_aix51` 5901 # ick iptatcp `gip sfo_app_redhat71` accept # wide open iptatcp `gip sfo_app_redhat9` accept # by decree # access developer test machine iptatcp `gip sfo_persona` ssh iptatcp `gip sfo_persona` sftp iptatcp `gip sfo_persona` 3000 # access any of our databases heh nice $_ipt -A abc --dport 1521 -j accept # this host apparently needs to have unrestricted access! $_ipt -A abc -d `gip sfo_app_db1` -j accept # haha just kidding... #$_ipt -A abc -j accept # instance of version 1.2.3 of the app iptatcp `gip sfo_ddns_app_ps_app48` 3389 iptatcp `gip sfo_ddns_app_ps_app48` 9001 iptatcp `gip sfo_ddns_app_ps_app48` 9003 iptatcp `gip sfo_ddns_app_ps_app48` 9005 iptatcp `gip sfo_ddns_app_ps_app48` 9010 # one developer's Project server iptatcp `gip sfo_ddns_win2k3_test` https iptatcp `gip sfo_ddns_win2k3_test` microsoft-ds # offshore team tracks hours spent on customer projects, # which ultimate is billed to the customer iptatcp `gip web_timesheet` https # Access Solaris 9 QA machines unrestricted # this loop makes me feel all warm and fuzzy inside. It's # so great to just leave your machines wide open. Everyone # should try it!! # for host in \ sfo_app_sol9_web_pa \ sfo_app_sol9_web_pb \ sfo_app_sol9_web \ sfo_app_sol9_db \ sfo_app_sol9_db0 \ sfo_app_sol9_db1 \ sfo_app_sol9_app \ sfo_app_sol9_analytics \ sfo_app_sol8_web_pa \ sfo_app_sol8_web_pb \ sfo_app_sol8_web \ sfo_app_sol8_db \ sfo_app_sol8_app \ sfo_app_sol8_app2 \ sfo_qa_sol9_analytics \ sfo_qa_sol9_app0 \ sfo_qa_sol9_app1 \ sfo_hpux1 \ sfo_hpux1_pa \ sfo_hpux1_pb \ sfo_hpux2 do $_ipt \ -A to_userlan \ -d `gip $host` \ -j accept done # external IMAP and POP service iptatcp `gip sfo_imap` imap iptatcp `gip sfo_imap` imaps # Offshore guys use us for relay of their FictCompany mail. # They should, but don't always configure TLS/SSL for SMTP. # Again the offshore teams has a heavenly mandate to ignore # common sense. iptatcp `gip sfo_smtp` smtp iptatcp `gip sfo_smtp` smtps # offshore devel team needs access to more instances of the # application since they can't afford UNIX machines even # though they're one of the biggest companies in all of India iptatcp `gip sfo_app_db0` ms-sql-s iptatcp `gip sfo_app_apps` 3389 iptatcp `gip sfo_app_apps` http iptatcp `gip sfo_app_apps` https iptatcp `gip sfo_app_apps` 9053 iptatcp `gip sfo_app_apps` 9999 iptatcp `gip sfo_app_apps0` 3389 iptatcp `gip sfo_app_apps0` http iptatcp `gip sfo_app_apps0` https iptatcp `gip sfo_app_apps0` 9053 # offshore team needs access to SMB file services in this branch iptatcp `gip sfo_file0` netbios-ns iptatcp `gip sfo_file0` netbios-dgm iptatcp `gip sfo_file0` netbios-ssn iptatcp `gip sfo_file0` netbios-ns iptatcp `gip sfo_file0` netbios-dgm iptatcp `gip sfo_file0` netbios-ssn # also needs access in to LDAP server order to do some # testing of our app's LDAP auth code; I guess they can't # be bothered to set one up themselves iptatcp `gip sfo_ldap` ldap iptatcp `gip sfo_ldap` ldaps # offshore team also needs access to file server via ssh but # it's further restricted by groups in sshd_config; we can # deny them login by making the system use ldap as a source # for groups data and then making sure they are not in the # specified group (perhaps users?) iptatcp `gip sfo_file0` ssh # defect tracking used by our software development team iptatcp `gip web_bug` https # QA tool to document test procedures and gather metrics iptatcp `gip web_test` https # Offshore team apparently does not have printers either. # They are working on an SNMP module for the software and # need to print and test SNMP interface. So we have to open # up the printers in this regard. iptaboth `gip sfo_printer1` snmp iptaboth `gip sfo_printer2` snmp iptaboth `gip sfo_printer3` snmp iptatcp `gip sfo_printer3` jetdirect ############################################################################## # END-OF-CHAIN RULES ############################################################################## $_ipt -A accept -j ACCEPT $_ipt -A drop -j DROP add_log_rule reject_horiz rejecting $_ipt -A reject_horiz -j REJECT $REJECT_HORIZ # Now we install defaults to drop, on all chains. Be # certain that if this is a chain that should *not* end in a # drop (i.e. "accept" chain), the terminating rule is # already installed. That means any subsequent rules (like # these drops below) will never actually execute. We are # covered in two ways: default policy of chains are already # set to DROP. Since that won't log we prefer a jump to the # user "drop" chain, which logs and *then* DROPs. Setting # all drops in the end in one location and at once removes # the requirement for each chain to do a drop at the end of # its rules installations. for chain in ${all_chains[@]}; do $_ipt -A $chain -j drop done ############################################################################## # START ROUTING ############################################################################## # When sysctl -p was run by the boot scripts, some of the # interfaces may not yet have been up...let's do it again # now that we are sure they are up: most important are proxy # arp and rp_filter. Without the former there is no one # that claims to be the hardware address for translated IPs. # Without the later, we are vulnerable to forged packet # sources. echo 'sysctl -p &>/dev/null' # Now that everything is configured, let's reverse the very # first thing we did when entering the script: tell the # kernel that it can route packets again. echo 'echo 1 > /proc/sys/net/ipv4/ip_forward'