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.
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.
Ü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.
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
Google Cloud Operations
Google Cloud
Kontakt
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.