cc.systems logo

gb flag

Log Aggregation und Alerting in KRITIS richtig umgesetzt

Wenn man alle Logs zentralisiert, lassen sich darauf einfach Alarme definieren. Was gilt es zu beachten und welche Fehler lassen sich leicht vermeiden.

Pascal L.

Pascal L.

30.07.2024

Insbesondere im Umfeld von kritischer Infrastruktur ist es besonders wichtig ein mehrstufiges Sicherheitskonzept zu implementieren. Wir haben uns damit beschäftigt, wie sich logbasierte Alarme umsetzen lassen, wenn man es mit einer umfangreichen Cloudlandschaft aus vielen Google Cloud Projekten zu tun hat, die unter einer gemeinsamen Organization zusammengefasst sind.

Im Folgenden zeige ich, wie sich Logs aller Google Cloud Projekte aggregieren lassen, um an zentraler Stelle projektübergreifende logbasierte Alarme zu realisieren. Ich werde dabei auf einige Hürden eingehen, in die wir auch gelaufen sind.

IAM role assignment to user email alert

Übersicht

Wir erstellen auf Höhe der Organization einen Log Router, der alle Logs in ein anderes Projekt leitet. Es ist notwendig den Filter der _Default Senke anzupassen, Berechtigungen zu erteilen und ein Log Alert zu erstellen. Optional können alle Logs anschließend noch in ein Logging Bucket gespeichert werden.

Log Router auf der Organization

Auf der GCP Organization installieren wir einen Log Router (Senke), der alle Logs inkl. der der darunter liegenden Projekte (include_children) einsammelt und an ein Google Cloud Projekt leitet. Den Parameter intercept_children belassen wir beim Default false, um auf die Logs auch weiterhin in den jeweiligen Projekten zugreifen zu können. Es ist theoretisch auch möglich, direkt in ein Logging Bucket umzuleiten, allerdings es ist dann nicht möglich darauf Log Alarme zu installieren. Somit ist der “Umweg” über ein Projekt notwendig.

1data "google_organization" "org" {
2  domain = "example.com"
3}
4
5resource "google_project" "logging" {
6  // Your project in which you collect all aggregated logs and implement log alerts
7}
8
9resource "google_logging_organization_sink" "org_aggregation" {
10  name             = "organization-logs-aggregation-sink"
11  description      = "Route organization logs to logging project"
12  org_id           = data.google_organization.org
13  destination      = "logging.googleapis.com/projects/${google_project.logging.id}"
14  include_children = true
15}

Berechtigungen richtig setzen

Die Logs werden mit einem dedizierten Service Account geschrieben, der auf das Logging Projekt entsprechende Berechtigungen braucht.

1resource "google_project_iam_member" "log_writer" {
2  project = google_project.logging.id
3  role    = "roles/logging.logWriter"
4  member  = google_logging_organization_sink.org_aggregation.writer_identity
5}

Anpassung des _Default Log Routers

Alle Logs, welche an ein anderes Projekt weitergeleitet werden, werden von den dort installierten Log Routern bearbeitet. Standardmäßig existiert auf jedem Projekt ein Log Router mit dem Namen _Default. In der Regel stellt das kein Problem dar, solange man nicht einen logbasierten Alarm auf den Audit Logs erstellen möchte. Die _Default Senke hat nämlich einen vordefinierten Filter, welcher die Audit Logs filtert:

1NOT LOG_ID("cloudaudit.googleapis.com/activity") AND NOT \
2LOG_ID("externalaudit.googleapis.com/activity") AND NOT \
3LOG_ID("cloudaudit.googleapis.com/system_event") AND NOT \
4LOG_ID("externalaudit.googleapis.com/system_event") AND NOT \
5LOG_ID("cloudaudit.googleapis.com/access_transparency") AND NOT \
6LOG_ID("externalaudit.googleapis.com/access_transparency")

Möchte man zum Beispiel einen Alarm auf den SetIamPolicy Audit Logs erstellen, so muss cloudaudit.googleapis.com/activity aus dem Filter entfernt werden. Dafür empfehle in den Import des Log Routers in den Terraform State, um den Filter einfach bearbeiten zu können.

1import {
2  id = "projects/<project-id>/sinks/_Default"
3  to = google_logging_project_sink.default
4}
5resource "google_logging_project_sink" "default" {
6  name        = "_Default"
7  project     = google_project.logging.id
8  destination = "logging.googleapis.com/projects/${google_project.logging.id}/locations/global/buckets/_Default"
9  filter      = <<-EOT
10  NOT LOG_ID("externalaudit.googleapis.com/activity") AND NOT
11  LOG_ID("cloudaudit.googleapis.com/system_event") AND NOT
12  LOG_ID("externalaudit.googleapis.com/system_event") AND NOT
13  LOG_ID("cloudaudit.googleapis.com/access_transparency") AND NOT
14  LOG_ID("externalaudit.googleapis.com/access_transparency")
15  EOT
16}

Logs im Log Explorer ansehen

Um die aggregierten Logs anzusehen, öffnet man den Log Explorer und wechselt den Scope zu _Default.

gcp log explorer log scope

Log Alarm implementieren

Bleibt man bei dem SetIamPolicy Beispiel, kann ein logbasierter Alarm wie folgt aussehen:

1resource "google_monitoring_alert_policy" "default" {
2  display_name = "IAM role assignment to user"
3  severity     = "WARNING"
4  project      = google_project.logging.id
5
6  alert_strategy {
7    auto_close = "1800s"
8
9    notification_rate_limit {
10      period = "300s"
11    }
12  }
13
14  combiner = "OR"
15  conditions {
16    display_name = "Log match condition"
17
18    condition_matched_log {
19      filter = <<-EOT
20      protoPayload.methodName="SetIamPolicy" AND
21      protoPayload.serviceData.policyDelta.bindingDeltas.action="ADD" AND
22      protoPayload.serviceData.policyDelta.bindingDeltas.member:"user:"
23      EOT
24    }
25  }
26
27  documentation {
28    content   = "An IAM role has been assigned directly to a user. Roles should always be assigned to security groups and not individuals."
29    mime_type = "text/markdown"
30  }
31
32  notification_channels = [
33    <your-notification-channels>
34  ]
35}

Optionales Routing in ein Logging Bucket

Möchte man die Logs zusätzlich speichern, ist es auch möglich ein Logging Bucket zu erstellen und dieses als Ziel im Log Router anzugeben. Wichtig hervorzuheben ist, dass der Service Account spezielles Berechtigungen benötigt, um in das Bucket schreiben zu können.

1resource "google_logging_project_bucket_config" "org_aggregation" {
2  project        = google_project.logging.id
3  location       = var.region
4  retention_days = 30
5  bucket_id      = "organization-logs-aggregation"
6}
7
8resource "google_logging_project_sink" "org_aggregation" {
9  name        = "organization-logs-aggregation-sink"
10  description = "Route aggregated organization logs to Cloud Logging bucket"
11  project     = google_project.logging.id
12  destination = "logging.googleapis.com/${google_logging_project_bucket_config.org_aggregation.id}"
13}
14
15resource "google_project_iam_member" "log_writer" {
16  project = google_project.logging.id
17  role    = "roles/logging.bucketWriter"
18  member  = google_logging_project_sink.org_aggregation.writer_identity
19}

Logs filtern

Es kann aus Datenschutz oder Kostengründen Sinn machen nicht alle Logs aller Projekte in ein weiteres Projekt zu kopieren. Für obiges Beispiel, welches nur an den SetIamPolicy Logs interessiert ist, würde es total ausreichen der google_logging_organization_sink.org_aggregation einen Filter mitzugeben.

1filter = "protoPayload.methodName=\"SetIamPolicy\""

Technologien

Terraform

Terraform

Google Cloud Operations

Google Cloud Operations

Google Cloud

Google Cloud

Kontakt

Cal.com preview image
Cal.com preview image

Damit du einen Termin buchen kannst, wird ein externer Kalender von Cal.com geladen.
Mit dem Button stimmst du auch den Cookies von Cal.com zu.

Für Details, schaue in unsere Datenschutzerklärung.

cc.systems logo

© 2024 cc.systems GmbH, Hamburg