#!/bin/sh

#
# GateWay + services firewall script
# Patrick MARIE <mycroft@virgaria.org>
#
# $Id: firewall.sh,v 1.3 2003/12/12 23:12:37 mycroft Exp $ 
#

INT_NET=ppp0

# Utilisation du LAN1 ou pas.
# Doit etre a "YES"
USE_LAN1="YES"
INT_LAN1=eth0

# Utilisation du LAN2 ou pas.
# Doit etre a "YES" (Decommentez juste.)
# USE_LAN2="YES"
# INT_LAN2=eth1

IP_NET=
IP_LAN1=
# IP_LAN2=

NET_LAN1=192.168.0.0/24
# NET_LAN2=

# Est ce qu'il faut que je sois routeur pour les LAN ?
# activer l'une des 2 active le forward dans le noyau.
FORWARD_LAN1="YES"
# FORWARD_LAN2="YES"

# Ranges de ports a deny par defaut.
# Attention !!! - Ports en TCP/UDP sans distinctions
#
# ex: 137:139 1212:1220 4660:4665 6695:6705 8885:8892 pour kazaa, edonkey, irc.
#
DENIED_PORT_RANGE="137:139 1212:1220 4660:4665 6695:6705 8885:8892"

# Services ouverts a tous sur le LAN (LAN1 -> GATEWAY)
# ex: 22 25 80
TCP_ACCEPT_PORTS_ON_LAN1="21 22 25 53 80 110 60000:65000"
UDP_ACCEPT_PORTS_ON_LAN1="53"

# Services ouverts a tous sur le 2nd LAN (LAN2 -> GATEWAY)
# ex "22 3128"
# TCP_ACCEPT_PORTS_ON_LAN2="22 53 110 113 143 993 995"
# UDP_ACCEPT_PORTS_ON_LAN2="53"

# Services ouverts a tous sur le NET (NET -> GATEWAY)
# ex: 80
TCP_ACCEPT_PORTS_ON_NET="21 22 25 53 80 110 60000:65000"
UDP_ACCEPT_PORTS_ON_NET="53"

# Ports sur le NET a ouvrir pour les IP trusted
# ex: TRUSTED_PORTS=22
# ex: TRUSTED_IP=194.98.185.18 62.212.107.216 212.85.128.2

TRUSTED_PORTS="22 23"
TRUSTED_IP="216.136.204.21"

# Redirections net -> machine dans le sub net
# ex: WITH_LANPORTREDIR="80:10.24.24.254:80" 
#WITH_LANPORTREDIR="80:10.24.24.254:80"

# Port redirection 
# WITH_LOCALPORTREDIR="from_ip:local_port_in:local_real_port 2nd 3rd ..."
WITH_LOCALPORTREDIR="216.136.204.21/32:23:22"


# Autoriser l'ipv6 ŕ passer sur les interfaces.
# Ceci nécessite une configuration ip6tables.
USE_IPV6="YES"

# Log ?
# Log kernel - dmesg -
USE_LOG="NO"

# Drop incomming ICMP
DROP_ICMP="YES"

# Nat pour machines ayant une adresse IP dynamiques
# USE_IP_DYNADDR="YES"

# Protections diverses, configurations sysctls perso
USE_SYSCTL_CHANGES="YES"

# Accept les clients du LAN a se connecter sur les ports de la machine mais
# d'une mauvaise interface (genre lan -> intnet)
# Attention: ne concerne que les ports ouverts sur le LAN et non sur le NET.
# Les ports ouverts par les ***_ACCEPT_PORTS_ON_NET doivent l'etre sur le
# ***_ACCEPT_PORTS_ON_LANx
USE_EXTIP_IN_LAN="YES"

# Firewall script output
# outputfile=/tmp/firewall.output

#############################################################################
#
# Do not touch after this.
#

# IPTABLES="/sbin/iptables"
IPTABLES="cmd /sbin/iptables"

if test ! "x${IP_NET}" = "x" ; then
  DST_IP_NET="-d ${IP_NET}"
  SRC_IP_NET="-s ${IP_NET}"
else
  DST_IP_NET=""
  SRC_IP_NET=""
fi

cmd()
{
  rc=$*
  if test "x${outputfile}" = "xYES" ; then 
    ${rc} 2>>${outputfile} 1>>${outputfile}
    if test $? -ne 0 ; then
      echo "\"${rc}\" returned code $?" 1>>${outputfile}
    fi
  else
    ${rc}
    res=$?
    if test $res -ne 0 ; then
      echo "\"${rc}\" returned code $res"
    fi
  fi
}

startfirewall()
{
#
# Basic rules: all is denied

${IPTABLES} -F
${IPTABLES} -F INPUT
${IPTABLES} -F OUTPUT
${IPTABLES} -F FORWARD
${IPTABLES} -F -t mangle
${IPTABLES} -F -t nat
${IPTABLES} -X
${IPTABLES} -Z

${IPTABLES} -P FORWARD DROP 
${IPTABLES} -P OUTPUT DROP
${IPTABLES} -P INPUT DROP 

# Protection against hypotetical ABfrags bug 
${IPTABLES} -I INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

# Pseudo protection against syn flood
${IPTABLES} -N syn-flood 
${IPTABLES} -A INPUT -i ${INT_NET} -p tcp --syn -j syn-flood 
${IPTABLES} -A syn-flood -m limit --limit 1/s --limit-burst 4 -j RETURN 
${IPTABLES} -A syn-flood -j DROP 

${IPTABLES} -N PRISON
${IPTABLES} -A PRISON -m state --state ESTABLISHED,RELATED -j ACCEPT

if test "x${USE_LOG}" = "xYES"; then
  ${IPTABLES} -A PRISON -i ${INT_NET} -j LOG \
    --log-level NOTICE --log-prefix "Drop "
fi

${IPTABLES} -A PRISON -j DROP 

#
# Packets we deny:

${IPTABLES} -A FORWARD -i ${INT_NET} -s 10.0.0.0/8 -j DROP
${IPTABLES} -A FORWARD -i ${INT_NET} -s 127.0.0.0/8 -j DROP
${IPTABLES} -A FORWARD -i ${INT_NET} -s 172.16.0.0/12 -j DROP
${IPTABLES} -A FORWARD -i ${INT_NET} -s 192.168.0.0/16 -j DROP

${IPTABLES} -A INPUT -i ${INT_NET} -s 10.0.0.0/8 -j DROP
${IPTABLES} -A INPUT -i ${INT_NET} -s 172.16.0.0/12 -j DROP
${IPTABLES} -A INPUT -i ${INT_NET} -s 127.0.0.0/8 -j DROP
${IPTABLES} -A INPUT -i ${INT_NET} -s 192.168.0.0/16 -j DROP

# ipv6 is okay from us

if test "x${USE_IPV6}" = "xYES"; then
  ${IPTABLES} -A INPUT -p ipv6 -j ACCEPT 
else
  ${IPTABLES} -A INPUT -p ipv6 -j DENY
fi

#
# Local redirection on localhost

for i in $(echo $WITH_LOCALPORTREDIR); do
  FROM_IP=$(echo $WITH_LOCALPORTREDIR|cut -d: -f1)
  LOCAL_PORT=$(echo $WITH_LOCALPORTREDIR|cut -d: -f2)
  LOCAL_TO_PORT=$(echo $WITH_LOCALPORTREDIR|cut -d: -f3)

  ${IPTABLES} -t nat -I PREROUTING -i ${INT_NET} -p tcp \
    -s ${FROM_IP} \
    --dport ${LOCAL_PORT} \
    -j REDIRECT --to-port ${LOCAL_TO_PORT}
done

#
# Applications du genre kazaa/edonkey/etc.. qu'on deny sans chercher

for i in $(echo ${DENIED_PORT_RANGE}); do
  ${IPTABLES} -A FORWARD -p tcp --syn --dport $i -m state --state NEW -j DROP
done

# Sysctls
if test "x${USE_SYSCTL_CHANGES}" = "xYES" ; then

  # Enable always defragging protection in /proc/sys/net/ipv4/ip_always_defrag
# XXX
#  echo 1 > /proc/sys/net/ipv4/ip_always_defrag

  #
  # echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

  # Enable broadcast echo Protection
  echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

  # Enable bad error message protection
  echo 1 > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses 

  # Enable TCP SYN Cookie Protection
  echo 1 > /proc/sys/net/ipv4/tcp_syncookies

  # This enables a fix for 'time-wait assassination hazards in tcp', described 
  # in RFC 1337. If enabled, this causes the kernel to drop RST packets for 
  # sockets in the time-wait state. 
  echo 1 > /proc/sys/net/ipv4/tcp_rfc1337

  # Default ttl value
  # echo 127 > /proc/sys/net/ipv4/ip_default_ttl 

  for device in $(ls /proc/sys/net/ipv4/conf) ; do
    # Drop Spoofed Packets coming in on an interface, which if replied to,
    # would result in the reply going out a different interface.
    echo 2 > /proc/sys/net/ipv4/conf/$device/rp_filter 

    # Disable ICMP Redirect Acceptance
    echo 0 > /proc/sys/net/ipv4/conf/$device/accept_redirects 

    # Don't send Redirect Messages
    echo 0 > /proc/sys/net/ipv4/conf/$device/send_redirects 

    # Enable ICMP redirect security so that only redirect messages originating 
    # from gateways listed in the default gateway list are accepted in 
    # /proc/sys/net/ipv4/conf/*/secure_redirects 
    echo 1 > /proc/sys/net/ipv4/conf/$device/secure_redirects 

    # Disable Source Routed Packets
    echo 0 > /proc/sys/net/ipv4/conf/$device/accept_source_route 

    # log les paquets avec des ips incorrectes
    if test "x${USE_LOG}" = "xYES" ; then
      echo 1 > /proc/sys/net/ipv4/conf/$device/log_martians 
    fi
  done 
fi

# Dynaddr
if test "x${USE_IP_DYNADDR}" = "xYES" ; then
  echo 1 > /proc/sys/net/ipv4/ip_dynaddr
else
  echo 0 > /proc/sys/net/ipv4/ip_dynaddr
fi

#
# Packets we accept on ${INT_LAN1}:

if test "x${USE_LAN1}" = "xYES" ; then
  for i in $(echo ${TCP_ACCEPT_PORTS_ON_LAN1}); do

    ${IPTABLES} -A INPUT -i ${INT_LAN1} -p tcp --syn \
      -s ${NET_LAN1} -d ${IP_LAN1} \
      -m state --state NEW --destination-port $i -j ACCEPT 

    if test "x${USE_EXTIP_IN_LAN}" = "xYES" ; then
      ${IPTABLES} -A INPUT -i ${INT_LAN1} -p tcp --syn \
        -s ${NET_LAN1} -d ${IP_NET} \
        -m state --state NEW --destination-port $i -j ACCEPT
    fi
  done

  for i in $(echo ${UDP_ACCEPT_PORTS_ON_LAN1}); do

    ${IPTABLES} -A INPUT -i ${INT_LAN1} -p udp -s ${NET_LAN1} -d ${IP_LAN1} \
      -m state --state NEW --destination-port $i -j ACCEPT

    if test "x${USE_EXTIP_IN_LAN}" = "xYES" ; then
      ${IPTABLES} -A INPUT -i ${INT_LAN1} -p udp -s ${NET_LAN1} -d ${IP_LAN1} \
        -m state --state NEW --destination-port $i -j ACCEPT
    fi
  done
fi

# Packets we accept on $INT_LAN2

if test "x${USE_LAN2}" = "xYES" ; then
  for i in $(echo ${TCP_ACCEPT_PORTS_ON_LAN2}); do

    ${IPTABLES} -A INPUT -i ${INT_LAN2} -p tcp --syn \
      -s ${NET_LAN2} -d ${IP_LAN2} \
      -m state --state NEW --destination-port $i -j ACCEPT

    if test "x${USE_EXTIP_IN_LAN}" = "xYES" ; then
      ${IPTABLES} -A INPUT -i ${INT_LAN2} -p tcp --syn \
        -s ${NET_LAN2} -d ${IP_NET} \
        -m state --state NEW --destination-port $i -j ACCEPT
    fi
  done

  for i in $(echo ${UDP_ACCEPT_PORTS_ON_LAN2}); do
    ${IPTABLES} -A INPUT -i ${INT_LAN2} -p udp -s ${NET_LAN2} -d ${IP_LAN2} \
      -m state --state NEW --destination-port $i -j ACCEPT
    if test "x${USE_EXTIP_IN_LAN}" = "xYES" ; then
      ${IPTABLES} -A INPUT -i ${INT_LAN2} -p udp -s ${NET_LAN2} -d ${IP_NET} \
        -m state --state NEW --destination-port $i -j ACCEPT
    fi
  done
fi

#
# Packets we accept both on ${INT_NET} 

for i in $(echo ${TCP_ACCEPT_PORTS_ON_NET}); do
  ${IPTABLES} -A INPUT -i ${INT_NET} -p tcp --syn ${DST_IP_NET} \
    -m state --state NEW --destination-port $i -j ACCEPT
done

for i in $(echo ${UDP_ACCEPT_PORTS_ON_NET}); do
  ${IPTABLES} -A INPUT -i ${INT_NET} -p udp ${DST_IP_NET} \
    -m state --state NEW --destination-port $i -j ACCEPT
done

#
# Redirections
#WITH_LANPORTREDIR="80:10.24.24.254:80

for i in $(echo ${WITH_LANPORTREDIR}); do
  PORT=$(echo ${WITH_LANPORTREDIR} | cut -d: -f1)
  DEST_IP=$(echo ${WITH_LANPORTREDIR} | cut -d: -f2)
  DEST_PORT=$(echo ${WITH_LANPORTREDIR} | cut -d: -f3)
 
  ${IPTABLES} -A INPUT -i ${INT_NET} -p tcp --syn ${DST_IP_NET} \
    -m state --state NEW --destination-port ${PORT} -j ACCEPT 

  ${IPTABLES} -t nat -A PREROUTING -j DNAT -i ${INT_NET} -p tcp \
    --dport ${PORT} --to-destination ${DEST_IP}:${DEST_PORT}

  ${IPTABLES} -I FORWARD -i ${INT_NET} -d ${DEST_IP} -p tcp \
    --dport ${DEST_PORT} -j ACCEPT
done

#
# Masquerading rules:

${IPTABLES} -t nat -A POSTROUTING -o ${INT_NET} -j MASQUERADE
${IPTABLES} -t nat -A PREROUTING -i ${INT_NET} -j ACCEPT ${DST_IP_NET}

#
# SSH TRUSTED IPs

for i in $(echo ${TRUSTED_IP}); do
  for j in $(echo ${TRUSTED_PORTS}); do
    ${IPTABLES} -A INPUT -i ${INT_NET} -p TCP --syn ${DST_IP_NET} \
      -m state --state NEW -s $i --destination-port $j -j ACCEPT
  done 
done

#
# End rules on ${INT_LAN1} / $INT_LAN2 

if test "x${USE_LAN1}" = "xYES" ; then
  ${IPTABLES} -A INPUT -i ${INT_LAN1} -p tcp --syn -j DROP
  ${IPTABLES} -A INPUT -i ${INT_LAN1} -j PRISON
fi
if test "x${USE_LAN2}" = "xYES" ; then
  ${IPTABLES} -A INPUT -i ${INT_LAN2} -p tcp --syn -j DROP
  ${IPTABLES} -A INPUT -i ${INT_LAN2} -j PRISON
fi

#
# Others rules on ${INT_NET} 

if test "x${USE_LOG}" = "xYES" ; then
  ${IPTABLES} -A INPUT -i ${INT_NET} -p ICMP -j LOG \
    --log-level NOTICE --log-prefix "Incomming icmp on ${INT_NET}: "
fi

#
# End rules on ${INT_NET} 

${IPTABLES} -A INPUT -i ${INT_NET} -p tcp --syn -j DROP
${IPTABLES} -A INPUT -i ${INT_NET} -j PRISON

#
# Forward rules:

if test "x${FORWARD_LAN1}" = "xYES" || test "x${FORWARD_LAN2}" = "xYES" ; then
  echo 1 > /proc/sys/net/ipv4/ip_forward
fi

if test "x${FORWARD_LAN1}" = "xYES" ; then
  ${IPTABLES} -A FORWARD -i ${INT_LAN1} -p ALL -s ${NET_LAN1} -j ACCEPT
fi

if test "x${FORWARD_LAN2}" = "xYES" ; then
  ${IPTABLES} -A FORWARD -i ${INT_LAN2} -p ALL -s ${NET_LAN2} -j ACCEPT
fi

${IPTABLES} -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

if test "x${USE_LOG}" = "xYES" ; then
  ${IPTABLES} -A FORWARD -m limit --limit 3/minute --limit-burst 3 -j LOG \
    --log-level DEBUG --log-prefix \"IPT FORWARD packet died: \"
fi

# Accept rules:

${IPTABLES} -A INPUT -i lo -s 127.0.0.0/8 -d 127.0.0.0/8 -j ACCEPT
${IPTABLES} -A INPUT -i lo -s ${IP_NET} -d ${IP_NET} -j ACCEPT

if test "x${USE_LAN1}" = "xYES" ; then
  ${IPTABLES} -A INPUT -i lo -s ${IP_LAN1} -d ${IP_LAN1} -j ACCEPT
fi

if test "x${USE_LAN2}" = "xYES" ; then
  ${IPTABLES} -A INPUT -i lo -s ${IP_LAN2} -d ${IP_LAN2} -j ACCEPT
fi

${IPTABLES} -A OUTPUT -p ALL -s 127.0.0.0/8 -j ACCEPT

if test "x${USE_LAN1}" = "xYES" ; then
  ${IPTABLES} -A OUTPUT -p ALL -s ${NET_LAN1} -j ACCEPT
fi

if test "x${USE_LAN2}" = "xYES" ; then
  ${IPTABLES} -A OUTPUT -p ALL -s ${NET_LAN2} -j ACCEPT
fi
${IPTABLES} -A OUTPUT -p ALL ${SRC_IP_NET} -j ACCEPT

if test "x${DROP_ICMP}" = "xYES" ; then
  ${IPTABLES} -A OUTPUT -m state -p icmp --state INVALID -j DROP
fi

${IPTABLES} -A OUTPUT -p ALL -m state --state ESTABLISHED,RELATED -j ACCEPT

if test -r /etc/ip.prison ; then
  for i in $(cat /etc/ip.prison) ; do
    ${IPTABLES} -I PRISON -s $i -j DROP
  done
fi

if test -r /etc/ssh.auth.ip ; then
  for tcpip in $(cat /etc/ssh.auth.ip); do
    iptables -I INPUT -i ppp0 -p tcp --syn -s $tcpip -m state --state NEW --destination-port 22 -j ACCEPT
  done
fi

} # Fin startfirewall

case "$1" in
  start)
    echo -n "Starting firewall"
    startfirewall
    echo "."
    exit 0
    ;;
  stop)
    echo -n "Stopping firewall"
    ${IPTABLES} -F
    ${IPTABLES} -F INPUT
    ${IPTABLES} -F OUTPUT
    ${IPTABLES} -F FORWARD
    ${IPTABLES} -F -t mangle
    ${IPTABLES} -F -t nat
    ${IPTABLES} -X
    ${IPTABLES} -Z

    ${IPTABLES} -P FORWARD ACCEPT
    ${IPTABLES} -P OUTPUT ACCEPT
    ${IPTABLES} -P INPUT ACCEPT
    echo "."
    exit 0
    ;;
  *) 
    echo "Usage: $0 [start|stop]"
    exit 1 
    ;;
esac

#-
# * Copyright (c) 2003, Patrick MARIE <mycroft@virgaria.org>
# * All rights reserved.
# *
# * Redistribution and use in source and binary forms, with or without
# * modification, are permitted provided that the following conditions
# * are met:
# * 1. Redistributions of source code must retain the above copyright
# *    notice, this list of conditions and the following disclaimer.
# * 2. Redistributions in binary form must reproduce the above copyright
# *    notice, this list of conditions and the following disclaimer in the
# *    documentation and/or other materials provided with the distribution.
# *
# * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# * SUCH DAMAGE.
# *
# */

