NSLU2-Linux
view · edit · print · history

Note: Only for Unslung, tested in 5.5

The Unslung open embedded ipkg feed includes the kernel modules needed to enable a packet filter & a "stateless" firewall on the Slug. There are many reasons why you might want one of these, not limited to:

  • placing your slug on the open internet
  • limiting access to services on the slug to specific ip ranges
    • for example, ssh and ftp might be blocked for all but one machine IP or mac address
  • paranoia - everything else on your network has a firewall, so should the slug

This HOWTO will walk you thru the basic installation of needed kernel modules, and setup of a packet filter firewall. The connection tracking kernel module has now been added to the feed, but the iptables script below has not been updated to reflect this. If you want NAT support (obviously requires a seperate USB Ethernet adaptor), it is recommended that you use OpenSlug, and have a good knowledge of iptables.

Install the needed Kernel Modules and iptables

Install these kernel modules. Currently, you'll need to have ipkg ignore dependencies.

 
ipkg install kernel-module-ip-tables -force-depends
ipkg install kernel-module-iptable-filter -force-depends
(if you need logging execute "ipkg install kernel-module-ipt-log" ,too)
ipkg install iptables

Generate modules.dep and map files

 
depmod -a

Create the Firewall Script

Firewalls are funny things. When you're working on them, its not all that uncommon to find you've locked yourself out of the box. On systes with consoles that's not a disaster. On the Slug it is. Make sure you can telnet or ssh into your Slug with the firewall rules running before you set up a firewall that runs at bootup. If you lock yourself out of your Slug, you will need to move the drive to some other system and remove/edit the firewall script.

Still here? Good! Create the following script somewhere. I made mine in /opt/etc/iptables.sh. Whatever you name it, the script below will get you started. You can download the script from http://schettino.us/slug/iptables.sh to save a bit of typing.

 
#!/bin/sh
###############################################################################
#
# Local Settings
#

# IPTables Location - adjust if needed

IPT="/opt/sbin/iptables"
IPTS="/opt/sbin/iptables-save"
IPTR="/opt/sbin/iptables-restore"

# Internet Interface
INET_IFACE="ixp0"

# Path to iptables modules
IPT_MODPATH="/lib/modules/2.4.22-xfs/kernel/net/ipv4/netfilter"

# CHANGE THIS TO MATCH YOUR SLUG IP ADDRESS
# currently not used
INET_ADDRESS="10.0.0.123"

# Localhost Interface

LO_IFACE="lo"
LO_IP="127.0.0.1"

# Save and Restore arguments handled here
if [ "$1" = "save" ]
then
        echo -n "Saving firewall to /etc/sysconfig/iptables ... "
        $IPTS > /opt/etc/iptables
        echo "done"
        exit 0
elif [ "$1" = "restore" ]
then
        echo -n "Restoring firewall from /etc/sysconfig/iptables ... "
        $IPTR < /opt/etc/iptables
        echo "done"
        exit 0
fi
# Load Modules

echo "Loading kernel modules ..."

insmod $IPT_MODPATH/ip_tables.o
insmod $IPT_MODPATH/iptable_filter.o
#insmod $IPT_MODPATH/ipt_LOG.o
# if you need logging, uncomment line above
#  (be aware you have installed the kernel-module-ipt-log

# Flush Any Existing Rules or Chains

echo "Flushing Tables ..."

# Reset Default Policies
$IPT -P INPUT ACCEPT
$IPT -P FORWARD ACCEPT
$IPT -P OUTPUT ACCEPT

# Flush all rules
$IPT -F

# Erase all non-default chains
$IPT -X

if [ "$1" = "stop" ]
then
        echo "Firewall completely flushed!  Now running with no firewall."
        exit 0
fi

###############################################################################
# Rules Configuration

# Filter Table

# Set Policies

$IPT -P INPUT DROP
$IPT -P OUTPUT DROP
$IPT -P FORWARD DROP

# User-Specified Chains


echo "Create and populate custom rule chains ..."

# Create a chain to filter INVALID packets

$IPT -N bad_packets

# Create another chain to filter bad tcp packets

$IPT -N bad_tcp_packets

# Create separate chains for icmp, tcp (incoming and outgoing),
# and incoming udp packets.

$IPT -N icmp_packets

# Used for UDP packets inbound from the Internet
$IPT -N udp_inbound

# Used to block outbound UDP services from internal network
# Default to allow all
$IPT -N udp_outbound

# Used to allow inbound services if desired
# Default fail except for established sessions
$IPT -N tcp_inbound

# Used to block outbound services from internal network
# Default to allow all
$IPT -N tcp_outbound

# Populate User Chains

# bad_packets chain

# Drop INVALID packets immediately
# needs conntrack
#$IPT -A bad_packets -p ALL -m state --state INVALID -j DROP

# Then check the tcp packets for additional problems

$IPT -A bad_packets -p tcp -j bad_tcp_packets

# All good, so return
$IPT -A bad_packets -p ALL -j RETURN

# bad_tcp_packets chain
#
# All tcp packets will traverse this chain.
# Every new connection attempt should begin with
# a syn packet.  If it doesn't, it is likely a
# port scan.  This drops packets in state
# NEW that are not flagged as syn packets.


# needs conntrack
#$IPT -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j LOG \
    #--log-prefix "New not syn: "
#$IPT -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j DROP

# Stealth scans
$IPT -A bad_tcp_packets -p tcp --tcp-flags ALL NONE -j DROP
$IPT -A bad_tcp_packets -p tcp --tcp-flags ALL ALL -j DROP
$IPT -A bad_tcp_packets -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
$IPT -A bad_tcp_packets -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP
$IPT -A bad_tcp_packets -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
$IPT -A bad_tcp_packets -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

# All good, so return
$IPT -A bad_tcp_packets -p tcp -j RETURN

# icmp_packets chain
# ICMP packets should fit in a Layer 2 frame, thus they should
# never be fragmented.  Fragmented ICMP packets are a typical sign
# of a denial of service attack.
#$IPT -A icmp_packets --fragment -p ICMP -j LOG \
    #--log-prefix "ICMP Fragment: "
$IPT -A icmp_packets --fragment -p ICMP -j DROP

# Echo - uncomment to allow your system to be pinged.
# Uncomment the LOG command if you also want to log PING attempts
#
# $IPT -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -j LOG \
#    --log-prefix "Ping detected: "
$IPT -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -j ACCEPT

# comment out above and uncomment below to drop pings without logging.
#$IPT -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -j DROP

# see ping reply packets
$IPT -A icmp_packets -p ICMP -s 0/0 --icmp-type 0 -j ACCEPT

# Time Exceeded
$IPT -A icmp_packets -p ICMP -s 0/0 --icmp-type 11 -j ACCEPT

# Not matched, so return so it will be logged
$IPT -A icmp_packets -p ICMP -j RETURN

# TCP & UDP
# Identify ports at:
#    http://www.chebucto.ns.ca/~rakerman/port-table.html
#    http://www.iana.org/assignments/port-numbers

#
# ADD UDP-based services here
#

# udp_inbound chain
# ports you want to accept udp packets on

# netbios/samba
$IPT -A udp_inbound -p UDP -s 0/0 --destination-port 137 -j ACCEPT
$IPT -A udp_inbound -p UDP -s 0/0 --destination-port 138 -j ACCEPT

# Network Time Protocol (NTP) Server
$IPT -A udp_inbound -p UDP -s 0/0 --destination-port 123 -j ACCEPT

# External DHCP Server
# Allow DHCP client request packets inbound from external network
$IPT -A udp_inbound -p UDP -s 0/0 --source-port 68 --destination-port 67 -j ACCEPT

# DNS in
#$IPT -A udp_inbound -p UDP -s 0/0 --destination-port 53 -j ACCEPT
$IPT -A udp_inbound -p UDP -s 0/0 --source-port 53 -j ACCEPT

# Not matched, so return for logging
$IPT -A udp_inbound -p UDP -j RETURN

# udp_outbound chain
# ports you send udp packets to

# netbios/samba
$IPT -A udp_outbound -p UDP -s 0/0 --destination-port 137 -j ACCEPT
$IPT -A udp_outbound -p UDP -s 0/0 --destination-port 138 -j ACCEPT

# Network Time Protocol (NTP) Server
$IPT -A udp_outbound -p UDP -s 0/0 --destination-port 123 -j ACCEPT

# DHCP out
$IPT -A udp_outbound -p UDP -s 0/0 --destination-port 68 -j ACCEPT

# DNS out
$IPT -A udp_outbound -p UDP -s 0/0 --destination-port 53 -j ACCEPT

# No match, so ACCEPT
# make this DROP if you want to block any other outbound udp traffic
$IPT -A udp_outbound -p UDP -s 0/0 -j ACCEPT

# tcp_inbound chain
#
# This chain is used to allow inbound connections to the SLUG

# smb
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 137 -j ACCEPT
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 139 -j ACCEPT
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 445 -j ACCEPT

# HTTP
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 80 -j ACCEPT

# FTP
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port ftp -j ACCEPT
# Passive
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 33201:33210 -j ACCEPT

# DNS
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 53 -j ACCEPT

# sshd
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 22 -j ACCEPT

# telnet
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 23 -j ACCEPT

# Not matched, so return so it will be logged
$IPT -A tcp_inbound -p TCP -j RETURN

# tcp_outbound chain
#
# This chain controlls what tcp traffic is allowed out

# http
$IPT -A tcp_outbound -p TCP -s 0/0 --destination-port 80 -j ACCEPT
# DNS
$IPT -A tcp_outbound -p TCP -s 0/0 --destination-port 53 -j ACCEPT
# sshd
$IPT -A tcp_outbound -p TCP -s 0/0 --destination-port 22 -j ACCEPT

# No match, so ACCEPT
# Note, you could make this DROP to block any other outbound traffic

$IPT -A tcp_outbound -p TCP -s 0/0 -j ACCEPT

###############################################################################
# INPUT Chain

echo "process INPUT chain ..."

# Allow all on localhost interface
$IPT -A INPUT -p ALL -i $LO_IFACE -j ACCEPT

# Drop bad packets
$IPT -A INPUT -p ALL -j bad_packets

# ******************************
# Inbound Internet Packet Rules

# Accept Established Connections
# Needs conntrack module
# $IPT -A INPUT -p ALL -i $INET_IFACE -m state --state ESTABLISHED,RELATED -j ACCEPT

# packet filter accepts inbound packets that are replies to an outbound connection
# use until conntrack is available
# this blocks all new connection attempts except to those allowed below
$IPT -A INPUT -p TCP -i $INET_IFACE ! --syn -j ACCEPT

# Route the rest to the appropriate user chain
$IPT -A INPUT -p TCP -i $INET_IFACE -j tcp_inbound
$IPT -A INPUT -p UDP -i $INET_IFACE -j udp_inbound
$IPT -A INPUT -p ICMP -i $INET_IFACE -j icmp_packets

# Drop without logging broadcasts that get this far.
# Comment this line if testing new rules that impact
# broadcast protocols.
#$IPT -A INPUT -m pkttype --pkt-type broadcast -j DROP

###############################################################################
#
# OUTPUT Chain
#

echo "Process OUTPUT chain ..."

# Generally trust the firewall on output

# However, invalid icmp packets need to be dropped
# to prevent a possible exploit.
# needs conntrack
#$IPT -A OUTPUT -m state -p icmp --state INVALID -j DROP

# Localhost
$IPT -A OUTPUT -p ALL -s $LO_IP -j ACCEPT
$IPT -A OUTPUT -p ALL -o $LO_IFACE -j ACCEPT

# If you want to block outbound connections, uncomment first section below, comment
# out second section, and add rules to tcp_outbound/udp_outbound

# To internet - filtered
#$IPT -A OUTPUT -p TCP -o $INET_IFACE -j tcp_outbound
#$IPT -A OUTPUT -p UDP -o $INET_IFACE -j udp_outbound

# To internet (unfiltered)
$IPT -A OUTPUT -p ALL -o $INET_IFACE -j ACCEPT

Looks like a lot! In a nutshell, the above script is going to allow your Slug to connect out without limits, but will block all inbound access except to the following services:

  • ICMP (ping)
  • SMB (Samba, file sharing)
  • HTTP (the web interface)
  • FTP (you also need to change the vsftpd.conf file, see below)
  • DNS (optional server)
  • DHCP (optional server)
  • NTPD (optional server)
  • SSH (so you can get in)
  • Telnet (you should use ssh)

You will need to open up additional ports for any other tcp or udp based services, and you should comment out any of the above services you are not using.

vsftpd and Passive Mode

If you are using vsftpd, the above script includes a rule to open a block of 10 ports for passive mode connections. Add the following to your vsftpd.conf file to tell vsftpd to use those same ports:

 
# for our firewall, only use this range of ports
pasv_min_port=33201
pasv_max_port=33210

Testing

Ssh or telnet into your Slug. Start the firewall:

 # /opt/etc/iptables.sh

If all goes well, you will not see much difference. Verify the firewall is installed by running:

 # iptables -L -v

That should give you a nice long output showing all the rules installed. Try logging out, and logging back in (via Ssh or telnet) - still works? Great! Also check that you can pull up the web interface, and that the Slug can connect out (do an ipkg update, it should still work.)

 # /opt/etc/iptables.sh stop

Will remove all firewalling from your Slug if you are having problems.

Problems

If you are locked out right now, with no shell open on the Slug, you can reboot the Slug via the power button as you normally would, and it will come back up without the firewall.

Making it start on boot

Once you are very very sure you are not locked out while the firewall is running, just make a symbolic link in /opt/etc/init.d/S30iptables to your iptables.sh script:

 # ln -s /opt/etc/iptables.sh /opt/etc/init.d/S30iptables

Avoiding lockouts

If you are experimenting with new firewall rules, it's always a good idea to delete the S30iptables link until you are sure you've not locked yourself out of your slug. That way, you can always just reboot it if things go horribly wrong. Once you're done making changes and you've tested that you can exit and get back in via ssh and/or telent, just re-create the link.

Other useful IPTables? clips

Here are some other useful bits for iptables that could be brought into the script above or used to develop your own particular rules:

To lock out multiple attempted connections on a TCP port: (source: http://tech.groups.yahoo.com/group/nslu2-linux/message/18019 )

NOTE astx813 - OK, I spent a few hours on this and I find myself giving up (for now) on implementing this. I was going to edit this page to remind people to install kernel-module-ipt-recent and to add the corresponding insmod line to iptables.sh, but that's not enough. We're missing the libipt_recent.so module. Any idea how to get that where it ran off to?

 

# With this set of rules, any one attempting more than 5 TCP connections on port
# 22 (ssh) in the last 60 seconds are totaly denied acces to the machine on any
# port.

iptables -N recent_rule
iptables -A INPUT -p tcp -m state --state NEW -j recent_rule
iptables -A recent_rule -m recent --name recent_ssh --update --seconds 60
--hitcount 5 -j DROP
iptables -A recent_rule -p tcp --dport 22 -m recent --name recent_ssh --set -j
ACCEPT

Related topics

I first got the idea that this was possible while reading the EnableTrafficShaping howto.

If you want to firewall the LAN without adding another ethernet port, see SingleInterfaceFirewall.

view · edit · print · history · Last edited by astx813.
Based on work by astx813, frustrated, Abraxas, the_orn, ben, blaster8, and strike-2.
Originally by JohnS.
Page last modified on November 13, 2008, at 11:14 PM