#!/bin/bash # # Jan K Labanowski, jkl@ccl.net, Nov, 2001 Thu Aug 30 23:17:39 EDT 2001 # Last modified: Wed Dec 19 02:50:04 EST 2001 # Last modified: Tue May 28 18:42:55 EDT 2002 # # Please help me to maintain the script. If you see something wrong, bad # or stupid, just tell me to fix it. Firewalls are not my specialty, what # the true gurus can see. Reward me for my humility... . # # This "yet another firewal script" (YAFS) makes the assumption which I have # at home. It uses kernel 2.4.7 which came with RH7.2. The # iptables --version # reports iptables v1.2.3 # # This assumes that you have a few static addresses (e.g., provided by # your ISP with your DSL service) and you want to setup your own servers # etc. This approach is suitable if you have A FEW addresses. It can be # reworked to do ranges quite easily, but as it is now, it does not # use ranges, subnetting, etc. It also assimes that you run RedHat 7.2 # with latest kernel and iptables updates (it will not run without updates, # since there is a bug in uptables 1.2.3 with log levels). The original # 7.1 RH may run, but I did not try it. Next, you need to disable the # iptables and ipchains startup scripts which came with the system (or # when you installed firewall on your system. You do that: # cd /etc/rc.d # find . -name '[KS]*iptables' -print # find . -name '[KS]*ipchains' -print # You get read of them as: # cd /etc/rc.d # find . -name '[KS]*iptables' -exec rm {} \; # find . -name '[KS]*ichains' -exec rm {} \; # # (of course, you can try: # chkconfig --del iptables # chkconfig --del ichains # assuming that links were placed by chkconfig --add and scripts were not # modified). # # # This setip assumes that you have 3 ethernet cards. # There are tons of confusing and often outdated notes on the Web about # "Multiple Ethernet Cards". They often assume that you have the drivers # compiled into the kernel. Read them, but remember that modern Linuces come # with drivers as kernel modules, so all this stuff about lilo.conf and # append is no longer valid. The way I do it is to have 3 cards which use # different drivers (go to Hardware Compatibility List on RedHat site # and see which cards use which drivers). In this case I was using # the 3Com ISA 3c509 (module: 3c509), the 3Com PCI 3c900 Boomerang (module # 3c59x), and Linksys NC100 (module tulip). After you install cards and # reboot the machine, Linux kudzu will usually find them and ask you to # configure the cards. Do it, but you will most likely have to correct the # default settings. Also, you may need to add the parameters if the cards # do not want to initialize with right interrupts numbers and IO addresses. # You may also easily run out of iterrupts when you have other things # installed on your system (like sound cards), or you have other things # build into your motherboard. In this case, you may need to run BIOS Setup # and disble serial ports or parallel port on your mother board, and play # with PCI/PnP setting on your slots, until you get it right. Try also to # change order in which modules are listed/loaded in /etc/modules.conf file. # The relevant piece /etc/modules.conf file is: # # alias eth2 3c59x # alias eth1 tulip # alias eth0 3c509 irq=5 # # I had to put them in reverse order, and "suggest" interrupt 5 for the # Etherlink III card, since they were getting conflict initially. Before # you give parameters, print the interrupts by: # # cat /proc/interrupts # # You can also list the IO port number which are taken by: # # cat /proc/ioports # # and if your card allows setting the ports, choose something which is not # already taken and specify it with io=xxxx parameter in /etc/modules.conf. # Note that the slowest card in my case (ISA 3c509) is used to talk to # external Internet (eth0), since the DSL is far from saturating the bandwidth # of the 10BT card. On the inside, I am using fast cards on my LAN and DMZ # side, since here I can use their speed for backup or copying files # back and forth, printer support, etc. # # # My physical arrangement is similar to the following: # # [Internet/DSL]--eth0(3c509-ISA-10BT)::[Firewall/NAT]::eth1[Linksys/NC100] # [5 static IPs] .. [PCI-10/100BT] # eth2[3com900] | 192.168.0.1 # / 192.168.1.1 | # / | # / | # / | # / | # / | # / LAN | # / uplink # / ------------ # / | EtherHub | # DMZ uplink | |--HOST1 # ------------ | | # | EtherHub | | |--HOST2 # DNS1-HOST(eth0)-----| | . . # 192.168.1.2 | | . . # | | | |--HOSTn # DNS2-HOST(eth0)-----| | ------------ # 192.168.1.3 | | # /----------\ # / | \ # eth0 | eth0 # WEB-SERVER-1 | WEB-SERVER-2 # 192.168.1.4 | 192.168.1.5 # | # eth0 # WEB-SERVER-3 # 192.168.1.6 # I have 5 static IP addresses and I want to do the following with them: # a) do IP masquerading to provide access to Internet for the local subnet # 192.168.0.0 (LAN). Connection to the computers on the LAN cannot be # initiated from the outside, but can be initiated from the inside, # i.e., when the computer on the LAN subnet wants to connect to some # Web Server. The source address of the LAN computer in the packet # will be changed to the address of the Firewall and Firewall (based # on packet serial number) will direct the responses rom the outside # net back to the appropriate computer on the LAN. While you can # ssh from the LAN computer to some computer on the Internet, no computer # will be able to ssh directly to LAN computers. To do this, one needs to # first log into the Firewall or one of the DMZ computers if they allow # logging in. # NOTE: by experiment I found that you cannot access the # DMZ computers from within DMZ using their "external" addresses. # In other words, you cannot use the same URL for the web page # as you would use outside. It may be a bug, or a feature, but the DNAT # and SNAT do not work for me together on the same interface (in my case # the eth2). Somehow, the netfilter gets confused. But you are perfectly # OK to access the DMZ computers from your LAN subnet with their # "external" IPs and ports. Do not ask me why... DNAT and SNAT should # work in principle on the same interface, but they do not work for me. # # # b) keep Web servers, DNSses, FTP servers, etc. in another local subnet # 192.168.1.0 (DMZ). Here I can keep servers for up to 5 domains # which represent 5 static IP addresses which I got assigned to my # DSL service. The services to be redirected to them will be # Port 20: ftp-data, Port 21: ftp, Port 25: smpt (e-mail), # Port 53: domain (DNS server), Port 80: http (Web Server), # Port 443: https (Secure Web Server). Nobody will be able to # log into them directly, only via Firewall, or from inside (LAN), # # Now, back to configuration. I did initially the default configuration # for the default /etc/rc.d/init.d/network script of Red Hat Linux. # I actually sugest you do it to, so you can test your network conncetivity. # My speakeasy.net Gateway was: # 215.236.123.1 # My DNS addresses were: 216.254.95.2 and 216.231.41.2 # I named my DMZ domain as it would be named for the Web or mail, or whatever # access on external Internet. Say, it was mymymy.com. You can name your # LAN domain whatever you want. If you can make the host names different # among ALL MACHINES (I mean, firewall, DMZ, and LAN), I would use the same # domain as I use for external hosts. # # On the firewall I assigned IP addresses to all interfaces (i.e., NIC cards) # as follows. For the eth0 interface, I used the the out-of-order address # which I was given by ISP (they usually give you a range of consecutive # addresses and one odd address, but it varies, and is probably random). # Say, I chose my address as: 215.236.123.210 for the eth0 facing the WORLD. # # On each of my computers in DMZ and LAN (and firewall), I have an /etc/hosts # file ( C:\WINNT\SYSTEM32\DRIVERS\ETC\HOSTS on Windoz2000 and C:\WINDOWS\HOSTS # on Windoz9x ) which lists all computer names and domains with their real, # actual, internal, IP addresses. I want them to talk to each other with their # real IP addresses on internal wires. Note, these computers (especially the # firewall and the computers in DMZ), will have different addresses in the # DNS server -- there they have their external, "official", Internet addresses. # Since the /etc/hosts file is by default searched first (you can make sure # by having a file /etc/hosts.conf with lines cat > /dev/null < /dev/null < /dev/null < /dev/null < /dev/null < /dev/null < /dev/null < /dev/null </dev/null <ToPort". All REALATED and # ESTABLISHED packets initiated by the connection to these ports will be # directed to the corresponding DMZ machines. # I am considering adding ranges syntax here, but I need to get it out fast. # So no 20:25,30:50->2020-46 yet, maybe later. # # The popular ports are: # FTP -- 20, 21 # SSH -- 22, # TELNET -- 23 # E-MAIL -- 25 # WHOIS -- 43 # DNS -- 53 # GOPHER -- 70 # FINGER -- 79 # HTTP -- 80 # KERBEROS -- 88 # POP3 -- 110 # IDENT/AUTH -- 113 # NNTP -- 119 # NTP -- 123 # IMAP -- 143 # WAIS -- 210 # HTTPS -- 443 # X-WINDOW -- 6000:6063 # Also note that the above are "official" port numbers, but you can run stuff # on other ports. I often saw people running SSHD on port 2222 (they had # to change the sshd_config file). This actually allows you to do the trick. # You can have the sshd running on DMZ machines on the different port than # on the firewall, and when you use ssh without port number, you log in to # firewall, and when you use it with a port number of sshd of the DMZ host # (e.g., like: ssh -p 2222 ...) you can login directly to DMZ host from # outside (you better have a password which cracker will not crack, though). #faminor.edu (215.236.123.103) INET_TO_DMZ[0]="215.236.123.103->192.168.1.129" # Hx DNAT_TCP_PORTS[0]="20->20 21->21 22->22 25->25 80->9083 9083->9083" DNAT_UDP_PORTS[0]="" #basement.edu (215.236.123.104) INET_TO_DMZ[1]="215.236.123.104->192.168.1.129" # Hx DNAT_TCP_PORTS[1]="20->20 21->21 22->22 25->25 80->9084 9084->9084" DNAT_UDP_PORTS[1]="" #server1.cclun.edu (215.236.123.129) INET_TO_DMZ[2]="215.236.123.129->192.168.1.129" # D3 DNAT_TCP_PORTS[2]="20->20 21->21 22->22 25->25 53->53 80->9081 9081->9081 443->9443 9443->9443" DNAT_UDP_PORTS[2]="53->53" #mail1.usdne.edu (215.236.123.130) INET_TO_DMZ[3]="215.236.123.130->192.168.1.129" # 03 DNAT_TCP_PORTS[3]="20->20 21->21 22->22 25->25 80->9080 9080->9080 8080->7080 7080->7080" DNAT_UDP_PORTS[3]="" #chumisty.edu (215.236.123.131) INET_TO_DMZ[4]="215.236.123.131->192.168.1.129" # 01 DNAT_TCP_PORTS[4]="20->20 21->21 22->22 25->25 53->53 80->9082 9082->9082" DNAT_UDP_PORTS[4]="53->53" #search1.cclun.edu (215.236.123.132) INET_TO_DMZ[5]="215.236.123.132->192.168.1.132" # H1 DNAT_TCP_PORTS[5]="20->20 21->21 22->22 25->25 53->53 443->8443 8443->8443 7080->7080 8080->8080 9081->9081 9082->9082 9083->9083 9084->9084 9443->8443" DNAT_UDP_PORTS[5]="53->53" #bubulka.org (215.236.123.210) INET_TO_DMZ[6]="215.236.123.210->192.168.1.210" # D3 DNAT_TCP_PORTS[6]="22->22 25->25 53->53" DNAT_UDP_PORTS[6]="53->53" LOOPBACK=127.0.0.0/8 # Idea taken from James C. Stephens # http://www.cs.princeton.edu/~jns/security/iptables IPTABLES=/sbin/iptables # this is where I have my iptables MY_IP_BCAST="" # put here broadcast address for your subnet # for example: MY_IP_BCAST="215.236.123.255" # find my BROADCAST address (X.X.X.255) if the script was not updated above if [ "${MY_IP_BCAST}x" = "x" ]; then MY_IP_BCAST=`echo $MY_IP_ADDRESS | /bin/sed -e 's/\.[0-9]*$/.255/'` fi # find RPC UDP ports RPC_UDP_PORTS=`/usr/sbin/rpcinfo -p localhost | grep udp | awk '{print $4}'` # find RPC TCP ports RPC_TCP_PORTS=`/usr/sbin/rpcinfo -p localhost | grep tcp | awk '{print $4}'` # Mark in the /var/log/messages/notice that we are starting the firewall /usr/bin/logger -p kern.notice -t NETFILTER \ "====== Netfilter Start: $DATE =========" # check if needed iptables modules are loaded /sbin/modprobe ip_tables /sbin/modprobe ip_queue /sbin/modprobe iptable_filter /sbin/modprobe iptable_mangle /sbin/modprobe iptable_nat /sbin/modprobe ipt_limit /sbin/modprobe ipt_LOG /sbin/modprobe ipt_MASQUERADE /sbin/modprobe ipt_MIRROR /sbin/modprobe ipt_multiport /sbin/modprobe ipt_REDIRECT /sbin/modprobe ipt_REJECT /sbin/modprobe ipt_state /sbin/modprobe ipt_unclean /sbin/modprobe ip_nat_ftp /sbin/modprobe ip_conntrack /sbin/modprobe ip_conntrack_ftp # do not accept source routed packets /bin/echo "0" > /proc/sys/net/ipv4/conf/all/accept_source_route # do not accept ICMP redirects /bin/echo "0" > /proc/sys/net/ipv4/conf/all/accept_redirects $IPTABLES -t nat -P PREROUTING ACCEPT $IPTABLES -t nat -P POSTROUTING ACCEPT $IPTABLES -t filter -P INPUT DROP $IPTABLES -t filter -P OUTPUT DROP $IPTABLES -t filter -P FORWARD DROP # Let us define some "routines" which we will "call" in other chains # Some of those is to drop stuff, while at the same time I set my polices # to drop, so it is redundant. In theory you could have only -j ACCEPT # as your actions, but it is easier to DROP junk as soon as possible, so # you can assume when you do ACCEPT stuff that the bad things are already # in the trash... # # [[======= DropSpoof user defined chain ====]] # # DropSpoof -- drops nonroutable source addresses and our addresses # if they came on $INET_IFACE. We will call them in INPUT # and FORWARD chain # $IPTABLES -N DropSpoof # $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 192.168.0.0/16 -j LOG \ # --log-level notice --log-prefix "192.168.x.x on eth0: " $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 192.168.0.0/16 -j DROP # $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 10.0.0.0/8 -j LOG \ # --log-level notice --log-prefix "10.x.x.x on eth0: " $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 10.0.0.0/8 -j DROP # $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 172.16.0.0/12 -j LOG \ # --log-level notice --log-prefix "10.x.x.x on eth0: " $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 172.16.0.0/12 -j DROP # $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 224.0.0.0/4 -j LOG \ # --log-level notice --log-prefix "224.0.0.0 on eth0: " $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 224.0.0.0/4 -j DROP # $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 240.0.0.0/5 -j LOG \ # --log-level notice --log-prefix "240.0.0.0 on eth0: " $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s 240.0.0.0/5 -j DROP # changed on Tue May 28 18:42:55 EDT 2002 on advice from # Pedro Rodrigues -- Thanks Pedro... # $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s $LOOPBACK -j LOG \ # --log-level notice --log-prefix "loopback eth0: " #replaced $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -d $LOOPBACK -j DROP $IPTABLES -t filter -A DropSpoof -i $INET_IFACE -s $LOOPBACK -j DROP # do not allow spoofing packets with your source address on the INET_IFACE # The packets can only have our address as destination address but not source to='*->'; from='->*'; let count=0 while (( $count < ${#INET_TO_DMZ[*]} )) do addresses=${INET_TO_DMZ[count]}; inet_address=${addresses%$from}; # extract the stuff before -> # changed on Tue May 28 18:42:55 EDT 2002 # $IPTABLES -t filter -A DropSpoof -p ! icmp -i $INET_IFACE -s \ # ${inet_address} -j LOG --log-level notice # --log-prefix "MY_IP on ${INET_IFACE}: " $IPTABLES -t filter -A DropSpoof -p ! icmp -s ${inet_address} \ -j LOG --log-level notice \ --log-prefix "MY_IP in source" #changed on Tue May 28 18:42:55 EDT 2002 $IPTABLES -t filter -A DropSpoof -p ! icmp -s ${inet_address} -j DROP # $IPTABLES -t filter -A DropSpoof -p ! icmp -i $INET_IFACE \ # -s ${inet_address} -j DROP let count="count + 1" done # =============== End of DropSpoof ====================== # Kill PortScans (the badly formatted packets # [[======= KillPortScans user defined chain ====]] $IPTABLES -N KillPortScans # REJECT ident requests... # $IPTABLES -t filter -A KillPortScans -p tcp --dport 113 \ # -j LOG --log-level notice --log-prefix "IDENT1 ACPT : " $IPTABLES -t filter -A KillPortScans -p tcp --dport 113 \ -j REJECT --reject-with tcp-reset # reject port scans coming from $INET_IFACE # $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ # --tcp-flags ALL FIN,URG,PSH \ # -j LOG --log-level notice --log-prefix "FUP to eth0: " $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ --tcp-flags ALL FIN,URG,PSH -j DROP # # $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ # --tcp-flags ALL FIN -j LOG --log-level notice --log-prefix "F to eth0: " $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ --tcp-flags ALL FIN -j DROP # $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ # --tcp-flags ALL NONE \ # -j LOG --log-level notice --log-prefix "NONE to eth0: " $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ --tcp-flags ALL NONE -j DROP # # $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ # --tcp-flags SYN,FIN SYN,FIN \ # -j LOG --log-level notice --log-prefix "SF to eth0: " $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ --tcp-flags SYN,FIN SYN,FIN -j DROP # # $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ # --tcp-flags SYN,RST SYN,RST \ # -j LOG --log-level notice --log-prefix "SR to eth0: " $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ --tcp-flags SYN,RST SYN,RST -j DROP # block RPC ports from INET_IFACE for TCP_PORT in $RPC_TCP_PORTS; do $IPTABLES -t filter -A KillPortScans -p tcp -i $INET_IFACE \ --dport $TCP_PORT -j DROP done # block RPC ports from INET_IFACE for UDP_PORT in $RPC_UDP_PORTS; do $IPTABLES -t filter -A KillPortScans -p udp -i $INET_IFACE \ --dport $UDP_PORT -j DROP done # ========================= End of KillPortScans ================= # These rules will be called in INPUT and FORWARD chain # [[======= FilterICMP user defined chain ====]] $IPTABLES -N FilterICMP $IPTABLES -t filter -A FilterICMP -p icmp -i $LAN_IFACE -s $MY_IP_ADDRESS \ -j ACCEPT $IPTABLES -t filter -A FilterICMP -p icmp -i $DMZ_IFACE -s $MY_IP_ADDRESS \ -j ACCEPT # $IPTABLES -t filter -A FilterICMP -p icmp -i $DMZ_IFACE -j ACCEPT # The -m limit option slows things down. Once I put something more robust # for a firewall, I am commenting the ACCEPT/limit stuff # You may comment the ACCEPT ones with -m limit, if your firewall is slow $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type echo-reply -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type destination-unreachable -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type network-unreachable -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type host-unreachable -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type protocol-unreachable -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type port-unreachable -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type fragmentation-needed -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type source-route-failed -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type network-unknown -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type host-unknown -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type network-prohibited -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type host-prohibited -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type TOS-network-unreachable -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type TOS-host-unreachable -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type communication-prohibited -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type host-precedence-violation -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type precedence-cutoff -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type source-quench -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type redirect -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type network-redirect -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type host-redirect -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type TOS-network-redirect -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type TOS-host-redirect -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type echo-request -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type router-advertisement -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type router-solicitation -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type time-exceeded -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type ttl-zero-during-transit -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type ttl-zero-during-reassembly -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type parameter-problem -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type ip-header-bad -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type required-option-missing -m limit --limit 1/s -j ACCEPT $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type timestamp-request -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type timestamp-reply -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type address-mask-request -j DROP $IPTABLES -t filter -A FilterICMP -i $INET_IFACE -p icmp \ --icmp-type address-mask-reply -j DROP # accept the others (though there should be no left, I hope...) $IPTABLES -t filter -A FilterICMP -p icmp -j ACCEPT # =============== End of FilterICMP ========================= # [[======= BlockPortFromOutside user defined chain ====]] $IPTABLES -N BlockPortFromOutside # block syslog, lpr, rsh, rexec $IPTABLES -t filter -A BlockPortFromOutside -i $INET_IFACE -p udp \ --dport 514 -j DROP $IPTABLES -t filter -A BlockPortFromOutside -i $INET_IFACE -p tcp \ --dport 514 -j DROP $IPTABLES -t filter -A BlockPortFromOutside -i $INET_IFACE -p tcp \ --dport 515 -j DROP $IPTABLES -t filter -A BlockPortFromOutside -i $INET_IFACE -p tcp \ --dport 512 -j DROP # =============== End BlockPortFromOutside ================= # These rules will be called in OUTPUT and FORWARD chain # [[======= DoNotSendOutside user defined chain ====]] $IPTABLES -N DoNotSendOutside # block all NetBios (Samba) stuff coming and going to INET_IFACE # $IPTABLES -t filter -A DoNotSendOutside -p udp -o $INET_IFACE \ # --sport 137:139 -j LOG --log-level notice --log-prefix "SMB to eth0: " $IPTABLES -t filter -A DoNotSendOutside -p udp -o $INET_IFACE \ --sport 137:139 -j DROP # protect NFS through the firewall # $IPTABLES -t filter -A DoNotSendOutside -p udp -o $INET_IFACE --sport 635 \ # -j LOG --log-level notice --log-prefix "UDP 635 to eth0: " $IPTABLES -t filter -A DoNotSendOutside -p udp -o $INET_IFACE \ --sport 635 -j DROP # $IPTABLES -t filter -A DoNotSendOutside -p udp -o $INET_IFACE --sport 2049 \ # -j LOG --log-level notice --log-prefix "UDP 2049 to eth0: " $IPTABLES -t filter -A DoNotSendOutside -p udp -o $INET_IFACE \ --sport 2049 -j DROP # $IPTABLES -t filter -A DoNotSendOutside -p udp -o $INET_IFACE --sport 111 \ # -j LOG --log-level notice --log-prefix "UDP 111 to eth0: " $IPTABLES -t filter -A DoNotSendOutside -p udp -o $INET_IFACE \ --sport 111 -j DROP # block all NetBios (Samba) stuff coming and going to INET_IFACE # $IPTABLES -t filter -A DoNotSendOutside -p tcp -o $INET_IFACE \ # --sport 137:139 -j LOG --log-level notice --log-prefix "SMB to eth0: " $IPTABLES -t filter -A DoNotSendOutside -p tcp -o $INET_IFACE \ --sport 137:139 -j DROP # protect NFS through the firewall # $IPTABLES -t filter -A DoNotSendOutside -p tcp -o $INET_IFACE --sport 635 \ # -j LOG --log-level notice --log-prefix "635 to eth0: " $IPTABLES -t filter -A DoNotSendOutside -p tcp -o $INET_IFACE \ --sport 635 -j DROP # $IPTABLES -t filter -A DoNotSendOutside -p tcp -o $INET_IFACE --sport 2049 \ # -j LOG --log-level notice --log-prefix "2049 to eth0: " $IPTABLES -t filter -A DoNotSendOutside -p tcp -o $INET_IFACE \ --sport 2049 -j DROP # $IPTABLES -t filter -A DoNotSendOutside -p tcp -o $INET_IFACE --sport 111 \ # -j LOG --log-level notice --log-prefix "111 to eth0: " $IPTABLES -t filter -A DoNotSendOutside -p tcp -o $INET_IFACE \ --sport 111 -j DROP # =============== End DoNotSendOutside ================= #================================================================== #[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] #================================================================== # # This is where we start filtering (the stuff above is "subroutines"... # While it is irrelevant which chain I start to shake first, I will try to # follow the "abstract" route of a packed in netfilter # #[][][][][][][][][] PREROUTING [][][][][][][][][][][][][][][][][][] # # []*** PREROUTING ***[] # Packet just entered the interface (ANY INTERFACE, i.e., eth0, eth1, or eth2) # # # [[======= Rules for UDP protocol in PREROUTING chain ====]] # perform DNAT for UDP addresses/ports to='*->'; from='->*'; let count=0 while (( $count < ${#INET_TO_DMZ[*]} )) do addresses=${INET_TO_DMZ[count]}; inet_address=${addresses%$from}; dmz_address=${addresses#$to}; for ports in ${DNAT_UDP_PORTS[count]} do inet_port=${ports%$from}; dmz_port=${ports#$to}; # $IPTABLES -t nat -A PREROUTING -p udp -i $INET_IFACE \ # -d ${inet_address} --dport ${inet_port} -j DNAT \ # --to ${dmz_address}:${dmz_port} $IPTABLES -t nat -A PREROUTING -p udp \ -d ${inet_address} --dport ${inet_port} -j DNAT \ --to ${dmz_address}:${dmz_port} done let count="count + 1" done # [[======= Rules for TCP protocol in PREROUTING chain ====]] # perform DNAT on TCP addresses/ports to='*->'; from='->*'; let count=0 while (( $count < ${#INET_TO_DMZ[*]} )) do addresses=${INET_TO_DMZ[count]}; inet_address=${addresses%$from}; dmz_address=${addresses#$to}; for ports in ${DNAT_TCP_PORTS[count]} do inet_port=${ports%$from}; dmz_port=${ports#$to}; # $IPTABLES -t nat -A PREROUTING -p tcp -i $INET_IFACE \ # -d $inet_address --dport ${inet_port} # --log-level notice --log-prefix "PRE port ${INET_IFACE}:${dmz_port}: " # $IPTABLES -t nat -A PREROUTING -p tcp -i $INET_IFACE \ # -d ${inet_address} --dport ${inet_port} -j DNAT \ # --to ${dmz_address}:${dmz_port} $IPTABLES -t nat -A PREROUTING -p tcp \ -d ${inet_address} --dport ${inet_port} -j DNAT \ --to ${dmz_address}:${dmz_port} done let count="count + 1" done #[][][][][][][][][] INPUT [][][][][][][][][][][][][][][][][][] # # []*** INPUT ***[] # Packet is destined FOR THE FIREWALL ITSELF. # # Reject spoof $IPTABLES -t filter -A INPUT -j DropSpoof # Kill port scans $IPTABLES -t filter -A INPUT -j KillPortScans # Filter ICMP $IPTABLES -t filter -A INPUT -j FilterICMP # Block certain ports from outside $IPTABLES -t filter -A INPUT -j BlockPortFromOutside # Allow SSH to Firewall # This strange looking port is the SSHD port which I will be using # on the firewall rather than the 22 port. I changed port 22 to 2222 in # /etc/ssh/sshd_config and restarted sshd. Note that Port 22 will be # forwarded to the DMZ zone (so I can log in directly to my DMZ machines). # There is one problem though if you are logging with ssh to FW and to the DMZ # machine which has matching address. Since on your local machine the file # $HOME/.ssh/known_hosts and/or known_hosts2 are created the ssh will not # allow you to connect to the "same" machine with different fingerprint # (for ssh it is the same machine, since it does not mark entries known_hosts # with the port number). You can cheat in a number of ways: # 1) by deleting (or moving it to some file temporarily) known_hosts # in $HOME/.ssh/known_host* before using ssh to FW # 2) By using different ssh protocol (so in one case, the known_hosts # is used, and in the other case, the known_hosts2 is used). For example # ssh -2 -l root -p 22 my.machine.com # ssh -1 -l root -p 2222 my.machine.com # 3) You can change the /etc/ssh/ss_config file for ssh on your machine # and set: StrictHostKeyChecking no # 4) You can use: # ssh -2 -l root -p 22 -o 'StrictHostKeyChecking no' 215.236.123.129 # One of them may work for you. # # $IPTABLES -t filter -A INPUT -p tcp --syn --dport 2222 -m state \ --state NEW -j LOG --log-prefix "New SSH on FW:" $IPTABLES -t filter -A INPUT -p tcp --syn --dport 2222 -m state \ --state NEW -j ACCEPT # Allow NEW connections to anything on LAN and DMZ # This assumes that you TRUST your LAN and DMZ. If you do not, you need # to modify this... But then, you may not be able to log into your FW # beside on its own keyboard... # I probably should be paranoid, and think "What if someone breaks to my # firewall? But then, if they break into, they will correct this table # the way they want, so why bother... # $IPTABLES -t filter -A INPUT -i $LAN_IFACE -m state --state NEW \ -j ACCEPT $IPTABLES -t filter -A INPUT -i $DMZ_IFACE -m state --state NEW \ -j ACCEPT $IPTABLES -t filter -A INPUT -m state --state ESTABLISHED,RELATED \ -j ACCEPT #[][][][][][][][][] FORWARD [][][][][][][][][][][][][][][][][][] # # []*** FORWARD ***[] # Packets that are going LAN/DMZ <=> INET, and LAN <=> DMZ. These # packets do not originate or end on the FW itself. # # # Reject spoof $IPTABLES -t filter -A FORWARD -j DropSpoof # Kill port scans $IPTABLES -t filter -A FORWARD -j KillPortScans # Filter ICMP $IPTABLES -t filter -A FORWARD -j FilterICMP # Block certain ports from outside $IPTABLES -t filter -A FORWARD -j BlockPortFromOutside # log each SSH connection to DMZ $IPTABLES -t filter -A FORWARD -p tcp -o $DMZ_IFACE --syn --dport 22 \ -m state --state NEW -j LOG --log-prefix "New SSH to DMZ" # Allow the DNAT packets in the FORWARD chain let count=0 while (( $count < ${#INET_TO_DMZ[*]} )) do addresses=${INET_TO_DMZ[count]}; inet_address=${addresses%$from}; dmz_address=${addresses#$to}; for ports in ${DNAT_UDP_PORTS[count]} do inet_port=${ports%$from}; dmz_port=${ports#$to}; # $IPTABLES -t filter -A FORWARD -p udp -i $INET_IFACE \ # -d ${dmz_address} --dport ${dmz_port} \ # -m state --state NEW \ # --log-level notice --log-prefix "FORW ${INET_IFACE}:${inet_port}: " # $IPTABLES -t filter -A FORWARD -p udp -i $INET_IFACE \ # -m state --state NEW \ # -d ${dmz_address} --dport ${dmz_port} -j ACCEPT $IPTABLES -t filter -A FORWARD -p udp \ -m state --state NEW \ -d ${dmz_address} --dport ${dmz_port} -j ACCEPT done let count="count + 1" done let count=0 while (( $count < ${#INET_TO_DMZ[*]} )) do addresses=${INET_TO_DMZ[count]}; inet_address=${addresses%$from}; dmz_address=${addresses#$to}; for ports in ${DNAT_TCP_PORTS[count]} do inet_port=${ports%$from}; dmz_port=${ports#$to}; # $IPTABLES -t filter -A FORWARD -p tcp -i $INET_IFACE \ # -d ${dmz_address} --dport ${dmz_port} \ # -m state --state NEW \ # --log-level notice --log-prefix "FORW ${INET_IFACE}:${inet_port}: " # $IPTABLES -t filter -A FORWARD -p tcp -i $INET_IFACE \ # -m state --state NEW \ # -d ${dmz_address} --dport ${dmz_port} -j ACCEPT $IPTABLES -t filter -A FORWARD -p tcp \ -m state --state NEW \ -d ${dmz_address} --dport ${dmz_port} -j ACCEPT done let count="count + 1" done # Allow NEW connections to anywhere and anything from LAN and DMZ # This assumes that you TRUST your LAN and DMZ. # $IPTABLES -t filter -A FORWARD -i $LAN_IFACE -m state --state NEW \ -j ACCEPT $IPTABLES -t filter -A FORWARD -i $DMZ_IFACE -m state --state NEW \ -j ACCEPT $IPTABLES -t filter -A FORWARD -m state --state ESTABLISHED,RELATED \ -j ACCEPT #[][][][][][][][][] OUTPUT [][][][][][][][][][][][][][][][][][] # # []*** OUTPUT ***[] # Packets that were created by FW itself and go outside: # FW => INET, FW => LAN, FW => DMZ # Again... I trust that all connections initiated from FW are OK # $IPTABLES -t filter -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED \ -j ACCEPT #[][][][][][][][][] POSTROUTING [][][][][][][][][][][][][][][][][][] # # []*** POSTROUTING ***[] # *** set SNAT/MASQUARADING on the MY_IP_ADDRESS on the list *** $IPTABLES -t nat -A POSTROUTING -o $INET_IFACE -s ${LAN_IP} -j SNAT \ --to $MY_IP_ADDRESS $IPTABLES -t nat -A POSTROUTING -o $INET_IFACE -s ${DMZ_IP} -j SNAT \ --to $MY_IP_ADDRESS # ================================================================= # These are commented out. You use them for debugging. # These LOG entries list all packets which were missed by previous rules # Not, all policies are set to DROP # You have a choice: # Uncomment only -j LOG rules (packets will be logged but dropped nevertheless # Use it occassionally to see what kind of garbage hits your firewall # Uncomment all lines, so packets are ACCEPTed and LOGged # Use this, when you want to provide rules for a new protocol or service # and you want to ALLOW everything, and then look up in messages.notice # which rules are need to make it through the firewall. # In production setting, these rules SHOULD BE ALWAYS commented out. # # $IPTABLES -t nat -A POSTROUTING -j LOG \ # --log-level notice --log-prefix "POST DEBUG: " # $IPTABLES -t nat -A POSTROUTING -j ACCEPT # # $IPTABLES -t nat -A PREROUTING -j LOG \ # --log-level notice --log-prefix "PRE DEBUG: " # $IPTABLES -t nat -A PREROUTING -j ACCEPT \ # # $IPTABLES -t filter -A INPUT -j LOG \ # --log-level notice --log-prefix "INPUT DEBUG: " # $IPTABLES -t filter -A INPUT -j ACCEPT # # $IPTABLES -t filter -A OUTPUT -j LOG \ # --log-level notice --log-prefix "OUTPUT DEBUG: " # $IPTABLES -t filter -A OUTPUT -j ACCEPT # # $IPTABLES -t filter -A FORWARD -j LOG \ # --log-level notice --log-prefix "FORWARD DEBUG: " # $IPTABLES -t filter -A FORWARD -j ACCEPT # # $IPTABLES -t nat -A POSTROUTING -j LOG \ # --log-level notice --log-prefix "POST DEBUG: " # $IPTABLES -t nat -A POSTROUTING -j ACCEPT # $IPTABLES -t filter -A INPUT -j LOG \ # --log-level notice --log-prefix "INPUT DEBUG: " # $IPTABLES -t filter -A INPUT -j ACCEPT # $IPTABLES -t filter -A OUTPUT -j LOG \ # --log-level notice --log-prefix "OUTPUT DEBUG: " # $IPTABLES -t filter -A OUTPUT -j ACCEPT # $IPTABLES -t filter -A FORWARD -j LOG \ # --log-level notice --log-prefix "FORWARD DEBUG: " # $IPTABLES -t filter -A FORWARD -j ACCEPT # Turn on the IP Forwarding /bin/echo 1 > /proc/sys/net/ipv4/ip_forward