howtos/BANANA PI GATEWAY.txt

580 lines
35 KiB
Plaintext

*****************************************************************************
*****************************************************************************
**************** BANANA PI GATEWAY *****************************************************************************
*****************************************************************************
http://www.awasu.com/weblog/bpi-gateway/
*****************************************************************************
*****************************************************************************
First, activate the WiFi module:
sudo modprobe bcmdhd
Second, connect to WiFi network
With interative commands:
sudo ip link set wlan0 up
sudo -s
wpa_passphrase <SSID> > /etc/wpa_supplicant.conf (write the password and push enter)
wpa_supplicant -i wlan0 -c /etc/wpa_supplicant.conf
sudo dhclient wlan0
sudo ip route change default via <gateway ip> dev wlan0
Connect at boot:
nano /etc/network/interfaces
delete '#' fron auto wlan 0
=============================== Setting up the Gateway ========================
We now have our Banana Pi up and running with a clean installation of Bananian Linux. To get it working as a gateway, we need to do 3 things:
set up a DHCP server, so that computers can join the network.
set up a DNS server, so that domain names can be resolved.
set up NAT, so that internet requests can be made on behalf of other computers on the network.
Once you've set up these 3 things, you will have a basic gateway up and running, and other computers on the internal network should have internet access.
The diagram below shows how it all fits together:
When a new computer[1] joins the network, it will contact the DHCP server running on the bPi to get an IP address. As part of this assignment, the bPi will tell the new computer that it also handles DNS requests, as well as any other internet activity.
If someone wants to go to a web site, they will ask the DNS server to look-up the domain name, to get the web site's IP address.
Finally, NAT ensures that internet requests go out, and replies come back in, transparently.
=============================== Setting up DHCP ===============================
Every computer on a network has a unique ID, known as an IP address, which might look something like 72.249.26.4. Back in the day, a network administrator would have to go around and configure these manually for every single computer on a network, but with the advent of DHCP[1], this can now be done automatically.
When a computer is turned on, and has been configured to use DHCP, it checks the network to see if a DHCP server is available and if so, asks it for an IP address. The DHCP server has a pool of IP addresses it can use, and if one is available, assigns it to the new computer, which can then use it to identify itself and communicate with other computers on the network. When the computer is turned off, its IP address is released and becomes available for use by another computer.
Installing and configuring the DHCP server
So, we need to install a DHCP server on our gateway, so that computers on our internal network can get IP addresses[2]. This is very simple:
sudo apt-get install isc-dhcp-server
It will probably fail when it tries to start up, because it hasn't been configured yet, but we can ignore this, since we will configure it now.
First, edit /etc/default/isc-dhcp-server and set the INTERFACES parameter. For example, in my home network, I connect to the internet via wifi (i.e. the wlan0 network interface), and use the wired network for my LAN, so I would set this to eth0[3].
Then edit /etc/dhcp/dhcpd.conf:
Set the domain-name option to what you would like to call your network e.g. gateway.
Set the domain-name-servers option to the IP address of the bPi[4].
Add a line to set the routers option, that configures the IP address of the gateway[5].
Un-comment the authoritative statement.
Configure the lease addresses[6] e.g.
subnet 10.0.0.0 netmask 255.255.255.0 {
range 10.0.0.100 10.0.0.199 ;
}
It is also possible to assign specific computers a fixed IP address, according to their MAC address e.g.
host mycomputer {
hardware ethernet XX:XX:XX:XX:XX:XX ;
fixed-address 10.0.0.2 ;
}
Restart the service:
sudo service isc-dhcp-server restart
We also want it to start up when the system is booted:
sudo update-rc.d isc-dhcp-server start
Finally, to check what IP addresses have been assigned, take a look inside /var/lib/dhcp/dhcpd.leases.
=============================== Setting up DNS ================================
Every computer is uniquely identified by an IP address, but these are inconvenient, and it's much easier to refer to them by name. The Domain Name System is a network's white pages, but instead of looking up somebody's name in order to get their telephone number, you can look up a domain name (e.g. awasu.com) and get its corresponding IP address (e.g. 72.249.26.4).
Installing and configuring the DNS server
We need to install a DNS server on our gateway, so that computers on the internal network can do domain name lookups[1].
First, we need to install the DNS server:
sudo apt-get install bind9
We can also install some useful tools that let us do DNS lookups from the command line (e.g. dig and nsupdate):
sudo apt-get install dnsutils
Now, computers on my internal network will have their DNS lookups done by the DNS server running on our gateway[2].
However, the gateway DNS server itself needs to do DNS lookups, and this is configured in /etc/resolv.conf. In the case of my home network, I get given the address of a DNS server when I connect via wifi, but you might want to use something else e.g. 8.8.8.8, if you want to use Google's DNS servers.
=============================== Setting up NAT ================================
Network Address Translation allows the gateway to send requests out to the internet on behalf of computers on the internal network. When the gateway receives an internet request, it tweaks it slightly to make it look like it was sending the request itself, and then forwards it on to the intended recipient on the internet. When the reply comes back, they gateway tweaks it again to make it look like nothing happened i.e. the computer on the internal network thinks it was communicating directly with the remote computer on the internet.
To set this up, we first need to enable IP forwarding in the kernel. Edit /etc/sysctl.conf and uncomment the net.ipv4.ip_forward=1 line.
To reload these settings:
sudo sysctl -p /etc/sysctl.conf
We also need to enable IP masquerading:
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
This command tells the gateway to forward any internet requests to the wlan0 interface i.e. my wifi connection, which is how I connect to the internet.
Since we need this to always happen, the above command should be added to /etc/rc.local.
=============================== Setting up a WiFi access point ================
Installing the WiFi adapter
The Banana/Raspberry Pi's are very finicky when it comes to WiFi adapters 🙁 so you need to do some research before buying one. A list of wifi adapters known to work with a Raspberry Pi can be found here, and Mikronauts have done some research on adapters that work with a Banana Pi.
I chose a TP-Link TL-WN723N.
After plugging it in, lsmod showed that the 8188eu driver had been loaded for it:
lsusb also showed it connected:
Configuring the access point
First, we want to give the wifi adapter a fixed IP address, so add the following to /etc/network/interfaces[1]:
allow-hotplug wlan1
iface wlan1 inet static
address 10.0.1.0
netmask 255.255.255.0
Next, we need to install hostapd, that will allow the bPi to function as an access point:
sudo apt-get install hostapd-ap6210
Configure hostapd by editing /etc/hostapd/hostapd.conf and changing the following settings:
interface = wlan1
ssid = ...YOUR SSID NAME...
wpa_passphrase = ...PASSWORD...
Now we can run hostapd:
sudo hostapd -B /etc/hostapd/hostapd.conf
If hostapd doesn't work
If you get a "invalid/unknown driver 'nl80211'" error[2], nl80211 driver support is not available and we will have to compile hostapd from source.
First, install the packages needed to compile:
sudo apt-get install gcc git make
sudo apt-get install libnl-3-dev libnl-genl-3-dev openssl libssl-dev pkg-config
Then, get the source for hostapd:
cd /tmp
git clone git://w1.fi/srv/git/hostap.git
Then, configure it:
cd hostap/hostapd
cp defconfig .config
Edit .config and make sure that CONFIG_DRIVER_NL80211=y and CONFIG_LIBNL32=y are active i.e. not commented out.
Then, compile and install it:
make
sudo cp hostapd /usr/sbin/
If hostapd still doesn't work 😐
In my case, it turns out I need to use a customized version of hostapd provided by RealTek :wall: Fortunately, Jens Segers has done some work to make this process easier.
If you already installed the distro hostapd, remove it:
sudo apt-get autoremove hostapd
Install the packages needed to compile:
sudo apt-get install gcc make
sudo apt-get install openssl libssl-dev
Get the source for the customized hostapd:
cd /tmp
wget https://github.com/jenssegers/RTL8188-hostapd/archive/v2.0.tar.gz
tar -zxvf v2.0.tar.gz
Compile hostapd:
cd RTL8188-hostapd-2.0/hostapd
make
sudo make install
Configure hostapd.conf as described above, then try to start it:
sudo service hostapd start
Configuring DHCP
Hopefully, you should be able to connect to the access point, so we now need to update our DHCP server to assign IP addresses to any computers connecting via WiFi.
Edit /etc/default/isc-dhcp-server and add the new interface to INTERFACES.
Edit /etc/dhcp/dhcpd.conf and add another subnet entry e.g.
subnet 10.0.1.0 netmask 255.255.255.0 {
range 10.0.1.100 10.0.1.199 ;
option domain-name-servers 10.0.0.1 ;
option routers 10.0.0.1 ;
}
Then restart the DHCP service:
sudo service isc-dhcp-server restart
You should now be able to connect to the access point, be given an IP address, and get online.
=============================== Setting up DNS-based ad-blocking ==============
Ad-blocking is becoming more of a necessity these days, and while there are many browser plugins that will strip ads, they can be slow and memory-hungry. Blocking ads via DNS is an interesting alternative, that can work along-side browser-based solutions, and because it's done on the gateway, any computer that connects to the internet via the gateway will benefit from it.
Normally, when a browser wants to load an ad from, say, annoyingads.com, it will do a DNS lookup on that domain name to get its IP address, and then send a request to that IP address to get an ad:
However, since we are running our own DNS server, we can circumvent this process by returning something different for the IP address. If we set up a dummy web server and return the IP address of that[1], when the browser tries to load an ad, it will connect to that instead of the real server. If we configure the dummy web server to return blank images, then all we will see in the web page is empty space, rather than ads.
Installing the web server
Download the daemon version of pixelserv from here and save it somewhere e.g. /root/bin/pixelserv.pl.
Make sure it's executable, then run it:
sudo chmod 755 pixelserv.pl
sudo ./pixelserv.pl
Open a browser and go to http://10.0.0.1 - you should receive a 1x1 image.
Setting up the DNS blacklist
Save the script below somewhere (e.g. /root/bin/get-dns-blacklist.sh), which downloads a list of domains known to serve ads:
curl "http://pgl.yoyo.org/adservers/serverlist.php?hostformat=bindconfig&showintro=0&mimetype=plaintext" \
| sed -e "s/null.zone.file/\/etc\/bind\/db.pixelserv/" \
>/etc/bind/zones.blacklist
service bind9 reload
Make sure it's executable, then run it:
sudo chmod 755 get-dns-blacklist.sh
sudo ./get-dns-blacklist.sh
Check that the zones blacklist file (/etc/bind/zones.blacklist) was created and populated.
Create a zone file e.g. /etc/bind/db.pixelserv:
$TTL 86400 ; one day
@ IN SOA ads.example.com. hostmaster.example.com. (
2001010100 ; serial number YYYYMMDDNN
28800 ; refresh 8 hours
7200 ; retry 2 hours
864000 ; expire 10 days
86400 ) ; min ttl 1 day
NS my.dns.server.org
A 10.0.0.1
@ IN A 10.0.0.1
* IN A 10.0.0.1
Then, add an entry to /etc/bind/named.conf.local:
include "/etc/bind/zones.blacklist" ;
Test the ad-blocker
The DNS server should still be running with the old settings, so if we do a DNS lookup on a blacklisted domain, it should still resolve to the real IP address:
Now, if we restart the DNS server:
sudo service bind9 restart
and run the same query again, we can see that the returned IP address is different, and the domain name is now pointing to us.
This means that any attempt to connect to 101com.com will actually connect to our pixelserv web server, and any attempt to retrieve an ad will receive a 1x1 image instead.
Configuring the ad-blocker to run at system startup
We want pixelserv to run at startup, so we can add it to /etc/rc.local:
# start pixelserv
/root/bin/pixelserv.pl &
Note the trailing ampersand, which tells the script to run in the background, so that the startup script doesn't block, waiting for it to finish.
We also want to regularly download the latest blacklist, so we schedule a cron job to run the download script:
sudo crontab -e
In the screenshot, I've set the job to run at 3am every day, and to log the output, in case there are problems.
=============================== Setting up OpenVPN ============================
A virtual private network gives you better security and privacy as you work online. An encrypted connection to a VPN server is created, and all your internet traffic is sent over that connection, where it is forwarded on to the real destination by the VPN server. Anyone snooping on your internet traffic will be able to see that you are using a VPN, but will not be able to decipher any of it, due to the encryption.
Installing and configuring OpenVPN
By far, the most popular open-source VPN software is OpenVPN, and to install it:
sudo apt-get install openvpn
Your VPN provider will supply you with configuration files (usually with a .ovpn extension) for connecting to their servers. Save these in /etc/openvpn/.
You will also be given a username and password - put these in a password file (e.g. /etc/openvpn/auth) with the username on the first line, and password on the second line.
We also need to change NAT so that internet requests are forwarded on to the VPN tunnel, instead of the normal network interface:
sudo iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
Now we can run OpenVPN[1]:
sudo openvpn \
--cd /etc/openvpn/ \
--config XXX.ovpn \
--auth-user-pass /etc/openvpn/auth \
--auth-nocache \
-keepalive 10 60
You can check your external IP address like this:
wget http://ipinfo.io/ip -qO -
Or just fire up a browser and go to http://ipinfo.io.
Configuring OpenVPN to run at startup
To get OpenVPN to run at startup, you need to change[2] the iptables command to forward internet requests to the tun0 interface instead of wlan0 (as described above).
Then, add the openvpn comand you used above, but with an extra --daemon parameter, so that it will run in the background.
=============================== Setting up an email relay =====================
Email is messy. It's often useful to have programs send emails[1], so rather than forcing everyone to deal with the messiness themselves, it's often easier to set up an email relay. This is a program that runs on the gateway and accepts email from computers on the internal network, then forwards them on to the real recipients on the internet.
Unfortunately, if we just send out the email ourself, there is virtually no chance of it reaching its destination, since most mail servers will not accept email from other servers unless they meet some stringent requirements[2]. So, the best way for our relay to forward emails is to forward them on again to another email server, one that is known to be acceptable to the internet at large, and have it send the email to the real recipient.
Installing and configuring the mail server
A popular mail server is exim4, and installing it is easy:
sudo apt-get install exim4
Edit[3] /etc/exim4/update-exim4.conf.conf and set the following options:
dc_eximconfig_configtype = 'satellite'[4]
dc_other_hostnames = ''
dc_local_interfaces = '10.0.0.1'[5]
dc_readhost = 'gateway'[6]
dc_smarthost = 'myserver.com'[7]
dc_hide_mailname = 'true'
dc_relay_nets = '10.0.0.0/24'[8]
Edit /etc/mailname and put in the host name of the gateway machine.
Your email server will almost certainly require authentication, so edit /etc/exim4/passwd.client and set the login details there e.g.
myserver.com:USERNAME:PASSWORD
Finally, edit /etc/email-addresses and set the email addresses you want to use when sending emails from each user account e.g.
taka: taka@myserver.com
root: root@myserver.com
This means that if I send an email when logged on as taka, it will appear to have come from taka@myserver.com, or root@myserver.com if I am logged on as root.
Now everything has been configured, we can install the new settings and restart the server:
sudo update-exim4.conf
sudo /etc/init.d/exim4 restart
Testing the email relay
Open a new console window and monitor the exim4 log file:
sudo tail -f /var/log/exim4/mainlog
Back in the original console, install the command-line email program and send a test email:
sudo apt-get install mailutils
echo body | mail -s "relay test" me@somewhere.com
In the exim4 log, you should see it receive the email, forward it on, and a few seconds later, hopefully receive it at the final email address.
A warning about relaying email
We have configured our relay to not require any login, so we need to be very careful about restricting access to it - spammers just love it when they find an open email relay 🙁 Above, we configured dc_relay_nets so that we only relay emails from computers on our internal network, and you should also control access via the firewall, so the only danger is if one your computers in your internal network gets compromised, since it will be able to send out emails without restriction. Keep an eye on your logs!
=============================== Setting up a firewall =========================
Of all the extra services we can have on our gateway, a firewall is the most useful and should be considered mandatory. In this section, we will block all incoming and outgoing traffic, and then selectively enable only the traffic we want to allow e.g. web browsing, email, etc.
Before we start, it's important to understand the different types of messages that can be sent over a network:
TCP messages
These are sent between two computers who have set up a connection between themselves, and are usually request/response i.e. one computer sends the other one a message, which then sends back a reply.
UDP messages
These are "shouted out" on the network, to whoever might be listening[1]. In this case, there is no concept of request/response, it's just computers broadcasting messages to the network to whoever might be there.
ICMP messages
These are low-level messages that computers send to each other to pass information about the state of the network itself.
We will use the iptables program to manage the firewall, which uses tables and chains. A chain is just a list of rules that are checked, in order, to see if a message should be allowed through, or blocked. There are several standard tables, but we will only be using the following:
filter
This table is used when checking whether to allow packets in or out, and has the following chains:
INPUT: a list of rules that control what packets are allowed in
OUTPUT: a list of rules that control what packets are allowed out
FORWARD: a list of rules that control what packets can be forwarded on
NAT
This table is used to control NAT, and we will use only the following chain:
POSTROUTING: allows packets to be modified after they have been routed
So, for example, if a program running on a computer on the internal network wants to send out an email, it must go through the gateway[2], and iptables will go to the filter table and check all the rules in the OUTPUT chain, looking for one that specifies whether outgoing email is allowed or blocked.
Basic configuration
WARNING! It is very easy to lock yourself out of the computer if you make a mistake configuring the firewall. If you're using a keyboard and monitor that have been plugged in, it doesn't matter, but if you're connecting via SSH and accidentally block SSH traffic, you will no longer be able to communicate with the computer!
This is especially bad if you've set the firewall rules to be applied when the system boots up, since even a reboot won't clear the problem - SSH will still be blocked and so the only solution will be to plug in a keyboard and monitor, or restore the SD card from a backup. For this reason, it's a good idea to disable firewall configuration in /etc/rc.local when you're working on it, until you're sure everything is OK.
We will start[3] by configuring the firewall to block everything, and log anything that gets blocked:
# clear out any existing configuration
iptables --flush
iptables --delete-chain
# set the default policy to DROP
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP
# TODO: allow selected traffic through
# create a custom chain that logs packets, then drops them
iptables -N LOGDROP
# nb: the first "limit-burst" packets will be logged, then only "limit" per minute
iptables -A LOGDROP \
-m limit --limit-burst 10 --limit 10/m \
-j LOG --log-prefix "iptables: "
iptables -A LOGDROP -j DROP
# log and drop any remaining packets (nb: this must appear last)
iptables -A INPUT -j LOGDROP
iptables -A OUTPUT -j LOGDROP
iptables -A FORWARD -j LOGDROP
The first few lines reset everything, then set the default policy[4] to DROP.
Near the end of the script, we create a custom chain called LOGDROP, and add a rule[5] to it to log the packet[6][7], and then another rule that drops the packet.
At the very end of the script, we add a final rule to each of the filter chains that tell iptables to jump to the LOGDROP chain. In other words, for each packet, iptables will scan a chain looking for a rule that allows or rejects it (we will add these next), but if it doesn't find a match, the last rule tells it to jump to the LOGDROP chain, which will log the packet, and then drop it.
Allowing TCP responses
The first type of traffic we want to allow through are TCP responses. If a request has been issued by a computer on the internal network, we always want to allow its response to come back in:
iptables -A INPUT -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
So, we append a rule to the INPUT chain (-A INPUT) that says: for TCP traffic[8] (-p tcp), check the connection's state (-m state) and if it's either ESTABLISHED[9] or RELATED[10], then allow the packet through (-j ACCEPT).
We also add a similar rule to the OUTPUT chain i.e. if we've already allowed a request to come in from outside, then allow the response to go out:
iptables -A OUTPUT -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
And also to the FORWARD chain:
iptables -A FORWARD -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
Allowing loopback traffic
Next, we allow all traffic on the loopback interface i.e. if a program on the computer is trying to communicate with another program on the same computer, always let that traffic through:
iptables -A INPUT -i lo -j ACCEPT
In other words, append a rule to the INPUT chain (-A INPUT) that says: traffic coming in on the loopback interface (-i lo) should be allowed (-j ACCEPT).
Similarly, we add a rule to the OUTPUT chain that allows all traffic going out on the loopback interface:
iptables -A OUTPUT -o lo -j ACCEPT
Allowing ICMP traffic
ICMP traffic is most commonly known as used by ping, but there's a whole lot more to it than that. In particular, DNS will get slow if you block it, so while there are certain types of attack that can be made using ICMP traffic, for a home gateway, you're probably better off just allowing everything:
iptables -A INPUT -p icmp -j ACCEPT
iptables -A OUTPUT -p icmp -j ACCEPT
iptables -A FORWARD -p icmp -j ACCEPT
Allowing DNS traffic
DNS uses UDP, so to allow outgoing DNS traffic, we need to add a rule that allows UDP messages to go in and out on port 53 :
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A INPUT -p udp --sport 53 -j ACCEPT
However, certain types of DNS traffic uses TCP, so we add a rule that allows TCP traffic to go out on port 53, but only for new connections:
iptables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
Note that there's no need to add a rule to allow the responses back in, since we added a generic rule earlier that allows all traffic for ESTABLISHED connections.
We add similar rules to allow incoming DNS traffic:
iptables -A INPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p udp --sport 53 -j ACCEPT
iptables -A INPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
Allowing DHCP traffic
DHCP also runs over UDP (on ports 67 and 68), so while we could add rules similar to those we added above for DNS, we want to add an extra restriction: only allow this traffic for computers on the internal network.
My computers connect to the gateway either via wired ethernet (on the eth0 interface) or via WiFi (on the wlan1 interface), so my rules look like this:
iptables -I INPUT -i eth0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT
iptables -I OUTPUT -o eth0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT
iptables -I INPUT -i wlan1 -p udp --dport 67:68 --sport 67:68 -j ACCEPT
iptables -I OUTPUT -o wlan1 -p udp --dport 67:68 --sport 67:68 -j ACCEPT
My gateway computer connects to the internet via the wlan0 interface, but since the rules above are explicitly for the eth0 and wlan1 interfaces, any DHCP traffic on the wlan0 interface will be dropped.
Allowing SSH traffic
I only ever want to SSH in[11] to my gateway computer, from another computer on my internal network, so my rule for SSH traffic looks like this:
iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
For safety, I explicitly allow incoming traffic for ESTABLISHED connections, and I also explicitly allow outgoing traffic:
iptables -A OUTPUT -o eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
These rules are important - if I make a mistake here, I will get locked out! :wall:
Allow incoming HTTP traffic
If you've set up the DNS-based ad-blocker, you will need to allow incoming requests to the pixel server:
iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW -j ACCEPT
Allow incoming email traffic
If you've set up email relaying, you will need to allow incoming email:
iptables -A INPUT -i eth0 -p tcp --dport 25 -m state --state NEW -j ACCEPT
Note that it's critical to only allow this traffic from the local network, otherwise somebody from the outside could connect to your mail server and use it to send spam.
Allowing other common traffic
The following rules allow outgoing HTTP/HTTPS traffic[12]:
PORTS=http,https
iptables -A OUTPUT -p tcp -m multiport --dports $PORTS -m state --state NEW -j ACCEPT
Note that we can use descriptive names for the ports[13] and again, we allow outgoing traffic for new connections only, responses are allowed by the generic "allow responses" rule defined earlier.
We also want to allow NTP traffic[14], which runs over UDP:
PORTS=ntp
iptables -A OUTPUT -p udp -m multiport --dports $PORTS -j ACCEPT
iptables -A INPUT -p udp -m multiport --sports $PORTS -j ACCEPT
Note that these rules apply only to traffic that originates on the gateway computer; for traffic that originates on another computer on the internal network, we need to add rules to the NAT table, which we'll do next.
Allowing common traffic over NAT
As described in the section on setting up NAT, we need a rule to enable NAT:
# clear out any existing configuration
iptables -t nat --flush
iptables -t nat --delete-chain
# enable NAT
iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
We can then allow outgoing traffic[15] for selected services:
PORTS=http,https,pop3,pop3s,imap,imaps,smtp,smtps,587,ssh,ftp
iptables -A FORWARD \
-i eth0 -o wlan0 \
-p tcp -m multiport --dports $PORTS \
-m state --state NEW \
-j ACCEPT
Similarly, we allow UDP traffic for selected services:
PORTS=ntp
iptables -A FORWARD \
-i eth0 -o wlan0 \
-p udp -m multiport --dports $PORTS \
-j ACCEPT
iptables -A FORWARD \
-i eth0 -o wlan0 \
-p udp -m multiport --sports $PORTS \
-j ACCEPT
I also allow web traffic only from my WiFi-connected devices:
PORTS=http,https
iptables -A FORWARD \
-i wlan1 -o wlan0 \
-p tcp -m multiport --dports $PORTS \
-m state --state NEW \
-j ACCEPT
Allowing VPN traffic
If you are running OpenVPN, you will need the following rules to allow it to work:
iptables -A OUTPUT -o wlan0 -p udp --dport 1194 -j ACCEPT
iptables -A INPUT -i wlan0 -p udp --sport 1194 -j ACCEPT
If you are running OpenVPN, any rules that specify the interface you use to connect to the internet (in my case, wlan0), need to be changed to refer to OpenVPN's tunnel interface instead i.e. tun0. The script supplied below takes care of this.
Putting it all together
There are a lot of rules described here, and the situation is complicated by OpenVPN, since the rules are different depending on whether or not you are using it, so I've put together a shell script[16] that takes care of everything, and can either:
disable the firewall
enables the firewall (with OpenVPN running)
enables the firewall (with OpenVPN not running)
You will obviously want to customize the rules for your own situation, but it's a good starting point, and once you've got it working the way you want, add it to /etc/rc.local so that your rules will be applied when the system boots up.
Monitoring blocked packets
At the very beginning of this section, we set things up so that any dropped packets get logged. By default, these appear in /var/log/messages, but since there will be a lot of these messages, it's far more convenient to collect them in a separate file. To do this, create the file /etc/rsyslog.d/iptables.conf that looks like this:
:msg, contains, "iptables: " -/var/log/iptables
& ~
This tells the logging daemon (rsyslogd) to look for messages that contain the string "iptables: " and send them to another file (/var/log/iptables).
Since we've created a new log file, it's a good idea to set up log rotation for it. Create the /etc/logrotate.d/iptables file like this:
/var/log/iptables
{
rotate 7
daily
missingok
notifempty
delaycompress
compress
postrotate
invoke-rc.d rsyslog rotate > /dev/null
endscript
}
Finally, the log file contains a lot of information about each packet that we're usually not interested in, so next script will filter out the less-important stuff and provide an easier-to-read log.
Script filter-iptables.py:
#!/usr/bin/python
# This script extracts key information from the iptables log file.
# Full tutorial is here: http://awasu.com/weblog/bpi-gateway/firewall/
#
# To process the entire log file:
# sudo filter-iptables.py </var/log/iptables
#
# Or to monitor the log file in real-time:
# sudo tail -f /var/log/iptables | filter-iptables.py
# COPYRIGHT: (c) Awasu Pty. Ltd. 2016 (all rights reserved).
# Unauthorized use of this code is prohibited.
#
# LICENSE: This software is provided 'as-is', without any express
# or implied warranty.
#
# In no event will the author be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software
# for any purpose, and to alter it and redistribute it freely,
# subject to the following restrictions:
#
# - The origin of this software must not be misrepresented;
# you must not claim that you wrote the original software.
# If you use this software, an acknowledgement is requested
# but not required.
#
# - Altered source versions must be plainly marked as such,
# and must not be misrepresented as being the original software.
# Altered source is encouraged to be submitted back to
# the original author so it can be shared with the community.
# Please share your changes.
#
# - This notice may not be removed or altered from any
# source distribution.
import sys
import os
import re
import time
# ---------------------------------------------------------------------
# This table defines log messages we want to filter out.
IGNORE_RULES = [
{ "proto": "UDP" , "in": "eth0" , "dpt": (137,138) } , # NetBIOS
{ "proto": "UDP" , "out": "eth0" , "dpt": (137,138) } , # NetBIOS
{ "proto": "UDP" , "in": "eth0" , "dpt": (67,68) } , # DHCP
{ "proto": "UDP" , "out": "eth0" , "dpt": (67,68) } , # DHCP
{ "proto": "UDP" , "in": "wlan1" , "dpt": (67,68) } , # DHCP
{ "proto": "UDP" , "out": "wlan1" , "dpt": (67,68) } , # DHCP
]
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def check_rule( rule , vals ) :
""" Check if an ignore rule matches a log message. """
for val_name,target_vals in rule.items() :
if not vals.has_key( val_name ) :
return False
if type(target_vals) not in (tuple,list) :
target_vals = [ target_vals ]
target_vals = [ str(v) for v in target_vals ]
if vals[val_name] not in target_vals :
return False
return True
# ---------------------------------------------------------------------
def make_addr( ip_addr , port_no ) :
if ip_addr and port_no :
return ip_addr + ":" + port_no
if ip_addr :
return ip_addr
if port_no :
return ":" + port_no
return ""
# ---------------------------------------------------------------------
while True :
# read the next line
line_buf = sys.stdin.readline()
if not line_buf : break
vals = { mo.group(1).lower(): mo.group(2) for mo in re.finditer( "([A-Z]+?)=(\S*)" , line_buf ) }
vals["_timestamp_"] = line_buf[:15]
vals["_src_"] = make_addr( vals.get("src") , vals.get("spt") )
vals["_dest_"] = make_addr( vals.get("dst") , vals.get("dpt") )
# filter out entries we're not interested in
if any( check_rule(r,vals) for r in IGNORE_RULES ) :
continue
# output the next line
print "{_timestamp_} | proto={proto:4} in={in:5} out={out:5} src={_src_:21} dest={_dest_:21}".format( **vals )
0