The firewall configuration at mkirby.org involves a PFSense firewall at the edge and a host-based iptables firewall.
The webserver is on a dedicated vlan to separate it from my other internal servers/desktops.
The following lines set the default for INPUT/OUTPUT/FORWARD to drop the packets. It also sets the chain for INPUTDROPLOG and OUTPUTDROPLOG, which is used as an explicit drop so that it may also be logged.
*filter
:FORWARD DROP [0:0]
:INPUT DROP [0:0]
:INPUTDROPLOG – [0:0]
:OUTPUT DROP [0:0]
:OUTPUTDROPLOG – [0:0]
:ACCEPTLOG – [0:0]
The INPUTDROPLOG and OUTPUTDROPLOG chains will first log the packet and then drop the packet. This is one of my biggest annoyances with iptables because all the other firewalls I have dealt with provide logging and dropping/allowing on the same line.
The limit feature sets the logging to a maximim of 60 logs per minute. This prevents log flooding. The log-prefix is what is prepended to the log, which I choose to use as a comment field. The log-tcp-sequence, log-tcp-options, and log-ip-options are usually not useful, but doesn’t hurt to log it nonetheless. The log-uid can only be used on output rules. This is very useful as my other rules restrict egress access based on user. It also serves as an indication that a service account may have been compromised.
-A OUTPUTDROPLOG -j LOG -m limit –limit 60/min –log-prefix “COMMENT=outputdroplog ” –log-tcp-sequence –log-tcp-options –log-ip-options –log-uid
-A OUTPUTDROPLOG -j DROP
-A INPUTDROPLOG -j LOG -m limit –limit 60/min –log-prefix “COMMENT=inputdroplog ” –log-tcp-sequence –log-tcp-options –log-ip-options –log-uid
-A INPUTDROPLOG -j DROP
-A ACCEPTLOG -j LOG -m limit –limit 60/min –log-prefix “COMMENT=acceptlog ” –log-tcp-sequence –log-tcp-options –log-ip-options –log-uid
-A ACCEPTLOG -j ACCEPT
The following lines allow services for ssh, smtp, smtps, http, https, etherpad, bind, and splunk. Any service that is not public is restricted to specific networks/IPs. After the services are defined, the INPUTDROPLOG will log everything that is dropped.
-A INPUT -p tcp -m tcp -m multiport –dports 22,25,465,80,443,9001 -j ACCEPTLOG
-A INPUT -p tcp -m tcp -s 127.0.0.1 –dport 953 -j ACCEPT
#-A INPUT -p tcp -m tcp -s 127.0.0.1 -m multiport –dports 8000,8089 -j ACCEPT
-A INPUT -p tcp -m tcp -s 192.168.1.0/24 -m multiport –dports 53,953,8000,8089 -j ACCEPT
-A INPUT -p tcp -m tcp -s 184.82.178.105 -m multiport –dports 53,953 -j ACCEPT
-A INPUT -p udp -m udp –dport 53 -j ACCEPT
-A INPUT -j INPUTDROPLOG
The following lines allow access from this server to my internal servers. The first line will instantly drop any connection from a user (4294967294 is the maximum uid on Linux). The other lines allow for nfs and splunk. Notice that the nfs lines do not filter based on uid. This is because nfs is performed at the kernel-level and not userspace, meaning there will not be a uid associated with the sockets.
-A OUTPUT -d 192.168.1.0/24 -m owner –uid-owner 1-4294967294 -j OUTPUTDROPLOG
-A OUTPUT -p tcp -m tcp -d 192.168.1.4 -m multiport –dports 111,2049,20000:65535 -j ACCEPT
-A OUTPUT -p udp -m udp -d 192.168.1.4 -m multiport –dports 111,2049,20000:65535 -j ACCEPT
-A OUTPUT -p tcp -m tcp -d 192.168.1.5 -m multiport –dports 111,2049,20000:65535 -j ACCEPT
-A OUTPUT -p udp -m udp -d 192.168.1.5 -m multiport –dports 111,2049,20000:65535 -j ACCEPT
-A OUTPUT -p tcp -m tcp -d 192.168.1.50 -m owner –uid-owner root –dport 9997 -j ACCEPT
These lines allow services to itself such as dns, smtp/s, and http/s.
-A OUTPUT -p udp -m udp -d 74.93.25.1 –dport 53 -j ACCEPT
-A OUTPUT -p udp -m udp -d 127.0.0.1 –dport 53 -j ACCEPT
-A OUTPUT -p udp -m udp -d 192.168.0.15 –dport 53 -j ACCEPT
-A OUTPUT -p tcp -m tcp -d 74.93.25.1 –dport 25 -j ACCEPT
-A OUTPUT -p tcp -m tcp -d 127.0.0.1 –dport 25 -j ACCEPT
-A OUTPUT -p tcp -m tcp -d 192.168.0.15 –dport 25 -j ACCEPT
-A OUTPUT -p tcp -m tcp -d 74.93.25.1 –dport 465 -j ACCEPT
-A OUTPUT -p tcp -m tcp -d 127.0.0.1 –dport 465 -j ACCEPT
-A OUTPUT -p tcp -m tcp -d 192.168.0.15 –dport 465 -j ACCEPT
-A OUTPUT -p tcp -m tcp -s 192.168.0.15 -d 192.168.0.15 -m multiport –dports 80,443 -m owner –uid-owner apache -j ACCEPT
These rules block access to any rfc1918 networks. The internal network services was already covered above and there isn’t any need for any other access to my internal networks. If a user were to try to run an internal scan, I would see it in the logs. The PFSense firewall also filters this access in case iptables is unloaded.
-A OUTPUT -p tcp -m tcp -d 192.168.0.0/16 -j OUTPUTDROPLOG
-A OUTPUT -p tcp -m tcp -d 10.0.0.0/8 -j OUTPUTDROPLOG
-A OUTPUT -p tcp -m tcp -d 172.16.0.0/12 -j OUTPUTDROPLOG
These rules allow only the named service to connect outbound for port 53, which is used for lookups. The resolv.conf is set to use the localhost, so the other users do not need to egress on 53.
-A OUTPUT -p tcp -m tcp -m owner –dport 53 –uid-owner named -j ACCEPT
-A OUTPUT -p udp -m udp -m owner –dport 53 –uid-owner named -j ACCEPT
This rule allows only the postfix service to connect outbound for smtp/s.
-A OUTPUT -p tcp -m tcp -m owner -m multiport –dports 25,465 –uid-owner postfix -j ACCEPT
This rule allows the chrony service to egress on udp/123. Chrony is Fedora’s replacement for NTP.
# chrony
-A OUTPUT -p udp -m udp -m owner –dport 123 –uid-owner chrony -j ACCEPT
These rules allow for return packets on bad/stale connections. I discovered that this was necessary after reviewing the iptables logs and seeing dropped FIN/RST packets. The rules will examine the tcp flags for the syn flag. The –tcp-flags will check the syn flag and only match if it is not set (aka none). The rules allow for egress from a source port and only from the respective service account, the root user, or the kernel. The “! –uid-owner 1-4294967294” tells iptables that only the root account (uid 0) or the kernel (no uid) is permitted to egress.
-A OUTPUT -p tcp -m tcp -m owner -m multiport –sports 25,465 –tcp-flags SYN NONE ! –uid-owner 1-4294967294 -j ACCEPT
-A OUTPUT -p tcp -m tcp -m owner -m multiport –sports 25,465 –tcp-flags SYN NONE –uid-owner postfix -j ACCEPT
-A OUTPUT -p tcp -m tcp -m owner -m multiport –sports 80,443 –tcp-flags SYN NONE ! –uid-owner 1-4294967294 -j ACCEPT
-A OUTPUT -p tcp -m tcp -m owner -m multiport –sports 80,443 –tcp-flags SYN NONE –uid-owner apache -j ACCEPT
These lines allow the root user and the kernel to egress. The only time this is used is when I patch the server, which a nightly cronjob. All other egress packets are dropped and logged.
-A OUTPUT -m owner ! –uid-owner 1-4294967294 -j ACCEPT
-A OUTPUT -j OUTPUTDROPLOG