def _process_resource_set(self, resources, event=None): client = self.manager.get_client( 'azure.mgmt.resource.locks.ManagementLockClient') result = [] for resource in resources: if resource.get('resourceGroup') is None: locks = [ r.serialize(True) for r in client.management_locks. list_at_resource_group_level(resource['name']) ] else: locks = [ r.serialize(True) for r in client.management_locks.list_at_resource_level( resource['resourceGroup'], ResourceIdParser.get_namespace(resource['id']), ResourceIdParser.get_resource_name( resource.get('c7n:parent-id')) or '', ResourceIdParser.get_resource_type(resource['id']), resource['name']) ] if StringUtils.equal('Absent', self.lock_type) and not locks: result.append(resource) else: for lock in locks: if StringUtils.equal('Any', self.lock_type) or \ StringUtils.equal(lock['properties']['level'], self.lock_type): result.append(resource) break return result
def _process_resource_set(self, resources, event=None): client = self.manager.get_client() result = [] for resource in resources: database_name = resource['name'] if StringUtils.equal(database_name, "master"): continue if 'c7n:data-masking-policy' not in resource: server_id = resource[ChildTypeInfo.parent_key] server_name = ResourceIdParser.get_resource_name(server_id) dmr = client.data_masking_policies.get( resource['resourceGroup'], server_name, database_name) if dmr: resource['c7n:data-masking-policy'] = dmr.serialize( True).get('properties', {}) else: resource['c7n:data-masking-policy'] = {} required_status = 'Enabled' if self.enabled else 'Disabled' if StringUtils.equal( resource['c7n:data-masking-policy'].get( 'dataMaskingState'), required_status): result.append(resource) return result
def _get_tag_value_from_event(self, event): principal_role = self.principal_role_jmes_path.search(event) principal_type = self.principal_type_jmes_path.search(event) user = None # The Subscription Admins role does not have a principal type if StringUtils.equal(principal_role, 'Subscription Admin'): user = self.service_admin_jmes_path.search(event) # ServicePrincipal type elif StringUtils.equal(principal_type, 'ServicePrincipal'): user = self.sp_jmes_path.search(event) if not user: known_claims = { 'upn': self.upn_jmes_path.search(event), 'name': self.name_jmes_path.search(event) } if known_claims[self.default_claim]: user = known_claims[self.default_claim] elif self.default_claim == 'upn' and known_claims['name']: user = known_claims['name'] elif self.default_claim == 'name' and known_claims['upn']: user = known_claims['upn'] # Last effort search for an email address in the claims if not user: claims = event['data'].get('claims', []) for c in claims: value = claims[c] if self._is_email(value): user = value break if not user: self.log.error('Principal could not be determined.') return user
def _get_user_from_event(self, event): principal_role = self.principal_role_jmes_path.search(event) principal_type = self.principal_type_jmes_path.search(event) user = None # The Subscription Admins role does not have a principal type if StringUtils.equal(principal_role, 'Subscription Admin'): user = self.service_admin_jmes_path.search(event) # ServicePrincipal type elif StringUtils.equal(principal_type, 'ServicePrincipal'): user = self.sp_jmes_path.search(event) # Other types and main fallback (e.g. User, Office 365 Groups, and Security Groups) if not user and self.upn_jmes_path.search(event): user = self.upn_jmes_path.search(event) # Last effort search for an email address in the claims if not user: claims = event['data']['claims'] for c in claims: value = claims[c] if self._is_email(value): user = value if not user: self.log.error('Principal could not be determined.') return user
def _get_tag_value_from_event(self, event): principal_role = self.principal_role_jmes_path.search(event) principal_type = self.principal_type_jmes_path.search(event) user = None # The Subscription Admins role does not have a principal type if StringUtils.equal(principal_role, 'Subscription Admin'): user = self.service_admin_jmes_path.search(event) # ServicePrincipal type elif StringUtils.equal(principal_type, 'ServicePrincipal'): user = self.sp_jmes_path.search(event) # Other types and main fallback (e.g. User, Office 365 Groups, and Security Groups) if not user and self.upn_jmes_path.search(event): user = self.upn_jmes_path.search(event) # Last effort search for an email address in the claims if not user: claims = event['data']['claims'] for c in claims: value = claims[c] if self._is_email(value): user = value if not user: self.log.error('Principal could not be determined.') return user
def _get_user_from_event(self, event): principal_role = self.principal_role_jmes_path.search(event) principal_type = self.principal_type_jmes_path.search(event) # The Subscription Admins role does not have a principal type if StringUtils.equal(principal_role, 'Subscription Admin'): return self.service_admin_jmes_path.search(event) # ServicePrincipal type elif StringUtils.equal(principal_type, 'ServicePrincipal'): return self.sp_jmes_path.search(event) # Other types (e.g. User, Office 365 Groups, and Security Groups) elif self.upn_jmes_path.search(event): return self.upn_jmes_path.search(event) else: self.log.error('Principal could not be determined.')
def is_match(self, security_rule): if not StringUtils.equal(self.direction_key, security_rule['properties']['direction']): return False ranges_match = self.is_ranges_match(security_rule) protocol_match = (self.ip_protocol is None) or \ (StringUtils.equal(self.ip_protocol, security_rule['properties']['protocol'])) if self.access is not None: access_match = StringUtils.equal( self.access, security_rule['properties']['access']) return self.match_op([ranges_match, protocol_match, access_match]) else: return self.match_op([ranges_match, protocol_match])
def _process_resource_set(self, resources, event=None): client = self.manager.get_client() result = [] for resource in resources: if 'transparentDataEncryption' not in resource['properties']: server_id = resource[ChildTypeInfo.parent_key] server_name = ResourceIdParser.get_resource_name(server_id) tde = client.transparent_data_encryptions.get( resource['resourceGroup'], server_name, resource['name'], "current") resource['properties']['transparentDataEncryption'] = \ tde.serialize(True).get('properties', {}) required_status = 'Enabled' if self.enabled else 'Disabled' if StringUtils.equal( resource['properties']['transparentDataEncryption'].get('status'), required_status): result.append(resource) return result
def resource_api_version(self, resource_id): """ latest non-preview api version for resource """ namespace = ResourceIdParser.get_namespace(resource_id) resource_type = ResourceIdParser.get_resource_type(resource_id) cache_id = namespace + resource_type if cache_id in self._provider_cache: return self._provider_cache[cache_id] resource_client = self.client( 'azure.mgmt.resource.ResourceManagementClient') provider = resource_client.providers.get(namespace) # The api version may be directly provided if not provider.resource_types and resource_client.providers.api_version: return resource_client.providers.api_version rt = next((t for t in provider.resource_types if StringUtils.equal(t.resource_type, resource_type)), None) if rt and rt.api_versions: versions = [ v for v in rt.api_versions if 'preview' not in v.lower() ] api_version = versions[0] if versions else rt.api_versions[0] self._provider_cache[cache_id] = api_version return api_version
def _provision(self, params): rg_unit = ResourceGroupUnit() rg_unit.provision_if_not_exists({'name': params['resource_group_name'], 'location': params['location']}) plan_params = AppServicePlan( app_service_plan_name=params['name'], location=params['location'], sku=SkuDescription( name=params['sku_name'], capacity=1, tier=params['sku_tier']), kind='linux', target_worker_size_id=0, reserved=True) plan = self.client.app_service_plans.begin_create_or_update(params['resource_group_name'], params['name'], plan_params).result() # Deploy default autoscale rule for dedicated plans if required by the policy autoscale_params = copy.deepcopy(params.get('auto_scale', {})) if bool(autoscale_params.get('enabled')) and \ not StringUtils.equal(plan.sku, 'dynamic'): autoscale_params['name'] = 'autoscale' autoscale_params['resource_group_name'] = params['resource_group_name'] autoscale_params['service_plan_id'] = plan.id autoscale_params['location'] = plan.location ac_unit = AutoScaleUnit() ac_unit.provision(autoscale_params) return plan
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 _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 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 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 resource_api_version(self, resource_id): """ latest non-preview api version for resource """ namespace = ResourceIdParser.get_namespace(resource_id) resource_type = ResourceIdParser.get_resource_type(resource_id) if resource_type in self._provider_cache: return self._provider_cache[resource_type] resource_client = self.client('azure.mgmt.resource.ResourceManagementClient') provider = resource_client.providers.get(namespace) rt = next((t for t in provider.resource_types if StringUtils.equal(t.resource_type, resource_type.split('/')[-1])), None) if rt and rt.api_versions: versions = [v for v in rt.api_versions if 'preview' not in v.lower()] api_version = versions[0] if versions else rt.api_versions[0] self._provider_cache[resource_type] = api_version return api_version
def resource_api_version(self, resource_id): """ latest non-preview api version for resource """ namespace = ResourceIdParser.get_namespace(resource_id) resource_type = ResourceIdParser.get_resource_type(resource_id) cache_id = namespace + resource_type if cache_id in self._provider_cache: return self._provider_cache[cache_id] resource_client = self.client('azure.mgmt.resource.ResourceManagementClient') provider = resource_client.providers.get(namespace) rt = next((t for t in provider.resource_types if StringUtils.equal(t.resource_type, resource_type)), None) if rt and rt.api_versions: versions = [v for v in rt.api_versions if 'preview' not in v.lower()] api_version = versions[0] if versions else rt.api_versions[0] self._provider_cache[cache_id] = api_version return api_version
def test_string_utils_equal(self): # Case insensitive matches self.assertTrue(StringUtils.equal("FOO", "foo")) self.assertTrue(StringUtils.equal("fOo", "FoO")) self.assertTrue(StringUtils.equal("ABCDEFGH", "abcdefgh")) self.assertFalse(StringUtils.equal("Foo", "Bar")) # Case sensitive matches self.assertFalse(StringUtils.equal("Foo", "foo", False)) self.assertTrue(StringUtils.equal("foo", "foo", False)) self.assertTrue(StringUtils.equal("fOo", "fOo", False)) self.assertFalse(StringUtils.equal("Foo", "Bar")) # Strip whitespace matches self.assertTrue(StringUtils.equal(" Foo ", "foo")) self.assertTrue(StringUtils.equal("Foo", " foo ")) self.assertTrue(StringUtils.equal(" Foo ", "Foo", False)) self.assertTrue(StringUtils.equal("Foo", " Foo ", False)) # Returns false for non string types self.assertFalse(StringUtils.equal(1, "foo")) self.assertFalse(StringUtils.equal("foo", 1)) self.assertFalse(StringUtils.equal(True, False))
def is_consumption_plan(function_params): return StringUtils.equal(function_params.service_plan['sku_tier'], 'dynamic')
def test_string_utils_equal(self): # Case insensitive matches self.assertTrue(StringUtils.equal("FOO", "foo")) self.assertTrue(StringUtils.equal("fOo", "FoO")) self.assertTrue(StringUtils.equal("ABCDEFGH", "abcdefgh")) self.assertFalse(StringUtils.equal("Foo", "Bar")) # Case sensitive matches self.assertFalse(StringUtils.equal("Foo", "foo", False)) self.assertTrue(StringUtils.equal("foo", "foo", False)) self.assertTrue(StringUtils.equal("fOo", "fOo", False)) self.assertFalse(StringUtils.equal("Foo", "Bar")) # Strip whitespace matches self.assertTrue(StringUtils.equal(" Foo ", "foo")) self.assertTrue(StringUtils.equal("Foo", " foo ")) self.assertTrue(StringUtils.equal(" Foo ", "Foo", False)) self.assertTrue(StringUtils.equal("Foo", " Foo ", False)) # Returns false for non string types self.assertFalse(StringUtils.equal(1, "foo")) self.assertFalse(StringUtils.equal("foo", 1)) self.assertFalse(StringUtils.equal(True, False))
def is_consumption_plan(function_params): return StringUtils.equal(function_params.service_plan['sku_tier'], 'dynamic')
def process_resource(self, resource, event_item=None): # if the auto-tag-user policy set update to False (or it's unset) then we # will skip writing their UserName tag and not overwrite pre-existing values if not self.should_update and resource.get('tags', {}).get( self.tag_key, None): return user = self.default_user if event_item: principal_role = self.principal_role_jmes_path.search(event_item) principal_type = self.principal_type_jmes_path.search(event_item) # The Subscription Admins role does not have a principal type if StringUtils.equal(principal_role, 'Subscription Admin'): user = self.service_admin_jmes_path.search(event_item) or user # ServicePrincipal type elif StringUtils.equal(principal_type, 'ServicePrincipal'): user = self.sp_jmes_path.search(event_item) or user # Other types (e.g. User, Office 365 Groups, and Security Groups) elif self.upn_jmes_path.search(event_item): user = self.upn_jmes_path.search(event_item) else: self.log.error('Principal could not be determined.') else: # Calculate start time delta_days = self.data.get('days', self.max_query_days) start_time = utcnow() - datetime.timedelta(days=delta_days) # resource group type if self.manager.type == 'resourcegroup': resource_type = "Microsoft.Resources/subscriptions/resourcegroups" query_filter = " and ".join([ "eventTimestamp ge '%s'" % start_time, "resourceGroupName eq '%s'" % resource['name'], "eventChannels eq 'Operation'" ]) # other Azure resources else: resource_type = resource['type'] query_filter = " and ".join([ "eventTimestamp ge '%s'" % start_time, "resourceUri eq '%s'" % resource['id'], "eventChannels eq 'Operation'" ]) # fetch activity logs logs = self.client.activity_logs.list(filter=query_filter, select=self.query_select) # get the user who issued the first operation operation_name = "%s/write" % resource_type first_op = self.get_first_operation(logs, operation_name) if first_op is not None: user = first_op.caller # issue tag action to label user try: TagHelper.add_tags(self, resource, {self.tag_key: user}) except CloudError as e: # resources can be locked if e.inner_exception.error == 'ScopeLocked': pass