> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/gcp/gcp_has_service_account_key.md).

# GCP\_HAS\_SERVICE\_ACCOUNT\_KEY

## Summary

|                            |                                 |
| -------------------------- | ------------------------------- |
| **FSProtect ACL Alias**    | GCP\_HAS\_SERVICE\_ACCOUNT\_KEY |
| **GCP Alias**              | Entity Relation (Structural)    |
| **Affected Object Types**  | Service Accounts                |
| **Exploitation Certainty** | Certain                         |

## Description

`GCP_HAS_SERVICE_ACCOUNT_KEY` is a structural edge representing that a **service account has a user-managed (downloadable) key**. In GCP, service accounts can authenticate using either:

1. **Google-managed keys** — short-lived, auto-rotated, not downloadable.
2. **User-managed keys** — long-lived JSON key files that can be downloaded and used from anywhere, indefinitely.

`GCP_HAS_SERVICE_ACCOUNT_KEY` edges represent user-managed key relationships — the most dangerous form of SA credential because they are **persistent**, **location-independent**, and **not tied to any GCP resource or IP**. A downloaded key file grants the SA's full permissions from any machine on the internet, bypassing network controls and logging that might detect unusual API calls.

The existence of a `GCP_HAS_SERVICE_ACCOUNT_KEY` edge on a privileged SA is critical — it means the SA's permissions are accessible via a static credential that may have been distributed, stored in code repositories, or leaked in past incidents.

## Identification

### gcloud CLI

```bash
# List all user-managed keys for a specific service account
PROJECT_ID="my-project"
SA_EMAIL="sa@$PROJECT_ID.iam.gserviceaccount.com"
gcloud iam service-accounts keys list \
  --iam-account=$SA_EMAIL \
  --managed-by=user \
  --project=$PROJECT_ID \
  --format="table(name.basename(), validAfterTime, validBeforeTime, keyAlgorithm)"

# Find all SAs in a project that have user-managed keys
for SA in $(gcloud iam service-accounts list --project=$PROJECT_ID --format="value(email)"); do
  KEYS=$(gcloud iam service-accounts keys list --iam-account=$SA --managed-by=user --project=$PROJECT_ID --format="value(name)" 2>/dev/null)
  if [ ! -z "$KEYS" ]; then
    echo "SA with user-managed keys: $SA"
    echo "$KEYS"
  fi
done
```

### GCP Console

1. Open **GCP Console** → **IAM & Admin** → **Service Accounts**.
2. Click a service account → **Keys** tab.
3. Any key listed under **User-managed keys** represents a `GCP_HAS_SERVICE_ACCOUNT_KEY` edge.

## Exploitation

If a SA key has been exfiltrated — leaked from a source repository, CI/CD log, cloud storage bucket, or config file — an attacker can authenticate as the target SA from any internet-connected machine without any GCP infrastructure.

### gcloud CLI

```bash
# Authenticate using the stolen key file
gcloud auth activate-service-account --key-file=/path/to/stolen-key.json

# Confirm the active identity and obtain an access token
gcloud config get-value account
gcloud auth print-access-token

# Call GCP APIs as the target SA
curl -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://cloudresourcemanager.googleapis.com/v1/projects"

# If the SA holds serviceAccountTokenCreator on other SAs, chain impersonation
HIGH_PRIV_SA="org-admin@target-project.iam.gserviceaccount.com"
gcloud auth print-access-token --impersonate-service-account=$HIGH_PRIV_SA

# Create additional keys on other SAs for redundant persistence
BACKUP_SA="backup-admin@target-project.iam.gserviceaccount.com"
gcloud iam service-accounts keys create /tmp/backup-key.json \
  --iam-account=$BACKUP_SA
```

The key file is valid indefinitely until explicitly revoked — even if the attacker's original IAM bindings are removed, the stolen key continues to authenticate as the SA.

## Mitigation

1. Audit all user-managed keys in the organization and delete any that are no longer needed, preferring Google-managed keys wherever possible.
2. Enforce the `constraints/iam.disableServiceAccountKeyCreation` org policy to prevent SA keys from being created across the organization.
3. When keys are genuinely required, set an expiration time at creation using the `--expiration-time` flag.
4. Rotate any existing keys on a maximum 90-day cycle.
5. For workloads running outside GCP, use Workload Identity Federation instead of SA keys to eliminate the need for long-lived downloadable credentials.

## Detection

```bash
# Monitor SA key creation
gcloud logging read \
  'protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"' \
  --project=$PROJECT_ID \
  --format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.resourceName)"
```

Alert on:

* Creation of SA keys on privileged service accounts.
* SA keys created outside of known CI/CD systems or automation accounts.

## References

* <https://cloud.google.com/iam/docs/service-account-creds>
* <https://cloud.google.com/iam/docs/best-practices-service-accounts#avoid-key-risks>
* <https://cloud.google.com/iam/docs/keys-create-delete>
