Skip to main content

Google.IAM.Admin.V1.Enableserviceaccount Remediation

Event Information​

  • Event Purpose & Semantics

    • google.iam.admin.v1.EnableServiceAccount indicates that a previously disabled service account has been re-enabled via the IAM API (e.g., projects.serviceAccounts.enable).
    • After this event, the service account can again obtain tokens, sign JWTs, and be used by workloads (Compute Engine, GKE, Cloud Run, CI/CD, etc.).
  • Security & Compliance Implications (e.g., SOC 2, ISO 27001, PCI DSS)

    • Re-enabling a service account can restore access to data and services; it is a privileged action that should be monitored and reviewed.
    • You should verify: who initiated the event, from where (IP, network), and whether the re-enablement aligns with change tickets or access requests; log and retain this as part of access-control and change-management evidence.
  • Practical Monitoring & Controls

    • Create Cloud Logging queries / Log-based metrics on protoPayload.methodName="google.iam.admin.v1.EnableServiceAccount" and alert in Cloud Monitoring when triggered, especially for high-privilege or production service accounts.
    • Use IAM Conditions and least-privilege roles so only tightly controlled admin identities (e.g., a break-glass group or CI pipeline with change records) can call iam.serviceAccounts.enable.

Examples​

  • Re-enabling a previously disabled high-privilege service account

    • Example: A “ci-prod-deployer@project.iam.gserviceaccount.com” with roles/owner was disabled after an incident, but an attacker (or misconfigured pipeline) calls google.iam.admin.v1.EnableServiceAccount to bring it back and reuse its OAuth2 tokens or keys.
    • Impact: Violates least-privilege principles (ISO 27001 A.9, CIS GCP 1.0 Controls) and can re-open a closed incident path if no additional monitoring is in place.
  • Re-activating a service account used by exfiltration tooling

    • Example: A service account with roles/storage.admin was disabled during a data-leak investigation; enabling it again allows access to sensitive buckets containing regulated data (e.g., PCI, HIPAA/PHI, GDPR personal data).
    • Impact: Potential non-compliance with data-protection requirements (PCI DSS 3/7, HIPAA 164.308/310, GDPR Art. 32), as a known risky identity regains read/write permissions to sensitive datasets.
  • Bypassing change-control and SoD (Segregation of Duties)

    • Example: A DevOps user with broad IAM permissions enables a service account tied to production billing or key management (roles/cloudkms.admin, roles/billing.admin) outside of an approved change window.
    • Impact: Breaches change-management and SoD requirements (SOC 2 CC6/CC7, ISO 27001 A.12/A.15); can lead to unauthorized key operations, cost manipulation, or escalation of privileges via restored access to other GCP services.

Remediation​

Using Console​

  • Immediately contain and verify (Console + Org Policy)

    • In Cloud Console, go to IAM & Admin → Service Accounts, filter by name/email (e.g., ci-prod-deployer@...), and check Status, Last modified, and Keys; if the account must remain disabled, click the three‑dot menu → Disable (or Delete if formally decommissioned).
    • In IAM & Admin → Audit Logs, filter google.iam.admin.v1.EnableServiceAccount and identify who/what re-enabled it; revoke or tighten that principal’s IAM (e.g., remove roles/iam.serviceAccountAdmin / roles/owner) under IAM.
    • Under IAM & Admin → Organization Policies, enforce org policies like constraints/iam.disableServiceAccountKeyCreation, constraints/iam.allowedPolicyMemberDomains, and ensure high‑risk SAs are covered by a policy/process that prohibits ad‑hoc enabling without CAB/SoD approval.
  • Lock down risky service accounts and access paths

    • For incident‑related or high‑privilege SAs (exfil tooling, roles/owner, roles/storage.admin, roles/cloudkms.admin, roles/billing.admin):
      • In Service Accounts → [SA] → Keys, delete all keys; in Details, remove unneeded roles and convert to least‑privilege or set to disabled with clear description/tags (e.g., “QUARANTINED – do not enable”).
      • In IAM, remove broad roles from CI/CD service accounts and users; prefer custom roles limited to specific resources and actions required by pipelines.
    • In Security → Access Approval / Access Control (where applicable) and your ticketing system, require a documented, approved change (linked ticket) to re-enable any SA associated with PCI/PHI/GDPR data or production financial/key‑management functions.
  • Implement preventive and detective controls (monitoring + workflow)

    • In Cloud Monitoring → Logs Explorer, build a log‑based metric on EnableServiceAccount and alerts to security/on‑call channels for:
      • Any enable of SAs with specific labels (e.g., env=prod, critical=true) or high‑risk roles; configure alerting policies under Monitoring → Alerting.
    • Use Security Command Center (SCC) and/or Cloud Logging sinks to export IAM audit logs to a SIEM; create detections for:
      • Re‑enablement of disabled SAs, key creation, or assignment of powerful roles outside approved change windows.
    • Document a standard change workflow (aligned with ISO 27001, SOC 2, PCI, HIPAA): re‑enablement only via a tracked ticket, dual approval (SoD), post‑change review, and periodic recertification of all high‑privilege service accounts via IAM access reviews.

Using CLI​

  • Immediate containment & forensic validation (who/what re-enabled it)

    • List and immediately disable the suspect service account; revoke keys and tokens:
      • gcloud iam service-accounts list \
        --project=PROJECT_ID
        gcloud iam service-accounts disable \
        SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
        --project=PROJECT_ID
        gcloud iam service-accounts keys list \
        --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com
        gcloud iam service-accounts keys delete KEY_ID \
        --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com
    • Identify who/what re-enabled it using Cloud Audit Logs (map to change-control & SoD requirements):
      • gcloud logging read \
        'protoPayload.methodName="google.iam.admin.v1.EnableServiceAccount" \
        AND resource.type="service_account"' \
        --project=PROJECT_ID \
        --format="table(timestamp,protoPayload.authenticationInfo.principalEmail,protoPayload.resourceName)"
    • For exfil-related accounts with storage or data roles, immediately remove high-risk bindings to protected resources:
      • gcloud projects get-iam-policy PROJECT_ID \
        --format=json > /tmp/policy.json
        # Edit policy.json to remove SA_NAME bindings, then:
        gcloud projects set-iam-policy PROJECT_ID /tmp/policy.json
  • Prevent re-enablement without formal change control & least privilege

    • Lock down who can enable/disable service accounts via IAM Conditions, aligning to ISO 27001 / SOC 2 / CIS GCP:
      • gcloud projects add-iam-policy-binding PROJECT_ID \
        --member="group:cloud-security-admins@org.com" \
        --role="roles/iam.serviceAccountAdmin"
        gcloud projects remove-iam-policy-binding PROJECT_ID \
        --member="user:overprivileged-devops@org.com" \
        --role="roles/owner"
    • For permanently retired or incident-related accounts, disable and deny use with an organization policy and labels:
      • gcloud iam service-accounts update \
        SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
        --description="DISABLED DUE TO INCIDENT YYYY-MM-DD - DO NOT ENABLE" \
        --project=PROJECT_ID
        gcloud iam service-accounts disable \
        SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
        --project=PROJECT_ID
      • Optionally add an org policy to disallow iam.serviceAccounts.enable for non-security groups via custom org policy + Conditions (enforced at org/folder).
  • Monitoring, guardrails, and safer alternatives

    • Create an alert on any EnableServiceAccount for high-privilege roles (PCI/ HIPAA/GDPR evidence):
      • gcloud logging metrics create sa_enable_highpriv_metric \
        --description="SA enable for high-priv SAs" \
        --log-filter='protoPayload.methodName="google.iam.admin.v1.EnableServiceAccount"
        AND protoPayload.serviceData.policyDelta.bindingDeltas:(member:serviceAccount: AND
        (role:"roles/owner" OR role:"roles/storage.admin" OR role:"roles/cloudkms.admin" OR role:"roles/billing.admin"))'
        # Then create a Log-based alert in Cloud Monitoring on this metric.
    • Replace broad roles with least-privilege custom roles and use short-lived Workload Identity Federation for CI/CD instead of long-lived keys:
      • gcloud iam roles create ci_deployer_minimal \
        --project=PROJECT_ID \
        --title="CI Deployer Minimal" \
        --permissions=compute.instances.update,run.services.update \
        --stage=GA
        gcloud projects add-iam-policy-binding PROJECT_ID \
        --member="serviceAccount:ci-prod-deployer@PROJECT_ID.iam.gserviceaccount.com" \
        --role="projects/PROJECT_ID/roles/ci_deployer_minimal"

Using Python​

  • Immediate technical containment & enforcement (org policy, deny, and guardrails)

    • Create an org-level Deny Policy that blocks re-enabling high‑risk service accounts except through a controlled group (GCP IAM Deny, preview/GA dependent):
      gcloud iam deny-policies create \
      --display-name="Deny enable of protected SAs" \
      --parent="organizations/ORG_ID" \
      --policy-file=deny-enable-protected-sas.yaml
      deny-enable-protected-sas.yaml (example):
      displayName: "Deny enable for protected SAs"
      rules:
      - denyRule:
      deniedPermissions:
      - iam.serviceAccounts.enable
      exceptionPrincipals: # only break-glass group can enable
      - group:breakglass-iam-admins@ORG_DOMAIN
      condition:
      expression: "starts_with(resource.name, 'projects/PROJECT_ID/serviceAccounts/ci-prod-deployer@project.iam.gserviceaccount.com')"
    • Where org‑level deny isn’t available, move critical SAs into a locked “admin only” project/folder and strip roles/iam.serviceAccountAdmin / roles/owner from CI users. Use pre‑defined custom roles with no iam.serviceAccounts.enable or iam.serviceAccounts.undelete permissions.
    • For exfiltration‑related SAs, also add: VPC‑SC around sensitive projects, CMEK protections, and bucket‑level conditional IAM (e.g., request.time < decommission_time) to ensure PCI/HIPAA/GDPR stores cannot be reached even if the SA is re‑enabled.
  • Detection, monitoring, and change-control guardrails (Cloud Audit Logs + SCC + pipeline checks)

    • Enable Data Access logs for iam.googleapis.com and build log‑based alerts for google.iam.admin.v1.EnableServiceAccount on tagged “protected” SAs (e.g., label risk_level=high), integrating with SIEM and ticketing to satisfy SOC 2 / ISO 27001 change‑management evidence.
    • Example Python script (Cloud Functions / Cloud Run) to auto‑re-disable protected SAs when an enable event is detected (triggered from Pub/Sub -> Log Sink):
      import base64
      import json
      from googleapiclient.discovery import build
      from google.oauth2 import service_account

      # Configure
      PROTECTED_SAS = {
      "ci-prod-deployer@project.iam.gserviceaccount.com",
      "exfil-sa@project.iam.gserviceaccount.com",
      }
      SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
      # If running as Cloud Function with appropriate IAM, you can use default credentials instead
      CREDS = service_account.Credentials.from_service_account_file(
      "svc-iam-automation.json", scopes=SCOPES
      )

      def _extract_principal_and_sa(proto_payload):
      method = proto_payload.get("methodName", "")
      sa_email = None
      resource_name = proto_payload.get("resourceName", "")
      # resourceName like: projects/PROJECT_ID/serviceAccounts/EMAIL
      if "serviceAccounts/" in resource_name:
      sa_email = resource_name.split("serviceAccounts/")[-1]
      principal = proto_payload.get("authenticationInfo", {}).get("principalEmail")
      return method, sa_email, principal

      def remediate_enable_sa(event, context):
      """Cloud Function entrypoint for log-based trigger."""
      data = base64.b64decode(event["data"]).decode("utf-8")
      log_entry = json.loads(data)
      proto = log_entry.get("protoPayload", {})
      method, sa_email, principal = _extract_principal_and_sa(proto)

      if method != "google.iam.admin.v1.EnableServiceAccount":
      return

      if sa_email not in PROTECTED_SAS:
      return

      # Re-disable the service account
      project = sa_email.split("@")[1].split(".")[0] # crude extraction; prefer labels/config
      name = f"projects/{project}/serviceAccounts/{sa_email}"

      iam = build("iam", "v1", credentials=CREDS, cache_discovery=False)
      iam.projects().serviceAccounts().disable(
      name=name, body={}
      ).execute()

      # Optionally: write a security log / notify SIEM with principal and SA info
      print(json.dumps({
      "action": "auto_disable_service_account",
      "service_account": sa_email,
      "triggered_by": principal,
      "log_insert_id": log_entry.get("insertId")
      }))
    • Add a pipeline‑side control: Python pre‑deployment check to fail CI/CD jobs if they attempt to enable blocked SAs (meets SoD / change‑control). Example snippet in a job step:
      import sys
      import json

      PROTECTED_SAS = {
      "ci-prod-deployer@project.iam.gserviceaccount.com",
      "exfil-sa@project.iam.gserviceaccount.com",
      }

      def main(plan_file_path: str):
      with open(plan_file_path) as f:
      plan = json.load(f)
      violations = []
      for change in plan.get("resource_changes", []):
      if change.get("type") == "google_service_account":
      after = change.get("change", {}).get("after", {})
      email = after.get("email")
      disabled = after.get("disabled")
      if email in PROTECTED_SAS and disabled is False:
      violations.append(email)
      if violations:
      print("ERROR: Attempt to enable protected service accounts:", violations)
      sys.exit(1)

      if __name__ == "__main__":
      main(sys.argv[1])
  • Lifecycle cleanup, least-privilege redesign, and compliance alignment

    • For formerly compromised / exfiltration SAs: permanently delete instead of disable where possible, rotate all keys/tokens, and re‑implement workloads using narrowly scoped SAs (workload‑identity with per‑bucket / per‑KMS‑key roles). Document in incident records for PCI DSS / HIPAA / GDPR and ensure DLP / access reviews reflect the removal.
    • Implement periodic Python-driven review of high‑privilege SAs (mapping to ISO 27001 A.9 & CIS GCP 1.0):
      from googleapiclient.discovery import build
      from google.oauth2 import service_account

      SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
      CREDS = service_account.Credentials.from_service_account_file(
      "svc-iam-audit.json", scopes=SCOPES
      )

      def list_high_priv_sas(project_id: str):
      iam = build("iam", "v1", credentials=CREDS, cache_discovery=False)
      res = iam.projects().serviceAccounts().list(
      name=f"projects/{project_id}"
      ).execute()

      high_risk_roles = {
      "roles/owner",
      "roles/storage.admin",
      "roles/cloudkms.admin",
      "roles/billing.admin",
      }

      high_risk = []
      for sa in res.get("accounts", []):
      email = sa["email"]
      # In practice, also query project IAM bindings to see roles for this SA.
      # Placeholder: tag all for manual review
      high_risk.append(email)

      return high_risk
    • Embed approvals (CAB / dual control) before any change that enables or modifies high‑privilege SAs: enforce via separate “requester” vs “approver” groups (SoD), and store approvals in a system of record linked to the underlying API calls/logs.