Repository: google/timesketch

Issue:
Analyzers: GCP Logging Enhancements
**Is your feature request related to a problem? Please describe.**
Currently there is a [GCP logging analyser](https://github.com/google/timesketch/blob/master/timesketch/lib/analyzers/gcp_servicekey.py) that will tag service account related events.

There are other security related events unrelated to service accounts that could be tagged and it could be useful to add attributes to the sketch based on accounts and resources.

**Describe the solution you'd like**
Change existing analyser to be more generic and tag all security relevant events to include:
- Firewall modifications
- VM creation
- IAM modifications
- Service account creation
- Service account key creation
- VM metadata modification (lateral movement/persistence mechanism)
- Actions taken with default compute service account

Extract all accounts (service and user) and resources and add them as sketch attributes and saved views. Resources should include:
- GCE instances
- GKE clusters
- GCS buckets

**Describe alternatives you've considered**
Creating a different analyser per cloud service - I don't think there is much benefit to be gained from separating functionality out into separate analysers.
Creating separate views per cloud service - It seems more likely that an investigation will focus on a specific account or VM rather than focusing on a cloud service.

**Additional context**
Add any other context or screenshots about the feature request here.



Discussion:

Resolving patch:
diff --git a/timesketch/lib/analyzers/__init__.py b/timesketch/lib/analyzers/__init__.py
--- a/timesketch/lib/analyzers/__init__.py
+++ b/timesketch/lib/analyzers/__init__.py
@@ -21,6 +21,7 @@
 from timesketch.lib.analyzers import domain
 from timesketch.lib.analyzers import expert_sessionizers
 from timesketch.lib.analyzers import feature_extraction
+from timesketch.lib.analyzers import gcp_logging
 from timesketch.lib.analyzers import geoip
 from timesketch.lib.analyzers import login
 from timesketch.lib.analyzers import phishy_domains
diff --git a/timesketch/lib/analyzers/gcp_logging.py b/timesketch/lib/analyzers/gcp_logging.py
new file mode 100644
--- /dev/null
+++ b/timesketch/lib/analyzers/gcp_logging.py
@@ -0,0 +1,109 @@
+"""Sketch analyzer plugin for GCP Logging."""
+import re
+
+from timesketch.lib.analyzers import interface
+from timesketch.lib.analyzers import manager
+
+
+class GCPLoggingSketchPlugin(interface.BaseAnalyzer):
+    """Analyzer for GCP Logging"""
+
+    NAME = 'gcp_logging'
+    DISPLAY_NAME = 'Google Cloud Logging Analyzer'
+    DESCRIPTION = ('Extract features and tag security relevant actions in '
+        'Google Cloud Logging.')
+
+    def _format_resource_name(self, resource_name):
+        """Format resource names for storage as sketch attributes.
+
+        Returns:
+          Tuple in format ('resource_type', 'resource_identifier')
+        """
+        resource_identifier = resource_name.rsplit('/', maxsplit=2)[-1]
+        resource_type = 'gcp_' + resource_name.rsplit('/', maxsplit=2)[-2]
+        return (resource_type, resource_identifier)
+
+    def run(self):
+        """Entry point for the analyzer.
+
+        Returns:
+            String with summary of the analyzer result
+        """
+        query = ('data_type:"gcp:log:json"')
+        return_fields = ['principalEmail', 'methodName', 'resourceName']
+        events = self.event_stream(query_string=query,
+            return_fields=return_fields)
+
+        users = []
+        resources = {}
+
+        for event in events:
+            principal_email = event.source.get('principalEmail')
+            method_name = event.source.get('methodName')
+            resource_name = event.source.get('resourceName')
+
+            if principal_email:
+                if principal_email not in users:
+                    users.append(principal_email)
+
+                if re.match(r'\d{12}-compute@developer\.gserviceaccount\.com',
+                  principal_email):
+                    event.add_tags(['default-service-account'])
+
+            if resource_name:
+                resource_type, resource_identifier = (
+                  self._format_resource_name(resource_name))
+                if resource_type not in resources:
+                    resources[resource_type] = []
+
+                if resource_identifier not in resources[resource_type]:
+                    resources[resource_type].append(resource_identifier)
+
+            if method_name:
+                if method_name.endswith('CreateServiceAccount'):
+                    event.add_tags(['service-account-created'])
+
+                if method_name.endswith('CreateServiceAccountKey'):
+                    event.add_tags(['service-account-key-created'])
+
+                if method_name.endswith('SetIamPolicy'):
+                    event.add_tags(['iam-policy-changed'])
+
+                if method_name.endswith('compute.instances.insert'):
+                    event.add_tags(['gce-instance-created'])
+
+                if method_name.endswith('compute.firewalls.insert'):
+                    event.add_tags(['fw-rule-created'])
+
+                if method_name.endswith('compute.networks.insert'):
+                    event.add_tags(['network-created'])
+
+                # pylint: disable-msg=line-too-long
+                if method_name.endswith('compute.projects.setCommonInstanceMetadata'):
+                    event.add_tags(['compute-metadata-changed'])
+
+                if method_name.endswith('compute.instances.setMetadata'):
+                    event.add_tags(['compute-metadata-changed'])
+
+            event.commit()
+
+        # TODO: Don't add attributes if they already exist.
+        self.sketch.add_sketch_attribute('gcp_identities', users, 'text')
+
+        for resource_type, resource_list in resources.items():
+            # TODO: Don't add attributes if they already exist.
+            self.sketch.add_sketch_attribute(
+              resource_type, resource_list, 'text')
+
+        for user in users:
+            view_name = 'GCP User {0:s}'.format(user)
+            query_string = ('principalEmail:"{0:s}"'.format(user))
+            self.sketch.add_view(
+              view_name=view_name, analyzer_name=self.NAME,
+              query_string=query_string)
+
+        return ('GCP logging analyzer completed with '
+                '{0:d} resource types extracted.').format(len(resources))
+
+
+manager.AnalysisManager.register_analyzer(GCPLoggingSketchPlugin)


Test patch: