Back to Checks
Project has at least one logging sink exporting copies of all log entries
logging_sink_created
Service: loggingSeverity: medium
OfficialBuilt-inVerified
Categories:loggingforensics-ready
Resource Type:logging.googleapis.com/LogSink
Cloud Logging project contains at least one sink that exports a copy of all log entries to a destination for centralized retention or processing
Risk
Without exporting all logs, audit evidence can expire or be altered in-project, reducing the availability and integrity of telemetry. This hinders threat detection and forensics, prevents cross-project correlation, and can let attacker actions evade scrutiny after log rotation or deletion.
Run this check with Prowler
Run in Prowler CloudRun
prowler gcp --checks logging_sink_createdRecommendation
Create a centralized export sink that routes all logs to a secured, durable, preferably immutable destination with extended retention. Apply least privilege to sink identities, separate duties by isolating destinations, use defense in depth (encryption, access controls), and monitor sink health for continuity.
Remediation
CLI
gcloud logging sinks create <example_resource_name> logging.googleapis.com/projects/<example_resource_id>Terraform
# Create a project-level logging sink exporting all logs
resource "google_logging_project_sink" "sink" {
name = "<example_resource_name>" # critical: creates the sink required by the check
destination = "logging.googleapis.com/projects/<example_resource_id>" # critical: required destination
# No filter set -> exports all log entries (fixes the finding)
}Other
1In Google Cloud Console, go to Logging > Log Router
2Click Create sink
3Set Sink name to <example_resource_name>
4For Sink destination, select Google Cloud project and choose <example_resource_id>
5Leave the inclusion filter empty (exports all logs)
6Click Create
Check Code
from prowler.lib.check.models import Check, Check_Report_GCP
from prowler.providers.gcp.services.logging.logging_client import logging_client
class logging_sink_created(Check):
def execute(self) -> Check_Report_GCP:
findings = []
# Map project_id -> sink for direct project-level sinks
projects_with_logging_sink = {}
for sink in logging_client.sinks:
if sink.filter == "all" and not sink.include_children:
projects_with_logging_sink[sink.project_id] = sink
# Collect org resource names that have a covering sink (includeChildren=True)
covering_org_sinks = {}
for sink in logging_client.sinks:
if sink.filter == "all" and sink.include_children:
covering_org_sinks[sink.project_id] = sink
for project in logging_client.project_ids:
project_obj = logging_client.projects.get(project)
# Determine whether this project is covered by an org-level sink
org = getattr(project_obj, "organization", None) if project_obj else None
org_resource = f"organizations/{org.id}" if org else None
covering_sink = (
covering_org_sinks.get(org_resource) if org_resource else None
)
if project in projects_with_logging_sink:
sink = projects_with_logging_sink[project]
sink_name = getattr(sink, "name", None) or "unknown"
report = Check_Report_GCP(
metadata=self.metadata(),
resource=sink,
resource_id=sink_name,
project_id=project,
location=logging_client.region,
resource_name=(
sink_name if sink_name != "unknown" else "Logging Sink"
),
)
report.status = "PASS"
report.status_extended = f"Sink {sink_name} is enabled exporting copies of all the log entries in project {project}."
findings.append(report)
elif covering_sink:
sink_name = getattr(covering_sink, "name", None) or "unknown"
report = Check_Report_GCP(
metadata=self.metadata(),
resource=covering_sink,
resource_id=sink_name,
project_id=project,
location=logging_client.region,
resource_name=(
sink_name if sink_name != "unknown" else "Logging Sink"
),
)
report.status = "PASS"
report.status_extended = f"Sink {sink_name} at organization level is exporting copies of all the log entries in project {project}."
findings.append(report)
else:
report = Check_Report_GCP(
metadata=self.metadata(),
resource=project_obj,
resource_id=project,
project_id=project,
location=logging_client.region,
resource_name=(getattr(project_obj, "name", None) or "GCP Project"),
)
report.status = "FAIL"
report.status_extended = f"There are no logging sinks to export copies of all the log entries in project {project}."
findings.append(report)
return findings