Gmail¶
Overview¶
This Adapter collects telemetry from one or many Gmail mailboxes using the Gmail REST API. Beyond incoming-email telemetry, it can collect the mailbox configuration and change signals most relevant to Business Email Compromise (BEC) — the mail rules, forwarding, aliases, delegates, protocol access, and deletions an intruder uses to persist, exfiltrate mail, and cover their tracks.
Each signal is an independent, opt-in capability that ships its own event type. They are all readable with the default gmail.readonly scope.
With the service-account flow the adapter can watch many mailboxes at once — an explicit list, or every mailbox in a Google Workspace domain via auto-discovery — and ships each mailbox to its own LimaCharlie sensor.
Capabilities¶
Enable any combination with the collect_* flags. If you set none, the adapter defaults to message telemetry only (collect_messages).
| Flag | Event type(s) | What it gives you |
|---|---|---|
collect_messages |
gmail_message |
Incoming email as telemetry — the raw signal for phishing/lure detection. |
collect_filters |
gmail_filter |
Mail rules. Attackers create rules that auto-delete, auto-forward, or hide replies about invoices/wires. |
collect_forwarding |
gmail_forwarding_address, gmail_auto_forwarding |
Forwarding destinations and the account-wide auto-forward toggle — a classic mail-exfiltration vector. |
collect_send_as |
gmail_send_as |
Send-as / "from" identities. An added identity is an impersonation/persistence signal. |
collect_delegates |
gmail_delegate |
Mailbox delegates — granting a delegate is persistence. Workspace only (see note below). |
collect_imap_pop |
gmail_imap, gmail_pop |
IMAP/POP access settings. Enabling these allows bulk mailbox download via a desktop client. |
collect_vacation |
gmail_vacation |
The vacation responder, occasionally abused for harvesting/social engineering. |
collect_history |
gmail_history |
Mailbox changes: message deletions and label changes (marking a security alert read, trashing the fraud thread). |
Delegates are Workspace-only. Google exposes the delegates listing only to service-account clients with domain-wide delegation. On a consumer account (or without delegation) the call returns an error, which the adapter logs and skips — it does not stop the adapter or affect the other capabilities.
The configuration-state capabilities (filters, forwarding, send-as, delegates, IMAP/POP, vacation) are change-only: an item is shipped when it first appears or its content changes, and suppressed otherwise. On adapter restart the in-memory dedupe state is empty, so the current state is re-emitted once as a fresh baseline — write detections against the state in these events rather than treating every event as a brand-new change.
Authentication¶
Choose one of two modes.
OAuth 2.0 refresh token (a single mailbox)¶
For collecting one user's mailbox. Create an OAuth client (Desktop or Web) in the Google Cloud console, enable the Gmail API, and complete the authorization-code flow once to obtain a refresh token for the gmail.readonly scope.
| Field | Description |
|---|---|
client_id |
OAuth client id |
client_secret |
OAuth client secret |
refresh_token |
Long-lived refresh token for the mailbox owner |
Service account with domain-wide delegation (Google Workspace)¶
For monitoring Workspace mailboxes without per-user consent. Create a service account, enable domain-wide delegation, and in the Workspace Admin console authorize its client id for the https://www.googleapis.com/auth/gmail.readonly scope.
| Field | Description |
|---|---|
service_account_credentials |
The service account JSON key, inline |
service_account_file |
Path to the service account JSON key file (alternative to the inline form) |
subject |
A single mailbox owner to impersonate, e.g. user@yourdomain.com |
Provide the mailbox(es) with subject (one), subjects (a list), and/or discover_mailboxes (the whole domain). At least one of these is required.
Multiple mailboxes¶
With the service-account flow, each mailbox is impersonated independently and shipped to its own sensor: when more than one mailbox is collected, the sensor seed key is derived as <sensor_seed_key>/<mailbox-address> and the sensor hostname is set to the mailbox address.
There are two ways to enumerate mailboxes, and they can be combined (the union is collected):
- Static list (
subjects): list the mailboxes explicitly — good for a fixed set of high-value mailboxes (executives, finance, AP). - Auto-discovery (
discover_mailboxes): enumerate the Workspace domain's mailboxes via the Admin SDK Directory API, re-run ondiscovery_interval(default 1h) so newly-provisioned mailboxes are picked up and deprovisioned ones dropped automatically. Suspended accounts are skipped unlessinclude_suspendedis set.
Auto-discovery has two extra requirements beyond the Gmail collection itself:
admin_subject— a Workspace admin user the service account impersonates for the Directory call.- An extra delegated scope — authorize the service account's client id for
https://www.googleapis.com/auth/admin.directory.user.readonlyin the Workspace Admin console.
If a discovery pass fails or comes back empty while mailboxes are already being collected, the current set keeps collecting (with a warning logged) — discovery never tears down working mailboxes on a transient blip.
Deployment Configurations¶
All adapters support the same client_options, which you should always specify if using the binary adapter:
client_options.identity.oid: the LimaCharlie Organization ID (OID) this adapter is used with.client_options.identity.installation_key: the LimaCharlie Installation Key this adapter should use to identify with LimaCharlie.client_options.platform:gmail.client_options.sensor_seed_key: an arbitrary name for this adapter which Sensor IDs (SID) are generated from.
Adapter-specific Options¶
Adapter Type: gmail
| Key | Default | Description |
|---|---|---|
client_id / client_secret / refresh_token |
— | OAuth refresh-token flow credentials (single mailbox). |
service_account_credentials / service_account_file |
— | Service-account flow credentials (Workspace). |
subject |
— | Single mailbox to impersonate (service-account flow). |
subjects |
— | Static list of mailboxes to impersonate. |
discover_mailboxes |
false |
Enumerate the domain's mailboxes via the Directory API. |
admin_subject |
— | Admin user impersonated for the Directory API (required with discover_mailboxes). |
customer |
my_customer |
Directory API customer id (mutually exclusive with domain). |
domain |
— | Restrict discovery to one domain of a multi-domain Workspace. |
discovery_query |
— | Optional Directory API user search filter, e.g. orgUnitPath='/Finance'. |
discovery_interval |
1h |
How often discovery re-enumerates. |
include_suspended |
false |
Also collect suspended mailboxes. |
max_concurrent_polls |
10 |
Cap on how many mailboxes poll the Gmail API at once. |
collect_messages … collect_history |
see Capabilities | Capability toggles. |
settings_poll_interval |
15m |
Cadence for the configuration-state capabilities. |
user_id |
me |
Mailbox path segment for the refresh-token flow. Ignored by the service-account flow. |
query |
in:inbox |
Gmail search query selecting messages. A time bound is appended automatically — do not add one. |
scopes |
gmail.readonly |
OAuth scopes to request. |
format |
full |
Message detail: minimal, full, raw, or metadata. |
metadata_headers |
— | Headers to keep when format is metadata. |
label_ids |
— | Only list messages carrying all of these label ids. |
include_spam_trash |
false |
Include SPAM and TRASH messages. |
max_results |
100 |
Page size for the message listing (max 500). |
poll_interval |
5m |
Wait between message/history polls. |
overlap |
2m |
Window backdating to avoid gaps from late-indexed mail; re-listed messages are deduped. |
initial_lookback |
0 |
On startup, reach back this far to backfill recent mail. |
dedupe_ttl |
168h (7d) |
How long a message id is remembered to suppress re-shipping. |
retry_base_delay / max_retry_delay / max_retry_attempts |
5s / 30s / 3 |
Transient-failure retry tuning. |
How collection works¶
- Messages: each poll lists message ids matching
queryover a rolling time window, fetches each message at the configuredformat, and forwards the full message resource verbatim. A deduper keyed on the immutable Gmail message id guarantees each message ships exactly once despite overlapping windows. The event timestamp is the message'sinternalDate. - Configuration state: polled on
settings_poll_interval; only appearances and changes are shipped. - History: the first run records a baseline
historyIdand ships nothing; later polls list forward from the cursor, filtered to deletions and label changes. Gmail retains history for roughly a week — if the cursor ages out, the adapter re-baselines and resumes rather than stopping. - Errors:
401triggers one transparent token refresh;429/5xx/403rate-limit errors are retried with backoff; persistently rejected credentials stop that mailbox's collector (other mailboxes are unaffected); a failing BEC capability is logged and skipped without affecting the others.
CLI Deployment¶
Adapter downloads are available on the deployment page.
chmod +x /path/to/lc_adapter
/path/to/lc_adapter gmail \
client_options.identity.oid=$OID \
client_options.identity.installation_key=$INSTALLATION_KEY \
client_options.platform=gmail \
client_options.sensor_seed_key=gmail \
client_id=$GMAIL_CLIENT_ID \
client_secret=$GMAIL_CLIENT_SECRET \
refresh_token=$GMAIL_REFRESH_TOKEN \
query="in:inbox" \
poll_interval=5m
Infrastructure as Code Deployment¶
Full BEC monitoring of a Workspace mailbox — message telemetry plus the persistence, exfiltration, and tamper signals:
# For cloud sensor deployment, store credentials as hive secrets:
#
# service_account_credentials: "hive://secret/gmail-service-account"
sensor_type: "gmail"
gmail:
service_account_credentials: "hive://secret/gmail-service-account"
subject: "soc-mailbox@yourdomain.com"
collect_messages: true
collect_filters: true
collect_forwarding: true
collect_send_as: true
collect_delegates: true
collect_imap_pop: true
collect_vacation: true
collect_history: true
poll_interval: 5m
settings_poll_interval: 15m
client_options:
identity:
oid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
installation_key: "YOUR_LC_INSTALLATION_KEY_GMAIL"
platform: "gmail"
sensor_seed_key: "gmail-sensor"
Domain-wide auto-discovery — every mailbox in the Workspace, each on its own sensor:
sensor_type: "gmail"
gmail:
service_account_credentials: "hive://secret/gmail-service-account"
discover_mailboxes: true
admin_subject: "admin@yourdomain.com"
discovery_interval: 1h
collect_messages: true
collect_filters: true
collect_forwarding: true
collect_history: true
client_options:
identity:
oid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
installation_key: "YOUR_LC_INSTALLATION_KEY_GMAIL"
platform: "gmail"
sensor_seed_key: "gmail-sensor"
Sample Rule¶
The BEC capabilities ship each signal under its own event type, so D&R rules can route directly on the signal. For example, flag every change to the account-wide auto-forwarding setting:
# Detection
event: gmail_auto_forwarding
op: is
path: event/enabled
value: true
# Response
- action: report
name: Gmail auto-forwarding enabled
Note: the
gmail.metadatascope does not allow theqsearch parameter. If you restrict the adapter to that scope, leavequeryempty and rely onlabel_ids/include_spam_trashinstead. The defaultgmail.readonlyscope covers every capability; the narrowergmail.metadatascope cannot read the settings sub-resources, so a capability using them will be logged and skipped.
API Docs¶
- Gmail API reference: https://developers.google.com/workspace/gmail/api/reference/rest
- Admin SDK Directory API (
users.list, used by auto-discovery): https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list