Athena

IP Whitelists, Blacklists, and Precedence

The exact order Athena uses when global rules, per-key rules, and lock-in state overlap.

Athena supports both per-key and global IP rules. The runtime model is stored in ApiKeyIpPolicy, api_key_ip_whitelist, api_key_ip_blacklist, api_key_ip_global_whitelist, and api_key_ip_global_blacklist.

Precedence order

The authoritative order is documented in enforce_ip_policy:

In plain English:

  1. Global blacklists win first.
  2. Per-key blacklists win next.
  3. Unresolved virgin_mode keys learn and return early.
  4. Global whitelists apply before per-key whitelists.
  5. If no whitelist exists at a level, Athena keeps evaluating the next rule set.

Global vs per-key rules

Rule typeScopeAdmin route
api_key_ip_global_whitelistentire deployment or one client_name/admin/ip-global-whitelist
api_key_ip_global_blacklistentire deployment or one client_name/admin/ip-global-blacklist
api_key_ip_whitelistone API key/admin/api-keys/{id}/ip-whitelist
api_key_ip_blacklistone API key/admin/api-keys/{id}/ip-blacklist

Global rules can have client_name = NULL, which makes them apply everywhere, or a specific client name, which narrows them to one logical client.

What counts as an IP rule

parse_cidr_list accepts:

  • IPv4 addresses like 203.0.113.10
  • IPv6 addresses like 2001:db8::10
  • CIDR blocks like 203.0.113.0/24 or 2001:db8::/32

Bare IPs are normalized to host-only networks:

  • IPv4 -> /32
  • IPv6 -> /128

How Athena finds the client IP

extract_client_ip resolves the caller in this order:

  1. X-Real-IP
  2. first hop of X-Forwarded-For, but only when ATHENA_RATE_LIMIT_TRUST_X_FORWARDED_FOR or the corresponding config value enables trust
  3. direct peer address from the socket

If any whitelist, blacklist, or virgin_mode rule is in play and Athena cannot resolve the client IP, the request is rejected with Client IP required.

Per-key policy examples

Add whitelist entries

curl -X POST "http://localhost:4052/admin/api-keys/$API_KEY_ID/ip-whitelist" \
  -H "X-Athena-Admin-Key: $ATHENA_KEY_12" \
  -H "Content-Type: application/json" \
  -d '{
    "addrs": ["203.0.113.10", "203.0.113.0/24"],
    "label": "office"
  }'

Add blacklist entries

curl -X POST "http://localhost:4052/admin/api-keys/$API_KEY_ID/ip-blacklist" \
  -H "X-Athena-Admin-Key: $ATHENA_KEY_12" \
  -H "Content-Type: application/json" \
  -d '{
    "addrs": ["198.51.100.0/24"],
    "label": "blocked-range"
  }'

Inspect the resolved key policy

curl "http://localhost:4052/admin/api-keys/$API_KEY_ID/ip-policy" \
  -H "X-Athena-Admin-Key: $ATHENA_KEY_12"

Global policy examples

Whitelist one client's deployment CIDR

curl -X POST "http://localhost:4052/admin/ip-global-whitelist" \
  -H "X-Athena-Admin-Key: $ATHENA_KEY_12" \
  -H "Content-Type: application/json" \
  -d '{
    "addr": "10.42.0.0/16",
    "client_name": "analytics",
    "label": "analytics cluster"
  }'

Blacklist a bad source everywhere

curl -X POST "http://localhost:4052/admin/ip-global-blacklist" \
  -H "X-Athena-Admin-Key: $ATHENA_KEY_12" \
  -H "Content-Type: application/json" \
  -d '{
    "addr": "198.51.100.77",
    "label": "abuse source"
  }'

Operational notes

  • IP policy caches are short-lived, around 2 seconds, and admin mutations call invalidate_ip_policy or invalidate_global_ip_rules so changes apply quickly.
  • api_key_ip_seen is not an allowlist by itself. It is observation data used by virgin_mode.
  • A blacklist can still reject traffic while a key is in virgin learning mode.