def _update_machine(self, client, device_type, jamf_id): logger.info("Update machine %s %s %s", client.source_repr, device_type, jamf_id) try: machine_d, tags = client.get_machine_d_and_tags( device_type, jamf_id) except Exception: logger.exception("Could not get machine_d and tags. %s %s %s", client.source_repr, device_type, jamf_id) return serial_number = machine_d.get("serial_number") if not serial_number: logger.warning("Machine %s %s %s without serial number", client.source_repr, device_type, jamf_id) return with transaction.atomic(): yield from commit_machine_snapshot_and_yield_events(machine_d) if tags: machine = MetaMachine(serial_number) for taxonomy_id, tag_names in tags.items(): taxonomy = self._get_taxonomy(taxonomy_id) if taxonomy: machine.update_taxonomy_tags(taxonomy, tag_names)
def test_source(self): tree = copy.deepcopy(self.machine_snapshot3) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) tree = copy.deepcopy(self.machine_snapshot3) tree["serial_number"] = tree["serial_number"][::-1] msc2, ms2 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) self.assertEqual(msc2.source, msc.source) self.assertEqual(ms2.source, ms.source) self.assertEqual([], list(Source.objects.current_machine_group_sources())) self.assertEqual([ms.source], list(Source.objects.current_business_unit_sources())) self.assertEqual( [ms.source], list(Source.objects.current_machine_snapshot_sources())) self.assertEqual([ms.source], list(Source.objects.current_macos_apps_sources())) for sn in (self.serial_number, ms2.serial_number): mm = MetaMachine(sn) mm.archive() self.assertEqual( [], list(Source.objects.current_machine_snapshot_sources())) self.assertEqual([], list(Source.objects.current_macos_apps_sources()))
def test_machine_tag(self): tree = copy.deepcopy(self.machine_snapshot) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) tag = Tag.objects.create(name="tag name") self.assertEqual(str(tag), "tag name") MachineTag.objects.create(tag=tag, serial_number=self.serial_number) self.assertEqual(list(Tag.objects.used_in_inventory()), [(tag, 1)]) mm = MetaMachine(self.serial_number) mm.archive() self.assertEqual(list(Tag.objects.used_in_inventory()), [])
def test_machine_tag(self): tree = copy.deepcopy(self.machine_snapshot) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) tag = Tag.objects.create(name="tag name") self.assertEqual(str(tag), "tag name") MachineTag.objects.create(tag=tag, serial_number=self.serial_number) self.assertEqual(list(Tag.objects.used_in_inventory()), [(tag, 1)]) mm = MetaMachine(self.serial_number) mm.archive() self.assertEqual(list(Tag.objects.used_in_inventory()), [])
def test_meta_machine(self): tree = copy.deepcopy(self.machine_snapshot) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) tree = copy.deepcopy(self.machine_snapshot2) msc2, ms2 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) tree = copy.deepcopy(self.machine_snapshot3) msc3, ms3 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) mm = MetaMachine(self.serial_number) self.assertEqual(mm.serial_number, self.serial_number) self.assertEqual(mm.snapshots, [ms3]) self.assertEqual(mm.platform, MACOS) tag1, _ = Tag.objects.get_or_create(name="tag111") tag2, _ = Tag.objects.get_or_create(name="tag222") MachineTag.objects.create(tag=tag1, serial_number=self.serial_number) self.assertEqual( (MACOS, None, {self.meta_business_unit.id}, {tag1.id}), mm.get_probe_filtering_values()) MetaBusinessUnitTag.objects.create( tag=tag2, meta_business_unit=self.meta_business_unit) self.assertEqual( (MACOS, None, {self.meta_business_unit.id}, {tag1.id, tag2.id}), mm.get_probe_filtering_values()) mm.archive() mm = MetaMachine(self.serial_number) self.assertEqual(mm.snapshots, []) self.assertEqual(MachineSnapshot.objects.count(), 3) self.assertEqual(MachineSnapshotCommit.objects.count(), 3) self.assertEqual(CurrentMachineSnapshot.objects.count(), 0)
def do_post(self, data): post_munki_request_event(self.machine_serial_number, self.user_agent, self.ip, request_type="job_details", enrollment={"pk": self.enrollment.pk}) # serialize configuration configuration = self.enrollment.configuration response_d = { "apps_full_info_shard": configuration.inventory_apps_full_info_shard } if configuration.principal_user_detection_sources: principal_user_detection = response_d.setdefault( "principal_user_detection", {}) principal_user_detection[ "sources"] = configuration.principal_user_detection_sources if configuration.principal_user_detection_domains: principal_user_detection[ "domains"] = configuration.principal_user_detection_domains if configuration.collected_condition_keys: response_d[ "collected_condition_keys"] = configuration.collected_condition_keys # add tags # TODO better cache for the machine tags m = MetaMachine(self.machine_serial_number) response_d["incidents"] = [ mi.incident.name for mi in m.open_incidents() ] response_d["tags"] = m.tag_names() # last seen sha1sum # last managed installs sync try: munki_state = MunkiState.objects.get( machine_serial_number=self.machine_serial_number) except MunkiState.DoesNotExist: pass else: response_d['last_seen_sha1sum'] = munki_state.sha1sum response_d['managed_installs'] = ( munki_state.last_managed_installs_sync is None or (datetime.utcnow() - munki_state.last_managed_installs_sync > timedelta( days=configuration.managed_installs_sync_interval_days))) return response_d
def dispatch(self, request, *args, **kwargs): try: token = request.META['HTTP_X_MONOLITH_TOKEN'].strip() api_data = verify_secret(token, 'zentral.contrib.monolith') except (KeyError, ValueError, APIAuthError): return HttpResponseForbidden("No no no!") # machine serial number h_msn = request.META.get("HTTP_X_ZENTRAL_SERIAL_NUMBER") # new way t_msn = api_data.get("machine_serial_number") # old way if h_msn and t_msn and h_msn != t_msn: logger.warning("Serial number mismatch. header: %s, token: %s", h_msn, t_msn) self.machine_serial_number = h_msn or t_msn # priority to h_msn because set in preflight script # business unit, manifest self.meta_business_unit = api_data['business_unit'].meta_business_unit self.manifest = get_object_or_404( Manifest, meta_business_unit=self.meta_business_unit) self.user_agent, self.ip = user_agent_and_ip_address_from_request( request) # machine extra infos self.machine = MetaMachine(self.machine_serial_number) self.tags = self.machine.tags if not self.machine_serial_number: logger.warning("Missing serial number. mbu: %s %s", self.meta_business_unit, self.meta_business_unit.pk) return super().dispatch(request, *args, **kwargs)
def commit_machine_snapshot(self, max_age=3600): module = 'zentral.contrib.jamf_protect' if MetaMachine(self.serial_number).has_recent_source_snapshot( module, max_age): logger.debug( "Skip Jamf Protect machine snapshot commit for machine %s.", self.serial_number) return tree = { 'source': { 'module': module, 'name': 'Jamf Protect' }, 'reference': self.serial_number, 'serial_number': self.serial_number, 'public_ip_address': self.ip } hostname = self.event.get("host", {}).get("hostname") if hostname: tree['system_info'] = {'computer_name': hostname} business_unit = self.enrolled_machine.enrollment.secret.get_api_enrollment_business_unit( ) if business_unit: tree['business_unit'] = business_unit.serialize() commit_machine_snapshot_and_trigger_events(tree)
def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx["mdm"] = True ctx["machine"] = machine = MetaMachine.from_urlsafe_serial_number(kwargs["urlsafe_serial_number"]) ctx["serial_number"] = machine.serial_number # enrolled devices ctx["enrolled_devices"] = (EnrolledDevice.objects.filter(serial_number=machine.serial_number) .order_by("-updated_at")) ctx["enrolled_devices_count"] = ctx["enrolled_devices"].count() # dep device? try: ctx["dep_device"] = DEPDevice.objects.get(serial_number=machine.serial_number) except DEPDevice.DoesNotExist: pass # dep enrollment sessions ctx["dep_enrollment_sessions"] = DEPEnrollmentSession.objects.filter( enrollment_secret__serial_numbers__contains=[machine.serial_number] ).order_by("-updated_at") ctx["dep_enrollment_sessions_count"] = ctx["dep_enrollment_sessions"].count() # ota enrollment sessions ctx["ota_enrollment_sessions"] = OTAEnrollmentSession.objects.filter( enrollment_secret__serial_numbers__contains=[machine.serial_number] ).order_by("-updated_at") ctx["ota_enrollment_sessions_count"] = ctx["ota_enrollment_sessions"].count() return ctx
def test_update_open_machine_incident(self): event_metadata = EventMetadata(event_type="test", machine_serial_number="YOLOFOMO") event_metadata.machine = MockMetaMachine([self.mbu1], [self.tag1], "WINDOWS", "LAPTOP", serial_number="YOLOFOMO") event = BaseEvent(event_metadata, {"joe": "jackson"}) self.assertTrue(self.probe.test_event(event)) machine_incident1, _ = update_or_create_open_machine_incident( self.probe_source, self.probe.get_matching_event_incident_severity(event), event.metadata.machine_serial_number, event.metadata.uuid ) machine_incident2, event_payloads = update_or_create_open_machine_incident( self.probe_source, self.probe.get_matching_event_incident_severity(event) + 100, event.metadata.machine_serial_number, event.metadata.uuid ) self.assertEqual(machine_incident1, machine_incident2) self.assertEqual(machine_incident2.incident, machine_incident1.incident) self.assertEqual(machine_incident2.incident.severity, SEVERITY_CRITICAL + 100) self.assertEqual(len(event_payloads), 1) event_payload = event_payloads[0] self.assertEqual(event_payload["action"], "updated") self.assertEqual(event_payload["diff"], {"removed": {"severity": SEVERITY_CRITICAL}, "added": {"severity": SEVERITY_CRITICAL + 100}}) self.assertEqual(event_payload["severity"], SEVERITY_CRITICAL + 100) self.assertEqual(event_payload.get("incident"), None) # meta machine self.assertEqual(MetaMachine("YOLOFOMO").max_incident_severity(), SEVERITY_CRITICAL + 100)
def setUpTestData(cls): # user cls.user = User.objects.create_user("godzilla", "*****@*****.**", get_random_string()) cls.group = Group.objects.create(name=get_random_string()) cls.user.groups.set([cls.group]) # machine cls.serial_number = "0123456789" cls.source_name = get_random_string() + "z" MachineSnapshotCommit.objects.commit_machine_snapshot_tree({ "source": { "module": "tests.zentral.io", "name": cls.source_name.upper() }, "serial_number": cls.serial_number, "os_version": { 'name': 'OS X', 'major': 10, 'minor': 11, 'patch': 1 }, "osx_app_instances": [{ 'app': { 'bundle_id': 'io.zentral.baller', 'bundle_name': 'Baller.app', 'bundle_version': '123', 'bundle_version_str': '1.2.3' }, 'bundle_path': "/Applications/Baller.app" }] }) cls.source = Source.objects.get(name=cls.source_name.upper()) cls.machine = MetaMachine(cls.serial_number) cls.url_msn = cls.machine.get_urlsafe_serial_number()
def setUpTestData(cls): # user cls.user = User.objects.create_user("godzilla", "*****@*****.**", get_random_string()) cls.group = Group.objects.create(name=get_random_string()) cls.user.groups.set([cls.group]) # probe cls.probe_source = ProbeSource.objects.create( model="BaseProbe", name=get_random_string(), status=ProbeSource.ACTIVE, body={"filters": {"metadata": [{"event_types": ["inventory_heartbeat"]}]}} ) # machine cls.serial_number = "0123456789" MachineSnapshotCommit.objects.commit_machine_snapshot_tree({ "source": {"module": "tests.zentral.io", "name": "Zentral Tests"}, "serial_number": cls.serial_number, "os_version": {'name': 'OS X', 'major': 10, 'minor': 11, 'patch': 1}, "osx_app_instances": [ {'app': {'bundle_id': 'io.zentral.baller', 'bundle_name': 'Baller.app', 'bundle_version': '123', 'bundle_version_str': '1.2.3'}, 'bundle_path': "/Applications/Baller.app"} ] }) cls.machine = MetaMachine(cls.serial_number) cls.url_msn = cls.machine.get_urlsafe_serial_number()
def test_source(self): tree = copy.deepcopy(self.machine_snapshot3) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) tree = copy.deepcopy(self.machine_snapshot3) tree["serial_number"] = tree["serial_number"][::-1] msc2, ms2 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) self.assertEqual(msc2.source, msc.source) self.assertEqual(ms2.source, ms.source) self.assertEqual([], list(Source.objects.current_machine_group_sources())) self.assertEqual([], list(Source.objects.current_business_unit_sources())) self.assertEqual([ms.source], list(Source.objects.current_machine_snapshot_sources())) self.assertEqual([ms.source], list(Source.objects.current_macos_apps_sources())) for sn in (self.serial_number, ms2.serial_number): mm = MetaMachine(sn) mm.archive() self.assertEqual([], list(Source.objects.current_machine_snapshot_sources())) self.assertEqual([], list(Source.objects.current_macos_apps_sources()))
def test_meta_machine(self): tree = copy.deepcopy(self.machine_snapshot) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) tree = copy.deepcopy(self.machine_snapshot2) msc2, ms2 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) tree = copy.deepcopy(self.machine_snapshot3) msc3, ms3 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) mm = MetaMachine(self.serial_number) self.assertEqual(mm.serial_number, self.serial_number) self.assertEqual(mm.snapshots, [ms3]) self.assertEqual(mm.platform, MACOS) mm.archive() mm = MetaMachine(self.serial_number) self.assertEqual(mm.snapshots, []) self.assertEqual(MachineSnapshot.objects.count(), 3) self.assertEqual(MachineSnapshotCommit.objects.count(), 3) self.assertEqual(CurrentMachineSnapshot.objects.count(), 0)
def post(self, request, *args, **kwargs): enrolled_device = get_object_or_404(EnrolledDevice, pk=kwargs["pk"]) send_device_notification(enrolled_device) messages.info(request, "Device poked!") return HttpResponseRedirect( reverse("mdm:device", args=(MetaMachine(enrolled_device.serial_number).get_urlsafe_serial_number(),)) )
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["mdm"] = True context["urlsafe_serial_number"] = MetaMachine(self.object.serial_number).get_urlsafe_serial_number() context["installed_device_artifacts"] = sorted(self.object.installeddeviceartifact_set.all(), key=lambda ida: ida.created_at, reverse=True) context["device_artifact_commands"] = sorted(self.object.deviceartifactcommand_set.all(), key=lambda dac: dac.id, reverse=True) return context
def do_node_post(self, data): queries = {} if self.machine_serial_number: machine = MetaMachine(self.machine_serial_number) queries = DistributedQueryProbeMachine.objects.new_queries_for_machine( machine) inventory_query = get_distributed_inventory_query(machine, self.ms) if inventory_query: queries[DEFAULT_ZENTRAL_INVENTORY_QUERY_NAME] = inventory_query return {'queries': queries}
def fetch_devices(self): query, args = self.build_query() with connection.cursor() as cursor: cursor.execute(query, args) attributes = [col.name for col in cursor.description] for row in cursor.fetchall(): device = dict(zip(attributes, row)) device["udids"] = sorted(udid for udid in device["udids"] if udid) device["urlsafe_serial_number"] = MetaMachine(device["serial_number"]).get_urlsafe_serial_number() yield device
def test_machine_snapshot_current(self): tree = copy.deepcopy(self.machine_snapshot) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) tree = copy.deepcopy(self.machine_snapshot2) msc2, ms2 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) tree = copy.deepcopy(self.machine_snapshot3) msc3, ms3 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) self.assertEqual(MachineSnapshot.objects.count(), 3) self.assertEqual(MachineSnapshot.objects.current().count(), 1) self.assertEqual(MachineSnapshot.objects.current().get(pk=ms3.id), ms3) mm = MetaMachine(self.serial_number) mm.archive() self.assertEqual(CurrentMachineSnapshot.objects.count(), 0) tree = copy.deepcopy(self.machine_snapshot3) msc4, ms4 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree(tree) self.assertEqual(ms3, ms4) self.assertEqual(CurrentMachineSnapshot.objects.count(), 1) cms = CurrentMachineSnapshot.objects.get(serial_number=self.serial_number) self.assertEqual(cms.machine_snapshot, ms3)
def authenticate(self): try: self.enrolled_machine = EnrolledMachine.objects.select_related( "enrollment__configuration", "enrollment__secret__meta_business_unit" ).get(node_key=self.get_node_key()) except EnrolledMachine.DoesNotExist: raise PermissionDenied("Wrong node_key") self.machine = MetaMachine(self.enrolled_machine.serial_number) self.enrollment = self.enrolled_machine.enrollment
def test_meta_machine_update_taxonomy_tags(self): # one machine serial_number = get_random_string(13) # two tags from taxonomy1 taxonomy1 = Taxonomy.objects.create(name=get_random_string(34)) tag11 = Tag.objects.create(taxonomy=taxonomy1, name=get_random_string(17)) MachineTag.objects.get_or_create(serial_number=serial_number, tag=tag11) tag12 = Tag.objects.create(taxonomy=taxonomy1, name=get_random_string(18)) MachineTag.objects.get_or_create(serial_number=serial_number, tag=tag12) # one tag from taxonomy2 taxonomy2 = Taxonomy.objects.create(name=get_random_string(27)) tag21 = Tag.objects.create(taxonomy=taxonomy2, name=get_random_string(20)) MachineTag.objects.get_or_create(serial_number=serial_number, tag=tag21) # one detached tag tag31 = Tag.objects.create(name=get_random_string(21)) MachineTag.objects.get_or_create(serial_number=serial_number, tag=tag31) # update the taxonomy1 tags. keep one, add two new ones, one collision, remove one. new_tag_names = [get_random_string(22), get_random_string(33)] updated_tag_names = [ tag11.name, # existing, # tag12.name # removed tag31. name, # collision, because we will try to add a tag with the same name, but within the taxonomy1 ] + new_tag_names # new ones mm = MetaMachine(serial_number) mm.update_taxonomy_tags(taxonomy1, updated_tag_names) # verify # two new tags new_tags = list(Tag.objects.filter(name__in=new_tag_names)) self.assertEqual(len(new_tags), 2) # in the taxonomy1 self.assertTrue(all(t.taxonomy == taxonomy1 for t in new_tags)) # expected tags for the machine expected_tags = [("machine", t) for t in [tag11, tag21, tag31] + new_tags] self.assertEqual(set(expected_tags), set(mm.tags_with_types))
def do_node_post(self, data): queries = {} if self.machine_serial_number: machine = MetaMachine(self.machine_serial_number) queries = DistributedQueryProbeMachine.objects.new_queries_for_machine(machine) for query_name, query in get_distributed_inventory_queries(machine, self.ms): if query_name in queries: logger.error("Conflict on the distributed query name %s", query_name) else: queries[query_name] = query return {'queries': queries}
def __init__(self, event_type, **kwargs): self.event_type = event_type self.uuid = kwargs.pop('uuid', uuid.uuid4()) if isinstance(self.uuid, str): self.uuid = uuid.UUID(self.uuid) self.index = int(kwargs.pop('index', 0)) self.created_at = kwargs.pop('created_at', None) if self.created_at is None: self.created_at = datetime.utcnow() elif isinstance(self.created_at, str): self.created_at = parser.parse(self.created_at) self.machine_serial_number = kwargs.pop('machine_serial_number', None) if self.machine_serial_number: self.machine = MetaMachine(self.machine_serial_number) else: self.machine = None self.observer = kwargs.pop('observer', None) self.request = kwargs.pop('request', None) self.tags = kwargs.pop('tags', []) self.incidents = kwargs.pop('incidents', [])
def post(self, request, *args, **kwargs): dep_device = get_object_or_404(DEPDevice, pk=kwargs["pk"]) try: refresh_dep_device(dep_device) except DEPClientError as error: messages.error(request, str(error)) else: messages.info(request, "DEP device refreshed") return HttpResponseRedirect("{}#dep_device".format( reverse("mdm:device", args=(MetaMachine(dep_device.serial_number).get_urlsafe_serial_number(),)) ))
def _update_machine(self, client, device_type, jamf_id): logger.info("Update machine %s %s %s", client.source_repr, device_type, jamf_id) try: machine_d, tags = client.get_machine_d_and_tags( device_type, jamf_id) except Exception: logger.exception("Could not get machine_d and tags. %s %s %s", client.source_repr, device_type, jamf_id) else: if not machine_d.get("serial_number"): logger.warning("Machine %s %s %s without serial number", client.source_repr, device_type, jamf_id) return try: with transaction.atomic(): msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( machine_d) except Exception: logger.exception("Could not commit machine snapshot") else: if msc: for idx, (event_type, created_at, payload) in enumerate( inventory_events_from_machine_snapshot_commit( msc)): event_cls = event_cls_from_type(event_type) metadata = EventMetadata( event_cls.event_type, machine_serial_number=ms.serial_number, index=idx, created_at=created_at, tags=event_cls.tags) event = event_cls(metadata, payload) yield event if tags: machine = MetaMachine(machine_d["serial_number"]) for taxonomy_id, tag_names in tags.items(): taxonomy = self._get_taxonomy(taxonomy_id) if taxonomy: machine.update_taxonomy_tags(taxonomy, tag_names)
def __init__(self, event_type, **kwargs): self.event_type = event_type self.uuid = kwargs.pop('uuid', uuid.uuid4()) if isinstance(self.uuid, str): self.uuid = uuid.UUID(self.uuid) self.index = int(kwargs.pop('index', 0)) self.created_at = kwargs.pop('created_at', datetime.utcnow()) if isinstance(self.created_at, str): self.created_at = parser.parse(self.created_at) self.machine_serial_number = kwargs.pop('machine_serial_number') self.machine = MetaMachine(self.machine_serial_number) self.request = kwargs.pop('request', None) self.tags = kwargs.pop('tags', [])
def dispatch(self, request, *args, **kwargs): try: token = request.META['HTTP_X_MONOLITH_TOKEN'].strip() api_data = verify_secret(token, 'zentral.contrib.monolith') except (KeyError, ValueError, APIAuthError): return HttpResponseForbidden("No no no!") self.machine_serial_number = api_data.get("machine_serial_number", None) self.user_agent, self.ip = user_agent_and_ip_address_from_request(request) self.machine = MetaMachine(self.machine_serial_number) self.tags = self.machine.tags self.meta_business_unit = api_data['business_unit'].meta_business_unit self.manifest = get_object_or_404(Manifest, meta_business_unit=self.meta_business_unit) return super().dispatch(request, *args, **kwargs)
def test_machine_snapshot_current(self): tree = copy.deepcopy(self.machine_snapshot) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) tree = copy.deepcopy(self.machine_snapshot2) msc2, ms2 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) tree = copy.deepcopy(self.machine_snapshot3) msc3, ms3 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) self.assertEqual(MachineSnapshot.objects.count(), 3) self.assertEqual(MachineSnapshot.objects.current().count(), 1) self.assertEqual(MachineSnapshot.objects.current().get(pk=ms3.id), ms3) mm = MetaMachine(self.serial_number) mm.archive() self.assertEqual(CurrentMachineSnapshot.objects.count(), 0) tree = copy.deepcopy(self.machine_snapshot3) msc4, ms4 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) self.assertEqual(ms3, ms4) self.assertEqual(CurrentMachineSnapshot.objects.count(), 1) cms = CurrentMachineSnapshot.objects.get( serial_number=self.serial_number) self.assertEqual(cms.machine_snapshot, ms3)
def test_close_open_machine_incident(self): event_metadata = EventMetadata(event_type="test", machine_serial_number="YOLOFOMO") event_metadata.machine = MockMetaMachine([self.mbu1], [self.tag1], "WINDOWS", "LAPTOP", serial_number="YOLOFOMO") event = BaseEvent(event_metadata, {"joe": "jackson"}) self.assertTrue(self.probe.test_event(event)) machine_incident1, _ = update_or_create_open_machine_incident( self.probe_source, self.probe.get_matching_event_incident_severity(event), event.metadata.machine_serial_number, event.metadata.uuid ) self.assertEqual(machine_incident1.status, STATUS_OPEN) self.assertEqual(machine_incident1.incident.status, STATUS_OPEN) machine_incident2, event_payloads = update_or_create_open_machine_incident( self.probe_source, 0, # severity == 0 => close event.metadata.machine_serial_number, event.metadata.uuid ) self.assertEqual(machine_incident1, machine_incident2) self.assertEqual(machine_incident2.incident, machine_incident1.incident) self.assertEqual(machine_incident2.status, STATUS_CLOSED) incident = machine_incident2.incident self.assertEqual(incident.severity, SEVERITY_CRITICAL) self.assertEqual(incident.status, STATUS_CLOSED) self.assertEqual(len(event_payloads), 2) event_payload1, event_payload2 = event_payloads # machine incident event payload self.assertEqual(event_payload1["action"], "closed") self.assertEqual(event_payload1["incident"]["pk"], incident.pk) self.assertEqual(event_payload1["incident"]["status"], STATUS_OPEN) # Incident still open self.assertEqual(event_payload1["pk"], machine_incident2.pk) self.assertEqual(event_payload1["status"], machine_incident2.status) self.assertEqual(event_payload1["event_id"], str(event.metadata.uuid)) self.assertEqual(event_payload1["diff"], {"removed": {"status": STATUS_OPEN}, "added": {"status": STATUS_CLOSED}}) # incident event payload self.assertEqual(event_payload2["action"], "closed") self.assertEqual(event_payload2["pk"], incident.pk) self.assertEqual(event_payload2["status"], STATUS_CLOSED) # Incident closed now self.assertEqual(event_payload2.get("incident"), None) self.assertEqual(event_payload2["diff"], {"removed": {"status": STATUS_OPEN}, "added": {"status": STATUS_CLOSED}}) # meta machine self.assertEqual(MetaMachine("YOLOFOMO").max_incident_severity(), None)
def test_create_open_machine_incident(self): event_metadata = EventMetadata(event_type="test", machine_serial_number="YOLOFOMO") event_metadata.machine = MockMetaMachine([self.mbu1], [self.tag1], "WINDOWS", "LAPTOP", serial_number="YOLOFOMO") event = BaseEvent(event_metadata, {"joe": "jackson"}) self.assertTrue(self.probe.test_event(event)) machine_incident, event_payloads = update_or_create_open_machine_incident( self.probe_source, self.probe.get_matching_event_incident_severity(event), event.metadata.machine_serial_number, event.metadata.uuid) # machine incident self.assertEqual([machine_incident], list(MachineIncident.objects.all())) self.assertEqual(machine_incident.status, STATUS_OPEN) self.assertEqual(machine_incident.event_id, event.metadata.uuid) # incident incident = machine_incident.incident self.assertEqual([incident], list(Incident.objects.all())) self.assertEqual(incident.probe_source, self.probe_source) self.assertEqual(incident.name, "base probe") self.assertEqual(incident.status, STATUS_OPEN) self.assertEqual(incident.severity, SEVERITY_CRITICAL) self.assertEqual(incident.event_id, event.metadata.uuid) # event payloads self.assertEqual(len(event_payloads), 2) event_payload1, event_payload2 = event_payloads # incident event payload self.assertEqual(event_payload1["action"], "created") self.assertEqual(event_payload1["pk"], incident.pk) self.assertEqual(event_payload1.get("machine_incident"), None) # machine incident event payload self.assertEqual(event_payload2["action"], "created") self.assertEqual( event_payload2["machine_incident"], { "pk": machine_incident.pk, "status": machine_incident.status, "event_id": str(event.metadata.uuid) }) # meta machine self.assertEqual( MetaMachine("YOLOFOMO").max_incident_severity(), SEVERITY_CRITICAL)
def authenticate(self): try: session_id = self.data["session_id"] except KeyError: raise SuspiciousOperation("Missing session_id") try: self.session = FileCarvingSession.objects.select_for_update().get(pk=session_id) except FileCarvingSession.DoesNotExist: raise PermissionDenied("Unknown session_id") # TODO: better. "There can be only one" try: self.enrolled_machine = ( EnrolledMachine.objects.select_related("enrollment__configuration") .filter(serial_number=self.session.serial_number) .order_by("-pk")[0] ) except IndexError: raise PermissionDenied("Unknown machine") self.machine = MetaMachine(self.session.serial_number) self.enrollment = self.enrolled_machine.enrollment
def update_osquery_enrolled_machine_platform_mask(apps, schema_editor): try: from zentral.contrib.inventory.models import MetaMachine from zentral.contrib.inventory.conf import LINUX, MACOS, WINDOWS except ImportError: pass EnrolledMachine = apps.get_model("osquery", "EnrolledMachine") for enrolled_machine in EnrolledMachine.objects.all(): mm = MetaMachine(enrolled_machine.serial_number) if mm.platform: if mm.platform == LINUX: enrolled_machine.platform_mask = 0x01 | 0x08 elif mm.platform == MACOS: enrolled_machine.platform_mask = 0x01 | 0x04 | 0x10 elif mm.platform == WINDOWS: enrolled_machine.platform_mask = 0x02 else: print("Unsupported osquery enrolled machine platform", mm.platform) continue enrolled_machine.save()
def post(self, request, *args, **kwargs): # URL kwargs self.enrollment_secret_secret = kwargs["enrollment_secret"] try: self.hardware_uuid = str(UUID(kwargs["machine_id"])) except ValueError: raise PermissionDenied("Invalid machine id") self.client_cert_dn = self._get_client_cert_dn() self.user_agent, self.ip = user_agent_and_ip_address_from_request( request) self.request_data = self._get_json_data(request) self.cache_key = f"tests/santa/fixtures/{self.enrollment_secret_secret}{self.hardware_uuid}" self.enrolled_machine = None self.tag_ids = [] if self.use_enrolled_machine_cache: try: self.enrolled_machine, self.tag_ids = cache.get(self.cache_key) except TypeError: pass else: if self.enrolled_machine.enrollment.configuration.client_certificate_auth and not self.client_cert_dn: raise PermissionDenied("Missing client certificate") if not self.enrolled_machine: self.enrolled_machine = self.get_enrolled_machine() if not self.enrolled_machine: raise PermissionDenied("Machine not enrolled") meta_machine = MetaMachine(self.enrolled_machine.serial_number) self.tag_ids = [t.id for t in meta_machine.tags] cache.set(self.cache_key, (self.enrolled_machine, self.tag_ids), 600) # TODO cache timeout hardcoded return JsonResponse(self.do_post())
def test_meta_machine(self): tree = copy.deepcopy(self.machine_snapshot) msc, ms = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) tree = copy.deepcopy(self.machine_snapshot2) msc2, ms2 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) tree = copy.deepcopy(self.machine_snapshot3) msc3, ms3 = MachineSnapshotCommit.objects.commit_machine_snapshot_tree( tree) mm = MetaMachine(self.serial_number) self.assertEqual(mm.serial_number, self.serial_number) self.assertEqual(mm.snapshots, [ms3]) self.assertEqual(mm.platform, MACOS) mm.archive() mm = MetaMachine(self.serial_number) self.assertEqual(mm.snapshots, []) self.assertEqual(MachineSnapshot.objects.count(), 3) self.assertEqual(MachineSnapshotCommit.objects.count(), 3) self.assertEqual(CurrentMachineSnapshot.objects.count(), 0)
def do_node_post(self, data): # TODO: The machine serial number is included in the string used to authenticate the requests # This is done in the osx pkg builder. The machine serial number should always be present here. # Maybe we could code a fallback to the available mbu probes if the serial number is not present. return build_osquery_conf(MetaMachine(self.machine_serial_number))
class EventMetadata(object): def __init__(self, event_type, **kwargs): self.event_type = event_type self.uuid = kwargs.pop('uuid', uuid.uuid4()) if isinstance(self.uuid, str): self.uuid = uuid.UUID(self.uuid) self.index = int(kwargs.pop('index', 0)) self.created_at = kwargs.pop('created_at', datetime.utcnow()) if isinstance(self.created_at, str): self.created_at = parser.parse(self.created_at) self.machine_serial_number = kwargs.pop('machine_serial_number') self.machine = MetaMachine(self.machine_serial_number) self.request = kwargs.pop('request', None) self.tags = kwargs.pop('tags', []) @classmethod def deserialize(cls, event_d_metadata): kwargs = event_d_metadata.copy() kwargs['event_type'] = kwargs.pop('type') kwargs['uuid'] = kwargs.pop('id') request_d = kwargs.pop('request', None) if request_d: kwargs['request'] = EventRequest(**request_d) return cls(**kwargs) def serialize(self, machine_metadata=True): d = {'created_at': self.created_at.isoformat(), 'id': str(self.uuid), 'index': self.index, 'type': self.event_type, 'machine_serial_number': self.machine_serial_number, } if self.request: d['request'] = self.request.serialize() if self.tags: d['tags'] = self.tags if not machine_metadata: return d machine_d = {} for ms in self.machine.get_snapshots(): source = ms.source ms_d = {'name': ms.get_machine_str()} if ms.business_unit: if not ms.business_unit.is_api_enrollment_business_unit(): ms_d['business_unit'] = {'reference': ms.business_unit.reference, 'key': ms.business_unit.get_short_key(), 'name': ms.business_unit.name} if ms.os_version: ms_d['os_version'] = str(ms.os_version) for group in ms.groups.all(): ms_d.setdefault('groups', []).append({'reference': group.reference, 'key': group.get_short_key(), 'name': group.name}) key = slugify(source.name) if key in ms_d: # TODO: earlier warning in conf check ? logger.warning('Inventory source slug %s exists already', key) machine_d[key] = ms_d for tag in self.machine.tags(): machine_d.setdefault('tags', []).append({'id': tag.id, 'name': tag.name}) for meta_business_unit in self.machine.meta_business_units(): machine_d.setdefault('meta_business_units', []).append({ 'name': meta_business_unit.name, 'id': meta_business_unit.id }) if machine_d: d['machine'] = machine_d return d