IP Whitelisting in API Gateway — Restrict Access by Source IP Address
In this tutorial, you will learn about IP Whitelisting in API Gateway. We cover key concepts, practical examples, and best practices to help you master this topic.
IP whitelisting is a security mechanism in API gateways that allows only requests from specific IP addresses or CIDR ranges to access certain routes, blocking all other traffic at the network level.
What You'll Learn
- How IP whitelisting works at the gateway level
- CIDR-based matching and dynamic whitelist management
- Combining IP whitelisting with authentication for defense in depth
Why It Matters
Some API endpoints should never be publicly accessible. Admin dashboards, internal health checks, and partner-only APIs benefit from IP whitelisting. Even if authentication credentials leak, the attacker's IP must also be whitelisted, providing a second layer of defense.
Real-World Use
Durga Antivirus Pro's admin panel at /admin/ is only accessible from the company's VPN IP range 10.0.0.0/8 and the office static IP 203.0.113.50. The gateway checks the source IP before forwarding any request to the admin service. All other IPs receive a 403 Forbidden response.
flowchart LR
Client["Request"] --> IPCheck["IP Whitelist\nCheck"]
IPCheck -->|"IP allowed"| Backend["Backend\nService"]
IPCheck -->|"IP blocked"| Deny["403 Forbidden"]
style IPCheck fill:#dbeafe,stroke:#2563eb
style Deny fill:#fecaca,stroke:#dc2626
CIDR-Based Whitelist Check
import ipaddress
from flask import Flask, request, jsonify
app = Flask(__name__)
WHITELISTED_CIDRS = [
"10.0.0.0/8",
"203.0.113.0/24",
"192.168.1.0/24",
]
def is_ip_allowed(client_ip):
try:
ip = ipaddress.ip_address(client_ip)
for cidr in WHITELISTED_CIDRS:
if ip in ipaddress.ip_network(cidr, strict=False):
return True
return False
except ValueError:
return False
@app.before_request
def check_ip_whitelist():
client_ip = request.remote_addr
if not is_ip_allowed(client_ip):
return jsonify({"error": "Access denied from your IP"}), 403
Expected behavior:
print(is_ip_allowed("10.0.0.5")) # True
print(is_ip_allowed("203.0.113.50")) # True
print(is_ip_allowed("1.2.3.4")) # False
Route-Specific Whitelisting
Different routes may have different whitelist rules:
ROUTE_WHITELISTS = {
"/admin": ["10.0.0.0/8", "203.0.113.50"],
"/internal": ["10.0.0.0/8"],
"/partner-api": ["198.51.100.0/24"],
}
@app.before_request
def check_route_whitelist():
for prefix, allowed_cidrs in ROUTE_WHITELISTS.items():
if request.path.startswith(prefix):
client_ip = request.remote_addr
ip = ipaddress.ip_address(client_ip)
allowed = any(
ip in ipaddress.ip_network(cidr, strict=False)
for cidr in allowed_cidrs
)
if not allowed:
return jsonify({"error": "IP not authorized for this route"}), 403
Dynamic Whitelist from Database
For frequently changing whitelists, load from a database instead of hardcoding:
def load_whitelist_from_db():
return [
row["cidr"]
for row in database.query("SELECT cidr FROM ip_whitelist WHERE active = 1")
]
@app.before_request
def check_dynamic_whitelist():
whitelist = cache.get("ip_whitelist") or load_whitelist_from_db()
ip = ipaddress.ip_address(request.remote_addr)
for cidr in whitelist:
if ip in ipaddress.ip_network(cidr, strict=False):
return
return jsonify({"error": "Access denied"}), 403
Common Mistakes
1. IPv6 Not Considered
Whitelisting only IPv4 addresses leaves the IPv6 path open. Always add whitelist rules for both address families.
2. Using Proxy Headers for IP Detection
If the gateway is behind another proxy, request.remote_addr may show the proxy IP, not the real client. Use X-Forwarded-For and trust only the last proxy IP.
3. Hardcoding IPs in Source Code
IP addresses change. Store whitelists in configuration files, environment variables, or databases, not in code.
4. Forgetting Loopback for Monitoring
Health check systems may run on the same host. Ensure 127.0.0.1 and ::1 are whitelisted for monitoring probes.
5. No Logging of Blocked Requests
Without logging blocked requests, you cannot detect attack patterns. Log all whitelist rejections with IP and timestamp.
Practice Questions
- Why combine IP whitelisting with authentication instead of relying on auth alone?
- How does CIDR notation help whitelist entire IP ranges?
- Why must IPv6 be considered in IP whitelisting?
- What is the risk of relying on
X-Forwarded-Forfor client IP detection? - How can you manage IP whitelists dynamically without redeploying the gateway?
Answers:
- Defense in depth. Even if credentials are compromised, the attacker must also connect from a whitelisted IP.
- CIDR notation (
10.0.0.0/8) matches 16 million IPs with one rule, making it efficient to define large ranges. - Clients with IPv6 addresses bypass IPv4-only whitelists. Attackers can use IPv6 to reach endpoints that block IPv4.
- The
X-Forwarded-Forheader can be spoofed. Only trust the last IP in the chain, which your upstream proxy adds. - Load whitelists from a database or configuration management system. The gateway reloads periodically or via a webhook.
Challenge: Design an IP access control system for a gateway with three tiers: public (all IPs), partner (specific /24 CIDRs), and admin (VPN IPs + office static IP). Include both IPv4 and IPv6.
FAQ
Mini Project
Build a Flask gateway with route-specific IP whitelisting. The /admin route allows only 10.0.0.0/8. The /partner route allows 198.51.100.0/24. All other routes are public. Load whitelist rules from a JSON file and add a reload endpoint that refreshes rules without restarting.
What's Next
Continue with Logging and Monitoring in Gateway for Observability, or explore Kong API Gateway for a production-grade gateway implementation.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro