24

我在 FastAPI 上有一个 API,当他请求我的页面时,我需要获取客户端的真实 IP 地址。

我很喜欢使用 starlette Request。但它返回我的服务器 IP,而不是客户端远程 IP。

我的代码:

@app.post('/my-endpoint')
async def my_endpoint(stats: Stats, request: Request):
    ip = request.client.host
    print(ip)
    return {'status': 1, 'message': 'ok'}

我做错了什么?如何获取真实 IP(如 Flask request.remote_addr)?

4

7 回答 7

28

request.client应该可以工作,除非您在代理(例如 nginx)后面运行,在这种情况下使用 uvicorn 的--proxy-headers标志来接受这些传入的标头并确保代理转发它们。

于 2020-02-07T20:57:40.640 回答
7

如果你使用 nginx 和 uvicorn,你应该设置proxy-headers为 uvicorn,你的 nginx 配置应该是 add Host、<code>X-Real-IPand X-Forwarded-For
例如

server {
  # the port your site will be served on
    listen 80;
  # the domain name it will serve for
    server_name <your_host_name>; # substitute your machine's IP address or FQDN

#    add_header Access-Control-Allow-Origin *;
    # add_header Access-Control-Allow-Credentials: true;
    add_header Access-Control-Allow-Headers Content-Type,XFILENAME,XFILECATEGORY,XFILESIZE;
    add_header access-control-allow-headers authorization;
    # Finally, send all non-media requests to the Django server.
    location / {
        proxy_pass http://127.0.0.1:8000/; # the uvicorn server address
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    }
}

在 nginx 文档上:

This middleware can be applied to add HTTP proxy support to an
application that was not designed with HTTP proxies in mind. It
sets REMOTE_ADDR, HTTP_HOST from X-Forwarded headers. While
Werkzeug-based applications already can use
:py:func:werkzeug.wsgi.get_host to retrieve the current host even if
behind proxy setups, this middleware can be used for applications which
access the WSGI environment directly。
If you have more than one proxy server in front of your app, set
num_proxies accordingly.
Do not use this middleware in non-proxy setups for security reasons.
The original values of REMOTE_ADDR and HTTP_HOST are stored in
the WSGI environment as werkzeug.proxy_fix.orig_remote_addr and
werkzeug.proxy_fix.orig_http_host
:param app: the WSGI application
:param num_proxies: the number of proxy servers in front of the app.  
于 2020-11-17T08:26:00.677 回答
3

FastAPI using-request-directly文档页面显示了此示例:

from fastapi import FastAPI, Request

app = FastAPI()


@app.get("/items/{item_id}")
def read_root(item_id: str, request: Request):
    client_host = request.client.host
    return {"client_host": client_host, "item_id": item_id}

有了这个例子,我就可以节省 10 分钟的时间来研究Starlette 的 Request 类

于 2021-02-02T18:59:55.883 回答
1

您不需要设置--proxy-headersbc 它默认启用,但它只信任--forwarded-allow-ips默认为的IP127.0.0.1

为了安全起见,您应该只信任来自反向代理的 ip 的代理标头(而不是信任 all with '*')。如果它在同一台机器上,那么默认值应该可以工作。虽然我从我的 nginx 日志中注意到它正在使用 ip6 与 uvicorn 通信,所以我不得不使用,--forwarded-allow-ips='[::1]'然后我可以在 FastAPI 中看到 ip 地址。您还可以使用--forwarded-allow-ips='127.0.0.1,[::1]'在 localhost 上同时捕获 ip4 和 ip6。

--proxy-headers / --no-proxy-headers- 启用/禁用 X-Forwarded-Proto、X-Forwarded-For、X-Forwarded-Port 以填充远程地址信息。默认为启用,但仅限于在 forwarded-allow-ips 配置中信任连接 IP。

--forwarded-allow-ips- 逗号分隔的 IP 列表以信任代理标头。默认为 $FORWARDED_ALLOW_IPS 环境变量(如果可用)或“127.0.0.1”。通配符“*”表示始终信任。

参考:https ://www.uvicorn.org/settings/#http

于 2021-11-17T22:50:33.277 回答
0

如果您已根据@AllenRen 的回答正确配置了 nginx 配置,请尝试使用 uvicorn 的--proxy-headers标志--forwarded-allow-ips='*'

于 2020-11-30T08:11:46.323 回答
0

您将使用以下代码从客户端获取真实 IP 地址。如果您使用反向代理和端口转发

@app.post('/my-endpoint')
async def my_endpoint(stats: Stats, request: Request):
    x = 'x-forwarded-for'.encode('utf-8')
    for header in request.headers.raw:
        if header[0] == x:
            print("Find out the forwarded-for ip address")
            origin_ip, forward_ip = re.split(', ', header[1].decode('utf-8'))
            print(f"origin_ip:\t{origin_ip}")
            print(f"forward_ip:\t{forward_ip}")
    return {'status': 1, 'message': 'ok'}
于 2021-09-04T17:33:12.440 回答
0

我已经部署了 docker-compose 文件并且更改是

nginx。配置文件

 location / {
  proxy_set_header   Host             $host;
  proxy_set_header   X-Real-IP        $remote_addr;
  proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  proxy_pass http://localhost:8000;
}

Dockerfile 的变化

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]

docker-compose.yaml 文件的变化

version: "3.7"
services:
  app:
    build: ./fastapi
    container_name: ipinfo
    restart: always
    ports:
      - "8000:8000"
    network_mode: host

  nginx:
    build: ./nginx
    container_name: nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    network_mode: host

在这些更改正确获得客户端外部 IP 之后

于 2022-02-01T13:43:17.653 回答