Skip to content

Controlling Access to Mail Content

Generally available

Email Security is generally available. Subscribe to the Email Security extension in your organization to purchase and enable it. Paid usage costs \(1 per protected mailbox per month**, billed daily at **\)1/30 per mailbox-day. Free-tier organizations (sensor quota of 2 or less) protect up to 10 mailboxes at no charge, with no time limit. See security product billing for pricing, the free allowance and upgrading.

CLI examples require LimaCharlie CLI 5.7.0 or later, which includes the mailsec commands. Install or upgrade, then check:

python -m pip install --upgrade limacharlie
limacharlie mailsec --help

Credential-file examples also require jq.

Email Security reads your users' mail, so the first access question for a new deployment is who in your organization may read it. This guide walks through the two controls that answer it: the mailsec.* permissions, and a Resource ACL on the mailbox sensors.

Set the permissions before you invite analysts, and add the Resource ACL right after you connect your first provider. The predefined roles give mail access to more people than most teams expect.

Where mail content lives

LimaCharlie keeps mail content in two places, and each place has its own gate.

Where What it holds Who can read it
The Email Security message store The queue, the message drawer, campaigns, user reports, the action audit, and the original message bytes Anyone with mailsec.get. The original bytes also need mailsec.get.eml and a justification
Organization telemetry The EMAIL_* events on the mailbox sensors. EMAIL_MESSAGE carries the whole parsed message, body included. Detections built from these events carry it too Anyone who can read events or detections (insight.evt.get, insight.evt.get.simple, insight.det.get), unless a Resource ACL restricts the sensors

The configuration that drives the product is a third, smaller place: the provider credential, the connection record, the mail rules and the mail policy are Hive records, gated by permissions and optionally by ACL tags on the records.

The two controls in this guide line up with the two places:

  • Permissions decide who can use the Email Security console, CLI, API and MCP tools.
  • A Resource ACL decides who can read the mail events in the timeline, in LCQL search, in detections and in outputs.

Resource ACLs do not apply to the Email Security message store. Anyone holding mailsec.get sees every protected mailbox in the organization, whatever acl: tags the mailbox sensors carry. Plan your permissions with that in mind.

Step 1: Decide who gets which permission

Email Security has four permissions of its own. They are described in Overview → Permissions.

Permission Lets the holder
mailsec.get Read the queue and the drawer, including message bodies, plus campaigns, sender profiles, user reports, the action audit and coverage. Also read the mail rules (dr-mail) and the mail policy (mailsec_policy)
mailsec.set Change triage state, such as resolving a user report. Also create, change and delete mail rules and mail policy records, which decide what gets quarantined, bannered or ignored
mailsec.act Remediate live mail at the provider (quarantine, trash, release, banner) and run the connection test
mailsec.get.eml Download a message's original bytes. Only works together with mailsec.get, and every download needs a written justification

The rest of the configuration uses ordinary Hive permissions: mailsec_provider.* for connections, secret.* for provider credentials, and lookup.* for VIP lists.

What the predefined roles grant

Role Email Security permissions Can read mail events in telemetry
Owner All of them Yes
Administrator mailsec.get, mailsec.set, mailsec.act, mailsec.get.eml, and mailsec_provider.* Yes
Operator mailsec.get, mailsec.set, mailsec.act Yes
Viewer mailsec.get Yes
Basic None No

Three things in this table surprise people:

  • Viewer reads mail. mailsec.get shows message bodies in the drawer, so every Viewer can read every protected mailbox. If you give Viewer to people outside the mail team, such as auditors or a help desk, remove mailsec.get from them or give them a custom permission set.
  • Operator can change mail policy. mailsec.set covers the mail rules and policy, not only triage, so an Operator can change what gets quarantined.
  • Every role above Basic can read mail events, even without any mailsec.* permission, because the events are ordinary telemetry. Step 2 closes that path.

Permissions are stored on the user when the role is assigned. A user who was given a role before Email Security existed in your organization's role definitions keeps the permissions they had then. Check with limacharlie user permissions list rather than assuming.

Suggested access levels

Person Grant
Mail administrator, who connects tenants and edits policy mailsec.get, mailsec.set, mailsec.act, mailsec_provider.*, secret.set, secret.get.mtd, and lookup.* if you keep VIP lists
Mail analyst, who triages and remediates mailsec.get and mailsec.act. Add mailsec.set if they also resolve user reports or tune rules
Mail reviewer, read-only mailsec.get
Investigator who needs original messages for an incident The analyst set, plus mailsec.get.eml, granted to named people only
Everyone else in the SOC No mailsec.* permission, and not a member of the ACL scope from Step 2

The full list of what setup needs, permission by permission, is in Setup with the CLI → Grant the permissions.

Granting and removing permissions

From the CLI:

# Start from a role, then adjust.
limacharlie user permissions set-role --email analyst@example.com --role Operator --oid $OID

# Give one investigator the right to download original messages.
limacharlie user permissions add --email investigator@example.com \
  --permission mailsec.get.eml --oid $OID

# Take mail access away from a Viewer who should not read mail.
limacharlie user permissions remove --email auditor@example.com \
  --permission mailsec.get --oid $OID

set-role replaces all of the user's current permissions. In the web console, the same changes are made from the organization's Users page. See User Access for groups and for managing access across several organizations.

Automation that calls Email Security should use an API key holding only what it needs. A SOAR playbook that quarantines messages needs mailsec.get and mailsec.act, and nothing that lets it download originals or change policy:

limacharlie api-key create --name mail-soar --permissions mailsec.get,mailsec.act --oid $OID

Step 2: Restrict the mail events with a Resource ACL

Every protected mailbox is a sensor on the email platform, and each connection also has its own sensor, mailsec-<connection name>, on the same platform. See Events & Automation → The sensors. Without an ACL, anyone who can search the organization's telemetry can read message bodies from EMAIL_MESSAGE, with or without mailsec.get.

A Resource ACL scope fixes this. You tag the mail sensors acl:<scope>, and from then on only members of the scope can read their events and detections. Everyone else still sees that the sensors exist, with their hostnames (the mailbox addresses), tags and online status. The general mechanism is described in Resource ACLs. The steps below apply it to Email Security, using a scope named email.

Follow the steps in order. D&R rules are refused on tagged sensors as soon as the tag lands, even while the scope is in warn-only mode, so the rules are fixed before anything is tagged.

2.1 Give yourself the ACL permissions

Managing scopes and acl: tags needs acl.set, and listing what a scope covers needs acl.get. The user who created the organization does not have them, and neither does anyone who was given Owner or Administrator before Resource ACLs were introduced. Add them, or assign the Owner role again:

limacharlie user permissions add --email you@example.com --permission acl.set --oid $OID
limacharlie user permissions add --email you@example.com --permission acl.get --oid $OID

The later steps also need ikey.set, sensor.tag, dr.set and output.set, which Owner and Administrator already include. See Resource ACLs → Permissions.

2.2 Create the scope in warn-only mode

List the people and keys that should read mail events. Use user IDs rather than email addresses, so that the members' personal API keys are covered too, and use an organization group if your mail team already has one. Include the mail administrators: once Step 3 tags the credentials, only scope members can edit them.

The user list commands show emails only. The user IDs are in the direct_users list of this call, which needs user.ctrl:

limacharlie api 'orgs/{oid}/users/permissions' --oid $OID --output yaml
# email.yaml
data:
  members:
    - type: user
      id: "<mail administrator UID>"
    - type: user
      id: "<mail analyst UID>"
    - type: group
      id: "<mail team group ID>"
  warn_only: true
limacharlie hive set --hive-name acl --key email --input-file email.yaml --enabled --oid $OID

Always pass --enabled. A disabled scope locks the tagged sensors for everyone, members included. Warn-only lets you find out who would lose read access before anyone does. It covers reads, outputs and extension access. It does not cover the D&R rules in the next step.

2.3 Prepare the rules and outputs that use mail events

D&R rules that act on mail. Three response actions send event content out of the platform and are refused on events from tagged sensors: extension request, service request and start ai agent. The ask ai operator is refused too. Warn-only mode does not change this. That covers the most common Email Security automations:

Add the scope to each such rule now, at the top level next to detect and respond. You can save acl_scopes before anything carries the tag:

# mail-quarantine.yaml
detect:
  # ... unchanged ...
respond:
  # ... unchanged ...
acl_scopes:
  - email
limacharlie hive set --hive-name dr-general --key mail-quarantine \
  --input-file mail-quarantine.yaml --enabled --oid $OID

Saving acl_scopes requires membership in every listed scope, or acl.set. Keep the field whenever you rewrite the rule: limacharlie dr set --detect ... --respond ... writes only detect and respond, which removes the scopes. Always save the whole rule file with limacharlie hive set. Rules that only report need no change. Details are in Resource ACLs → D&R rules.

Outputs. Events and detections from restricted sensors are excluded from outputs by default, including outputs that already exist. If you stream EMAIL_* events or mail detections to a SIEM, add the scope to that output's acl_scopes. That output then carries full message bodies to its destination, so only opt in outputs whose destination is allowed to hold mail. See Resource ACLs → Outputs.

API keys that search mail events. An AI agent or a script that reads mail events through LCQL or the timeline reads as its own API key. For an AI agent, that is the API key whose value is stored in the secret its record's lc_api_key_secret points to. Add that API key's name, not the secret's name, as an api_key member of the scope. The Email Security extension itself, ext-email-security, reads no telemetry and needs no membership. Its extension request calls only need the rule's acl_scopes.

Listing a scope on a rule or output, or adding a key to it, is a decision to let that rule, output or key carry restricted mail content. An ask ai rule with acl_scopes sends message content to your AI provider. Make each of these decisions on purpose.

2.4 Tag each connection's installation key

Email Security creates one installation key per connection the first time the connection runs. It is described as email security ingestion (<connection name>) and tagged lc:system,mailsec. Every mail sensor of that connection enrolls through it, and an installation key stamps its tags on every sensor that enrolls with it. Tagging the key therefore covers the mailboxes that appear later, for example when a new employee starts receiving mail.

Enable the connection first, so that its key exists, then find the keys:

limacharlie installation-key list --oid $OID --output yaml

The CLI has no command to edit a key, so update it through the API. Pass the key's iid, its description and its full tag list: the update replaces the tags, so keep lc:system and mailsec and add the scope.

limacharlie api -X POST 'installationkeys/{oid}' \
  -f iid=<IID> \
  -f 'desc=email security ingestion (<connection name>)' \
  -f 'tags=lc:system,mailsec,acl:email' \
  --oid $OID

Repeat for each connection. Check the result with limacharlie installation-key get --iid <IID> --oid $OID.

Repeat this step, and 2.5, whenever a connection gets a new key: when you add a connection, rename one, or delete its key. The new key starts with only lc:system,mailsec, so a periodic check that every email security ingestion key carries your scope is worth adding to your configuration review.

2.5 Tag the mail sensors that already exist

The key's tags only reach sensors that enroll after the change. Tag the existing mail sensors directly. The selector covers the per-mailbox sensors and each connection's own mailsec-<connection name> sensor, which holds older EMAIL_MESSAGE events from before per-mailbox sensors existed:

limacharlie tag mass-add --selector 'plat == email' --tag acl:email --oid $OID

The CLI tags matching sensors one at a time, so on a tenant with thousands of mailboxes this takes a while, and an interrupted run leaves some sensors untagged. It is safe to run again. Run it once more a few minutes after it finishes, too: tags from an installation key are applied right after enrollment, so a mailbox that enrolled while you were editing the keys can miss both steps.

Confirm that every mail sensor carries the tag, including the mailsec-<connection name> sensors. The two lists should be the same length:

limacharlie sensor list --selector 'plat == email' --oid $OID --output yaml
limacharlie tag find --tag acl:email --oid $OID --output yaml

2.6 Watch, then enforce

While the scope is warn-only, every read it would have refused is reported as an organization error under acl/email, naming the user or key and the resource. Refused D&R actions show up there too, naming the rule:

limacharlie org errors --oid $OID --output yaml

The organization keeps the most recent violation per scope, reported about once every 15 minutes, so leave warn-only on long enough to see your regular users and automations come through. Add the ones that should read mail events. For everyone else, a refusal is the point.

When the errors only name people who should not read mail, remove warn_only from email.yaml (or set it to false) and apply the record again:

limacharlie hive set --hive-name acl --key email --input-file email.yaml --enabled --oid $OID

What changes for people outside the scope

  • The mailbox sensors still appear in the sensor list, with the mailbox address as the hostname.
  • The timeline, LCQL search and Replay return no EMAIL_* events from those sensors. That includes EMAIL_ACTION, which records remediation and downloads. Results are filtered, not refused, so a search returns fewer rows rather than an error.
  • Detections from the mail sensors are hidden.
  • Live event streams in the console carry only the scopes the viewer holds.
  • The Email Security console is unchanged. If they hold mailsec.get, they still read the queue and the drawer.

Email Security itself keeps working: it reads its own configuration and writes its events as the platform, not as a user, so ingestion, verdicts and remediation from the console are not affected by the scope.

Step 3: Restrict the credentials and connection records (optional)

The provider credentials in the secret Hive are the most sensitive part of the configuration. A Microsoft 365 or Google Workspace credential grants read access to every mailbox in scope. An IMAP connection has one secret per mailbox, each holding that mailbox's password. Anyone with secret.get can read them.

Tag each credential secret and each connection record with the same scope. For an IMAP connection, tag every secret named in its mailboxes' credentials:

limacharlie hive set --hive-name secret --key <credential secret name> --tag-add acl:email --oid $OID
limacharlie hive set --hive-name mailsec_provider --key <connection name> --tag-add acl:email --oid $OID

Without --input-file, hive set reads record data from standard input when it is not a terminal. In a script, add < /dev/null so these commands change the tags only.

Users outside the scope then see each record's name and metadata, but its data comes back as {"acl_restricted": true}, and they cannot overwrite it from that view. Only scope members can edit the records from then on, which is why the mail administrators belong in the scope. Ingestion and the connection test are not affected. You can tag mailsec_policy, dr-mail and VIP lookup records the same way if your policy or VIP lists are themselves sensitive.

Adding or removing an acl: tag on a Hive record needs acl.set on top of the Hive's own set permission.

Step 4: Audit who read mail

Access controls decide who can read mail. The audit trail records who did.

Question Where to look
Who opened a message in the console or read its body through the API? The mailsec_message_content_read audit event, with content: mdm
Who downloaded an original message, and why? The same audit event with content: eml, which carries the justification. Each download attempt, served or refused, also emits an EMAIL_ACTION event with action: get_eml and is recorded in the action audit, where repeats of the same refusal share one row. After Step 2, only scope members can read the EMAIL_ACTION events
Who changed the ACL scope or an installation key's tags? The organization audit log. Scope changes are writes to the acl Hive, and key edits are edit_installation_key entries
Who would have been refused while the scope was warn-only? Organization errors under acl/<scope>
limacharlie audit list --event-type mailsec_message_content_read \
  --start $(( $(date +%s) - 7*86400 )) --end $(date +%s) --oid $OID

Reading the audit log needs audit.get. Details of the content-read event, and how often it is written, are in Messages & Triage → Who read a message.

Limits to know

  • No per-mailbox access in the Email Security console. mailsec.get covers every protected mailbox in the organization. You cannot let one analyst triage the sales mailboxes but not the executive ones.
  • acl.set can see everything in the telemetry. Holding acl.set does not grant read access by itself, but its holder can add themselves to the scope. Treat it as equivalent to membership.
  • ACLs never grant access. A scope member still needs the normal permission, such as insight.evt.get, to read events.
  • Retention outputs keep everything. Long-term retention outputs keep restricted events, so that later searches can still enforce the scope. Outputs that list the scope in acl_scopes deliver full message bodies to their destination.
  • Exports drop restricted events. Exporting query or Replay results to an output drops every restricted record, whatever scopes you hold.
  • Audit logs are not restricted. The content-read events name the mailbox address and message ID, not the content, and anyone with audit.get can read them.

Troubleshooting

Symptom Likely cause Fix
Mail events disappeared from the SIEM after you enforced the scope The output does not list the scope Add email to the output's acl_scopes
Automatic quarantine stopped as soon as the sensors were tagged, and org errors shows cannot run "extension request" The remediation rule does not list the scope. Warn-only does not apply to D&R actions Add acl_scopes to the rule
AI triage stopped, and org errors shows cannot run "ask ai" or cannot run "start ai agent" The triage rule does not list the scope Add acl_scopes to the rule
A new mailbox's events are visible to everyone The mailbox enrolled through a key without the acl: tag: a new connection, a renamed one, or a tag added after it enrolled Tag the connection's installation key (2.4), then re-run the tag mass-add from 2.5
Nobody, members included, can read mail events The email scope record is missing, disabled or expired Recreate it with the members from 2.2, or apply it again with --enabled. An empty members list also locks everyone out. Deleting or disabling a scope locks its sensors, it does not unlock them
A mail administrator can no longer edit a connection or its credential The records are tagged (Step 3) and the administrator is not a scope member Add them to the scope
A user outside the scope can still read message bodies They hold mailsec.get. The scope does not cover the Email Security console Remove mailsec.get from the user
A member lost access to mail events through a personal API key The member is listed by email address List the member by user ID

See also