Ript: quick, reliable, and painless firewalling

Running your own servers? Hate managing firewall rules?

For the last year at Bulletproof Networks I’ve been working on a little tool called Ript to make writing firewall rules a joy, and applying them quick, reliable, and painless.

Ript is a clean and opinionated Domain Specific Language for describing firewall rules, and a tool with database migrations-like functionality for applying these rules with zero downtime.

The DSL

At Ript’s core is an easy to use Ruby DSL for describing both simple and complex sets of iptables firewall rules. After defining the hosts and networks you care about:

partition "joeblogsco" do
  label "www.joeblogsco.com",      :address => "172.19.56.216"
  label "app-01",                  :address => "192.168.5.230"
  label "joeblogsco uat subnet",   :address => "192.168.5.0/24"
  label "joeblogsco stage subnet", :address => "10.60.2.0/24"
  label "joeblogsco prod subnet",  :address => "10.60.3.0/24"
  label "bad guy",                 :address => "172.19.110.247"
  label "bad guys",                :address => "10.0.0.0/8"
end

…you use Ript’s helpers for accepting, dropping, & rejecting packets, as well as for performing DNAT and SNAT:

partition "joeblogsco" do
  label "www.joeblogsco.com",      :address => "172.19.56.216"
  label "app-01",                  :address => "192.168.5.230"
  label "joeblogsco uat subnet",   :address => "192.168.5.0/24"
  label "joeblogsco stage subnet", :address => "10.60.2.0/24"
  label "joeblogsco prod subnet",  :address => "10.60.3.0/24"
  label "bad guy",                 :address => "172.19.110.247"
  label "bad guys",                :address => "10.0.0.0/8"

  rewrite "public website + ssh access" do
    ports 80, 22
    dnat  "www.joeblogsco.com" => "app-01"
  end

  rewrite "private to public" do
    snat  [ "joeblogsco uat subnet",
            "joeblogsco stage subnet",
            "joeblogsco prod subnet"  ] => "www.joeblogsco.com"
  end

  reject "bad guy" do
    from "bad guy"
    to   "www.joeblogsco.com"
  end

  drop "bad guys" do
    protocols "udp"
    from      "bad guys"
    to        "www.joeblogsco.com"
  end
end

The DSL provides many helpful shortcuts for DRYing up your firewall rules, and tries to do as much of the heavy lifting for you as possible.

Part of Ript being opinionated is that it doesn’t expose all the underlying features of iptables. This was done for several reasons:

  • The DSL would become complex, and thus harder to use.
  • Not all features within iptables map to Ript’s DSL
  • Ript caters for the simple-to-moderately complex use cases that 80% of users have. If you need to use iptables features documented deep within the man pages, Ript is almost certainly not the tool for you.

Rule application

While the DSL is pretty, we didn’t write Ript because of it - we wrote it because we’re working with tens of thousands of iptables rules & making several changes a day to those rules, and the traditional way of applying changes doesn’t cut it at scale.

Most tools try to apply firewall rules by flushing all the loaded rules and loading in new ones. This works fine if you only have a few hundred rules, but as soon as you start scaling into thousands of rules, the load time becomes very noticable.

The effects of this are fairly simple: the rule load time manifests itself as downtime.

Because the ruleset has to be applied serially, rules at the end of the set are held up by rules still being applied at the beginning of the set. From a service provider’s perspective, this means that a rule change for one customer can end up causing downtime for other completely unrelated customers. Not cool.

iptables-save and iptables-restore help with this, but you still end up writing + applying rules by hand - a tedious task if you’re making lots of firewall changes every day.

Ript’s killer feature is incrementally applying rules.

Ript generates firewall chains in a very specific way that allows it to apply new rules incrementally, and clean out old rules intelligently. Here’s an example session:

# Output all the generated rules by interpreting all files under /etc/firewall
ript rules generate /etc/firewall
# Output a diff of rules to apply, based on what rules are currently loaded in memory
ript rules diff /etc/firewall
# Apply the aforementioned diff
ript rules apply /etc/firewall
# Output the currently loaded rule in iptables-restore format
ript rules save
# Output a diff of rules to delete
ript clean diff /etc/firewall
# Apply the aforementioned diff
ript clean apply /etc/firewall

Getting started

Ript has been Open Sourced under an MIT license, and is available on GitHub. To get you going, Ript ships with extensive DSL usage documentation, and a boatload of examples used by the tests.

I’ll also be giving a talk about Ript at linux.conf.au in Canberra in January 2013.

Happy Ripting!