Farsight TXT Record

Distributed Firewall Configuration Part 2: Ferm Configuration with Salt

Written by: 
Published on: 
May 20, 2015
On This Page
Share:

This article is a continuation of a previous article on managing a distributedfirewall using salt and ferm, which you can readhere.

First, a few notes about the ferm configuration files in this post:

  • SM stands for “Salt Managed” in file names, as a hint to administrators of the system.
  • Feel free to copy / paste for the formatting, but be sure to change all of the contents, or you’ll be giving random people on the internet access to your systems.

I suggest building a testing system specifically for the design of yourfirewall. On that system, install your basesoftware and the packages you want to test first, then install the

ferm

and

salt-minion

packages.

Step 1: Establish your internal and trusted lists

You need to have a list of all the IP addresses that should have wideaccess to most things, like internal documentation servers, outboundSMTP relays, and communication servers. For our purposes, we call thelist of all the IP address blocks that our systems use “internal” andthe list of all the static blocks our home users use are “trusted”. Youwould probably also want VPN users and offices to be on the “trusted”list.

SM-trusted-all.conf

:

#centrally managed by Salt. Please be aware any changes to this could be overwritten at any time.

@def $NET_TRUSTED_ALL = (
# Billy User
123.45.67.88/29
2654:184A:2C56:F329::/64

# Bobby User
45.76.34.96/28
25FF:F932:1::/48

# All Users VPN
10.10.0.0/16

# Main Office
124.133.98.83
);

domain (ip ip6) {
chain TRUSTED_ALL {
saddr @ipfilter($NET_TRUSTED_ALL) ACCEPT;
}
# THIS PART IS OPTIONAL, ONLY INCLUDE IT IF YOU HAVE SERVICES YOU WANT ALL TRUSTED USERS TO GET TO ON EVERY SYSTEM, example ICMP
chain INPUT {
proto icmp jump TRUSTED_ALL;
}
}

SM-trusted-ops.conf

:

#centrally managed by Salt. Please be aware any changes to this could be overwritten at any time.

@def $NET_TRUSTED_OPS = (

# Bobby User
45.76.34.96/28
25FF:F932:1::/48

# Bobby User VPN
10.10.3.243

# Operations Office
10.9.8.0/24
);

domain (ip ip6) {
chain TRUSTED_OPS {
saddr @ipfilter($NET_TRUSTED_OPS) ACCEPT;
}
# THIS PART IS OPTIONAL, ONLY INCLUDE IT IF YOU HAVE SERVICES YOU WANT ALL TRUSTED OPS USERS TO GET TO ON EVERY SYSTEM, example ssh
chain INPUT {
proto tcp dport sshd jump TRUSTED_OPS;
}
}

SM-internal-all.conf

:

#centrally managed by Salt. Please be aware any changes to this could be overwritten at any time.

@def $NET_INTERNAL_ALL = (
# West Coast DC
48.34.87.0/24
5432:1234:6789::/48

# East Coast DC
34.46.78.0/24
DD33:FF88:123::/48

);

domain (ip ip6) {
chain INTERNAL_ALL {
saddr @ipfilter($NET_INTERNAL_ALL) ACCEPT;
}
# THIS PART IS OPTIONAL, ONLY INCLUDE IT IF YOU HAVE SERVICES YOU WANT ALL INTERNAL SYSTEMS TO GET TO ON EVERY SYSTEM, example ICMP
chain INPUT {
proto icmp jump INTERNAL_ALL;
}
}

SM-internal-web.conf

:

#centrally managed by Salt. Please be aware any changes to this could be overwritten at any time.

@def $NET_INTERNAL_WEB = (
# West Coast DC
48.34.87.48/29
5432:1234:6789:48::/64

# East Coast DC
34.46.78.48/29
DD33:FF88:123:48::/64

);

domain (ip ip6) {
chain INTERNAL_WEB {
saddr @ipfilter($NET_INTERNAL_WEB) ACCEPT;
}
# No optional section provided to let your web servers access everything, as I can’t do so in good conscience
}

You should build a separate “trusted” configuration for each groupof users (e.g., operations staff, developers, and IT users) and“internal” configuration for each group of servers (e.g., web, database,authentication, and monitoring servers). These can also be broken downby project if you want to get even more secure, or merged if it makessense. Tune it to your needs, this is a tool not a rulebook. We’ll set itup later so that if a config isn’t used it’s not placed on the system,and we’re not running through unneeded iptables rules.

Remember, if you include the optional portion in the aboveconfigurations, it will be on every system that config gets droppedon. The primary purpose of those configuration files is to establishthe groups, so the optional portion should only be used for similarlyuniversal reasons, such as operations access to ssh and monitoringaccess to nrpe. It’s important to use a different file for each of thegroups to more easily determine what’s going on on each individual host.Note that breaking them into more files will not impact performance(unless you’re really low on inodes), since they are read and processedinto iptables rules.

Step 2: Making the Rules

After you have the groups of servers and users built, then you’re readyto begin using them to allow access to services. We’re going to do thisby building them into salt. We start with a base set of rules, as follows:

SM-base.conf

:

#Centrally managed by Salt. Please be aware any changes to this could be overwritten at any time.
domain (ip ip6) {
chain INPUT {
policy DROP;

# connection tracking
mod state state INVALID DROP;
mod state state (ESTABLISHED RELATED) ACCEPT;

# allow local packet
interface lo ACCEPT;

# respond to ping
proto icmp ACCEPT;
}
chain OUTPUT {
policy ACCEPT;

# connection tracking
mod state state INVALID DROP;
mod state state (ESTABLISHED RELATED) ACCEPT;
}
chain FORWARD {
policy DROP;
}
}

We then integrate the salt pillars from the last article with a jinja formatted servicesfile like the one below. Please note that if you haven’t done the saltconfiguration part this won’t work yet: we have to tell salt what to dowith it. You’ll need to tweak it for your config. I’m going to make itmatch the four above.

SM-services.conf

:

#Centrally managed by Salt. Please be aware any changes to this could be overwritten at any time.

domain (ip ip6) {
chain INPUT {
{%- if salt['pillar.get’](‘services:public') %}
{%- for service in pillar['services']['public'] %}
proto {{pillar['services']['public’][service]['protocol']}} dport {{pillar['services']['public'][service]['port']}} ACCEPT;
{%- endfor %}
{%- endif %}
{%- if salt['pillar.get']('services:trusted-all’) %}
{%- for service in pillar['services']['trusted-all’] %}
proto {{pillar['services']['trusted-all’][service]['protocol']}} dport {{pillar['services']['trusted-all’][service]['port']}} jump TRUSTED-ALL;
{%- endfor %}
{%- endif %}
{%- if salt['pillar.get']('services:trusted-ops’) %}
{%- for service in pillar['services']['trusted-ops’] %}
proto {{pillar['services']['trusted-ops’][service]['protocol']}} dport {{pillar['services']['trusted-ops’][service]['port']}} jump TRUSTED-OPS;
{%- endfor %}
{%- endif %}
{%- if salt['pillar.get']('services:internal-all’) %}
{%- for service in pillar['services']['internal-all’] %}
proto {{pillar['services']['internal-all’][service]['protocol']}} dport {{pillar['services']['internal-all’][service]['port']}} jump INTERNAL-ALL;
{%- endfor %}
{%- endif %}
{%- if salt['pillar.get']('services:internal-web’) %}
{%- for service in pillar['services']['internal-web’] %}
proto {{pillar['services']['internal-web’][service]['protocol']}} dport {{pillar['services']['internal-web’][service]['port']}} jump INTERNAL-WEB;
{%- endfor %}
{%- endif %}
}
}

Let’s break down what’s happening in the above config file. There are 5 possiblesections of the file above, one for each of the groups of rules wewant to check for: public, trusted-all, trusted-ops, internal-all,internal-web. If there are any rules in that section, then it loopsthrough all of the entries in that section and inserts a rule for eachof the ports for the appropriate group. You’ll notice that “public”wasn’t defined in one of the earlier groups… but that’s because it’s asimple ACCEPT rule, instead of a redirection to a group, meaning it’sallowed for everyone, everywhere. Public servers like your mail and webservers should be done that way, but just abouteverything else from your databases to your file servers should bedefined in groups with access specialized.

Now we need to build the main ferm configuration, which is going to pullin all of these configuration files. For your production systems, you maywant to include the specific files instead of the whole directory, sincethey should be fully automated. You’d use a very similar jinja template to theservices file or the init.sls checks in the salt configuration sectionto accomplish that task.

ferm.conf

:

#Centrally managed by Salt. Please be aware any changes to this could be overwritten at any time.

@include 'conf.d/SM-base.conf’;
@include 'conf.d/SM-trusted-all.conf’;
@include 'conf.d/SM-internal-all.conf’;

{%- if salt['pillar.get']('services') %}
@include 'conf.d/SM-services.conf’;
{%- endif %}
{%- if salt['pillar.get']('services:trusted-ops) %}
@include 'conf.d/SM-trusted-ops.conf’;
{%- endif %}
{%- if salt['pillar.get']('services:internal-web’) %}
@include 'conf.d/SM-internal-web.conf’;
{%- endif %}

Making Exceptions

For development systems where you want to allow local configuration files:

ferm.conf

:

#centrally managed by Salt. Please be aware any changes to this could be overwritten at any time.
@include 'conf.d/';

I’ll provide an example of a local configuration here,especially helpful during the development cycle, or fora quick one-off which should be only on the one localserver, and not deployed through salt:

local-web.conf

:

# Local Configuration with thall’s home IP for testing the development website. Expires June 12, 2015.
domain (ip ip6) {
chain INPUT {
proto tcp saddr 43.123.21.12 dport (80 443) ACCEPT;
proto tcp saddr F954:11C:9832:1234:4321:7773:1298:1324 dport (80 443) ACCEPT;
}

You should watch the file count, and (depending on your level ofparanoia) also the md5sum of the files being pushed with your monitoringsystem to ensure the firewall stays secure. Every time salt is pushedto the box it will ensure that the files are returned to their expectedstate and including the entire directory allows for local configurationfiles to be added.

Conclusion

Now that we’ve done all the work, here’s the payoff: When your webserver gets compromised, the attackers won’t be able to bounce from there to yourmail server and publish all of your company’s email. If you’ve dividedthe frontend and backend in a security conscious manner where thefrontend only talks to the backend over specific, limited APIs, the attackerscan’t get from your frontend web servers to your databases toget your customer list. Even your users will only be able to accessthe systems that they are allowed to, limiting the effect of socialengineering or malware to only the systems that they are allowed toaccess. And when you consider the overall security benefits to your organization, it’s really not all that muchwork.

Travis Hall is a Systems Administrator for Farsight Security, Inc.