Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 94 additions & 14 deletions libcloud/compute/drivers/gce.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,15 +569,23 @@ def __repr__(self):
class GCEFirewall(UuidMixin):
"""A GCE Firewall rule class."""

def __init__(self, id, name, allowed, network, source_ranges, source_tags,
target_tags, driver, extra=None):
def __init__(self, id, name, allowed, denied, direction, network,
source_ranges, source_tags, priority,
source_service_accounts, target_service_accounts,
target_tags, target_ranges, driver, extra=None):
self.id = str(id)
self.name = name
self.network = network
self.allowed = allowed
self.denied = denied
self.direction = direction
self.priority = priority
self.source_ranges = source_ranges
self.source_tags = source_tags
self.source_service_accounts = source_service_accounts
self.target_tags = target_tags
self.target_service_accounts = target_service_accounts
self.target_ranges = target_ranges
self.driver = driver
self.extra = extra
UuidMixin.__init__(self)
Expand Down Expand Up @@ -3096,14 +3104,22 @@ def ex_create_healthcheck(self, name, host=None, path=None, port=None,
self.connection.async_request(request, method='POST', data=hc_data)
return self.ex_get_healthcheck(name)

def ex_create_firewall(self, name, allowed, network='default',
def ex_create_firewall(self, name, allowed=None, denied=None,
network='default', target_ranges=None,
direction='INGRESS', priority=1000,
source_service_accounts=None,
target_service_accounts=None,
source_ranges=None, source_tags=None,
target_tags=None):
target_tags=None, description=None):
"""
Create a firewall on a network.
Create a firewall rule on a network.
Rules can be for Ingress or Egress, and they may Allow or
Deny traffic. They are also applied in order based on action
(Deny, Allow) and Priority. Rules can be applied using various Source
and Target filters.

Firewall rules should be supplied in the "allowed" field. This is a
list of dictionaries formated like so ("ports" is optional)::
Firewall rules should be supplied in the "allowed" or "denied" field.
This is a list of dictionaries formatted like so ("ports" is optional):

[{"IPProtocol": "<protocol string or number>",
"ports": "<port_numbers or ranges>"}]
Expand All @@ -3115,15 +3131,32 @@ def ex_create_firewall(self, name, allowed, network='default',
"ports": ["8080"]},
{"IPProtocol": "udp"}]

Note that valid inputs vary by direction (INGRESS vs EGRESS), action
(allow/deny), and source/target filters (tag vs range etc).

See `Firewall Reference <https://developers.google.com/compute/docs/
reference/latest/firewalls/insert>`_ for more information.

:param name: Name of the firewall to be created
:type name: ``str``

:param allowed: List of dictionaries with rules
:param description: Optional description of the rule.
:type description: ``str``

:param direction: Direction of the FW rule - "INGRESS" or "EGRESS"
Defaults to 'INGRESS'.
:type direction: ``str``

:param priority: Priority integer of the rule -
lower is applied first. Defaults to 1000
:type priority: ``int``

:param allowed: List of dictionaries with rules for type INGRESS
:type allowed: ``list`` of ``dict``

:param denied: List of dictionaries with rules for type EGRESS
:type denied: ``list`` of ``dict``

:keyword network: The network that the firewall applies to.
:type network: ``str`` or :class:`GCENetwork`

Expand All @@ -3132,6 +3165,10 @@ def ex_create_firewall(self, name, allowed, network='default',
['0.0.0.0/0']
:type source_ranges: ``list`` of ``str``

:keyword source_service_accounts: A list of source service accounts
the rules apply to.
:type source_service_accounts: ``list`` of ``str``

:keyword source_tags: A list of source instance tags the rules apply
to.
:type source_tags: ``list`` of ``str``
Expand All @@ -3140,6 +3177,15 @@ def ex_create_firewall(self, name, allowed, network='default',
to.
:type target_tags: ``list`` of ``str``

:keyword target_service_accounts: A list of target service accounts
the rules apply to.
:type target_service_accounts: ``list`` of ``str``

:keyword target_ranges: A list of IP ranges in CIDR format that the
EGRESS type rule should apply to. Defaults
to ['0.0.0.0/0']
:type target_ranges: ``list`` of ``str``

:return: Firewall object
:rtype: :class:`GCEFirewall`
"""
Expand All @@ -3150,16 +3196,29 @@ def ex_create_firewall(self, name, allowed, network='default',
nw = network

firewall_data['name'] = name
firewall_data['allowed'] = allowed
firewall_data['direction'] = direction
firewall_data['priority'] = priority
firewall_data['description'] = description
if direction == 'INGRESS':
firewall_data['allowed'] = allowed
elif direction == 'EGRESS':
firewall_data['denied'] = denied
firewall_data['network'] = nw.extra['selfLink']
if source_ranges is None and source_tags is None:
if source_ranges is None and source_tags is None \
and source_service_accounts is None:
source_ranges = ['0.0.0.0/0']
if source_ranges is not None:
firewall_data['sourceRanges'] = source_ranges
if source_tags is not None:
firewall_data['sourceTags'] = source_tags
if source_service_accounts is not None:
firewall_data['sourceServiceAccounts'] = source_service_accounts
if target_tags is not None:
firewall_data['targetTags'] = target_tags
if target_service_accounts is not None:
firewall_data['targetServiceAccounts'] = target_service_accounts
if target_ranges is not None:
firewall_data['destinationRanges'] = target_ranges

request = '/global/firewalls'

Expand Down Expand Up @@ -5279,13 +5338,23 @@ def ex_update_firewall(self, firewall):
firewall_data = {}
firewall_data['name'] = firewall.name
firewall_data['allowed'] = firewall.allowed
firewall_data['denied'] = firewall.denied
# Priority updates not yet exposed via API
firewall_data['network'] = firewall.network.extra['selfLink']
if firewall.source_ranges:
firewall_data['sourceRanges'] = firewall.source_ranges
if firewall.source_tags:
firewall_data['sourceTags'] = firewall.source_tags
if firewall.source_service_accounts:
firewall_data['sourceServiceAccounts'] = \
firewall.source_service_accounts
if firewall.target_tags:
firewall_data['targetTags'] = firewall.target_tags
if firewall.target_service_accounts:
firewall_data['targetServiceAccounts'] = \
firewall.target_service_accounts
if firewall.target_ranges:
firewall_data['destinationRanges'] = firewall.target_ranges
if firewall.extra['description']:
firewall_data['description'] = firewall.extra['description']

Expand Down Expand Up @@ -8215,7 +8284,7 @@ def _to_firewall(self, firewall):
"""
Return a Firewall object from the JSON-response dictionary.

:param firewall: The dictionary describing the firewall.
:param firewall: The dictionary deFscribing the firewall.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an extra F here. I'll remove it.

:type firewall: ``dict``

:return: Firewall object
Expand All @@ -8229,15 +8298,26 @@ def _to_firewall(self, firewall):
'network'])['name']

network = self.ex_get_network(extra['network_name'])

allowed = firewall.get('allowed')
denied = firewall.get('denied')
priority = firewall.get('priority')
direction = firewall.get('direction')
source_ranges = firewall.get('sourceRanges')
source_tags = firewall.get('sourceTags')
source_service_accounts = firewall.get('sourceServiceAccounts')
target_tags = firewall.get('targetTags')
target_service_accounts = firewall.get('targetServiceAccounts')
target_ranges = firewall.get('targetRanges')

return GCEFirewall(id=firewall['id'], name=firewall['name'],
allowed=firewall.get('allowed'), network=network,
source_ranges=source_ranges,
allowed=allowed, denied=denied,
network=network, target_ranges=target_ranges,
source_ranges=source_ranges, priority=priority,
source_tags=source_tags, target_tags=target_tags,
driver=self, extra=extra)
source_service_accounts=source_service_accounts,
target_service_accounts=target_service_accounts,
direction=direction, driver=self, extra=extra)

def _to_forwarding_rule(self, forwarding_rule):
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"denied": [
{
"IPProtocol": "tcp",
"ports": [
"4567"
]
}
],
"creationTimestamp": "2013-06-26T10:04:43.773-09:00",
"description": "Libcloud Deny Firewall",
"direction": "INGRESS",
"id": "0565629596395414123",
"kind": "compute#firewall",
"name": "lcfirewall-deny",
"network": "https://www.googleapis.com/compute/v1/projects/project_name/global/networks/default",
"priority": 900,
"selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/firewalls/lcfirewall-deny",
"sourceRanges": [
"10.240.100.0/24"
],
"sourceTags": [
"libcloud"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"allowed": [
{
"IPProtocol": "tcp",
"ports": [
"4567"
]
}
],
"creationTimestamp": "2013-06-26T10:05:43.773-07:00",
"description": "Libcloud Egress Firewall",
"direction": "EGRESS",
"id": "0565629596395414122",
"kind": "compute#firewall",
"name": "lcfirewall-egress",
"network": "https://www.googleapis.com/compute/v1/projects/project_name/global/networks/default",
"priority": 900,
"selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/firewalls/lcfirewall-egress",
"targetServiceAccounts": [
"lctarget@gserviceaccount.com"
],
"destinationRanges": [
"8.8.8.8/32"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@
}
],
"creationTimestamp": "2013-06-26T10:04:43.773-07:00",
"description": "Libcloud Test Firewall",
"direction": "INGRESS",
"id": "0565629596395414121",
"kind": "compute#firewall",
"name": "lcfirewall",
"network": "https://www.googleapis.com/compute/v1/projects/project_name/global/networks/default",
"priority": 900,
"selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/firewalls/lcfirewall",
"sourceTags": [
"sourceServiceAccounts": [
"lcsource@gserviceaccount.com"
],
"targetTags": [
"libcloud"
]
}
60 changes: 54 additions & 6 deletions libcloud/test/compute/test_gce.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,13 +819,53 @@ def test_ex_copy_image(self):
self.assertEqual(image.extra['guestOsFeatures'], expected_features)

def test_ex_create_firewall(self):
firewall_name = 'lcfirewall'
name = 'lcfirewall'
priority = 900
description = "Libcloud Test Firewall"
allowed = [{'IPProtocol': 'tcp', 'ports': ['4567']}]
source_service_accounts = ['lcsource@gserviceaccount.com']
target_tags = ['libcloud']
network = 'default'
firewall = self.driver.ex_create_firewall(
name, allowed, description=description,
network=network, priority=priority, target_tags=target_tags,
source_service_accounts=source_service_accounts)
self.assertTrue(isinstance(firewall, GCEFirewall))
self.assertEqual(firewall.name, name)

def test_ex_create_firewall_egress(self):
name = 'lcfirewall-egress'
priority = 900
direction = 'EGRESS'
description = "Libcloud Egress Firewall"
allowed = [{'IPProtocol': 'tcp', 'ports': ['4567']}]
target_service_accounts = ['lctarget@gserviceaccount.com']
target_ranges = ['8.8.8.8/32']
network = 'default'
firewall = self.driver.ex_create_firewall(
name, allowed,
description=description, network=network,
priority=priority, direction=direction,
target_ranges=target_ranges,
target_service_accounts=target_service_accounts)
self.assertTrue(isinstance(firewall, GCEFirewall))
self.assertEqual(firewall.name, name)

def test_ex_create_firewall_deny(self):
name = 'lcfirewall-deny'
priority = 900
denied = [{'IPProtocol': 'tcp', 'ports': ['4567']}]
description = "Libcloud Deny Firewall"
source_ranges = ['10.240.100.0/24']
source_tags = ['libcloud']
firewall = self.driver.ex_create_firewall(firewall_name, allowed,
source_tags=source_tags)
network = 'default'
firewall = self.driver.ex_create_firewall(
name, denied=denied,
description=description, network=network,
priority=priority, source_tags=source_tags,
source_ranges=source_ranges)
self.assertTrue(isinstance(firewall, GCEFirewall))
self.assertEqual(firewall.name, firewall_name)
self.assertEqual(firewall.name, name)

def test_ex_create_forwarding_rule(self):
fwr_name = 'lcforwardingrule'
Expand Down Expand Up @@ -1394,7 +1434,7 @@ def test_ex_update_firewall(self):
firewall_name = 'lcfirewall'
firewall = self.driver.ex_get_firewall(firewall_name)
firewall.source_ranges = ['10.0.0.0/16']
firewall.source_tags = ['libcloud', 'test']
firewall.description = "LCFirewall-2"
firewall2 = self.driver.ex_update_firewall(firewall)
self.assertTrue(isinstance(firewall2, GCEFirewall))

Expand Down Expand Up @@ -1660,7 +1700,7 @@ def test_ex_get_firewall(self):
firewall = self.driver.ex_get_firewall(firewall_name)
self.assertEqual(firewall.name, firewall_name)
self.assertEqual(firewall.network.name, 'default')
self.assertEqual(firewall.source_tags, ['libcloud'])
self.assertEqual(firewall.target_tags, ['libcloud'])

def test_ex_get_forwarding_rule(self):
fwr_name = 'lcforwardingrule'
Expand Down Expand Up @@ -2284,6 +2324,14 @@ def _global_firewalls_lcfirewall(self, method, url, body, headers):
body = self.fixtures.load('global_firewalls_lcfirewall.json')
return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])

def _global_firewalls_lcfirewall_egress(self, method, url, body, headers):
body = self.fixtures.load('global_firewalls_lcfirewall-egress.json')
return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])

def _global_firewalls_lcfirewall_deny(self, method, url, body, headers):
body = self.fixtures.load('global_firewalls_lcfirewall-deny.json')
return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])

def _global_images(self, method, url, body, headers):
if method == 'POST':
body = self.fixtures.load('global_images_post.json')
Expand Down