Posts Tagged ‘iptables’
With residential broadband, you only get one IP address typically and that isn’t even usually static – pretty unsuitable for servers at home. You can get round this problem with dynamic DNS services which allow you to update your DNS record each time your ISP-assigned IP address changes. But this still means that the only machine visible to the Internet at large is that server and only that server. All your other machines behind your router have DHCP-assigned IP addresses which are private to that internal network, like 192.168.1.43. These machines are not visible to the Internet, only the router is.
A router is really just a specialised computer server that directs Internet traffic to the right destination. A Linux server itself might be the gateway server. Indeed, to get dynamic DNS to work, you also have to configure traffic to route the traffic from the outside to your Linux server.
For the purposes of this article, I’ll assume the router and gateway are the same machine. If not, the router will be configured in such a way that it routes all traffic to the gateway server so that, in practical terms, they are the same.
So what happens if you want to have a web server on a separate machine on your network? Well, technically you can’t – it only has a private (or ‘local’) IP address. But you can cheat using a method called port forwarding. This allows the gateway server to change the destination and source of the data packets based on certain criteria dynamically which allows packets destined for the gateway server to be rewritten for the new web server with it’s private IP address. The gateway server also needs to be able to send packets back out of the local network to the originating client because the internal web server will only be talking to the gateway server so it’s packets source address needs to be rewritten.
So we need a mechanism that that distinguish between traffic meant for the gateway server and traffic
So if Internet traffic is hitting the gateway server with it’s static IP address (or at least host name and DynDNS), that traffic is destined for the gateway server. It doesn’t know anything about the web server on the internal network. The gateway server’s firewall has to decide which packets are for itself and which packets should be passed to the web server which only the gateway server knows about. The easiest way to distinguish the traffic is send the web server traffic to another non-standard port. I’ll use 7070.
Think about two web servers in this scenario, the gateway and internal. Normal web traffic will hit the gateway server (public IP address of 89.232.33.123 and therefore visible to the Internet) on the standard port 80. Web traffic for the internal web server will hit port 7070 on the same gateway server and the it knows to re-route the destination for each packet to the internal private IP address of 192.168.1.43 because of the non-standard port.
So if a client requests 89.232.33.123:80 they’ll get a reply from the gateway server as normal. If a client requests 89.232.33.123:7070 (note the same IP address of the gateway) the client will get a reply that appears to be from 89.232.33.123 but is actually from 192.168.1.43, the internal web server.
If you want more than one machine visible to the outside world but only have the one static IP address, you can configure the gateway Linux server to to route traffic to anywhere you like using iptables. While iptables is primarily a firewall, the rules you can set up to deny or allow a specific packet are flexible enough to allow traffic routing as well.
On the gateway server which is accessible from the Internet, open the iptables configuration file.
vi /etc/sysconfig/iptables-config
Add the following line: -
IPTABLES_MODULES="iptable_nat"
This adds the Network Address Translation (NAT) kernel module to the system so that iptables can use NAT. This will allow iptables to hold the packets before comparing them to the NAT table we will create.
Open a new file under /etc/sysconfig called iptables.NAT-RULES.
vi /etc/sysconfig/iptables.NAT-RULES
This will hold all our iptables configuration rules. First thing we need to do define our network address translation rules. The first rules will be to reset everything so that nothing gets in.
*nat
:PREROUTING DROP [0:0]
:OUTPUT DROP [0:0]
:POSTROUTING DROP [0:0]
Next, we’ll switch on masquerading. This is known as “many-to-one NAT”. In other words, traffic from all devices on one or more protected networks will appear as if it originated from a single IP address on the Internet side of the firewall. The masquerade IP address always defaults to the IP address of the firewall’s main interface. The advantage of this is that you never have to specify the NAT IP address. This makes it much easier to configure iptables NAT with DHCP, which we are doing as the internal web server has a DHCP-assigned address rather than a static address.
-A POSTROUTING -o eth0 -j MASQUERADE
-A PREROUTING -i eth0 -p tcp --dport 7070 -j DNAT --to-destination 192.168.1.43:80
COMMIT
The next line is the real “meat” of the NAT table. If the gateway gets a data packet on port 7070 of the main ethernet card (eth0), it should send it on to 192.168.1.43 on the internal network on it’s port 80 (HTTP traffic). These rules are then committed to the NAT table. Next, iptables deals the filters – the main firewall configuration which will allow or deny data packets.
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
Again, you can see the configuration is reset first. Next, comes the first section of filter rules which are the INPUT rules, or the rules governing what the firewall does with incoming packets.
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
The first line allows data packets through to wherever they want to go assuming that the connection has already been established. The next line simply states that we will accept ICMP or (‘ping’) packets used for network diagnostics. The next section deals with the actual ports that the firewall will accept.
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7070 -j ACCEPT
The first line above appends a new rule to the rule-set accepting new connections on TCP port 22 (which is the port for remote access SSH). The next line does the same for our custom 7070 port which will handle web traffic from the internal web server. Next we come to the FORWARDing rules for the firewall, what it does with the packets it’s accepting.
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
-A FORWARD -i lo -j ACCEPT
-A FORWARD -o eth0 -j ACCEPT
-A FORWARD -p 7070 -m state --state NEW -m tcp -p tcp -d 192.168.1.43 --dport 80 -j ACCEPT
Again, it’ll accept established connections for forwarding, these are default settings for the most part. The last line is the most important, where it forwards the port 7070 to the internal web server.
Here is the completed iptables rule-set for NAT and filtering.
*nat
:PREROUTING DROP [0:0]
:OUTPUT DROP [0:0]
:POSTROUTING DROP [0:0]
-A POSTROUTING -o eth0 -j MASQUERADE
-A PREROUTING -i eth0 -p tcp --dport 7070 -j DNAT --to-destination 192.168.1.43:80
COMMIT
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7070 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
-A FORWARD -i lo -j ACCEPT
-A FORWARD -p 7070 -m state --state NEW -m tcp -p tcp -d 192.168.1.43 --dport 80 -j ACCEPT
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
This concludes the quick n’ dirty guide to port forwarding with iptables :-)
Sometimes, let’s be frank, you just want to block certain countries from accessing your server. This may be for legal reasons or because…ahem…certain countries are more likely to have hackers and other miscreants attempting to do bad things to your server. Depending on your server’s purpose, it may be easier to simply block the offending countries from accessing your server completely.
As explained in this article, I have set iptables to have a file called /etc/sysconfig/iptables.BACKUP, which is a set of iptables rules that open all the default ports that I want for the services my server offers. It’s exactly the same as the /etc/sysconfig/iptables file, except that it’s backed up. This way, whenever I do anything to the iptables ruleset, I can always be sure that the services I want are available, regardless of whatever else I do with iptables. This becomes important when using the script I am about to show you…
1 #!/bin/bash
2 TEMP_DIR=/tmp/
3 LANG_SETTINGS_DIR=/root/langblock.cfg
4 /bin/cp -rf /etc/sysconfig/iptables.BACKUP /etc/sysconfig/iptables
5 /etc/rc.d/init.d/iptables restart
6 for lang in $(cat ${LANG_SETTINGS_DIR})
7 do
8 wget --output-document=${TEMP_DIR}${lang}.zone http://www.ipdeny.com/ipblocks/data/countries/${lang}.zone
9 for line in $(cat ${TEMP_DIR}${lang}.zone)
10 do
11 /sbin/iptables -I INPUT -s ${line} -i eth0 -j DROP
12 echo "Adding...$line from file: ${TEMP_DIR}${lang}.zone"
13 done
14 /bin/rm ${TEMP_DIR}${lang}.zone
15 done
16 exit 0
Save this as “ipblock.sh” or somesuch in your root user directory and make it executable with: -
chmod u+x ipblock.sh
Next, you’ll need to create a text file called “langblock.cfg”. You can obviously change the path to this file in LANG_SETTINGS_DIR. This will contain international two-letter country codes, a list of which is available here. Each two-letter country code must be on a separate line like: -
country1
country2
...
country7
…and so on. Next, you’ll have to make sure you have the “wget” utility installed. This can be installed on Fedora with: -
yum install wget
and…
sudo apt-get install wget
…under Ubuntu. Once you’ve got all this set up, run the script above. It’s fairly simple as scripts go – first, it reloads your default iptables ruleset from /etc/sysconfig/iptables.BACKUP into /etc/sysconfig/iptables and restarts the iptables firewall. Next, it steps through each line in your langblock.cfg config file and downloads the IP address blocks for that country from ipdeny.com using wget to the /tmp directory. It then reads this file, adding each IP address block to the iptables ruleset as IP addresses to DROP upon connection. Once the end of the file is reached, the file is deleted from the /tmp directory and the program moves on to the next country code in the langblock.cfg file or, if it’s the end of the file, it quits.
You have now successfully block those countries from accessing your server :-) You could add this script to a cronjob to run every month to refresh the list if you like. Like I said, this will probably be considered by some to be an extreme measure to take in the name of security, but should you have the need to do this, you can.
Last time I wrote about basic server security, I talked a little about ports, firewalls and SSH. You’ll probably notice after a time that you get lots of failed attempts to connect in your /var/log/secure system logs, sometimes directly to SSH but especially if you use such juicy services as FTP. These are more likely to be automated scripts running against your server rather than somebody actually trying to get in as they tend to use accounts such as “Administrator” to try and gain access and they’ll hit these accounts over and over with common passwords. While these are annoying, it would be useful to block such attempts at the system firewall level. This is where a little tool called fail2ban becomes handy. It’s a tool that monitors your system logs (such as /var/log/secure) for failed connection attempts and when a certain threshold is reached, will dynamically add rules to iptables to ban that IP address for a certain amount of time. Usually this is enough for the attacking host to give up and move on. It sure beats manually adding IP addresses to your firewall rules for each attacker and I’ve found it incredibly useful. I often get entries such as the following in my /var/log/secure system log: -
Jan 23 14:04:14 Fedora6Srv1 vsftpd: pam_unix(vsftpd:auth): authentication failure; logname= uid=0 euid=0 tty=ftp ruser=Administrator rhost=123.123.123.123
Here, a remote host “123.123.123.123″ is trying to use a default Administrator account on FTP to try and gain access and this is the sort of thing that fail2ban will monitor. fail2ban is available through most distribution repositories, but since I use Fedora, this is the command I’ll use to install it: -
yum install fail2ban
Once this is done, you’ll need to edit the /etc/fail2ban/jail.conf which controls the default behaviour of fail2ban. You can leave the /etc/fail2ban/fail2ban.conf on the defaults for the most part.
The first section you’ll see is [DEFAULT], which are settings that apply to all other sections below it unless specifically overridden.
[DEFAULT]
ignoreip = 127.0.0.1 192.168.1.172
bantime = 1000
findtime = 1000
maxretry = 3
backend = auto
“ignoreip” contains a list of space-separated IP addresses that you wish fail2ban to ignore. Here I’ve set the localhost IP address to be ignored, else fail2ban will ban itself from connecting and another machine in my local network.
“bantime” is the default number of seconds a specific host should be banned for in seconds. “findtime” is a setting that states that a host is banned if it has generated “maxretry” attempts during the last “findtime” in seconds. “backend” should generally be left as auto.
After this, you’ll see various services that fail2ban can monitor – we’ll concentrate on SSH and vsFTPd, although you can add other by setting “enabled” from false to true.
[ssh-iptables]
enabled = true
filter = sshd
action = iptables[name=SSH, port=ssh, protocol=tcp]
sendmail-whois[name=SSH, dest=root, sender=root@localhost]
logpath = /var/log/secure
maxretry = 3
Above is the jail.conf entry for monitoring SSH. The default log path for Fedora is not correct, so I’ve set it to monitor the /var/log/secure log file as this is the log where all failed connection attempts are logged. This has been set to enabled and I’ve also set the “sendmail-whois” to mail the WHOIS details of the attacking host to the root system account – useful to see where the attacker is coming from :-) I’ve set the “maxretry” for SSH connection attempts to 3.
Next, we’ll set fail2ban to monitor vsFTPd as well.
[vsftpd-iptables]
enabled = true
filter = vsftpd
action = iptables[name=VSFTPD, port=ftp, protocol=tcp]
sendmail-whois[name=VSFTPD, dest=root@localhost]
#logpath = /var/log/vsftpd.log
logpath = /var/log/secure
maxretry = 10
bantime = 1800
The above details are similar to the entry for SSH, except that I’ve set the “logpath” to /var/log/secure again, as /var/log/vsftpd.log only logs successful connection attempts and what a connected host has downloaded. I’ve upped the “maxretry” number because if you’re using a browser-based FTP client by using ftp:// instead of http://, this sometimes logs many anonymous connections even if you’ve got a password-protected FTP account as the browser seems to attempt to connect anonymously before it thinks to ask for your login details.
Once you’ve set up the service entries pointing to the correct log files that log such connections, you’ll want to set fail2ban to start in certain Linux runlevels automatically. I’ll set it to run in runlevels 3, 4 and 5 as these are the most common.
chkconfig --level345 fail2ban on
And lastly, start the fail2ban service: -
/etc/rc.d/init.d/fail2ban start
…and you’re done. Any failed connection attempts from a specific host that exceed the number that you’ve specified to the services you’ve defined will now be banned by iptables dynamically and the WHOIS details mailed to your root account. Fail2ban logs to /var/log/fail2ban.log by default (you can change this path in the /etc/fail2ban/fail2ban.log file), so you can check that file to find out if/what hosts got banned. You can also list your current iptables firewall rules with: -
iptables -L
to verify the current firewall rules and that fail2ban is writing blocking rules if you wish. Happy banning!
