The Guru College
Auto-blocking abusive hosts with iptables
We’ll get back to HA LAMP soon, I promise. However, it’s time to stop and make sure you are running a logging host-based firewall on your servers. If you’re not, and I’m not kidding on this, you need to start right now. Look through the output of netstat -a | grep LISTEN
. My laptop has less than half a dozen ports open, and that’s after I’ve shut off most services. Here are the two most important (and most dangerous) ones:
Active Internet connections (including servers) Proto Recv-Q Send-Q Local Address Foreign Address (state) ...snip... tcp4 0 0 *.ssh *.* LISTEN tcp6 0 0 *.ssh *.* LISTEN
This represents allowing anyone (*.*) to establish an SSH connection (*.ssh) over IPv4 or IPv6 to my laptop. Unlike most users, I actually look though my system logs on a regular basis, checking for strange things like failed login attempts from Asia. I also pick good passwords, change them frequently, and never re-use them. I seriously don’t think even a rainbow tables-based attack would do me in.
Your server probably has the same ports open to the world. How often do you check the system logs looking for unauthorized logins? Even more worrying: is *.3306
in your netstat output? While MySQL has a robust internal ACL system to allow specific users to access from specific hosts, why let people even know you are running a MySQL server on the host? This is where a host-based firewall comes in handy. The fact that you can automatically block abusive hosts with a little scripting magic is just a bonus.
First things first: I’m talking about iptables
today, and I’m focusing on the systems that use it, which is mostly Linux-based OSes. I’m sure you can do similar things with ipfw
on Solaris, but you’ll have to interpret my examples as you follow along. Also, I’m going to be focusing on RHEL5/RHEL6, as that’s what I use for servers these days.
Check to see if the firewall is turned on: /sbin/service iptables status
and then see if it’s set to start at boot: /sbin/chkconfig --list iptables
. If it’s not on, and not set to start, you’ve got some work to do. The base ruleset lives in /etc/sysconfig/iptables
and this is where iptables reads and writes it’s config files from. (Yes, with the right commands, iptables saves changes you make to the running configuration so they survive host reboots etc). When iptables is running, to see the current ruleset and the hit counters against each rule, use: iptables -L -n -v
. The layout of iptables consists of “chains” of rules, with each rule processed in order. The default chains on a RHEL6 install are INPUT, OUTPUT and FORWARD. These should make sense just by their names, but if it’s not clear, the INPUT chain is processed for all inbound packets, the OUTPUT chain for all outbound traffic, and the FORWARD chain is for all traffic being bridged or routed elsewhere (uncommon, unless using keepalived as a router, or when using KVM or Xen). These base chains is where we manage things like SSH and MySQL traffic, and lock it down to networks you control.
To add a rule to the INPUT chain, that allows MySQL connections from the private IP address range 10.0.15.0/24:
sudo /sbin/iptables -A INPUT -m state --state NEW \<br />
-m tcp -p tcp -s 10.32.1.0/24 --dport 3306 -j ACCEPT
</p>
<p>This appends a rule to the end of the INPUT chain. If you already have a default deny rule at the end of the chain, you'll need to use an insert rule instead, and insert your MySQL rule above the default deny rule. If the default deny rule is in position 12, you'll want to insert the new rule into position 12 (which then bumps the deny rule to position 13):</p>
<p>
sudo /sbin/iptables -I 12 INPUT -m state --state NEW \<br />
-m tcp -p tcp -s 10.32.1.0/24 --dport 3306 -j ACCEPT
``We'll get back to HA LAMP soon, I promise. However, it's time to stop and make sure you are running a logging host-based firewall on your servers. If you're not, and I'm not kidding on this, you need to start _right now_. Look through the output of
netstat -a | grep LISTEN`. My laptop has less than half a dozen ports open, and that’s after I’ve shut off most services. Here are the two most important (and most dangerous) ones:
Active Internet connections (including servers) Proto Recv-Q Send-Q Local Address Foreign Address (state) ...snip... tcp4 0 0 *.ssh *.* LISTEN tcp6 0 0 *.ssh *.* LISTEN
This represents allowing anyone (*.*) to establish an SSH connection (*.ssh) over IPv4 or IPv6 to my laptop. Unlike most users, I actually look though my system logs on a regular basis, checking for strange things like failed login attempts from Asia. I also pick good passwords, change them frequently, and never re-use them. I seriously don’t think even a rainbow tables-based attack would do me in.
Your server probably has the same ports open to the world. How often do you check the system logs looking for unauthorized logins? Even more worrying: is *.3306
in your netstat output? While MySQL has a robust internal ACL system to allow specific users to access from specific hosts, why let people even know you are running a MySQL server on the host? This is where a host-based firewall comes in handy. The fact that you can automatically block abusive hosts with a little scripting magic is just a bonus.
First things first: I’m talking about iptables
today, and I’m focusing on the systems that use it, which is mostly Linux-based OSes. I’m sure you can do similar things with ipfw
on Solaris, but you’ll have to interpret my examples as you follow along. Also, I’m going to be focusing on RHEL5/RHEL6, as that’s what I use for servers these days.
Check to see if the firewall is turned on: /sbin/service iptables status
and then see if it’s set to start at boot: /sbin/chkconfig --list iptables
. If it’s not on, and not set to start, you’ve got some work to do. The base ruleset lives in /etc/sysconfig/iptables
and this is where iptables reads and writes it’s config files from. (Yes, with the right commands, iptables saves changes you make to the running configuration so they survive host reboots etc). When iptables is running, to see the current ruleset and the hit counters against each rule, use: iptables -L -n -v
. The layout of iptables consists of “chains” of rules, with each rule processed in order. The default chains on a RHEL6 install are INPUT, OUTPUT and FORWARD. These should make sense just by their names, but if it’s not clear, the INPUT chain is processed for all inbound packets, the OUTPUT chain for all outbound traffic, and the FORWARD chain is for all traffic being bridged or routed elsewhere (uncommon, unless using keepalived as a router, or when using KVM or Xen). These base chains is where we manage things like SSH and MySQL traffic, and lock it down to networks you control.
To add a rule to the INPUT chain, that allows MySQL connections from the private IP address range 10.0.15.0/24:
sudo /sbin/iptables -A INPUT -m state --state NEW \<br />
-m tcp -p tcp -s 10.32.1.0/24 --dport 3306 -j ACCEPT
</p>
<p>This appends a rule to the end of the INPUT chain. If you already have a default deny rule at the end of the chain, you'll need to use an insert rule instead, and insert your MySQL rule above the default deny rule. If the default deny rule is in position 12, you'll want to insert the new rule into position 12 (which then bumps the deny rule to position 13):</p>
<p>
sudo /sbin/iptables -I 12 INPUT -m state --state NEW \<br />
-m tcp -p tcp -s 10.32.1.0/24 --dport 3306 -j ACCEPT
``sudo /sbin/iptables -N INPUT-AUTO`
and then add rules in the existing chains to jump to them:
sudo /sbin/iptables -I 1 INPUT -j INPUT-AUTO
This will insert the jump rule into position 1, and all traffic will now flow through the INPUT-AUTO chain. However, INPUT-AUTO is empty, so t’s just going to jump there and jump back out. To add a rule to INPUT-AUTO for testing, use:
sudo /sbin/iptables -I INPUT-AUTO -j LOG
This will make any traffic that comes into your system log to /var/log/messages. This will generate a lot of logs. If you used “-j DROP” instead, the traffic would have been left on the cutting room floor and not logged, but you’ll also lose your SSH connection to the box, and you will have to console in. Don’t say I didn’t warn you. Always test new rules with “-j LOG” before you use “-j DROP”, at least until you are comfortable with what you are doing.
This is all fine and good. How are we going to automaticly block abusive hosts, you ask? Sadly, we’ll have to leave that to the next installment.