Skip to content

Google Cloud Storage Setup for OpenContracts

Last Updated: 2026-01-09

This document provides instructions for configuring Google Cloud Storage (GCS) as the storage backend for OpenContracts.

Note: For the authoritative list of GCS environment variables and their defaults, see config/settings/base.py (search for STORAGE_BACKEND == "GCP").

Overview

OpenContracts supports three storage backends, configurable via the STORAGE_BACKEND environment variable: 1. LOCAL (default) - Files stored on local disk 2. AWS - Amazon S3 cloud storage 3. GCP - Google Cloud Storage

Prerequisites

  • Google Cloud Platform account
  • GCS bucket created
  • Service account with appropriate permissions (for production)

Setup Instructions

1. Create GCS Bucket

# Using gcloud CLI
gcloud storage buckets create gs://YOUR-BUCKET-NAME \
    --location=US \
    --default-storage-class=STANDARD \
    --uniform-bucket-level-access

2. Configure Service Account

  1. Create a service account:

    gcloud iam service-accounts create opencontracts-storage \
        --display-name="OpenContracts Storage Service Account"
    

  2. Grant necessary permissions:

    # Grant Storage Admin role to the service account
    gcloud projects add-iam-policy-binding YOUR-PROJECT-ID \
        --member="serviceAccount:opencontracts-storage@YOUR-PROJECT-ID.iam.gserviceaccount.com" \
        --role="roles/storage.admin"
    
    # Or for more granular permissions:
    gcloud storage buckets add-iam-policy-binding gs://YOUR-BUCKET-NAME \
        --member="serviceAccount:opencontracts-storage@YOUR-PROJECT-ID.iam.gserviceaccount.com" \
        --role="roles/storage.objectAdmin"
    

  3. If using GKE/Cloud Run, attach the service account to your workload:

    # For GKE
    kubectl annotate serviceaccount YOUR-KSA \
        iam.gke.io/gcp-service-account=opencontracts-storage@YOUR-PROJECT-ID.iam.gserviceaccount.com
    
    # For Cloud Run
    gcloud run services update YOUR-SERVICE \
        --service-account=opencontracts-storage@YOUR-PROJECT-ID.iam.gserviceaccount.com
    

For Local Development:

  1. Create and download a service account key:

    gcloud iam service-accounts keys create ~/opencontracts-gcs-key.json \
        --iam-account=opencontracts-storage@YOUR-PROJECT-ID.iam.gserviceaccount.com
    

  2. Set the environment variable:

    export GOOGLE_APPLICATION_CREDENTIALS=~/opencontracts-gcs-key.json
    

3. Configure Environment Variables

Update your .env file with the following settings:

# Required: Set storage backend to GCP
STORAGE_BACKEND=GCP

# Required: GCS bucket name
GS_BUCKET_NAME=your-bucket-name

# Optional: Google Cloud project ID
GS_PROJECT_ID=your-project-id

# Authentication (choose one method):
# Method 1: Use default credentials (production with Workload Identity)
# Leave GS_CREDENTIALS empty

# Method 2: Specify path to service account key file
GS_CREDENTIALS=/path/to/service-account-key.json

# Method 3: Use environment variable (set outside of Django)
# GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json

# Security Settings
GS_QUERYSTRING_AUTH=true        # Generate signed URLs for private files (default: true)
GS_EXPIRATION_SECONDS=86400     # URL expiration in seconds (default: 86400 = 1 day)
GS_FILE_OVERWRITE=false         # Prevent file overwrites (default: false)
GS_DEFAULT_ACL=                 # Leave empty for private files (default: None)

# Performance Settings
GS_BLOB_CHUNK_SIZE=2621440      # Chunk size for uploads in bytes (default: 2621440 = 2.5MB)
GS_MAX_MEMORY_SIZE=0            # Max memory before disk rollover, 0 = no limit (default: 0)
GS_IS_GZIPPED=false             # Enable GZIP compression for static files (default: false)

# Optional: Custom endpoint (for emulators or alternative endpoints)
GS_CUSTOM_ENDPOINT=             # Custom GCS endpoint URL (default: None)

# Optional: File location prefix within bucket
GS_LOCATION=                    # Subdirectory prefix for files (default: "")

# Optional: IAM Sign Blob API (required when not using service account key file)
GS_IAM_SIGN_BLOB=false          # Use IAM Sign Blob API for signed URLs (default: false)
GS_SA_EMAIL=opencontracts-storage@YOUR-PROJECT-ID.iam.gserviceaccount.com  # Service account email for signing

Security Best Practices

1. Service Account Permissions

Follow the principle of least privilege. The service account needs:

Minimum Required Permissions: - storage.objects.create - Upload files - storage.objects.delete - Delete files - storage.objects.get - Read files - storage.objects.list - List files - storage.buckets.get - Get bucket metadata

For Signed URLs (if using IAM Sign Blob): - iam.serviceAccounts.signBlob - Sign URLs

2. Bucket Configuration

# Enable uniform bucket-level access (recommended)
gcloud storage buckets update gs://YOUR-BUCKET-NAME --uniform-bucket-level-access

# Enable versioning for data protection
gcloud storage buckets update gs://YOUR-BUCKET-NAME --versioning

# Set lifecycle rules to delete old versions
cat > lifecycle.json <<EOF
{
  "lifecycle": {
    "rule": [
      {
        "action": {"type": "Delete"},
        "condition": {
          "age": 30,
          "isLive": false
        }
      }
    ]
  }
}
EOF
gcloud storage buckets update gs://YOUR-BUCKET-NAME --lifecycle-file=lifecycle.json

3. Access Control

The application implements different ACLs for different file types:

  • Static Files: publicRead ACL for CSS, JS, images
  • Media Files: Private by default, served via signed URLs

4. Security Headers

The custom storage backends automatically add security headers:

  • X-Content-Type-Options: nosniff - Prevent MIME sniffing
  • X-Frame-Options: DENY - Prevent clickjacking
  • Content-Disposition: attachment - Force download for media files

5. Monitoring

Set up logging and monitoring:

# Enable access logs
gcloud storage buckets update gs://YOUR-BUCKET-NAME \
    --log-bucket=gs://YOUR-LOG-BUCKET \
    --log-object-prefix=access-logs/

# Set up alerts for unusual activity
gcloud monitoring alert create \
    --display-name="High GCS Egress" \
    --condition="..." \
    --notification-channels=...

IAM Policy Example

Create a custom IAM role with minimal permissions:

{
  "title": "OpenContracts Storage Role",
  "description": "Minimal permissions for OpenContracts GCS storage",
  "stage": "GA",
  "includedPermissions": [
    "storage.buckets.get",
    "storage.objects.create",
    "storage.objects.delete",
    "storage.objects.get",
    "storage.objects.list",
    "storage.objects.update"
  ]
}

Apply the role:

gcloud iam roles create opencontractsStorage \
    --project=YOUR-PROJECT-ID \
    --file=role-definition.json

gcloud projects add-iam-policy-binding YOUR-PROJECT-ID \
    --member="serviceAccount:opencontracts-storage@YOUR-PROJECT-ID.iam.gserviceaccount.com" \
    --role="projects/YOUR-PROJECT-ID/roles/opencontractsStorage"

CORS Configuration (if needed)

If serving files directly from GCS to browsers:

[
  {
    "origin": ["https://yourdomain.com"],
    "method": ["GET", "HEAD"],
    "responseHeader": ["Content-Type"],
    "maxAgeSeconds": 3600
  }
]

Apply CORS:

gsutil cors set cors.json gs://YOUR-BUCKET-NAME

Migration from AWS S3

To migrate existing files from S3 to GCS:

# Using gsutil
gsutil -m rsync -r s3://YOUR-S3-BUCKET gs://YOUR-GCS-BUCKET

# Or using Storage Transfer Service
gcloud transfer jobs create \
    --source-bucket=YOUR-S3-BUCKET \
    --destination-bucket=YOUR-GCS-BUCKET

Troubleshooting

Common Issues

  1. 403 Forbidden Errors
  2. Check service account permissions
  3. Verify bucket IAM policies
  4. Ensure CORS is configured if accessing from browser

  5. Signed URL Issues

  6. Enable IAM Sign Blob API: GS_IAM_SIGN_BLOB=true
  7. Verify service account has iam.serviceAccounts.signBlob permission
  8. Check URL expiration time

  9. Authentication Errors

  10. Verify GOOGLE_APPLICATION_CREDENTIALS is set correctly
  11. Check service account key file permissions
  12. For production, ensure Workload Identity is configured

  13. Performance Issues

  14. Adjust GS_BLOB_CHUNK_SIZE for large files
  15. Enable GS_IS_GZIPPED for static files
  16. Use Cloud CDN for frequently accessed content

Additional Resources