Firewall - Reliably Deploying Rails Applications: Hassle free provisioning, reliable deployment (2014)

Reliably Deploying Rails Applications: Hassle free provisioning, reliable deployment (2014)

7.3 - Firewall

Overview

A firewall acts as a gate keeper layer between the outside world and the services running on your server. You can restrict which remote IP addresses are allowed to collect to which ports on your server.

We’ll set up a Firewall which by default blocks everything and then look at how to use chef to manage which ports to open.

UFW (Uncomplicated Firewall)

UFw acts a friendly layer to IPTables, making it simple to add and manage the rules which define what can and cannot connect.

The following section of the default.rb recipe sets up our basic ufw installation:

1 # now allow SSH traffic through the firewall and restart SSH

2 # unless otherwise specified, block everything

3 bash "opening ufw for ssh traffic" do

4 user "root"

5 code <<-EOC

6 ufw default deny

7 ufw allow 22

8 ufw --force enable

9 EOC

10 end

You’ll see once again, we’re just using chef to execute standard terminal commands. The first

1 ufw allow 22

Tells ufw to allow all traffic to and from port 22 (the port we’re running SSH on). The second:

1 ufw --force enable

Enables ufw. It’s important to make sure you’ve allowed ssh traffic through the firewall before enabling it otherwise you can end up unable to SSH in.

You can use the following command on your remote server to see whether ufw is enabled and its current rules

1 sudo ufw status

which will show output something like:

1 Status: active

2

3 To Action From

4 -- ------ ----

5 22 ALLOW Anywhere

By default our firewall will block everything unless we specify otherwise. This is desirable because it means that by default when we install a new service, unless we manually specify otherwise, it won’t be accessible to the ourside world.

The next section is a little more advanced:

1 # if we've specified firewall rules in the node definition

2 # then apply them here. These should be in the format:

3 # {"port": "x", "ip": "xxx.xxx.xxx.xxx"}

4 if node['firewall_allow']

5 node['firewall_allow'].each do |rule|

6 bash "open ufw from #{rule['ip']} on port #{rule['port']}" do

7 user "root"

8 code "ufw allow from #{rule['ip']} to any port #{rule['port']}"

9 end

10 end

11 end

You’ll recall in the default attributes of our server.json role, we had a line which looked like this:

1 "firewall_allow":[]

This array can contain hashes in the form:

1 {"port": "x", "ip": "xxx.xxx.xxx.xxx"}

The basic_security-tlq recipe will then iterate over each of these hashes, opening the specified port to the specified IP.

So, for example, if the server you’re configuring was running a MySQL server on port 3306 and you needed that server to be accessible to another one of your servers (168.154.2.3) you would add the following entry:

1 {"port": "3306", "ip": "168.154.2.3"}

This would mean that the server with IP address 168.154.2.3 would be allowed to connect to port 3306, but requests on all other IP’s to that port will still be dropped.

Remember you can always check your current rules using sudo ufw status.

You can also watch what’s being blocked by ufw by looking at its log file, by default this is located at /var/log/ufw.log. So a simple:

1 sudo tail -f /var/log/ufw.log

Will show you a live tail of what’s being added to the log. You can then attempt to connect to various ports on the server from different hosts and see if ufw blocks them or not.

This is useful firstly for checking that ports you know you don’t want to be externally accessible are being properly blocked and secondly for troubleshooting connection issues between machines that should be able to connect to one another but can’t.