Least Privilege¶
Complete guide to implementing the principle of least privilege in Google Cloud Platform for maximum security.
📚 Overview¶
The principle of least privilege means granting only the minimum permissions necessary to perform a task. This fundamental security principle reduces risk, limits blast radius, and improves compliance.
Key Benefits: - Reduced Attack Surface: Fewer permissions = fewer exploitation opportunities - Limited Blast Radius: Compromised accounts have minimal impact - Compliance: Meet regulatory requirements (SOC 2, ISO 27001, PCI DSS) - Audit Trail: Clear understanding of who can do what
🎯 Core Principles¶
1. Least Privilege Definition¶
┌────────────────────────────────────────────────────────┐
│ Least Privilege Principle │
├────────────────────────────────────────────────────────┤
│ │
│ Grant the MINIMUM permissions needed to: │
│ • Perform the required task │
│ • For the required duration │
│ • On the required resources │
│ • Nothing more │
│ │
│ Example: │
│ ❌ Bad: Grant roles/editor to developer │
│ • 3,000+ permissions │
│ • Can modify almost everything │
│ • Permanent access │
│ │
│ ✓ Good: Grant specific roles │
│ • roles/compute.instanceAdmin.v1 │
│ • roles/storage.objectViewer │
│ • ~100 permissions │
│ • Only what's needed │
│ │
│ ✓✓ Better: Add time constraints │
│ • Same specific roles │
│ • With expiration condition │
│ • Automatic revocation │
└────────────────────────────────────────────────────────┘
2. Zero Trust Model¶
┌────────────────────────────────────────────────────────┐
│ Zero Trust Principles │
├────────────────────────────────────────────────────────┤
│ │
│ Never Trust, Always Verify: │
│ • No implicit trust based on network location │
│ • Verify every access request │
│ • Assume breach mentality │
│ • Continuous verification │
│ │
│ Implementation in GCP: │
│ ✓ Identity-Aware Proxy (IAP) │
│ ✓ VPC Service Controls │
│ ✓ Context-aware access │
│ ✓ Binary Authorization │
│ ✓ Workload Identity │
│ ✓ Private Google Access │
└────────────────────────────────────────────────────────┘
🔧 Implementation Strategies¶
1. Start with Minimal Access¶
# ❌ WRONG: Start with broad access
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='user:newdev@company.com' \
--role='roles/editor'
# ✓ RIGHT: Start with read-only
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='user:newdev@company.com' \
--role='roles/viewer'
# Then add specific permissions as needed
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='user:newdev@company.com' \
--role='roles/compute.instanceAdmin.v1'
# Principle: Start restrictive, expand as needed
# Never start permissive and try to restrict later
2. Use Predefined Roles Over Basic Roles¶
┌────────────────────────────────────────────────────────┐
│ Role Selection Hierarchy │
└────────────────────────────────────────────────────────┘
Level 1: Predefined Roles (BEST)
├─ Service-specific
├─ Curated by Google
├─ Regularly updated
└─ Example: roles/compute.instanceAdmin.v1
Level 2: Custom Roles (GOOD)
├─ Tailored to exact needs
├─ You maintain
├─ More work to manage
└─ Example: custom.vmOperator
Level 3: Basic Roles (AVOID)
├─ Too broad
├─ Legacy
├─ Not recommended
└─ Example: roles/editor
Level 4: Owner Role (NEVER)
├─ Full control
├─ Can delete project
├─ Highest risk
└─ Example: roles/owner
3. Implement Time-Bound Access¶
# Temporary access with expiration
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='user:contractor@company.com' \
--role='roles/compute.instanceAdmin.v1' \
--condition='expression=request.time < timestamp("2026-06-30T23:59:59Z"),title=Q2 Contract,description=Access expires end of Q2 2026'
# Business hours only access
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='user:support@company.com' \
--role='roles/compute.viewer' \
--condition='expression=request.time.getHours("UTC") >= 9 && request.time.getHours("UTC") < 17,title=Business Hours,description=9 AM to 5 PM UTC only'
# Weekend access for maintenance
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='user:maintenance@company.com' \
--role='roles/compute.instanceAdmin.v1' \
--condition='expression=request.time.getDayOfWeek("UTC") == 0 || request.time.getDayOfWeek("UTC") == 6,title=Weekend Only,description=Saturday and Sunday only'
4. Resource-Specific Permissions¶
# Grant access to specific bucket only
gcloud storage buckets add-iam-policy-binding gs://app-data-prod \
--member='serviceAccount:app@project.iam.gserviceaccount.com' \
--role='roles/storage.objectViewer'
# Grant access to specific VM only
gcloud compute instances add-iam-policy-binding my-vm \
--zone=us-central1-a \
--member='user:admin@company.com' \
--role='roles/compute.instanceAdmin.v1'
# Use conditions for resource-based access
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='user:developer@company.com' \
--role='roles/compute.instanceAdmin.v1' \
--condition='expression=resource.labels.environment == "dev",title=Dev VMs Only,description=Can only manage VMs labeled as dev'
📊 Access Review Process¶
1. Regular Access Audits¶
# Weekly: Review new access grants
gcloud logging read \
'protoPayload.methodName="SetIamPolicy"' \
--limit=50 \
--format='table(timestamp,protoPayload.authenticationInfo.principalEmail,resource.labels.project_id)'
# Monthly: Review all project access
gcloud projects get-iam-policy PROJECT_ID \
--format='table(bindings.role,bindings.members.flatten())' > access-report-$(date +%Y%m%d).txt
# Quarterly: Full organization audit
gcloud asset search-all-iam-policies \
--scope=organizations/ORG_ID \
--query="policy:user:*@company.com" \
--format=json > org-access-audit-$(date +%Y%m%d).json
2. IAM Recommender Integration¶
# Get over-permission recommendations
gcloud recommender recommendations list \
--project=PROJECT_ID \
--recommender=google.iam.policy.Recommender \
--location=global \
--format='table(name,primaryImpact.category,stateInfo.state,description)'
# Get detailed recommendation
gcloud recommender recommendations describe RECOMMENDATION_ID \
--project=PROJECT_ID \
--recommender=google.iam.policy.Recommender \
--location=global
# Apply recommendation
gcloud recommender recommendations mark-claimed RECOMMENDATION_ID \
--project=PROJECT_ID \
--recommender=google.iam.policy.Recommender \
--location=global
# After applying changes
gcloud recommender recommendations mark-succeeded RECOMMENDATION_ID \
--project=PROJECT_ID \
--recommender=google.iam.policy.Recommender \
--location=global
3. Automated Access Review Script¶
#!/usr/bin/env python3
"""
Automated IAM access review script
Identifies over-privileged accounts and generates report
"""
from google.cloud import resourcemanager_v3
from google.cloud import recommender_v1
from datetime import datetime
import csv
def review_project_access(project_id):
"""Review and report on project IAM access"""
# Get project IAM policy
client = resourcemanager_v3.ProjectsClient()
policy = client.get_iam_policy(
request={"resource": f"projects/{project_id}"}
)
issues = []
# Check for basic roles (Owner, Editor, Viewer)
for binding in policy.bindings:
if binding.role in ['roles/owner', 'roles/editor', 'roles/viewer']:
for member in binding.members:
issues.append({
'severity': 'HIGH' if binding.role == 'roles/owner' else 'MEDIUM',
'member': member,
'role': binding.role,
'issue': f'Using basic role instead of predefined role',
'recommendation': 'Replace with specific predefined roles'
})
# Check for service accounts with Editor/Owner
for binding in policy.bindings:
if binding.role in ['roles/owner', 'roles/editor']:
for member in binding.members:
if member.startswith('serviceAccount:'):
issues.append({
'severity': 'CRITICAL',
'member': member,
'role': binding.role,
'issue': 'Service account with overly broad permissions',
'recommendation': 'Use least privilege service account roles'
})
# Get IAM Recommender suggestions
recommender_client = recommender_v1.RecommenderClient()
parent = f"projects/{project_id}/locations/global/recommenders/google.iam.policy.Recommender"
try:
recommendations = recommender_client.list_recommendations(parent=parent)
for rec in recommendations:
if rec.state.name == "ACTIVE":
issues.append({
'severity': 'MEDIUM',
'member': 'See recommendation',
'role': 'Various',
'issue': rec.description,
'recommendation': 'Review IAM Recommender in Console'
})
except Exception as e:
print(f"Could not fetch recommendations: {e}")
return issues
def generate_report(project_id, output_file):
"""Generate CSV report of access issues"""
issues = review_project_access(project_id)
with open(output_file, 'w', newline='') as csvfile:
fieldnames = ['severity', 'member', 'role', 'issue', 'recommendation']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for issue in issues:
writer.writerow(issue)
print(f"Report generated: {output_file}")
print(f"Total issues found: {len(issues)}")
# Print summary
critical = sum(1 for i in issues if i['severity'] == 'CRITICAL')
high = sum(1 for i in issues if i['severity'] == 'HIGH')
medium = sum(1 for i in issues if i['severity'] == 'MEDIUM')
print(f"\nSummary:")
print(f" CRITICAL: {critical}")
print(f" HIGH: {high}")
print(f" MEDIUM: {medium}")
# Usage
if __name__ == "__main__":
project_id = "your-project-id"
output_file = f"iam-review-{datetime.now().strftime('%Y%m%d')}.csv"
generate_report(project_id, output_file)
🎨 Common Patterns¶
Pattern 1: Developer Access¶
# ❌ WRONG: Grant Editor role
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='group:developers@company.com' \
--role='roles/editor'
# ✓ RIGHT: Grant specific roles needed
# Compute access
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='group:developers@company.com' \
--role='roles/compute.instanceAdmin.v1'
# Storage read access
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='group:developers@company.com' \
--role='roles/storage.objectViewer'
# Cloud SQL client access
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='group:developers@company.com' \
--role='roles/cloudsql.client'
# Logging viewer
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='group:developers@company.com' \
--role='roles/logging.viewer'
# Result: ~200 permissions instead of 3,000+
Pattern 2: Service Account for Application¶
# Create service account
gcloud iam service-accounts create web-app-sa \
--display-name="Web Application"
# ❌ WRONG: Grant broad access
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='serviceAccount:web-app-sa@PROJECT_ID.iam.gserviceaccount.com' \
--role='roles/editor'
# ✓ RIGHT: Grant only what's needed
# Read from specific bucket
gcloud storage buckets add-iam-policy-binding gs://app-data \
--member='serviceAccount:web-app-sa@PROJECT_ID.iam.gserviceaccount.com' \
--role='roles/storage.objectViewer'
# Connect to Cloud SQL
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='serviceAccount:web-app-sa@PROJECT_ID.iam.gserviceaccount.com' \
--role='roles/cloudsql.client'
# Access secrets
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='serviceAccount:web-app-sa@PROJECT_ID.iam.gserviceaccount.com' \
--role='roles/secretmanager.secretAccessor'
# Result: ~20 permissions instead of 3,000+
Pattern 3: Temporary Admin Access¶
# Grant temporary elevated access for incident response
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='user:oncall@company.com' \
--role='roles/compute.instanceAdmin.v1' \
--condition='expression=request.time < timestamp("2026-03-05T23:59:59Z"),title=Incident Response,description=24-hour emergency access'
# Automatically expires after 24 hours
# No manual cleanup needed
Pattern 4: Environment Separation¶
# Production: Read-only for most users
gcloud projects add-iam-policy-binding prod-project \
--member='group:engineers@company.com' \
--role='roles/viewer'
# Development: Full access
gcloud projects add-iam-policy-binding dev-project \
--member='group:engineers@company.com' \
--role='roles/editor'
# Or use resource labels with conditions
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='group:engineers@company.com' \
--role='roles/compute.instanceAdmin.v1' \
--condition='expression=resource.labels.environment == "dev",title=Dev Only'
🛡️ Advanced Techniques¶
1. Just-In-Time (JIT) Access¶
┌────────────────────────────────────────────────────────┐
│ Just-In-Time Access Pattern │
└────────────────────────────────────────────────────────┘
Concept: Grant elevated access only when needed, for limited time
Implementation Options:
1. Manual JIT (Simple):
• User requests access via ticket
• Admin grants with time condition
• Access auto-expires
2. Automated JIT (Advanced):
• User requests via self-service portal
• Approval workflow
• Automatic grant with expiration
• Audit logging
3. Context-Aware JIT:
• Access based on context (location, device, time)
• Risk-based decisions
• Continuous verification
Benefits:
✓ Reduced standing privileges
✓ Lower attack surface
✓ Better audit trail
✓ Compliance friendly
2. Privilege Access Management (PAM)¶
# Example: Break-glass access for emergencies
# 1. Create break-glass service account
gcloud iam service-accounts create break-glass-admin \
--display-name="Break Glass Admin"
# 2. Grant necessary permissions
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='serviceAccount:break-glass-admin@PROJECT_ID.iam.gserviceaccount.com' \
--role='roles/owner'
# 3. Restrict who can impersonate
gcloud iam service-accounts add-iam-policy-binding \
break-glass-admin@PROJECT_ID.iam.gserviceaccount.com \
--member='group:incident-commanders@company.com' \
--role='roles/iam.serviceAccountTokenCreator'
# 4. Set up alerting for any use
gcloud logging sinks create break-glass-alert \
--log-filter='protoPayload.authenticationInfo.principalEmail="break-glass-admin@PROJECT_ID.iam.gserviceaccount.com"' \
--destination=pubsub.googleapis.com/projects/PROJECT_ID/topics/security-alerts
# Usage only in emergencies with full audit trail
3. Separation of Duties¶
┌────────────────────────────────────────────────────────┐
│ Separation of Duties Example │
├────────────────────────────────────────────────────────┤
│ │
│ Scenario: Deploy application to production │
│ │
│ Role 1: Developer │
│ • Can write code │
│ • Can create container images │
│ • Cannot deploy to production │
│ │
│ Role 2: Approver │
│ • Can approve deployments │
│ • Cannot write code │
│ • Cannot create images │
│ │
│ Role 3: Deployer (Service Account) │
│ • Can deploy to production │
│ • Requires approval │
│ • Automated process │
│ │
│ Result: No single person can deploy without approval │
└────────────────────────────────────────────────────────┘
# Developer: Can build images
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='group:developers@company.com' \
--role='roles/cloudbuild.builds.editor'
# Approver: Can approve releases
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='group:release-managers@company.com' \
--role='roles/cloudrun.admin' \
--condition='expression=resource.labels.approved == "true",title=Approved Only'
# Deployer SA: Can deploy (used by CI/CD)
gcloud projects add-iam-policy-binding PROJECT_ID \
--member='serviceAccount:deployer@PROJECT_ID.iam.gserviceaccount.com' \
--role='roles/run.admin'
📈 Monitoring and Compliance¶
1. Access Monitoring¶
# Monitor privileged role assignments
gcloud logging read \
'protoPayload.methodName="SetIamPolicy" AND
(protoPayload.request.policy.bindings.role="roles/owner" OR
protoPayload.request.policy.bindings.role="roles/editor")' \
--limit=50 \
--format=json
# Monitor service account key creation
gcloud logging read \
'protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"' \
--limit=50
# Monitor impersonation events
gcloud logging read \
'protoPayload.serviceData.policyDelta.bindingDeltas.action="ADD" AND
protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/iam.serviceAccountTokenCreator"' \
--limit=50
2. Compliance Reporting¶
#!/usr/bin/env python3
"""
Generate compliance report for least privilege
"""
from google.cloud import resourcemanager_v3
from google.cloud import asset_v1
import json
def generate_compliance_report(organization_id):
"""Generate least privilege compliance report"""
client = asset_v1.AssetServiceClient()
# Search for all IAM policies
request = asset_v1.SearchAllIamPoliciesRequest(
scope=f"organizations/{organization_id}",
)
policies = client.search_all_iam_policies(request=request)
violations = []
for policy in policies:
for binding in policy.policy.bindings:
# Check for basic roles
if binding.role in ['roles/owner', 'roles/editor', 'roles/viewer']:
violations.append({
'resource': policy.resource,
'violation': 'Basic role usage',
'role': binding.role,
'members': list(binding.members),
'severity': 'HIGH' if binding.role == 'roles/owner' else 'MEDIUM'
})
# Check for service accounts with broad permissions
if binding.role in ['roles/owner', 'roles/editor']:
for member in binding.members:
if member.startswith('serviceAccount:'):
violations.append({
'resource': policy.resource,
'violation': 'Over-privileged service account',
'role': binding.role,
'member': member,
'severity': 'CRITICAL'
})
# Generate report
report = {
'generated_at': datetime.now().isoformat(),
'organization_id': organization_id,
'total_violations': len(violations),
'violations': violations
}
with open('compliance-report.json', 'w') as f:
json.dump(report, f, indent=2)
print(f"Compliance report generated: {len(violations)} violations found")
return report
# Usage
generate_compliance_report("123456789")
✅ Least Privilege Checklist¶
Initial Setup¶
- [ ] Identify required permissions for each role
- [ ] Use predefined roles when possible
- [ ] Create custom roles for specific needs
- [ ] Document permission rationale
- [ ] Implement approval process
Ongoing Management¶
- [ ] Weekly: Review new access grants
- [ ] Monthly: Review IAM Recommender
- [ ] Quarterly: Full access audit
- [ ] Remove unused permissions
- [ ] Update documentation
Security Measures¶
- [ ] No basic roles in production
- [ ] Time-bound access for temporary needs
- [ ] Service accounts with minimal permissions
- [ ] Separation of duties implemented
- [ ] Break-glass procedures documented
Monitoring¶
- [ ] Audit logging enabled
- [ ] Alerts for privileged operations
- [ ] Regular compliance reports
- [ ] Track permission changes
- [ ] Monitor for anomalies
🎓 Next Steps¶
- Learn about Identity-Aware Proxy for application-level access control
- Explore Advanced IAM features
- Review Best Practices for enterprise IAM
- Return to IAM Policies for policy management
Last Updated: March 2026 Version: 2.0