Neutron/blueprint ovs-firewall-driver

This page is out of date
Please visit: https://review.openstack.org/#/c/89712/

Purpose
 To support the security groups extension in the OVS neutron agent through OVS flows using the existing OVS library with feature parity to the existing iptables-based implementations. In Icehouse, the existing openvswitch plugin is being deprecated, so the blueprint is compatible with the ML2 plugin with the openvswitch mechanism driver.

Current neutron.agent.firewall.FirewallDriver implementations are based off of iptables (neutron/agent/linux/iptables_firewall.py: IptablesFirewallDriver, OVSHybridIptablesFirewallDriver). This blueprint describes implementing a FirewallDriver sub-class with Open vSwitch.

Overview
To implement a performant OVS-based security groups solution in Neutron today, source port matching is a required addition to the security groups extension API.

Background
In Open vSwitch today, there are two best practice options of implementing firewalls[1]:

1. reflexive learn actions (available in OVS today)

2. stateless ACLs with tcp_flags=ack (available in OVS git, to be released in v2.1.0 - early 2014[6])

In the same e-mail thread[2], the tradeoffs between the two choices were discussed:

- reflexive learning is not as performant as it cuts into how many flows a megaflow can wildcard, e.g. the less that can be wildcarded, the more OVS will have to hit userspace for flows

- "Using the learn action is strictly more correct, since it's only allowing return traffic that's in response to traffic that was previously seen. TCP flag matching allows reasonable megaflows, but just blocking on the SYN flags isn't as secure, since an attacker can get traffic through--they just can't initiate a new connection."

My preferred implementation is 'stateless ACLs with tcp_flags=ack' to emulate stateful behavior (at least in TCP) because reflexive learning is not as performant.

Discussion: why?
Following through with the 'stateless ACLs with tcp_flags=ack' approach, UDP clients on the instance will need explicit security group rules to match source IP address and source port.

Example 1. A remote UDP client connecting to an instance UDP server

A. nw_src=$remote_ip, tp_src=random, nw_dst=$instance_ip, tp_dst=9987

B. nw_src=$instance_ip, tp_src=9987, nw_dst=$remote_ip, tp_src=random

So, in the case of the instance being a UDP server and default security groups already allowing all egress, adding a rule to allow ingress on UDP destination port 9987 will behave as expected.

Example 2. An instance UDP client connecting to a remote UDP server

C. nw_src=$instance_ip, tp_src=random, nw_dst=$remote_ip, tp_dst=9987

D. nw_src=$remote_ip, tp_src=9987, nw_dst=$instance_ip, tp_dst=random

In the case of the instance being a UDP client and default security groups already allowing all egress, we will need a new security group rule to allow ingress from source port 9987 from the remote UDP server in a stateless firewall. This is different behavior than the iptables-based stateful firewall implementation because iptables is able to add the reverse flow in its state table for a specific timeout length when it initially sees flow C.

So, in security groups, we will need an additional rule that will define flow D (remote UDP server’s IP address, UDP source port 9987, and of course the instance’s IP address). However, if you look at the security groups API as it is today[3], you will see there is no match for source port (tp_src), only destination port (—port-range-min, —port-range-max).

Suggested change: how to fix it
So, to solve the lack of source port information, I propose the following addition to the security groups extension API to allow a match on source port: —source-port-range-min, —source-port-range-max. I already have WIP patches uploaded for neutron[4] and python-neutronclient[5] implementing these suggested additions.

The security groups RPC API already has a field for source-port-range-min and source-port-range-max so this change would only affect the DB and frontend API. The iptables firewall already supports the RPC API in this capacity and unit tests around source port can be added to neutron/tests/unit/test_iptables_firewall.py. Looking at APIChangeGuidelines, this change would fall under "Adding an optional property to a resource representation which may be supplied by clients, assuming the API previously would ignore this property"

Overview
ovs_neutron_agent has some obstacles in making an Open vSwitch-based security groups firewall implementation work. These limitations are described in the following sections.

1. Firewall is invoked before local VLAN is assigned
Without the local VLAN for a given Neutron network, an OVS-based firewall will not know where to forward the packet on the allow path.

2. Agent removes all flows at initialization
Upon startup the agent removes all flows on all relevant bridges. This issue is outside the scope of this particular blueprint and is being covered by blueprint neutron-agent-soft-restart.

3. Agent removes a VIF's flows on port_bound
Related to the previous point, all the flows on the integration bridge belonging to a vif's OpenFlow port are removed. Since the firewall is invoked to setup port filters before connectivity is established (i.e. before port_bound is called) this defeats the firewall. To solve this issue, I propose using a distinct cookie on flows that belong to the firewall and a separate, distinct cookie on flows that belong to the agent. This will require OVS version 1.5.0+ to delete flows based on a cookie (current version in XenServer 6.2 and Ubuntu P/Q-release is 1.4.6).

neutron/agent/firewall.py
FirewallDriver: abstract base class

NoopFirewallDriver: no-op implementation

neutron/agent/linux/iptables_firewall.py
IptablesFirewallDriver: iptables-based FirewallDriver implementation

OVSHybridIptablesFirewallDriver: subclass of IptablesFirewallDriver with additional bridge

iptables-based firewall features:
 * prepares, updates, removes firewall filters
 * drops all packets by default
 * prevents IP spoofing based on port's mac address (compatible with allowed_address_pairs extension)
 * allows incoming DHCP and ICMPv6 RA
 * blocks outgoing DHCP
 * drops INVALID packets
 * allows stateful, established connections
 * converts security group rules to iptables rules (IPv4, IPv6, TCP, UDP, ICMP, ICMPv6)
 * multiple TCP/UDP ports per iptables rule using multiport module

neutron/agent/linux/ovs_lib.py
Open vSwitch command line wrapper library

neutron/plugins/openvswitch/agent/ovs_neutron_agent.py

 * handles local, flat, vlan, gre, vxlan type networks
 * implements security groups through agent-based RPC
 * port_update, treat_devices_added, treat_devices_removed
 * L2 population feature on tunneling (fdb_add, fdb_remove, fdb_update)
 * handles integration, physical, and tunnel bridges
 * provisions each Neutron network to a local vlan
 * patches physical and tunnel bridge to integration bridge via veth pairing
 * adds base flows for connectivity

neutron/db/securitygroups_db.py, neutron/db/securitygroups_rpc_base.py, neutron/extensions/securitygroup.py

 * security groups models and RPC implementation
 * ensures default security group for booting instance if no security group specified
 * allows all egress traffic
 * allows all inbound traffic from other instances in default security group