Envoy:经过envoy代理后获取客户端真实IP
在envoy作为前端代理时,用户ip的获取很重要,一般获取ip的方式。都是通过Header中的 X-Forward-For、 X-Real-IP或 Remote addr 等属性获取,但是如果确保Envoy可以获取到的ip是真实的用户ip呢?本篇继续解密!
概念说明
Remote Address
是nginx与客户端进行TCP连接过程中,获得的客户端真实地址。Remote Address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求。
一般情况下,在Envoy作为最外层代理时,此IP为真实的IP客户端IPX-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的更多相关文章
- nginx获取经过层层代理后的客户端真实IP(使用正则匹配)
		
今天帮兄弟项目搞了一个获取客户端真实IP的问题,网上这种问题很多,但是对于我们的场景都不太合用,现把我的解决方案share给大家,如有问题,请及时指出. 场景: 在请求到达后端服务之前,会经过层层代理 ...
 - 多级nginx代理,获取客户端真实ip
		
今天服务里的微信公众号支付业务突然不能用了,报错为网络环境未能通过安全验证,请稍后再试.检查后端日志,没有任何问题,看来是成功创建支付订单,但是调起支付时出现了问题.上网查了一下,这个报错的直接原因是 ...
 - ngnix反向代理后获取用户真实ip及https配置
		
server {listen 80;listen 802;server_name test111.xxxx.com 118.24.122.101; gzip on;gzip_min_length 10 ...
 - JAVA获取客户端请求的当前网络ip地址(附:Nginx反向代理后获取客户端请求的真实IP)
		
1. JAVA获取客户端请求的当前网络ip地址: /** * 获取客户端请求的当前网络ip * @param request * @return */ public static String get ...
 - Nginx反向代理后应用程序获取客户端真实IP
		
Nginx反向代理后,Servlet应用通过request.getRemoteAddr()取到的IP是Nginx的IP地址,并非客户端真实IP,通过request.getRequestURL()获取的 ...
 - nginx 代理模式下,获取客户端真实IP
		
最近做博友推荐,发现个小问题,用$_SERVER['REMOTE_ADDR'];得到的都是服务器的地址192.168.96.52,搜索了一下,发现问题,改为$_SERVER['HTTP_X_REAL_ ...
 - Java Web 获取客户端真实IP
		
Java Web 获取客户端真实IP 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP.一般分为两种情况: 方 ...
 - 获取客户端真实IP地址
		
Java-Web获取客户端真实IP: 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP. 一般分为两种情况: ...
 - Java 获取客户端真实IP地址
		
本文基于方法 HttpServletRequest.getHeader 和 HttpServletRequest.getRemoteAddr 介绍如何在服务器端获取客户端真实IP地址. 业务背景 服务 ...
 
随机推荐
- Android控件 之 TextClock & AnalogClock(模拟时钟)
			
TextClock •简介 关于时间的文本显示,Android 提供了 DigitalClock 和 TextClock. DigitalClock是Android第1版本发布,功能很简单,只显示时间 ...
 - Python 实现自动化 Excel 报表
			
Py 实现自动化Excel报表 好几个月没有写笔记了, 并非没有积累, 而是有点懒了. 想想还是要续上, 作为工作成长的一部分哦. 最近有做一些报表, 但一直找不到一个合适的报表工具, 又实在不想写前 ...
 - APP | edxposed框架+trustmealredy模块抓包小程序
			
出品|MS08067实验室(www.ms08067.com) 本文作者:ketchup(Ms08067实验室 SRSP TEAM小组成员) 一.下载edxposed框架,由于安卓5.0版本以下的不支持 ...
 - 亲测有效JS中9种数组去重方法
			
码文不易,转载请带上本文链接,感谢~ https://www.cnblogs.com/echoyya/p/14555831.html 目录 码文不易,转载请带上本文链接,感谢~ https://www ...
 - 「HTML+CSS」--自定义加载动画【014】【疑问未解决】
			
前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...
 - Java后端进阶-网络编程(Netty线程模型)
			
前言 我们在使用Netty进行服务端开发的时候,一般来说会定义两个NioEventLoopGroup线程池,一个"bossGroup"线程池去负责处理客户端连接,一个"w ...
 - 并发编程(共享模型之管程wait notify)
			
本文主要讲解wait/notify的正确使用姿势.park/unpark.join()的原理.模式之生产者-消费者模式(异步).保护性暂停模式(同步).线程状态转换的流程.死锁和活锁以及如何检查死锁等 ...
 - Centos6无法使用yum解决办法
			
12月后Centos 6 系统无法使用yum出现错误(文章底部看) 相信已经有一部分朋友今天连接到CentOS 6的服务器后执行yum后发现报错,那么发生了什么? CentOS 6已经随着2020年1 ...
 - 如何查看显著性SNP在数据中的频率?
			
我们做完GWAS的关联分析后需要查看显著性SNP在我们数据中的频率分布情况.这时候我们需要用到plink和我们做关系分析所用的二进制文件datas. 第一步,我们用R语言读取分析结果,即*.assoc ...
 - 图解高性能网络架构:Reactor 和 Proactor
			
小林,来了. 这次就来图解 Reactor 和 Proactor 这两个高性能网络模式. 别小看这两个东西,特别是 Reactor 模式,市面上常见的开源软件很多都采用了这个方案,比如 Redis.N ...