How to limit access to Flask for a single IP address?

This IPTABLES/Netfilter rule will attend your need, dropping all incoming traffic, EXCEPT the traffic originated from your_ip_address to port 80:

$ /sbin/iptables -A INPUT -s ! your_ip_address --dport 80 -j DROP

Here's something presented on many forums, which allows localhost traffic + external access to your Flask app from your_ip_address, but reject all traffic from other IP address:

$ /sbin/iptables -A INPUT -i lo -j ACCEPT
$ /sbin/iptables -A INPUT -s your_ip_address --dport 80 -j DROP
$ /sbin/iptables -A INPUT --dport 80 -j REJECT

Although you can easily achieve the expected result via Flask (as pointed out on the elected answer), this kind of issue should be treated at the Network Layer of the Operating System. Considering that you're using a Nix-like OS, you can deny/allow incoming connections using Netfilter via IPTABLES, with rules like these.

Incoming traffic/packets, firstly, they pass through the analysis of the Kernel of your Operating System. To deny/allow traffic, from any source to specific ports, it's a job for the Firewall of the Operating System, on the Network Layer of its Kernel. If you don't have a Firewall running on your server, you should configure it.

Here's a takeaway:

  • Traffic must be treated at the Network Layer of your Operating System. Do not let application handle this task, at least on a Production environment. No one will do a best job regarding this task, than the Kernel of you Operating System (hoping that you're using a Nix-like OS). The Linux Kernel and its modules (Netfilter) are much more reliable, competent and effective to treat these kind of tasks.

Using just the features of Flask, you could use a before_request() hook testing the request.remote_addr attribute:

from flask import abort, request

@app.before_request
def limit_remote_addr():
    if request.remote_addr != '10.20.30.40':
        abort(403)  # Forbidden

but using a firewall rule on the server is probably the safer and more robust option.

Note that the Remote_Addr can be masked if there is a reverse proxy in between the browser and your server; be careful how you limit this and don't lock yourself out. If the proxy lives close to the server itself (like a load balancer or front-end cache), you can inspect the request.access_route list to access the actual IP address. Do this only if remote_addr itself is a trusted IP address too:

trusted_proxies = ('42.42.42.42', '82.42.82.42', '127.0.0.1')

def limit_remote_addr():
    remote = request.remote_addr
    route = list(request.access_route)
    while remote in trusted_proxies:
        remote = route.pop()

    if remote != '10.20.30.40':
        abort(403)  # Forbidden