#!/usr/bin/env python3 """Agent for auditing GCP IAM permissions using google-cloud libraries.""" import json import argparse from datetime import datetime from google.cloud import asset_v1 from google.cloud import resourcemanager_v3 def search_iam_policies(scope, query=""): """Search IAM policies across the GCP organization.""" client = asset_v1.AssetServiceClient() request = asset_v1.SearchAllIamPoliciesRequest(scope=scope, query=query, page_size=500) results = [] for result in client.search_all_iam_policies(request=request): for binding in result.policy.bindings: results.append({ "resource": result.resource, "role": binding.role, "members": list(binding.members), }) return results def find_primitive_roles(scope): """Find all IAM bindings using primitive roles (Owner, Editor).""" query = "policy:roles/owner OR policy:roles/editor" return search_iam_policies(scope, query) def find_public_bindings(scope): """Find resources accessible to allUsers or allAuthenticatedUsers.""" query = "policy:allUsers OR policy:allAuthenticatedUsers" return search_iam_policies(scope, query) def list_service_accounts(project_id): """List all service accounts in a project with key info.""" from google.cloud import iam_admin_v1 client = iam_admin_v1.IAMClient() request = iam_admin_v1.ListServiceAccountsRequest(name=f"projects/{project_id}") accounts = [] for sa in client.list_service_accounts(request=request): sa_info = { "email": sa.email, "display_name": sa.display_name, "disabled": sa.disabled, "user_managed_keys": [], } key_request = iam_admin_v1.ListServiceAccountKeysRequest( name=sa.name, key_types=[iam_admin_v1.ListServiceAccountKeysRequest.KeyType.USER_MANAGED], ) keys = client.list_service_account_keys(request=key_request) for key in keys.keys: sa_info["user_managed_keys"].append({ "name": key.name.split("/")[-1], "valid_after": str(key.valid_after_time), "valid_before": str(key.valid_before_time), }) accounts.append(sa_info) return accounts def get_project_iam_policy(project_id): """Get IAM policy for a specific project.""" client = resourcemanager_v3.ProjectsClient() request = {"resource": f"projects/{project_id}"} policy = client.get_iam_policy(request=request) bindings = [] for binding in policy.bindings: bindings.append({"role": binding.role, "members": list(binding.members)}) return bindings def analyze_permissions(scope, identity): """Analyze what resources an identity can access.""" client = asset_v1.AssetServiceClient() request = asset_v1.AnalyzeIamPolicyRequest( analysis_query=asset_v1.IamPolicyAnalysisQuery( scope=scope, identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector( identity=identity ), ) ) response = client.analyze_iam_policy(request=request) results = [] for entry in response.main_analysis.analysis_results: for acl in entry.access_control_lists: resources = [r.full_resource_name for r in acl.resources] accesses = [a.role for a in acl.accesses] results.append({"resources": resources, "roles": accesses}) return results def classify_risk(bindings): """Classify risk for IAM bindings.""" critical = [] high = [] for b in bindings: role = b.get("role", "") members = b.get("members", []) if "allUsers" in members or "allAuthenticatedUsers" in members: critical.append(b) elif role in ("roles/owner", "roles/editor"): for m in members: if "serviceAccount" in m: critical.append(b) break else: high.append(b) return {"critical": critical, "high": high} def main(): parser = argparse.ArgumentParser(description="GCP IAM Permissions Audit Agent") parser.add_argument("--org-id", help="GCP Organization ID") parser.add_argument("--project-id", help="GCP Project ID") parser.add_argument("--identity", help="Identity to analyze (user:email or serviceAccount:email)") parser.add_argument("--output", default="gcp_iam_audit.json") parser.add_argument("--action", choices=[ "primitive_roles", "public_access", "service_accounts", "analyze_identity", "full_audit" ], default="full_audit") args = parser.parse_args() scope = f"organizations/{args.org_id}" if args.org_id else f"projects/{args.project_id}" report = {"audit_date": datetime.utcnow().isoformat(), "scope": scope, "findings": {}} if args.action in ("primitive_roles", "full_audit"): primitives = find_primitive_roles(scope) report["findings"]["primitive_roles"] = primitives print(f"[+] Primitive role bindings: {len(primitives)}") if args.action in ("public_access", "full_audit"): public = find_public_bindings(scope) report["findings"]["public_access"] = public print(f"[+] Public access bindings: {len(public)}") if args.action in ("service_accounts", "full_audit") and args.project_id: sas = list_service_accounts(args.project_id) report["findings"]["service_accounts"] = sas keys_count = sum(len(sa["user_managed_keys"]) for sa in sas) print(f"[+] Service accounts: {len(sas)}, user-managed keys: {keys_count}") if args.action == "analyze_identity" and args.identity: access = analyze_permissions(scope, args.identity) report["findings"]["identity_access"] = access print(f"[+] Resources accessible by {args.identity}: {len(access)}") with open(args.output, "w") as f: json.dump(report, f, indent=2, default=str) print(f"[+] Report saved to {args.output}") if __name__ == "__main__": main()