探讨4层代理和7层代理行为以及如何获取真实客户端IP

准备工作

实验环境

IP角色
192.168.1.100客户端请求IP
192.168.1.100python 启动的HTTP服务
192.168.1.102nginx服务
192.168.1.103haproxy 服务

HTTP服务

这是一个简单的HTTP服务,主要打印HTTP报文用于分析客户端IP

#!/usr/bin/env python
# coding: utf-8

import socket
from threading import Thread

# 创建socket对象
sock_srv = socket.socket()

# 绑定IP和port
sock_srv.bind(('0.0.0.0', 5001))

# 开启服务
sock_srv.listen()

# 定义一个函数, 处理来自客户端链接的处理
def socket_deal(conn: socket.socket, address: tuple):
	# 通过socket获取客户端的IP; 这里的客户端IP其实指的是TCP报文中的原始IP和原始Port
	# 就是上一个发起TCP发起的地址 
    print(address)

    # 打印HTTP的报文
    print(conn.recv(1024).decode())

    # 不做特殊处理,所有的请求均返回Hello Word
    template = """
HTTP/1.1 200 OK
Service: HTTP
Version: 1.1.2.2

<h1>hello word</h1>
    """
    conn.send(template.encode())

    # 关闭此次HTTP的请求
    conn.close()


while True:
    # 接受Client的数据请求
    conn, address = sock_srv.accept()
    Thread(target=socket_deal, args=(conn, address)).start()

Nginx报文分析

nginx 4层代理

  • 配置启动4层代理, 并请求 http://192.168.1.102 并观察 101 的请求信息
stream {
	server {
	  listen 80 ;
	  proxy_pass 192.168.1.100:5001;  		# Python的HTTP服务
	  # proxy_protocol on;					# 可选性, 4层携带真实IP
	}
}
  • 客户端请求4层转发,默认不传递客户端IP。
# print(address), 可以从socket得到客户端IP
('192.168.1.102', 52842)

# 得到HTTP的报文信息如下
GET / HTTP/1.1
Host: 192.168.1.102
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
  • 客户端请求4层转发,要求传递客户端IP。
# print(address), 可以从socket得到客户端IP
('192.168.1.102', 52848)

# 得到HTTP的报文信息如下,多了一行PROXY。其余信息不变
PROXY TCP4 192.168.1.100 192.168.1.102 55360 80
GET / HTTP/1.1
Host: 192.168.1.102
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

nginx 7层代理

  • 配置文件
server {                                                                           
    listen       80;                                                               
    server_name  localhost;                                                        
                                                                                   
                                                                                   
    location / {                                                                   
        proxy_pass http://192.168.1.100:8000/;                                                                         
        # proxy_set_header X-Forwarded-For $remote_addr;   # nginx 去掉注释请求携带客户端真实IP                                
    }                                                                              
                                                                                   
}
  • 客户端请求7层代理,默认不传递客户端IP真实IP。
# print(address), 可以从socket得到客户端IP
('192.168.1.102', 52854)

# 得到HTTP的报文信息如下.
GET / HTTP/1.0
Host: 192.168.1.100:5001
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
  • 客户端请求7层,要求传递客户端IP真实IP。
# print(address), 可以从socket得到客户端IP
('192.168.1.102', 52858)

# 得到HTTP的报文信息如下,多了一行PROXY。其余信息不变
GET / HTTP/1.0
Host: 192.168.1.100:5001
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
X-Forwarded-For: 192.168.1.100

分析NGINX获取客户端IP方法

方式修改header信息是否修改原始报文获取客户端IP方式
4层转发不带客户端IP不修改不修改服务端可以获取上一层发起请求的socket 源IP, 但不是客户端真实IP
4层转发携带客户端IP不修改原始报文前增加PROXY格式内容通过PROXY内容可以获取客户端IP
7层转发不带客户端IP不修改nginx重新封装HTTP报文服务端 socket 获取
7层转发携带客户端IPnginx 通过增加header信息传递客户端IPnginx重新封装HTTP报文通过nginx封装的报文获取XFF
  1. 7层代理会对报文进行重新封装,封装过程中可以通过增加XFF的header传递客户端IP。

  2. 4层转发不会修改报文。在不修改HTTP报文前提下,前置补充代理信息, 格式: PROXY TCP 客户端IP 代理端IP 客户端端口 代理端端口

Haproxy 代理分析

Haproxy 4层代理

  • 配置
defaults
    mode                    tcp

frontend  main *:80
    default_backend             app

backend app
    balance     roundrobin
    server  app1 192.168.1.100:5001 check # 不携带真实IP
    # server  app1 192.168.1.100:5001 send-proxy check  # 携带真实IP
  • 客户端请求4层转发,默认不要求传递客户端IP
# print(address) 获取客户端的address信息
('192.168.1.103', 56790)

# 这个信息和NGINX 4层信息一样
GET / HTTP/1.1
Host: 192.168.1.103
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
  • 客户端请求4层转发,要求传递客户端IP
# print(address) 获取客户端的address信息
('192.168.1.103', 58410)

# 与Nginx 4层带真实IP一样, 报文之前增加了PROXY信息
PROXY TCP4 192.168.1.100 192.168.1.103 57871 80
GET / HTTP/1.1
Host: 192.168.1.103
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

Haproxy 7层代理

  • 配置
defaults
    mode                    http
    option forwardfor       except 127.0.0.0/8   # 默认携带客户端IP,

frontend  main *:80
    default_backend             app

backend app
    balance     roundrobin
    server  app1 192.168.1.100:5001 check 
  • 客户端请求7层代理,默认传递客户端IP
# print(address) 获取客户端的address信息
('192.168.1.103', 53178)

# 与Nginx 7层带客户端IP一样, 报文包含X-Forwarded-For
GET /favicon.ico HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Referer: http://192.168.1.103/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
X-Forwarded-For: 192.168.1.100
Connection: close
  • 客户端请求7层代理,注释不传递客户端IP
# print(address) 获取客户端的address信息
('192.168.1.103', 53576)

# 与Nginx 7层不传递客户端IP一样, 报文包含没有X-Forwarded-For
GET / HTTP/1.1
Host: 192.168.1.103
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

分析Haproxy获取客户端IP方法

其实和nginx一样

方式修改header信息是否修改原始报文获取客户端IP方式
4层转发不带客户端IP不修改不修改服务端可以获取上一层发起请求的socket 源IP, 但不是客户端真实IP
4层转发携带客户端IP不修改原始报文前增加PROXY格式内容通过PROXY内容可以获取客户端IP
7层转发不带客户端IP不修改nginx重新封装HTTP报文服务端 socket 获取
7层转发携带客户端IPnginx 通过增加header信息传递客户端IPnginx重新封装HTTP报文通过nginx封装的报文获取XFF

案例小结

上述操作主要是完成: Nginx和Haproxy两款服务分别完成: 4层转发和7层代理。 携带IP与不携带客户端IP配置上的区别和报文展示

转发方式传递方式特点
7层代理增加HTTP报文的header信息X-Forwarded-For, 进行传递客户端IP在原始报文进行修改
4层代理在HTTP报文前方附加一层PROXY信息, 进行传递客户端IP不修改原始报文,在报文前方附加数据

在这里插入图片描述

附伪代码获取客户端IP

#!/usr/bin/env python
# coding: utf-8
import socket
from threading import Thread

# 创建socket对象
sock_srv = socket.socket()

# 绑定IP和port
sock_srv.bind(('0.0.0.0', 5001))

# 开启服务
sock_srv.listen()


# 简单的Response结构
def response(content, status, msg, conn):
    template = """
HTTP/1.1 %d %s
Service: HTTP
Version: 1.1.2.2
Content-Type: text/html; charset=UTF-8
Connection: close

%s  
    """ % (status, msg, content)
    return conn.send(template.encode())


# 定义一个函数, 处理来自客户端链接的处理
def socket_deal(conn: socket.socket, address: tuple):
    try:
        # 通过socket获取客户端的IP; 这里的客户端IP其实指的是TCP报文中的原始IP和原始Port
        # 就是上一个发起TCP发起的地址
        _client_ip, _client_port = address
        data = conn.recv(1024).decode()

        # 打印HTTP的报文
        _data_lines = data.splitlines()

        # 代理模式
        _proxy_type = "可能HTTP代理"

        # header 信息收集
        extend_data = ["header信息", ]
        for line in _data_lines:

            # 如果4层传递客户端IP,会得到如下信息。
            if line.startswith('PROXY'):
                # PROXY TCP4 192.168.1.100 192.168.1.102 55360 80
                _, protocol, _c_ip, _p_ip, _c_port, _p_port = line.split()
                if protocol != 'TCP4':
                    response("PROXY ERROR", 500, "PROXY_ERROR", conn)
                else:
                    _proxy_type = "TCP代理"
                    _client_ip = _c_ip
                    # 有时候报文只收到PROXY信息, 就需要第二次接收报文信息
                    if len(_data_lines) == 1:
                        socket_deal(conn, (_c_ip, _c_port))
                        return
                    # 如果一次性收完报文信息则继续处理
                    else:
                        continue

            if ":" not in line:
                # 不是K:V 形式,那不是header。 可能是post数据, 也可能是HTTP协议。 此处忽略
                continue

            # 拿到header信息
            header_key, header_value = [item.strip() for item in line.split(":", 1)]
            if header_key == 'X-Forwarded-For':
                _client_ip = header_value

            # header 信息入库
            extend_data.append(":".join((header_key, header_value)))

        content = ["真实的客户端IP可能是" + _client_ip, "<br />"]
        content.extend(extend_data)
        response("<br />".join(content), 200, 'ok', conn)
        
    except Exception as e:
        print(e)
    finally:
        # 关闭此次HTTP的请求
        conn.close()


while True:
    # 接受Client的数据请求
    conn, address = sock_srv.accept()
    Thread(target=socket_deal, args=(conn, address)).start()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/770496.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

地理信息科学:生态保护的智慧经纬

在地球这颗蓝色星球上&#xff0c;每一片森林的呼吸、每一条河流的流淌&#xff0c;都是生命交响曲中不可或缺的音符。而地理信息科学&#xff08;GIS&#xff09;&#xff0c;正是我们手中解读自然密码、护航生态平衡的精密仪器。今天&#xff0c;让我们深入探讨GIS如何在生物…

新加坡博士申请|中国社科院-新加坡社科大学联合培养工商管理博士

新加坡博士申请|中国社科院-新加坡社科大学联合培养工商管理博士 【项目名称】中国社会科学院大学与新加坡新跃社科大学工商管理博士项目 【学制】最短3年&#xff0c;最长不超过7年 【学位证书】新加坡新跃社科大学工商管理博士学位 【招生对象】企业高管、咨询顾问及其他有…

智能舌诊应用开发:结合通义千问与OpenAI库

项目介绍 所有的项目都是基于 TailwindCSS 实现了响应式&#xff0c;同时支持网页端和移动端的显示效果。 这期尝试开发的 AI 应用是使用通义千问的大模型 API&#xff0c;开发一个 AI 看舌苔的应用。 整个项目的操作流程比较简单&#xff0c;第一屏用户上传自己的舌头的照片…

【JavaWeb程序设计】页面编程

目录 一、使用divCSS实现页面的布局 1. HTML结构代码 2. CSS样式代码 3. 运行截图 二、使用各类标签制作一个静态页面 1. 我做的页面运行截图 2. HTML结构代码 3. CSS代码 一、使用divCSS实现页面的布局 以下代码实现如图的页面布局&#xff0c;请完善相关代码 1. HT…

Docker的架构原理

例子可以想象成一个买手机的场景 clien可以想象 你个人 docker deamon &#xff1a;店员 images&#xff1a; 样机 regisitry&#xff1a; 手机仓库 container: 使用的手机 首先我要在店员买一个手机&#xff0c;店员发现是样机&#xff0c;但是仓库有&#xff0c;&…

SwiftUI九创建watchOS应用

代码下载 这篇教程让可以应用之前所学到的SwiftUI知识&#xff0c;把Landmarks应用从iOS平台迁移到watchOS平台上。在拷贝可以共用的数据和视图文件之前&#xff0c;需要先给项目中添加一个对应watchOS的Target编译目标。在所有 assets 就绪后&#xff0c;将自定义SwiftUI视图…

【TS】TypeScript 中的 any 与 unknown:理解与实践

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 TypeScript 中的 any 与 unknown&#xff1a;理解与实践一、引言二、any&#x…

找不到msvcr110.dll是怎么回事?彻底解决msvcr110.dll丢失的方法

当您的电脑提示遇到msvcr110.dll丢失时&#xff0c;您知道如何解决此问题吗&#xff1f;事实上&#xff0c;解决此类dll文件丢失的问题相对较为简单。只要我们深入了解msvcr110.dll丢失的具体情况&#xff0c;便可轻松解决此问题。以下为您介绍msvcr110.dll修复方法。 一&#…

vue table表格 ( parseTime-格式化时间)

<el-table-column label"发布时间" width"420px" prop"bidPublishDatetime"><template slot-scope"scope"><span>{{ parseTime(scope.row.bidPublishDatetime, {y}-{m}-{d}) }}</span></template></…

从手工到智能:乐财业鹦鹉系统引领财税管理新纪元

随着税制改革的不断施行&#xff0c;我国数字经济顶层设计与地方推进举措相结合的政策体系已基本完善。中小企业的数字化转型尤为关键&#xff0c;也是大势所趋。 精益研发 数智创新 乐财业以敢于创新的精神&#xff0c;扎根在财税行业数字化的土壤&#xff0c;历时3年的精心研…

海豚调度监控:新增依赖缺失巡检,上游改动再也不用担心了!

&#x1f4a1; 本系列文章是 DolphinScheduler 由浅入深的教程&#xff0c;涵盖搭建、二开迭代、核心原理解读、运维和管理等一系列内容。适用于想对 DolphinScheduler了解或想要加深理解的读者。 祝开卷有益:) 用过 DolphinScheduler 的小伙伴应该都知道&#xff0c;Dolphin…

SpringBoot整合DataX数据同步(自动生成job文件)

SpringBoot整合Datax数据同步 文章目录 SpringBoot整合Datax数据同步1.简介设计理念 DataX3.0框架设计DataX3.0核心架构核心模块介绍DataX调度流程 2.DataX3.0插件体系3.数据同步1.编写job的json文件2.进入bin目录下&#xff0c;执行文件 4.SpringBoot整合DataX生成Job文件并执…

【Linux】目录和文件的权限意义

现在我们知道了Linux系统内文件的三种身份&#xff08;拥有者、用户组与其他人&#xff09;&#xff0c;知道每种身份都有三种权限&#xff08;rwx&#xff09;&#xff0c;也知道能够使用chown、chgrp、chmod修改这些权限与属性&#xff0c;当然&#xff0c;利用IS-l去查看文件…

一文了解“大数据招商思维”,读懂什么是大数据招商!

近年来&#xff0c;随着大数据及人工智能等新一代信息技术的快速发展&#xff0c;数据作为重要的资源和资产&#xff0c;成为推动经济发展的核心驱动力&#xff0c;广泛应用于各个领域&#xff0c;深刻的改变着我们的生产和生活方式。那么对于“招商引资”来说&#xff0c;大数…

超级加密狗——CBS(赛博锁)

智能终端设备安全现状&#xff1a; 随着网络和智能终端普及&#xff0c;云管端的智能物联应用越来越多&#xff0c;如何保证云端平台安全&#xff0c;以及各种智能终端&#xff08;含智能仪器&#xff0c;车载终端、智能摄像头、工控机、网关路由器、智能设备、 IoT设备等&…

3D模型格式转换工具HOOPS Exchange如何实现对PRC文档的支持?

随着三维模型在各个行业中的应用越来越广泛&#xff0c;高效、准确的3D模型格式转换工具变得尤为重要。在众多工具中&#xff0c;HOOPS Exchange因其强大的功能和广泛的格式支持赢得了用户的青睐。本文将详细探讨HOOPS Exchange如何实现对PRC&#xff08;Product Representatio…

XLSX + LuckySheet + LuckyExcel实现前端的excel预览

文章目录 功能简介简单代码实现效果参考 功能简介 通过LuckyExcel的transformExcelToLucky方法&#xff0c; 我们可以把一个文件直接转成LuckySheet需要的json字符串&#xff0c; 之后我们就可以用LuckySheet预览excelLuckyExcel只能解析xlsx格式的excel文件&#xff0c;因此对…

封装stater时配置导入配置类提示功能

提示功能如下 使用注解导入配置属性时添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>

Element中的选择器组件Select (一级选择组件el-select)

简述&#xff1a;在 Element UI 中&#xff0c;ElSelect&#xff08;或简称为 Select&#xff09;是一个非常常用的选择器组件&#xff0c;它提供了丰富的功能来帮助用户从一组预定义的选项中选择一个或多个值。这里来简单记录一下 一. 组件和属性配置 <el-selectv-model&q…

为什么说牛企查查企业超好用?

步入职场的职场人士&#xff0c;经济相关专业的学生&#xff0c;都有查企业的需求&#xff0c;市面上查企业的软件平台那么多&#xff0c;每个功能都不怎么一样。 有的便宜&#xff0c;但是信息不全。有的信息还可以&#xff0c;但是会员费又很贵&#xff0c;让我这个打工人没…