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_UNSPECIFIEDorCLIENT_IP_SOURCE_REMOTE_ADDR— TCP remote address (default)CLIENT_IP_SOURCE_CF_CONNECTING_IP—Cf-Connecting-Ip, behind CloudflareCLIENT_IP_SOURCE_X_FORWARDED_FOR—X-Forwarded-For(leftmost IP), behind a standard reverse proxyCLIENT_IP_SOURCE_X_REAL_IP—X-Real-Ip, behind NGINXCLIENT_IP_SOURCE_TRUE_CLIENT_IP—True-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:
- CLI
- curl
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')
RESPONSE=$(curl -s -X POST "$TALOS_URL/v2alpha1/admin/issuedApiKeys" \
-H "Content-Type: application/json" \
-d '{
"name": "restricted-key",
"actor_id": "service_payments",
"ip_restriction": {
"allowed_cidrs": ["127.0.0.1/32", "10.0.0.0/8"]
}
}')
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:
- CLI
- curl
talos keys verify "$API_SECRET" -e "$TALOS_URL"
VERIFY_RESPONSE=$(curl -s -X POST "$TALOS_URL/v2alpha1/admin/apiKeys:verify" \
-H "Content-Type: application/json" \
-d "{\"credential\": \"$API_SECRET\"}")
echo "$VERIFY_RESPONSE" | jq .
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:
- CLI
- curl
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"
UPDATE_RESPONSE=$(curl -s -X PATCH "$TALOS_URL/v2alpha1/admin/issuedApiKeys/$KEY_ID" \
-H "Content-Type: application/json" \
-d '{
"ip_restriction": {
"allowed_cidrs": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
}
}')
echo "$UPDATE_RESPONSE" | jq .
To remove all IP restrictions and allow requests from any IP, set allowed_cidrs to an empty array:
- CLI
- curl
talos keys issued update "$KEY_ID" \
--allowed-cidrs "" \
-e "$TALOS_URL"
UNRESTRICT_RESPONSE=$(curl -s -X PATCH "$TALOS_URL/v2alpha1/admin/issuedApiKeys/$KEY_ID" \
-H "Content-Type: application/json" \
-d '{
"ip_restriction": {
"allowed_cidrs": []
}
}')
echo "$UNRESTRICT_RESPONSE" | jq .
For the complete update field reference, see the UpdateIssuedAPIKey API reference.
Import keys with IP restrictions
Set IP restrictions when you import external keys:
- CLI
- curl
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"
IMPORT_RESPONSE=$(curl -s -X POST "$TALOS_URL/v2alpha1/admin/importedApiKeys" \
-H "Content-Type: application/json" \
-d '{
"name": "imported-restricted",
"raw_key": "sk_live_example_key_for_import_test",
"actor_id": "user_restrict",
"ip_restriction": {
"allowed_cidrs": ["203.0.113.0/24"]
}
}')
echo "$IMPORT_RESPONSE" | jq .
For the complete import field reference, see the ImportAPIKey API reference.
Behavior notes
- Allowlist model: Talos permits only listed CIDRs. An empty
allowed_cidrsarray 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
/32for single IPv4 addresses and/128for 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
aclclaim. 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.
