在envoy作为前端代理时,用户ip的获取很重要,一般获取ip的方式。都是通过Header中的 X-Forward-ForX-Real-IPRemote addr 等属性获取,但是如果确保Envoy可以获取到的ip是真实的用户ip呢?本篇继续解密!

概念说明

  • Remote Address

    是nginx与客户端进行TCP连接过程中,获得的客户端真实地址。Remote Address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求。

    一般情况下,在Envoy作为最外层代理时,此IP为真实的IP客户端IP

  • X-Real-IP

    是一个自定义头。X-Real-Ip 通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。X-Real-Ip 目前并不属于任何标准,代理和 Web 应用之间可以约定用任何自定义头来传递这个信息。

  • X-Forwarded-For

    X-Forwarded-For 是一个扩展头。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP,现在已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。通常,X-Forwarded-For可被伪造,并且使用CDN会被重写

Envoy中如何获取真实IP

在Envoy中,涉及到客户端IP的配置如下:

use_remote_address: 默认值false,设置为true,使用客户端连接的真实远程地址,false是使用x-forwarded-for

skip_xff_append: 设置为true,则不会将远程地址附加到x-forwarded-for中

request_headers_to_add 添加请求头

request_headers_to_remove 删除一个请求头

实验环境配置准备

admin:
access_log_path: /dev/null
address:
socket_address: { address: 0.0.0.0, port_value: 9901 } static_resources:
listeners:
- name: listener_80
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
access_log: filter_chains:
- filters:
- name: envoy_http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
access_log:
- name: envoy.listener.accesslog
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
path: /var/log/envoy.log
log_format:
text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n"
http_filters:
- name: envoy.filters.http.router
use_remote_address: true
skip_xff_append: false
xff_num_trusted_hops: 0
stat_prefix: local_route
codec_type: AUTO
route_config:
name: local_route
#request_headers_to_remove: "X-Forwarded-For"
request_headers_to_add:
header:
key: "X-Forwarded-For"
value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
#value: "%REQ(REMOTE_ADDR)%"
append: true
virtual_hosts:
- name: split_traffic
domains: [ "*" ]
routes:
- match:
prefix: "/"
route:
cluster: version_v1
request_mirror_policies:
cluster: version_v2
runtime_fraction:
default_value:
numerator: 10
denominator: HUNDRED
runtime_key: routing.request_mirror.version
clusters:
- name: version_v1
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: version_v1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: version1, port_value: 90 }
health_checks:
timeout: 3s
interval: 30s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: /ping
expected_statuses: { start: 200, end: 201 }
- name: version_v2
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: version_v2
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: version2, port_value: 90 }
health_checks:
timeout: 3s
interval: 30s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: /ping
expected_statuses: { start: 200, end: 201 }

docker-compose

version: '3'
services:
envoy:
image: envoyproxy/envoy-alpine:v1.15-latest
environment:
- ENVOY_UID=0
ports:
- 80:80
- 443:443
- 82:9901
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml
networks:
envoymesh:
aliases:
- envoy
depends_on:
- webserver1
- webserver2
- webserver3
- webserver4 webserver1:
image: sealloong/envoy-end:latest
networks:
envoymesh:
aliases:
- version1
environment:
- VERSION=v1
- COLORFUL=blue
expose:
- 90
webserver2:
image: sealloong/envoy-end:latest
networks:
envoymesh:
aliases:
- version1
environment:
- VERSION=v1
- COLORFUL=blue
expose:
- 90
webserver3:
image: sealloong/envoy-end:latest
networks:
envoymesh:
aliases:
- version2
environment:
- VERSION=v2
- COLORFUL=red
expose:
- 90
webserver4:
image: sealloong/envoy-end:latest
environment:
- VERSION=v2
- COLORFUL=red
networks:
envoymesh:
aliases:
- version2
expose:
- 90
networks:
envoymesh: {}

实际使用Envoy作为代理时的外在环境

环境1:客户端直接和Envoy通信

当一个正常请求时,此处可以正常获得客户端IP,实际上envoy拿的值是 X-Forwarded-For

后端日志

在伪造或者重写X-Forwarded-For后实际上是获取的伪造的值。





在Envoy直接作为外层代理时,可以使用如下参数,在不管如何伪造,都可以拿到对应的参数。

name: local_route
request_headers_to_remove: "X-Forwarded-For" # 怕X-Forwarded-For为伪造值,可以删除此值,
request_headers_to_add: # 删除后还需要向后端传递,故还需要添加上此值
header:
key: "X-Forwarded-For"
value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" # 获取 remote_addr,此值无法伪造,为Envoy变量,表示 下游主机真实IP不加端口,即remote_addr 无端口
append: true # 表面值是追加还是重写

可以看到envoy获取的为真实的ip并非伪造的请求

环境2:Envoy前段存在代理(无CDN)

此环境下,前端存在代理,如f5、nginx等。这种情况下不能使用remote_addr 这样获取的为前端代理的IP并非真实IP

前端存在f5或nginx,可以在f5中配置irule传递真实的remote_addr,替换为真实的客户端IP,又前端代理重写配置,可自定义值。

request_headers_to_remove: "X-Forwarded-For"
request_headers_to_add:
header:
key: "X-Forwarded-For"
value: "%REQ(custom_header)%"

环境3:Envoy前段存在代理(单CDN)

此环境下,前端存在代理,并且使用了CDN,应为每个CDN厂商获取客户真实IP的方式并不一致,这里需要找到cdn厂商找到获取真实IP的方法,在按照步骤2进行。

举例:

阿里云cdn获取真实IP方法

加速乐获取真实IP方法

环境4:Envoy前段存在代理(多CDN)

由于各CDN的带宽、价格、使用场景等因素,在实际情况下,可能使用多种CDN;如:正常情况下使用cdn加速,遇到攻击时切换安全防御高的CDN。一般仅加速的CDN价格比带防御的要便宜很多。

此处Enovy待更新,后端应用可根据CDN的http头正常获取IP

环境5:内部代理

无特殊需求可无需配置

Envoy:经过envoy代理后获取客户端真实IP的更多相关文章

  1. nginx获取经过层层代理后的客户端真实IP(使用正则匹配)

    今天帮兄弟项目搞了一个获取客户端真实IP的问题,网上这种问题很多,但是对于我们的场景都不太合用,现把我的解决方案share给大家,如有问题,请及时指出. 场景: 在请求到达后端服务之前,会经过层层代理 ...

  2. 多级nginx代理,获取客户端真实ip

    今天服务里的微信公众号支付业务突然不能用了,报错为网络环境未能通过安全验证,请稍后再试.检查后端日志,没有任何问题,看来是成功创建支付订单,但是调起支付时出现了问题.上网查了一下,这个报错的直接原因是 ...

  3. ngnix反向代理后获取用户真实ip及https配置

    server {listen 80;listen 802;server_name test111.xxxx.com 118.24.122.101; gzip on;gzip_min_length 10 ...

  4. JAVA获取客户端请求的当前网络ip地址(附:Nginx反向代理后获取客户端请求的真实IP)

    1. JAVA获取客户端请求的当前网络ip地址: /** * 获取客户端请求的当前网络ip * @param request * @return */ public static String get ...

  5. Nginx反向代理后应用程序获取客户端真实IP

    Nginx反向代理后,Servlet应用通过request.getRemoteAddr()取到的IP是Nginx的IP地址,并非客户端真实IP,通过request.getRequestURL()获取的 ...

  6. nginx 代理模式下,获取客户端真实IP

    最近做博友推荐,发现个小问题,用$_SERVER['REMOTE_ADDR'];得到的都是服务器的地址192.168.96.52,搜索了一下,发现问题,改为$_SERVER['HTTP_X_REAL_ ...

  7. Java Web 获取客户端真实IP

    Java Web 获取客户端真实IP 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP.一般分为两种情况: 方 ...

  8. 获取客户端真实IP地址

    Java-Web获取客户端真实IP: 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP. 一般分为两种情况: ...

  9. Java 获取客户端真实IP地址

    本文基于方法 HttpServletRequest.getHeader 和 HttpServletRequest.getRemoteAddr 介绍如何在服务器端获取客户端真实IP地址. 业务背景 服务 ...

随机推荐

  1. java例题_41 利用递归给猴子分桃

    1 /*41 [程序 41 猴子分桃] 2 题目:海滩上有一堆桃子,五只猴子来分.第一只猴子把这堆桃子平均分为五份,多了一个,这只猴子把 3 多的一个扔入海中,拿走了一份.第二只猴子把剩下的桃子又平均 ...

  2. mysql中FIND_IN_SET函数用法

    本篇文章主要介绍mysql中FIND_IN_SET函数用法,用来精确查询字段中以逗号分隔的数据 以及其与 like 和 in 的区别 1.问题发现 之前在做工作任务时有这么一个需求:需要用接口所传的服 ...

  3. 整合一套高性能网关Kong

    前言 相信大家对Api网关都比较的熟悉,我们之前的文章也介绍过ASP.NET Core的网关Ocelot,也介绍过Spring Cloud Gateway.说到网关的主要功能,其实总结起来就两个字&q ...

  4. XSS-change通关历程

    Level1:没有过滤. <script>alert(1)</script> <svg/onload=alert(1)> <script>confirm ...

  5. Hadoop完整搭建过程(一):本地模式

    1 本地模式 本地模式是最简单的模式,所有模块都运行在一个JVM进程中,使用本地文件系统而不是HDFS. 本地模式主要是用于本地开发过程中的运行调试用,下载后的Hadoop不需要设置默认就是本地模式. ...

  6. 强大的工具(一):Capslock+ 3.x版本

    1 概述 作者终于把Capslock+更新到了3.x版本了,所以就更了这篇博客. 2.x版本的可以戳这里. 3.x版本与2.x版本的主要不同是更新了新的键盘布局,更加顺手,下面让我们一起来看一下吧. ...

  7. 《TCP/IP网络编程》学习笔记整理

    简介 本笔记目前已包含 <TCP/IP网络编程>中的前 5 章,后续章节会在近期内补充完整. 我在整理笔记时所考虑的是:在笔记记完后,当我需要查找某个知识点时,不需要到书中去找,只需查看笔 ...

  8. xPath,beautifulsoup和pyquery

    一.XPath from lxml import etree html = etree.parse('html源代码',etree.HTMLPaser()) 1.节点的获取 a.html.xpath( ...

  9. 1075 PAT Judge

    The ranklist of PAT is generated from the status list, which shows the scores of the submissions. Th ...

  10. Think5之ajax批量删除数据功能

    //批量删除学员信息 public function deleteMany() { $id = input('post.'); //判断id是数组还是一个数值 if(is_array($id)){ f ...