def process(self, network_security_groups): ip_protocol = self.data.get(IP_PROTOCOL, '*') direction = self.data[DIRECTION] prefix = self.data.get(PREFIX, 'c7n-policy-') # Build a list of ports described in the action. ports = PortsRangeHelper.get_ports_set_from_string(self.data.get(PORTS, '0-65535')) except_ports = PortsRangeHelper.get_ports_set_from_string(self.data.get(EXCEPT_PORTS, '')) self.action_ports = ports.difference(except_ports) for nsg in network_security_groups: nsg_name = nsg['name'] resource_group = nsg['resourceGroup'] # Get list of ports to Deny or Allow access to. ports = self._build_ports_strings(nsg, direction, ip_protocol) if not ports: # If its empty, it means NSG already blocks/allows access to all ports, # no need to change. self.manager.log.info("Network security group %s satisfies provided " "ports configuration, no actions scheduled.", nsg_name) continue rules = nsg['properties']['securityRules'] rules = sorted(rules, key=lambda k: k['properties']['priority']) rules = [r for r in rules if StringUtils.equal(r['properties']['direction'], direction)] lowest_priority = rules[0]['properties']['priority'] if len(rules) > 0 else 4096 # Create new top-priority rule to allow/block ports from the action. rule_name = prefix + str(uuid.uuid1()) new_rule = { 'name': rule_name, 'properties': { 'access': self.access_action, 'destinationAddressPrefix': '*', 'destinationPortRanges': ports, 'direction': self.data[DIRECTION], 'priority': lowest_priority - PRIORITY_STEP, 'protocol': ip_protocol, 'sourceAddressPrefix': '*', 'sourcePortRange': '*', } } self.manager.log.info("NSG %s. Creating new rule to %s access for ports %s", nsg_name, self.access_action, ports) try: self.manager.get_client().security_rules.create_or_update( resource_group, nsg_name, rule_name, new_rule ) except CloudError as e: self.manager.log.error('Failed to create or update security rule for %s NSG.', nsg_name) self.manager.log.error(e)
def process(self, network_security_groups): ip_protocol = self.data.get(IP_PROTOCOL, '*') direction = self.data[DIRECTION] # Build a list of ports described in the action. ports = PortsRangeHelper.get_ports_set_from_string(self.data.get(PORTS, '0-65535')) except_ports = PortsRangeHelper.get_ports_set_from_string(self.data.get(EXCEPT_PORTS, '')) self.action_ports = ports.difference(except_ports) for nsg in network_security_groups: nsg_name = nsg['name'] resource_group = nsg['resourceGroup'] # Get list of ports to Deny or Allow access to. ports = self._build_ports_strings(nsg, direction, ip_protocol) if not ports: # If its empty, it means NSG already blocks/allows access to all ports, # no need to change. self.manager.log.info("Network security group %s satisfies provided " "ports configuration, no actions scheduled.", nsg_name) continue rules = nsg['properties']['securityRules'] rules = sorted(rules, key=lambda k: k['properties']['priority']) rules = [r for r in rules if StringUtils.equal(r['properties']['direction'], direction)] lowest_priority = rules[0]['properties']['priority'] if len(rules) > 0 else 4096 # Create new top-priority rule to allow/block ports from the action. rule_name = 'c7n-policy-' + str(uuid.uuid1()) new_rule = { 'name': rule_name, 'properties': { 'access': self.access_action, 'destinationAddressPrefix': '*', 'destinationPortRanges': ports, 'direction': self.data[DIRECTION], 'priority': lowest_priority - PRIORITY_STEP, 'protocol': ip_protocol, 'sourceAddressPrefix': '*', 'sourcePortRange': '*', } } self.manager.log.info("NSG %s. Creating new rule to %s access for ports %s", nsg_name, self.access_action, ports) try: self.manager.get_client().security_rules.create_or_update( resource_group, nsg_name, rule_name, new_rule ) except CloudError as e: self.manager.log.error('Failed to create or update security rule for %s NSG.', nsg_name) self.manager.log.error(e)
def _build_ports_strings(self, nsg, direction_key, ip_protocol): nsg_ports = PortsRangeHelper.build_ports_dict(nsg, direction_key, ip_protocol) IsAllowed = StringUtils.equal(self.access_action, ALLOW_OPERATION) # Find ports with different access level from NSG and this action diff_ports = sorted([p for p in self.action_ports if nsg_ports.get(p, False) != IsAllowed]) return PortsRangeHelper.get_ports_strings_from_list(diff_ports)
def test_get_ports_strings_from_list(self): self.assertEqual(PortsRangeHelper.get_ports_strings_from_list([]), []) self.assertEqual(PortsRangeHelper.get_ports_strings_from_list([10, 11]), ['10-11']) self.assertEqual(PortsRangeHelper.get_ports_strings_from_list([10, 12, 13, 14]), ['10', '12-14']) self.assertEqual(PortsRangeHelper.get_ports_strings_from_list([10, 12, 13, 14, 20, 21, 22]), ['10', '12-14', '20-22'])
def test_get_ports(self): self.assertEqual( PortsRangeHelper.get_ports_set_from_string("5, 4-5, 9"), {4, 5, 9}) rule = {'properties': {'destinationPortRange': '10-12'}} self.assertEqual(PortsRangeHelper.get_ports_set_from_rule(rule), {10, 11, 12}) rule = {'properties': {'destinationPortRanges': ['80', '10-12']}} self.assertEqual(PortsRangeHelper.get_ports_set_from_rule(rule), {10, 11, 12, 80})
def validate(self): # Check that variable values are valid if PORTS in self.data: if not PortsRangeHelper.validate_ports_string(self.data[PORTS]): raise FilterValidationError("ports string has wrong format.") if EXCEPT_PORTS in self.data: if not PortsRangeHelper.validate_ports_string(self.data[EXCEPT_PORTS]): raise FilterValidationError("exceptPorts string has wrong format.") return True
def test_validate_ports_string(self): self.assertEqual(PortsRangeHelper.validate_ports_string('80'), True) self.assertEqual(PortsRangeHelper.validate_ports_string('22-26'), True) self.assertEqual(PortsRangeHelper.validate_ports_string('80,22'), True) self.assertEqual(PortsRangeHelper.validate_ports_string('80,22-26'), True) self.assertEqual(PortsRangeHelper.validate_ports_string('80,22-26,30-34'), True) self.assertEqual(PortsRangeHelper.validate_ports_string('65537'), False) self.assertEqual(PortsRangeHelper.validate_ports_string('-1'), False) self.assertEqual(PortsRangeHelper.validate_ports_string('10-8'), False) self.assertEqual(PortsRangeHelper.validate_ports_string('80,30,25-65538'), False) self.assertEqual(PortsRangeHelper.validate_ports_string('65536-65537'), False)
def process(self, network_security_groups, event=None): # Get variables self.ip_protocol = self.data.get(IP_PROTOCOL, '*') self.IsAllowed = StringUtils.equal(self.data.get(ACCESS), ALLOW_OPERATION) self.match = self.data.get(MATCH, 'all') # Calculate ports from the settings: # If ports not specified -- assuming the entire range # If except_ports not specifed -- nothing ports_set = PortsRangeHelper.get_ports_set_from_string(self.data.get(PORTS, '0-65535')) except_set = PortsRangeHelper.get_ports_set_from_string(self.data.get(EXCEPT_PORTS, '')) self.ports = ports_set.difference(except_set) nsgs = [nsg for nsg in network_security_groups if self._check_nsg(nsg)] return nsgs
def _check_nsg(self, nsg): nsg_ports = PortsRangeHelper.build_ports_dict(nsg, self.direction_key, self.ip_protocol) num_allow_ports = len([p for p in self.ports if nsg_ports.get(p)]) num_deny_ports = len(self.ports) - num_allow_ports if self.match == 'all': if self.IsAllowed: return num_deny_ports == 0 else: return num_allow_ports == 0 if self.match == 'any': if self.IsAllowed: return num_allow_ports > 0 else: return num_deny_ports > 0
def test_build_ports_dict(self): securityRules = [ {'properties': {'destinationPortRange': '80-84', 'priority': 100, 'direction': 'Outbound', 'access': 'Allow', 'protocol': 'TCP'}}, {'properties': {'destinationPortRange': '85-89', 'priority': 110, 'direction': 'Outbound', 'access': 'Allow', 'protocol': 'UDP'}}, {'properties': {'destinationPortRange': '80-84', 'priority': 120, 'direction': 'Inbound', 'access': 'Deny', 'protocol': 'TCP'}}, {'properties': {'destinationPortRange': '85-89', 'priority': 130, 'direction': 'Inbound', 'access': 'Deny', 'protocol': 'UDP'}}, {'properties': {'destinationPortRange': '80-89', 'priority': 140, 'direction': 'Inbound', 'access': 'Allow', 'protocol': '*'}}] nsg = {'properties': {'securityRules': securityRules}} self.assertEqual(PortsRangeHelper.build_ports_dict(nsg, 'Inbound', 'TCP'), {k: k > 84 for k in range(80, 90)}) self.assertEqual(PortsRangeHelper.build_ports_dict(nsg, 'Inbound', 'UDP'), {k: k < 85 for k in range(80, 90)}) self.assertEqual(PortsRangeHelper.build_ports_dict(nsg, 'Inbound', '*'), {k: False for k in range(80, 90)}) self.assertEqual(PortsRangeHelper.build_ports_dict(nsg, 'Outbound', 'TCP'), {k: True for k in range(80, 85)}) self.assertEqual(PortsRangeHelper.build_ports_dict(nsg, 'Outbound', 'UDP'), {k: True for k in range(85, 90)}) self.assertEqual(PortsRangeHelper.build_ports_dict(nsg, 'Outbound', '*'), {k: True for k in range(80, 90)})
def test_get_ports(self): self.assertEqual(PortsRangeHelper.get_ports_set_from_string("5, 4-5, 9"), set([4, 5, 9])) rule = {'properties': {'destinationPortRange': '10-12'}} self.assertEqual(PortsRangeHelper.get_ports_set_from_rule(rule), set([10, 11, 12])) rule = {'properties': {'destinationPortRanges': ['80', '10-12']}} self.assertEqual(PortsRangeHelper.get_ports_set_from_rule(rule), set([10, 11, 12, 80]))