Skip to main content

IP restrictions

IP restrictions limit which client IPs can use an API key. Verification rejects requests from IPs outside the allowed CIDR ranges. Keys without IP restrictions accept requests from any IP.

Prerequisites

A running Ory Talos server. See the quickstart to start one locally.

Configure client IP source

By default, Ory Talos uses the TCP remote address (REMOTE_ADDR) to determine the client IP. If your server runs behind a reverse proxy or CDN, configure the correct header in your Ory Talos config:

serve:
http:
client_ip_source: CLIENT_IP_SOURCE_CF_CONNECTING_IP

Supported values:

  • CLIENT_IP_SOURCE_UNSPECIFIED or CLIENT_IP_SOURCE_REMOTE_ADDR — TCP remote address (default)
  • CLIENT_IP_SOURCE_CF_CONNECTING_IPCf-Connecting-Ip, behind Cloudflare
  • CLIENT_IP_SOURCE_X_FORWARDED_FORX-Forwarded-For (leftmost IP), behind a standard reverse proxy
  • CLIENT_IP_SOURCE_X_REAL_IPX-Real-Ip, behind NGINX
  • CLIENT_IP_SOURCE_TRUE_CLIENT_IPTrue-Client-Ip, behind Akamai or Cloudflare Enterprise

The setting is global: all IP restriction checks use the same source. If the selected header is empty, Talos falls back to the TCP remote address. Talos reads this value per request, so changes take effect without a restart.

Issue a key with IP restrictions

Add the ip_restriction field when creating a key. The allowed_cidrs array accepts both individual IPs (with /32 or /128 suffix) and CIDR ranges:

RESPONSE=$(talos keys issue "restricted-key" \
--actor service_payments \
--allowed-cidrs "127.0.0.1/32,10.0.0.0/8" \
--format json \
-e "$TALOS_URL" 2>/dev/null)

echo "$RESPONSE" | jq .

export API_SECRET=$(echo "$RESPONSE" | jq -er '.secret')
export KEY_ID=$(echo "$RESPONSE" | jq -er '.issued_api_key.key_id')

For the complete request field reference, see the IssueAPIKey API reference.

Verify from an allowed IP

When the client IP is within an allowed CIDR range, verification succeeds:

talos keys verify "$API_SECRET" -e "$TALOS_URL"

The response includes the key metadata with is_valid: true.

Verify from a disallowed IP

When the client IP is outside all allowed CIDR ranges, verification returns VERIFICATION_ERROR_IP_NOT_ALLOWED. The response does not reveal which CIDRs are configured.

For the full list of verification error codes, see the error codes reference.

Update IP restrictions on an existing key

Use PATCH to add, change, or remove IP restrictions on an existing key:

talos keys issued update "$KEY_ID" \
--allowed-cidrs "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" \
-e "$TALOS_URL"

To remove all IP restrictions and allow requests from any IP, set allowed_cidrs to an empty array:

talos keys issued update "$KEY_ID" \
--allowed-cidrs "" \
-e "$TALOS_URL"

For the complete update field reference, see the UpdateIssuedAPIKey API reference.

Import keys with IP restrictions

Set IP restrictions when you import external keys:

talos keys imported import "imported-restricted" \
--raw-key "sk_live_example_key_for_import_test" \
--actor "user_restrict" \
--allowed-cidrs "203.0.113.0/24" \
-e "$TALOS_URL"

For the complete import field reference, see the ImportAPIKey API reference.

Behavior notes

  • Allowlist model: Talos permits only listed CIDRs. An empty allowed_cidrs array allows requests from any IP.
  • Cache TTL: Talos enforces IP restrictions on every verification, including cache hits, by re-checking the current request IP. Changes to the allowed CIDRs take effect after the cache TTL expires. See cache configuration for TTL settings.
  • Fail-closed: If client IP resolution fails, Talos denies the request with VERIFICATION_ERROR_IP_NOT_ALLOWED.
  • IPv4 and IPv6: Talos supports both address families. Use /32 for single IPv4 addresses and /128 for single IPv6 addresses.
  • Derived tokens: Deriving a token checks the parent key's IP restrictions, then seals the parent's allowed CIDRs into the token's acl claim. Verifying the token enforces that sealed list against the request IP. A token from an unrestricted parent stays unrestricted for its lifetime; a token from a restricted parent keeps that allowlist even if the parent later changes.