【发布时间】:2018-12-25 21:18:16
【问题描述】:
根据 CloudFront 的文档
(https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html),客户端IP可以是X-Forwarded-For头的前、中、尾。
它是正确的吗?那么如何获取真实的客户端IP呢?
【问题讨论】:
根据 CloudFront 的文档
(https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html),客户端IP可以是X-Forwarded-For头的前、中、尾。
它是正确的吗?那么如何获取真实的客户端IP呢?
【问题讨论】:
对吗?
不完全是。
CloudFront 遵循 X-Forwarded-For 的正确语义。具体来说,每个处理请求的系统都会将其客户端的地址附加到右侧。这意味着来自 CloudFront 的请求中X-Forwarded-For 中最右边的地址始终是连接到 CloudFront 的机器的地址。
如果客户端(与 CloudFront 建立连接的机器)在其请求中包含 X-Forwarded-For 标头,则该标头可能是伪造的,或者如果客户端是代理服务器,它可能是合法的,但您很少有办法知道...所以无论哪种方式,您都应该将其视为具有潜在价值,但严格来说是非权威的。
最右边的值(也可能是唯一值)是您在从 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
您可以信任并删除 f,只要它在您的平衡器子网的 CIDR 范围内。
您可以信任并删除 e,只要它在 CloudFront 中 address ranges。
这让您使用d 作为客户端地址。
在这个例子中,a 和 b 和 c 的值几乎毫无价值,因为你不能相信它们的真实性,因为它们在第一个的左边(来自正确的)不受信任的地址...有时,它们可能在以后在取证上有用,但您无法根据它们做出任何实时决策。
X-Forwarded-For总是就是这样工作的。由于缺乏理解,许多开发人员似乎做出了幼稚的假设。在将其用于任何重要的事情之前,请确保您了解它。
在Lambda@Edge 触发器中,CloudFront 为您提供event.Records[0].cf.request.clientIp 中的客户端 IP 地址。这始终只是一个地址,并且与最右边的 X-Forwarded-For 值相同,因为请求将 CloudFront 发送到您的源(如上所述,它可能会在右侧添加其他值)。
【讨论】:
借助 CloudFront 函数,您现在无需使用 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;
}
在 AWS 文档中查看 guide
【讨论】:
我的建议是使用 CloudFront 提供的标头,链接 - [https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-cloudfront-headers.html]
您首先需要做的就是转到 Cloudfront -> 选择分发 -> 行为 -> 并在“缓存密钥和源请求”中执行以下操作
如果您不想缓存任何内容,请为“缓存策略”下拉菜单选择“CachingDisabled”。如果我没有选择此选项,我个人会在我的应用中遇到问题。
对于源请求策略,请执行以下操作 -
创建一个新策略,如 'Origin-Policy-For-Cloudfront' 并选择 'CloudFront-Viewer-Address' 并检查其他选项。
它看起来像这样 -
现在,打开 conf.d/node.conf 或 nginx.conf,无论您在哪里编写了“服务器 -> /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;
}
}
在 NodeJs 后端,您可以按如下方式获取请求中的客户端 IP -
exports.get = (req, res, next) => {
console.log('Clinet IP:', req.headers['x-client-ip']);
}
这是一种更简单的获取客户端 Ip 的方法,而不是乱用 Cloudfront CIDR 等等。
【讨论】:
我们可以采用的方法是利用 "CloudFront Edge with Lambda" 并将最后一个 IP 复制到不同的标头中(比如说 My-X-Forwarded-For) 然后将此标头覆盖复制到应用服务器之前层中的 X-Forwarded-For 上。
假设流量低于方式
Client sends => CloudFront => ALB => WebServer => AppServer
CloudFront Edge 必须使用来自 X-Forwarded-For 的最右侧 IP 注入新标头 My-X-Forwarded-For。 WebServer 将有标头规则来覆盖 X-Forwarded-For 标头,其值来自 My-X-Forwarded-For
这对应用层是透明的。 基础设施问题按基础设施分类。我们引入了 AWS CloudFront 层,因此无需对应用程序层进行更改。
【讨论】: