My employer, as part of the default security posture for production Linux systems, requires the use of iptables. If you've ever looked at an iptables file, they tend to be a spaghetti of arcana. Most tables start out fairly basic and might look something like:
-A INPUT -i lo -j ACCEPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p udp -m udp --dport 53 -j ACCEPT -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT -A INPUT -p tcp -m tcp --dport 25 -j ACCEPT -A INPUT -p tcp -m tcp --dport 587 -j ACCEPT -A INPUT -p tcp -m tcp --dport 993 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibitedThis would probably be typical of a LAMP server that's also providing DNS and mail services.As it stands, it's fairly manageble and easy to follow if you've got even a slight familiarity with iptables or even firewalls in general.
Where iptables starts to become unfun is when you start to get fancy with it. I started going down this "unfun" path when I put in place a defense against SSHD brute-forcers. I had to add a group of rules just to handle what, above, was done with a single line. Initially, this started to "spaghettify" my iptables configuration. It ended up making the above look like:
-A INPUT -i lo -j ACCEPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p udp -m udp --dport 53 -j ACCEPT -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --set --name ssh_safe --rsource -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --update --seconds 300 --hitcount 3 --name ssh_safe --rsource -j LOG --log-prefix "SSH CONN. REJECT: " -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --update --seconds 300 --hitcount 3 --name ssh_safe --rsource -j DROP -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT -A INPUT -p tcp -m tcp --dport 25 -j ACCEPT -A INPUT -p tcp -m tcp --dport 587 -j ACCEPT -A INPUT -p tcp -m tcp --dport 993 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibitedNot quite as straight-forward any more. Not "tidy", as I tend to refer to these kinds of things. It gave me that "tick" I get whenever I see something messy. So, how to fix it? Well, my first step was to use iptables' comments module. That allowed me to make the configuration a bit more self-documenting (if you ever look at my shell scripts or my "real" programming, they're littered with comments - makes it easier to go back and remember what the hell you did and why). However, it still "wasn't quite right". So, I decided, "I'll dump all of those SSH-related rules into a single rule group" and then reference that group from the main iptables policy:
-A INPUT -i lo -j ACCEPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p udp -m udp --dport 53 -j ACCEPT -A INPUT -p tcp -m comment --comment "Forward to SSH attack-handler" -m tcp --dport 22 -j ssh-defense -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT -A INPUT -p tcp -m tcp --dport 25 -j ACCEPT -A INPUT -p tcp -m tcp --dport 587 -j ACCEPT -A INPUT -p tcp -m tcp --dport 993 -j ACCEPT -A ssh-defense -p tcp -m comment --comment "SSH: track" -m tcp --dport 22 -m state --state NEW -m recent --set --name ssh_safe --rsource -A ssh-defense -p tcp -m comment --comment "SSH: attack-log" -m tcp --dport 22 -m state --state NEW -m recent --update --seconds 300 --hitcount 3 --name ssh_safe --rsource -j LOG --log-prefix "SSH CONN. REJECT: " -A ssh-defense -p tcp -m comment --comment "SSH: attack-block" -m tcp --dport 22 -m state --state NEW -m recent --update --seconds 300 --hitcount 3 --name ssh_safe --rsource -j DROP -A ssh-defense -p tcp -m comment --comment "SSH: accept" -m tcp --dport 22 -j ACCEPTOk, so the above doesn't really look any less spaghetti-like. That's ok. This isn't exactly where we-despaghettify things. The above is mostly meant to be machine read. If you want to see the difference in things, use the `iptables -L` command. Or, to really see the difference, issue `iptables -L INPUT ; iptables -L ssh-defense`:
Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT all -- anywhere anywhere DROP all -- anywhere loopback/8 ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED ACCEPT udp -- anywhere anywhere udp dpt:domain ssh-defense tcp -- anywhere anywhere /* Forward to SSH attack-handler */ tcp dpt:ssh ACCEPT tcp -- anywhere anywhere tcp dpt:domain ACCEPT tcp -- anywhere anywhere tcp dpt:http ACCEPT tcp -- anywhere anywhere tcp dpt:https ACCEPT tcp -- anywhere anywhere tcp dpt:smtp ACCEPT tcp -- anywhere anywhere tcp dpt:submission ACCEPT tcp -- anywhere anywhere tcp dpt:imaps Chain ssh-defense (1 references) target prot opt source destination tcp -- anywhere anywhere /* SSH: track */ tcp dpt:ssh state NEW recent: SET name: ssh_safe side: source LOG tcp -- anywhere anywhere /* SSH: attack-log */ tcp dpt:ssh state NEW recent: UPDATE seconds: 300 hit_count: 3 name: ssh_safe side: source LOG level warning prefix `SSH CONN. REJECT: ' DROP tcp -- anywhere anywhere /* SSH: attack-block */ tcp dpt:ssh state NEW recent: UPDATE seconds: 300 hit_count: 3 name: ssh_safe side: source ACCEPT tcp -- anywhere anywhere /* SSH: accept */ tcp dpt:sshEven if you don't find the above any more self-documenting or easier to handle (in a wide xterm, it looks much better), it does have one other value: it makes it harder for people to muck up whatever flow or readability that your iptables configuration has. Because you've externalized a group of directives, someone's going to have to go out of their way to intersperse random rules into your iptables configuration. If it's just your own server, this probably has little value (unless you've got MPD). However, if you have shared administration duties, it can be a sanity-saver.
No comments:
Post a Comment