如何通过 CloudFront 获取请求的客户端 IP?

How to get client IP of requests via CloudFront?

根据 CloudFront 的文档 (https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html), client IP可以是X-Forwarded-For header的前面,中间,结尾。

对吗?那我怎样才能得到真正的客户端IP呢?

Is it right?

不完全是。

CloudFront 遵循 X-Forwarded-For 的正确语义。具体来说,处理请求的每个系统都会将其客户端地址附加到右侧。这意味着来自 CloudFront 的请求中 X-Forwarded-For 中最右边的地址始终是连接到 CloudFront 的机器的地址。

如果客户端(与 CloudFront 建立连接的机器)在其请求中包含 X-Forwarded-For header,则 header 可能是伪造的,或者如果客户端是一个代理服务器,但你很少有办法知道这一点......所以无论哪种方式,你都应该将其视为潜在的有价值的,但严格来说 non-authoritative.

最右边的值(也可能是唯一的值)是您在从 CloudFront 收到的请求中可以信任的唯一值。

一般来说,从右开始解析,任何您已知并信任已正确识别其上游客户端的地址都可以从列表中删除...但是一旦您遇到第一个不受信任的地址,从右到左,这就是您要查找的地址,因为该地址左侧的任何内容都不可信任。

这意味着如果您的堆栈中的组件——例如 Application Load Balancer 或您的 Web 服务器也在添加 X-Forwarded-For,那么您将需要考虑这些组件的内容 添加到值的右侧,修改 CloudFront 提供的内容。

例如。客户端发送:

X-Forwarded-For: a, b, c

CloudFront 添加客户端的 IP d:

X-Forwarded-For: a, b, c, d

ALB 收到来自 CloudFront 的请求,因此它添加了 CloudFront 出口地址 e:

X-Forwarded-For: a, b, c, d, e

然后你的web服务器添加平衡器的内部地址f:

X-Forwarded-For: a, b, c, d, e, f

只要在您的平衡器子网的 CIDR 范围内,您就可以信任并删除 f

您可以信任并删除 e,只要它在 CloudFront address ranges

这样您就可以使用 d 作为客户端地址。

abc 几乎 毫无价值,在这个例子中,因为你不能相信它们的真实性因为它们位于第一个(从右起)不受信任地址的左侧...偶尔,它们可能在以后的取证上有用,但您不能根据它们做出任何 real-time 决定。

这就是X-Forwarded-For总是的工作方式。由于缺乏了解,许多开发人员似乎做出了幼稚的假设。在将它用于任何重要的事情之前,请确保您了解它。


Lambda@Edge 触发器中,CloudFront 在 event.Records[0].cf.request.clientIp 中为您提供客户端 IP 地址。这始终只是一个地址,并且与 X-Forwarded-For 最右边的值相同,因为请求离开 CloudFront 前往您的来源(如上所述,可能会在右侧添加其他值)。

我们可以采用的方法是利用 “CloudFront Edge with Lambda” 并将最后一个 IP 复制到不同的 propitiatory header(比方说 My-X-Forwarded-For),然后将此 header 覆盖复制到应用服务器之前层中的 X-Forwarded-For

让我们假设交通顺畅

Client sends => CloudFront => ALB => WebServer => AppServer

CloudFront Edge 必须使用 [=38= 最右边的 IP 注入新的 header My-X-Forwarded-For ]. WebServer 将有 header 规则覆盖 X-Forwarded-For header 值来自 My-X-Forwarded-For

这对应用层是透明的。 基础设施问题在基础设施中分类。由于我们引入了 AWS CloudFront 层,因此无需在应用层中进行更改。

借助 CloudFront Functions,现在您无需使用 Lambda@Edge 即可执行此操作:

function handler(event) {
    var request = event.request;
    var clientIP = event.viewer.ip;

    //Add the true-client-ip header to the incoming request
    request.headers['true-client-ip'] = {value: clientIP};

    return request;
}

查看 guide AWS 文档

我的建议是使用提供的 CloudFront headers、link - [https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-cloudfront-headers.html]

  1. 您首先需要做的就是转到 Cloudfront -> Select Distribution -> Behaviors -> 然后在 'Cache key and origin requests'

    中执行以下操作
  2. Select 'CachingDisabled' 对于 'Cache policy' 下拉列表,如果您不想缓存任何内容。如果我没有 select 这个选项,我个人在我的应用程序中遇到了问题。

  3. 对于源请求策略,请执行以下操作 -

    创建一个新策略,例如 'Origin-Policy-For-Cloudfront' 和 select 'CloudFront-Viewer-Address' 并检查其他选项还有。

    看起来像这样 -

  1. 保存它,最后 Cloudfront 行为应该如下所示 -

  1. 现在,打开conf.d/node.confnginx.conf,随便你已经写好了你的'server -> /location',然后简单地写下-

     server {
         listen 80;
         server_name my-server CLOUDFRONT_URL;
         location / {
             proxy_set_header   X-Client-IP $http_CloudFront_Viewer_Address;
             proxy_set_header   Host $http_host;
             proxy_pass         "http://127.0.0.1:
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection $connection_upgrade;
         }
     }
    
  2. 在NodeJs Backend,你可以在请求中获取Client IP,如下所示-

     exports.get = (req, res, next) => {
         console.log('Clinet IP:', req.headers['x-client-ip']);
     }
    

这是一种获取客户端 Ip 的更简单的方法,而不是乱用 Cloudfront CIDR 和所有东西。