How To Set Up an Iptables Firewall to Protect Traffic Between your Servers
Source DigitalOcean – Justin Ellingwood
Introduction
Deploying discrete components in your application setup onto different nodes is a common way to decrease load and begin scaling horizontally. A typical example is configuring a database on a separate server from your application. While there are a great number of advantages with this setup, connecting over a network involves a new set of security concerns.
In this guide, we’ll demonstrate how to set up a simple firewall on each of your servers in a distributed setup. We will configure our policy to allow legitimate traffic between our components while denying other traffic.
For the demonstration in this guide, we’ll be using two Ubuntu 14.04 servers. One will have a WordPress instance served with Nginx and the other will host the MySQL database for the application. Although we will be using this setup as an example, you should be able to extrapolate the techniques involved to fit your own server requirements.
Prerequisites
To get started, you will have to have two fresh Ubuntu 14.04 servers. Add a regular user account with sudo
privileges on each. To learn how to do this correctly, follow our Ubuntu 14.04 initial server setup guide.
The application setup we will be securing is based on this guide. If you’d like to follow along, set up your application and database servers as indicated by that tutorial.
Setting Up a Basic Firewall
We will begin by implementing a baseline firewall configuration for each of our servers. The policy that we will be implementing takes a security-first approach. We will be locking down almost everything other than SSH traffic and then poking holes in the firewall for our specific application.
The firewall in this guide provides the basic setup that we need. Install the iptables-persistent
package and paste the basic rules into the /etc/iptables/rules.v4
file:
sudo apt-get update sudo apt-get install iptables-persistent sudo nano /etc/iptables/rules.v4
/etc/iptables/rules.v4
*filter
# Allow all outgoing, but drop incoming and forwarding packets by default
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Custom per-protocol chains
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]
# Acceptable UDP traffic
# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT
# Acceptable ICMP traffic
# Boilerplate acceptance policy
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT
# Drop invalid packets
-A INPUT -m conntrack --ctstate INVALID -j DROP
# Pass traffic to protocol-specific chains
## Only allow new connections (established and related should already be handled)
## For TCP, additionally only allow new SYN packets since that is the only valid
## method for establishing a new TCP connection
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP
# Reject anything that's fallen through to this point
## Try to be protocol-specific w/ rejection message
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
# Commit the changes
COMMIT
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
*security
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
If you are implementing this in a live environment do not reload your firewall rules yet. Loading the basic rule set outlined here will immediately drop the connection between your application and database server. We will need to adjust the rules to reflect our operational needs before reloading.