made the Security Group backend throw the same error as AWS if the nb of sec groups limit is hit (#742)
* made the Security Group backend throw the same error as AWS if the security group limit is hit * included in the security group limit the count of grants to other security groups & updated the unit tests to cover these * refactored a few things about the sec group rule count limit
This commit is contained in:
parent
d42432bfef
commit
a967ec0d39
@ -318,3 +318,11 @@ class InvalidCIDRSubnetError(EC2ClientError):
|
|||||||
"InvalidParameterValue",
|
"InvalidParameterValue",
|
||||||
"invalid CIDR subnet specification: {0}"
|
"invalid CIDR subnet specification: {0}"
|
||||||
.format(cidr))
|
.format(cidr))
|
||||||
|
|
||||||
|
|
||||||
|
class RulesPerSecurityGroupLimitExceededError(EC2ClientError):
|
||||||
|
def __init__(self):
|
||||||
|
super(RulesPerSecurityGroupLimitExceededError, self).__init__(
|
||||||
|
"RulesPerSecurityGroupLimitExceeded",
|
||||||
|
'The maximum number of rules per security group '
|
||||||
|
'has been reached.')
|
||||||
|
@ -58,6 +58,7 @@ from .exceptions import (
|
|||||||
InvalidVpnGatewayIdError,
|
InvalidVpnGatewayIdError,
|
||||||
InvalidVpnConnectionIdError,
|
InvalidVpnConnectionIdError,
|
||||||
InvalidCustomerGatewayIdError,
|
InvalidCustomerGatewayIdError,
|
||||||
|
RulesPerSecurityGroupLimitExceededError,
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import (
|
||||||
EC2_RESOURCE_TO_PREFIX,
|
EC2_RESOURCE_TO_PREFIX,
|
||||||
@ -1264,6 +1265,16 @@ class SecurityGroup(TaggedEC2Resource):
|
|||||||
def add_egress_rule(self, rule):
|
def add_egress_rule(self, rule):
|
||||||
self.egress_rules.append(rule)
|
self.egress_rules.append(rule)
|
||||||
|
|
||||||
|
def get_number_of_ingress_rules(self):
|
||||||
|
return sum(
|
||||||
|
len(rule.ip_ranges) + len(rule.source_groups)
|
||||||
|
for rule in self.ingress_rules)
|
||||||
|
|
||||||
|
def get_number_of_egress_rules(self):
|
||||||
|
return sum(
|
||||||
|
len(rule.ip_ranges) + len(rule.source_groups)
|
||||||
|
for rule in self.egress_rules)
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupBackend(object):
|
class SecurityGroupBackend(object):
|
||||||
|
|
||||||
@ -1360,6 +1371,10 @@ class SecurityGroupBackend(object):
|
|||||||
if not is_valid_cidr(cidr):
|
if not is_valid_cidr(cidr):
|
||||||
raise InvalidCIDRSubnetError(cidr=cidr)
|
raise InvalidCIDRSubnetError(cidr=cidr)
|
||||||
|
|
||||||
|
self._verify_group_will_respect_rule_count_limit(
|
||||||
|
group, group.get_number_of_ingress_rules(),
|
||||||
|
ip_ranges, source_group_names, source_group_ids)
|
||||||
|
|
||||||
source_group_names = source_group_names if source_group_names else []
|
source_group_names = source_group_names if source_group_names else []
|
||||||
source_group_ids = source_group_ids if source_group_ids else []
|
source_group_ids = source_group_ids if source_group_ids else []
|
||||||
|
|
||||||
@ -1425,6 +1440,10 @@ class SecurityGroupBackend(object):
|
|||||||
if not is_valid_cidr(cidr):
|
if not is_valid_cidr(cidr):
|
||||||
raise InvalidCIDRSubnetError(cidr=cidr)
|
raise InvalidCIDRSubnetError(cidr=cidr)
|
||||||
|
|
||||||
|
self._verify_group_will_respect_rule_count_limit(
|
||||||
|
group, group.get_number_of_egress_rules(),
|
||||||
|
ip_ranges, source_group_names, source_group_ids)
|
||||||
|
|
||||||
source_group_names = source_group_names if source_group_names else []
|
source_group_names = source_group_names if source_group_names else []
|
||||||
source_group_ids = source_group_ids if source_group_ids else []
|
source_group_ids = source_group_ids if source_group_ids else []
|
||||||
|
|
||||||
@ -1472,6 +1491,20 @@ class SecurityGroupBackend(object):
|
|||||||
return security_rule
|
return security_rule
|
||||||
raise InvalidPermissionNotFoundError()
|
raise InvalidPermissionNotFoundError()
|
||||||
|
|
||||||
|
def _verify_group_will_respect_rule_count_limit(
|
||||||
|
self, group, current_rule_nb,
|
||||||
|
ip_ranges, source_group_names=None, source_group_ids=None):
|
||||||
|
max_nb_rules = 50 if group.vpc_id else 100
|
||||||
|
future_group_nb_rules = current_rule_nb
|
||||||
|
if ip_ranges:
|
||||||
|
future_group_nb_rules += len(ip_ranges)
|
||||||
|
if source_group_ids:
|
||||||
|
future_group_nb_rules += len(source_group_ids)
|
||||||
|
if source_group_names:
|
||||||
|
future_group_nb_rules += len(source_group_names)
|
||||||
|
if future_group_nb_rules > max_nb_rules:
|
||||||
|
raise RulesPerSecurityGroupLimitExceededError
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupIngress(object):
|
class SecurityGroupIngress(object):
|
||||||
|
|
||||||
|
@ -379,6 +379,136 @@ def test_authorize_all_protocols_with_no_port_specification():
|
|||||||
sg.rules[0].to_port.should.equal(None)
|
sg.rules[0].to_port.should.equal(None)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_sec_group_rule_limit():
|
||||||
|
ec2_conn = boto.connect_ec2()
|
||||||
|
sg = ec2_conn.create_security_group('test', 'test')
|
||||||
|
other_sg = ec2_conn.create_security_group('test_2', 'test_other')
|
||||||
|
|
||||||
|
# INGRESS
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
cidr_ip=['{0}.0.0.0/0'.format(i) for i in range(110)])
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
|
||||||
|
sg.rules.should.be.empty
|
||||||
|
# authorize a rule targeting a different sec group (because this count too)
|
||||||
|
success = ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
src_security_group_group_id=other_sg.id)
|
||||||
|
success.should.be.true
|
||||||
|
# fill the rules up the limit
|
||||||
|
success = ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
cidr_ip=['{0}.0.0.0/0'.format(i) for i in range(99)])
|
||||||
|
success.should.be.true
|
||||||
|
# verify that we cannot authorize past the limit for a CIDR IP
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1', cidr_ip=['100.0.0.0/0'])
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
# verify that we cannot authorize past the limit for a different sec group
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
src_security_group_group_id=other_sg.id)
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
|
||||||
|
# EGRESS
|
||||||
|
# authorize a rule targeting a different sec group (because this count too)
|
||||||
|
ec2_conn.authorize_security_group_egress(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
src_group_id=other_sg.id)
|
||||||
|
# fill the rules up the limit
|
||||||
|
# remember that by default, when created a sec group contains 1 egress rule
|
||||||
|
# so our other_sg rule + 98 CIDR IP rules + 1 by default == 100 the limit
|
||||||
|
for i in range(98):
|
||||||
|
ec2_conn.authorize_security_group_egress(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
cidr_ip='{0}.0.0.0/0'.format(i))
|
||||||
|
# verify that we cannot authorize past the limit for a CIDR IP
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group_egress(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
cidr_ip='101.0.0.0/0')
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
# verify that we cannot authorize past the limit for a different sec group
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group_egress(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
src_group_id=other_sg.id)
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_sec_group_rule_limit_vpc():
|
||||||
|
ec2_conn = boto.connect_ec2()
|
||||||
|
vpc_conn = boto.connect_vpc()
|
||||||
|
|
||||||
|
vpc = vpc_conn.create_vpc('10.0.0.0/8')
|
||||||
|
|
||||||
|
sg = ec2_conn.create_security_group('test', 'test', vpc_id=vpc.id)
|
||||||
|
other_sg = ec2_conn.create_security_group('test_2', 'test', vpc_id=vpc.id)
|
||||||
|
|
||||||
|
# INGRESS
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
cidr_ip=['{0}.0.0.0/0'.format(i) for i in range(110)])
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
|
||||||
|
sg.rules.should.be.empty
|
||||||
|
# authorize a rule targeting a different sec group (because this count too)
|
||||||
|
success = ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
src_security_group_group_id=other_sg.id)
|
||||||
|
success.should.be.true
|
||||||
|
# fill the rules up the limit
|
||||||
|
success = ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
cidr_ip=['{0}.0.0.0/0'.format(i) for i in range(49)])
|
||||||
|
# verify that we cannot authorize past the limit for a CIDR IP
|
||||||
|
success.should.be.true
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1', cidr_ip=['100.0.0.0/0'])
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
# verify that we cannot authorize past the limit for a different sec group
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
src_security_group_group_id=other_sg.id)
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
|
||||||
|
# EGRESS
|
||||||
|
# authorize a rule targeting a different sec group (because this count too)
|
||||||
|
ec2_conn.authorize_security_group_egress(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
src_group_id=other_sg.id)
|
||||||
|
# fill the rules up the limit
|
||||||
|
# remember that by default, when created a sec group contains 1 egress rule
|
||||||
|
# so our other_sg rule + 48 CIDR IP rules + 1 by default == 50 the limit
|
||||||
|
for i in range(48):
|
||||||
|
ec2_conn.authorize_security_group_egress(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
cidr_ip='{0}.0.0.0/0'.format(i))
|
||||||
|
# verify that we cannot authorize past the limit for a CIDR IP
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group_egress(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
cidr_ip='50.0.0.0/0')
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
# verify that we cannot authorize past the limit for a different sec group
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
ec2_conn.authorize_security_group_egress(
|
||||||
|
group_id=sg.id, ip_protocol='-1',
|
||||||
|
src_group_id=other_sg.id)
|
||||||
|
cm.exception.error_code.should.equal('RulesPerSecurityGroupLimitExceeded')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Boto3
|
Boto3
|
||||||
'''
|
'''
|
||||||
|
Loading…
Reference in New Issue
Block a user