GCP Storage Permissions Logging
More Info:
Ensures that logging and log alerts exist for storage permission changes. Storage permissions include access to the buckets that store the logs, any changes in storage permissions should be heavily monitored to prevent unauthorized changes.
Risk Level
High
Address
Security
Compliance Standards
HIPAA PCI
Triage and Remediation
- Remediation
Remediation
Using Console
To fix “Storage Permissions Logging” for Google Cloud Storage using the GCP Console, you essentially need to ensure that Cloud Audit Logs (especially Data Access logs) are enabled for Google Cloud Storage and that they are being routed to a log destination you retain.
Below are the step‑by‑step instructions in the Console.
1. Enable Audit Logs for Google Cloud Storage
- Sign in to the Google Cloud Console.
- Make sure you have selected the correct project (top-left project selector).
- In the left navigation menu, go to:
IAM & Admin → Audit Logs. - In the “Audit Logs” page:
- In the Service list, locate and select:
“Cloud Storage” (sometimes listed as “Storage” or “storage.googleapis.com”).
- In the Service list, locate and select:
- You will see log types for this service:
- Admin Read
- Admin Write
- Data Read
- Data Write
- For comprehensive permissions logging:
- Check Admin Read and Admin Write (if not already enabled).
- Check Data Read and Data Write (these are typically off by default and are crucial for object/permission access logging).
- Click Save at the bottom of the page.
This ensures that actions on buckets/objects and permission changes are written to Cloud Audit Logs.
2. Verify Logs Are Being Stored (Log Router / Sinks)
Audit logs automatically go to Cloud Logging, but compliance often requires storing them longer (e.g., in a bucket, BigQuery, or Pub/Sub). To route them properly:
- In the left navigation menu, go to:
Logging → Logs Router. - Look for an existing sink that captures audit logs (especially from
cloudaudit.googleapis.com).- If one exists and routes to a storage bucket/BigQuery with sufficient retention for your requirements, you may not need to change anything.
- To create a dedicated sink (if needed):
- Click Create sink.
- Sink name: e.g.,
gcs-audit-logs-sink. - Sink destination: choose one, commonly:
- Cloud Storage bucket (recommended for archive),
- or BigQuery (for querying),
- or Pub/Sub (for streaming/forwarding).
- Click Next.
- Choose logs to include:
- Under “Choose logs to include in the sink”, set a filter such as:
logName:"cloudaudit.googleapis.com"protoPayload.serviceName="storage.googleapis.com"
- This captures Storage audit logs (including permission changes and access).
- Under “Choose logs to include in the sink”, set a filter such as:
- Click Create sink.
3. Confirm Logs for Storage Permissions Are Appearing
- Go to Logging → Logs Explorer.
- In the query box, use a filter like:
resource.type="gcs_bucket"logName:"cloudaudit.googleapis.com"
- Click Run query.
- Inspect entries:
- Look for
protoPayload.methodNamevalues related to IAM or ACL changes (e.g.,storage.setIamPermissions,storage.objects.get,storage.buckets.update, etc.). - Confirm that these actions on your buckets are being logged.
- Look for
4. (Optional) Narrow to a Specific Bucket / Project
If the misconfiguration is flagged for a specific bucket:
- In the Logs Explorer query, further filter:
resource.type="gcs_bucket"resource.labels.bucket_name="YOUR_BUCKET_NAME"logName:"cloudaudit.googleapis.com"
- Confirm that both:
- Permission changes (
setIamPolicy,update), and - Access operations (
get,list,insert, etc.)
are present.
- Permission changes (
Once you have:
- Enabled Admin and Data Access audit logs for Cloud Storage in IAM & Admin → Audit Logs, and
- Ensured they are routed and stored via a Logs Router sink,
the “Storage Permissions Logging” finding for GCP Storage should be remediated.
Using CLI
Below are concise, CLI‑only steps to enable Storage permissions logging (audit logging for Cloud Storage IAM & access) in GCP.
1. Set project ID
PROJECT_ID="your-project-id"
gcloud config set project "$PROJECT_ID"
2. Export current IAM policy
gcloud projects get-iam-policy "$PROJECT_ID" \
--format=json > iam-policy.json
3. Edit IAM policy to add audit logging for Cloud Storage
Open iam-policy.json in an editor and add / merge this auditConfigs block at the top level (alongside bindings):
"auditConfigs": [
{
"service": "storage.googleapis.com",
"auditLogConfigs": [
{
"logType": "ADMIN_READ"
},
{
"logType": "DATA_READ"
},
{
"logType": "DATA_WRITE"
}
]
}
]
Notes:
- If
auditConfigsalready exists, just append or merge thestorage.googleapis.comentry into the array. - Keep the rest of the file unchanged.
4. Apply the updated IAM policy
gcloud projects set-iam-policy "$PROJECT_ID" iam-policy.json
This enables:
- ADMIN_READ logs: IAM / permission changes on Cloud Storage.
- DATA_READ logs: reads of bucket/objects.
- DATA_WRITE logs: writes/deletes of bucket/objects.
5. (Optional) Verify audit logs
Use Cloud Logging to confirm logs are being generated:
gcloud logging read \
'logName:"cloudaudit.googleapis.com" AND resource.type="gcs_bucket"' \
--limit=10 --format="table(timestamp, protoPayload.methodName, resource.labels.bucket_name)"
This remediates the “Storage Permissions Logging” gap by ensuring Cloud Storage access and permission changes are captured via Cloud Audit Logs.
Using Python
Below is how to remediate “Storage Permissions Logging” for a Google Cloud Storage bucket using Python, assuming the finding means “bucket access logging is not enabled”.
1. Prerequisites
- Install the client library:
pip install google-cloud-storage
- Authenticate:
gcloud auth application-default login
- Ensure your service account / ADC has
storage.buckets.updateon the target bucket andstorage.buckets.geton the log bucket.
2. Create (or choose) a logging bucket
You need a separate bucket to receive access logs.
from google.cloud import storage
def create_bucket_if_not_exists(project_id: str, bucket_name: str, location: str = "US"):
storage_client = storage.Client(project=project_id)
bucket = storage_client.bucket(bucket_name)
if not bucket.exists():
bucket.storage_class = "STANDARD"
bucket.location = location
bucket = storage_client.create_bucket(bucket)
print(f"Created log bucket {bucket.name}")
else:
print(f"Log bucket {bucket.name} already exists")
return bucket
project_id = "YOUR_PROJECT_ID"
log_bucket_name = "my-project-storage-logs"
create_bucket_if_not_exists(project_id, log_bucket_name)
3. Grant write permission for logging
GCS uses a special writer identity to store logs: cloud-storage-analytics@google.com
Grant it roles/storage.objectCreator on the log bucket.
from google.cloud import storage
def allow_logging_writer_on_log_bucket(log_bucket_name: str):
storage_client = storage.Client()
bucket = storage_client.bucket(log_bucket_name)
bucket.iam_configuration.uniform_bucket_level_access_enabled = True
bucket.patch()
policy = bucket.get_iam_policy(requested_policy_version=3)
role = "roles/storage.objectCreator"
member = "group:cloud-storage-analytics@google.com"
if member not in policy.get(role, []):
policy[role].add(member)
bucket.set_iam_policy(policy)
print(f"Granted {role} to {member} on {log_bucket_name}")
else:
print(f"{member} already has {role} on {log_bucket_name}")
log_bucket_name = "my-project-storage-logs"
allow_logging_writer_on_log_bucket(log_bucket_name)
4. Enable logging on the source bucket
This sets the log destination bucket and an optional log object prefix.
from google.cloud import storage
def enable_bucket_access_logging(
source_bucket_name: str,
log_bucket_name: str,
log_object_prefix: str = "access_logs/"
):
storage_client = storage.Client()
bucket = storage_client.bucket(source_bucket_name)
bucket.logging = {
"logBucket": log_bucket_name,
"logObjectPrefix": log_object_prefix,
}
bucket.patch()
print(
f"Enabled access logging for {source_bucket_name} "
f"to {log_bucket_name} with prefix '{log_object_prefix}'"
)
source_bucket_name = "my-app-data-bucket"
log_bucket_name = "my-project-storage-logs"
enable_bucket_access_logging(source_bucket_name, log_bucket_name)
5. Verify configuration
from google.cloud import storage
def show_logging_config(bucket_name: str):
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)
bucket.reload()
print("Logging configuration:", bucket.logging)
show_logging_config("my-app-data-bucket")
You should see something like:
{'logBucket': 'my-project-storage-logs', 'logObjectPrefix': 'access_logs/'}
If your tool checks multiple buckets, loop over all non-logging buckets and call enable_bucket_access_logging for each.
Using Terraform
resource "google_storage_bucket" "app_data" {
name = "APP_DATA_BUCKET_NAME" # replace with your data bucket name
location = "BUCKET_LOCATION" # e.g. "US"
storage_class = "STANDARD"
# ...other settings...
# Enable access logging for this bucket
logging {
log_bucket = google_storage_bucket.app_data_logs.name
log_object_prefix = "ACCESS_LOG_PREFIX" # e.g. "gcs-access/"
}
}
# Dedicated bucket to receive access logs
resource "google_storage_bucket" "app_data_logs" {
name = "APP_DATA_LOG_BUCKET_NAME" # replace with your log bucket name
location = "BUCKET_LOCATION" # usually same region
storage_class = "STANDARD"
uniform_bucket_level_access = true
# Optionally restrict public access, add lifecycle rules, etc.
}
Enabling the logging block does not force replacement of the existing bucket; Terraform will show an in-place update on google_storage_bucket.app_data adding logging.log_bucket and logging.log_object_prefix, and creation of google_storage_bucket.app_data_logs if it does not yet exist.