Open Checks

Daniel Lindeman

CIS 654 - W2017 

Concept

  • Configuration Time SDN Checking
  • "Security Expert" in a box

Problem(s)

  • SDN Configuration is difficult
  • Many inexperienced people
  • All choices left up to the Admin
  • Order matters
    • 3 + 2 != 2 + 3
  • Potential insane defaults
    • SSL is not default
    • Opt-in security

Firewall Proof of Concept

// A small network

f1: firewall

h2: server

h1: client
h3: client


Routes:

h1 <---> f1 <---> h2
h3 <---> f1 <-/-> h2

Security Checking Options

  • Wait for hackers (bad)
  • Hire a security firm auditor (expensive)
  • Manual Test (slow)
  • Automated Test (best option)

Common thread?

There is a better way

Find it before you write it

Vendor Solutions Exist

  • Paid for
  • Cisco/VMWare
  • None for OpenFlow

Just like software bugs

  • Customer finds it: Really bad
  • Tester Finds it: Fine
  • Developer Tests Find it: Okay
  • IDE finds it: Best

Catchable Problems

Define f1 as a Firewall

Is there a route through f1 for HTTP traffic?
Is there a rule to block anything else?
Is there a source IP whitelist?
Is there a destination IP whitelist?
Is handling port 80 the firewall's only job?

mininet> h2 python -m SimpleHTTPServer 80 &
mininet> h1 wget -O - h2
mininet> h3 wget -O - h2


We know all of this before starting the network!

How do we find out?

  • Dump Flow tables or other artifacts
  • Introduce "Type System" to SDNs
    • Type - Check - Interface
  • Check an element against its "Type"
    • By running tests on instantiation

Given

"If you see h1 and h2 talking on port 80, cool.

Otherwise, drop it."

tables = [
{'dl_src':'00:00:00:00:00:01', 'dl_dst':'00:00:00:00:00:02', 'tp_dst': 80, 'actions':'NORMAL'},
{'dl_src':'00:00:00:00:00:02', 'dl_dst':'00:00:00:00:00:01', 'tp_dst': 80, 'actions':'NORMAL'},
{'actions':'ANY'}
]
class FirewallCheck(object):
    
    def __init__(self, tables):
        self.tables = tables
        self.src_ips = [ table['dl_src'] for table in tables if 'dl_src' in table]
        self.dst_ips = [ table['dl_dst'] for table in tables if 'dl_dst' in table]
        self.tp_dsts = set([ table['tp_dst'] for table in tables if 'tp_dst' in table])
        self.run_all_checks()

    def run_all_checks(self):
        self.specify_source_ips()
        self.specify_destination_ip()
        self.specify_destination_port()
        self.block_by_default()
        self.allow_specific_traffic()        

    def specify_source_ips(self):
        raise NotImplementedError

    def specify_destination_ip(self):
        raise NotImplementedError

    def specify_destination_port(self):
        raise NotImplementedError

    def block_by_default(self):
        raise NotImplementedError

    def allow_specific_traffic(self):
        raise NotImplementedError
class HttpFirewallCheck(FirewallCheck):

    def __init__(self, table):
        super().__init__(table)

    def block_by_default(self):
        # assert that there is a catch-all block to drop anything else
        for table in tables:
            if 'dl_src' not in table:
                assert(table['actions'] == 'ANY')
        print("block_by_default check passed")

    def allow_specific_traffic(self):
        # assert that there exists an entry in tables that will allow port 80 traffic
        for table in tables:
            if ('tp_dst' in table and table['tp_dst'] == 80):
                assert(table['dl_dst'])
                assert(table['dl_src'])
                assert(table['actions'] == 'NORMAL')
        print("allow_specific_traffic check passed")
    
    def specify_source_ips(self):
        # assert that source IPs have been specified in the tables
        assert(self.src_ips)
        print("specify_soure_ips check passed")

    def specify_destination_ip(self):
        # assert that destination IPs have been specified in the tables
        assert(self.dst_ips)
        print("specify_destination_ip check passed")

    def specify_destination_port(self):
        # assert that port 80 is the only port with a NORMAL entry
        assert(set([80]) == self.tp_dsts)
        assert(len(self.tp_dsts) == 1)
        for table in tables:
            if 'dl_src' in table and 'dl_dst' in table and 'tp_dst' in table and table['tp_dst'] == 80 and 'actions' in table:
                assert(table['actions'] == "NORMAL")
        print("specify_destination_port check passed")

Running:

daniellindeman@Daniels-MBP:~/Desktop
] python firewall_check.py
specify_soure_ips check passed
specify_destination_ip check passed
specify_destination_port check passed
block_by_default check passed
allow_specific_traffic check passed

Running with bad config:

tables = [
{'dl_src':'00:00:00:00:00:01', 'dl_dst':'00:00:00:00:00:02', 'tp_dst': 80, 'actions':'NORMAL'},
{'dl_src':'00:00:00:00:00:02', 'dl_dst':'00:00:00:00:00:01', 'tp_dst': 90, 'actions':'NORMAL'},
{'actions':'ANY'}]

daniellindeman@Daniels-MBP:~/Desktop
] python firewall_check.py
specify_soure_ips check passed
specify_destination_ip check passed
Traceback (most recent call last):
  File "firewall_check.py", line 78, in <module>
    f1 = HttpFirewallCheck(tables)
  File "firewall_check.py", line 37, in __init__
    super().__init__(table)
  File "firewall_check.py", line 9, in __init__
    self.run_all_checks()
  File "firewall_check.py", line 14, in run_all_checks
    self.specify_destination_port()
  File "firewall_check.py", line 67, in specify_destination_port
    assert(set([80]) == self.tp_dsts)
AssertionError

What good is it?

  • Exception raising == awareness raising
    • If a test is not implemented -> you know
  • A "contract" of security
  • A starting point

Limitations

  • There are many ways to configure SDNs
    • ways of thinking about programming
      • not in vs in
  • Errors without breadcrumbs
    • Runtime changes
    • Things not left in artifacts like Flow Tables

Future Work

  • Expand Definitions
    • Have more than one
    • Explore the static space more completely
  • Composable Checks
  • Create a Static Typing Framework for OpenFlow
  • Create a Linter/Static Analysis Tool

References

  • https://support.rackspace.com/how-to/best-practices-for-firewall-rules-configuration/

Open Checks

By dlindema

Open Checks

  • 324