Permalink
Browse files

security group rules!

	new file:   bf
	modified:   bf.py
	modified:   botoform/builders.py
	modified:   botoform/enriched/vpc.py
	modified:   botoform/enriched/vpc_endpoint.py
	modified:   botoform/util.py
	modified:   tests/unit/test_utils.py
  • Loading branch information...
Russell Ballestrini
Russell Ballestrini committed Dec 9, 2015
1 parent a0d77b8 commit 731db7c971140601fdd14400daca598c9132143b
Showing with 107 additions and 4 deletions.
  1. +1 −0 bf
  2. +1 −0 bf.py
  3. +37 −3 botoform/builders.py
  4. +4 −0 botoform/enriched/vpc.py
  5. +1 −1 botoform/enriched/vpc_endpoint.py
  6. +24 −0 botoform/util.py
  7. +39 −0 tests/unit/test_utils.py
View
1 bf
View
1 bf.py 100644 → 100755
@@ -1,2 +1,3 @@
+#!/usr/bin/env python
from botoform.__main__ import main
main()
View
@@ -5,6 +5,7 @@
Log,
update_tags,
make_tag_dict,
+ get_port_range,
)
from botoform.subnetallocator import allocate
@@ -53,6 +54,7 @@ def _apply_all(self, config):
self.associate_route_tables_with_subnets(config.get('subnets', no_cfg))
self.endpoints(config.get('endpoints', []))
self.security_groups(config.get('security_groups', no_cfg))
+ self.security_group_rules(config.get('security_groups', no_cfg))
try:
self.evpc.lock_instances()
@@ -152,11 +154,11 @@ def endpoints(self, route_tables):
def security_groups(self, security_group_cfg):
"""Build Security Groups defined in config."""
- for name, data in security_group_cfg.items():
- sg = self.evpc.get_security_group(name)
+ for sg_name, rules in security_group_cfg.items():
+ sg = self.evpc.get_security_group(sg_name)
if sg is not None:
continue
- longname = '{}-{}'.format(self.evpc.name, name)
+ longname = '{}-{}'.format(self.evpc.name, sg_name)
self.log.emit('creating security_group {}'.format(longname))
security_group = self.evpc.create_security_group(
GroupName = longname,
@@ -167,3 +169,35 @@ def security_groups(self, security_group_cfg):
)
update_tags(security_group, Name = longname)
+ def security_group_rules(self, security_group_cfg):
+ """Build Security Group Rules defined in config."""
+ msg = "'{}' into '{}' over ports {} ({})"
+ for sg_name, rules in security_group_cfg.items():
+ sg = self.evpc.get_security_group(sg_name)
+ permissions = []
+ for rule in rules:
+ protocol = rule[1]
+ from_port, to_port = get_port_range(rule[2], protocol)
+ src_sg = self.evpc.get_security_group(rule[0])
+
+ permission = {
+ 'IpProtocol' : protocol,
+ 'FromPort' : from_port,
+ 'ToPort' : to_port,
+ }
+
+ if src_sg is None:
+ permission['IpRanges'] = [{'CidrIp' : rule[0]}]
+ else:
+ permission['UserIdGroupPairs'] = [{'GroupId':src_sg.id}]
+
+ permissions.append(permission)
+
+ fmsg = msg.format(rule[0],sg_name,rule[2],rule[1].upper())
+ self.log.emit(fmsg)
+
+ sg.authorize_ingress(
+ IpPermissions = permissions
+ )
+
+
View
@@ -221,6 +221,10 @@ def delete_internet_gateways(self):
def delete_security_groups(self):
"""Delete related security groups."""
+ for sg in self.security_groups.all():
+ if len(sg.ip_permissions) >= 1:
+ sg.revoke_ingress(IpPermissions = sg.ip_permissions)
+
for sg in self.security_groups.all():
if sg.group_name == 'default':
continue
@@ -39,5 +39,5 @@ def delete_related(self):
"""Delete all VPC endpoints related to this VPC."""
ids = self.related_ids()
if len(ids) == 0: return None
- return self.evpc.ec2_client.delete_vpc_endpoints(VpcEndpointIds = ids)
+ return self.evpc.boto.ec2_client.delete_vpc_endpoints(VpcEndpointIds = ids)
View
@@ -195,6 +195,30 @@ def key_value_to_dict(key_value_list, sep='=', pair_sep=','):
d[key] = value
return d
+def get_port_range(raw_range, ip_protocol='tcp'):
+ """
+ Accept raw_range and return (from_port, to_port) tuple.
+
+ raw_range is a string or integer in the following forms:
+
+ 443, 'all', '5000-5009', ' 8080'
+ """
+ if not raw_range:
+ raise Exception('Missing or empty port range')
+
+ if ip_protocol == 'icmp':
+ return (-1, -1)
+
+ raw_range = str(raw_range).replace(' ','')
+
+ if raw_range == 'all' or raw_range == 'ALL':
+ port_range = [1, 65535]
+ elif '-' in raw_range:
+ port_range = raw_range.split('-')
+ else:
+ port_range = [raw_range, raw_range]
+ return tuple(map(int, port_range))
+
def snake_to_camel_case(name, answers=None):
"""
Accept a snake_case string and return a CamelCase string.
View
@@ -6,6 +6,7 @@
key_value_to_dict,
snake_to_camel_case,
make_tag_dict,
+ get_port_range,
)
class TestLog(TestCase):
@@ -54,6 +55,44 @@ def test_name_vpc_id(self):
'VpcId'
)
+
+class TestGetPortRange(TestCase):
+
+ def test_all_port(self):
+ self.assertTupleEqual(get_port_range('all'), (1, 65535))
+
+ def test_all_caps_port(self):
+ self.assertTupleEqual(get_port_range('ALL'), (1, 65535))
+
+ def test_icmp_is_negative_one_tuple(self):
+ self.assertTupleEqual(get_port_range('anything', 'icmp'), (-1, -1))
+
+ def test_all_mixed_port_raises_value_error(self):
+ with self.assertRaises(ValueError):
+ get_port_range('aLL')
+
+ def test_taco_port_raises_value_error(self):
+ with self.assertRaises(ValueError):
+ get_port_range('taco')
+
+ def test_no_port_raises_exception(self):
+ with self.assertRaises(TypeError):
+ get_port_range()
+
+ def test_empty_port_raises_exception(self):
+ with self.assertRaises(Exception):
+ get_port_range('')
+
+ def test_443_port(self):
+ self.assertTupleEqual(get_port_range('443'), (443, 443))
+
+ def test_5000_5009_port(self):
+ self.assertTupleEqual(get_port_range('5000-5009'), (5000, 5009))
+
+ def test_5000_5009_port_with_whitespace(self):
+ self.assertTupleEqual(get_port_range(' 5000- 5009'), (5000, 5009))
+
+
def test_make_tag_dict():
class TestSubject(object):
tags = [

0 comments on commit 731db7c

Please sign in to comment.