需求背景:
研发发了一张redis查看信息截图,说sdk服务没有获取到用户真实ip
他想要能获取到用的真实请求ip
sdk服务访问架构
思路:
这里犯了致命失误 ,没有问清楚,上面那个redis信息是从那个代码上获取,怎么获取到的,虽然在语言方面运维天然劣势,不过可以问研发嘛,那些代码是干什么作用的,如果不去当面问清楚,恐怕自以为解决后但实质研发的问题并没有解决会让你的研发伙伴感觉你是不专业的,后面了解到研发通过获取请求头信息中的REMOTE_ADDR来作为用户真实请求ip存入redis中的,那么我们想下是不是研发拿错了请求头信息了,因为一般remote_addr只是代表服务上一层客户端的ip地址,而请求头信息中的X-Forwarded-For才会有包含客户端真实请求ip,为了验证这个想法
第一步:需要准备一个测试域名,服务器架构要和正式sdk服务架构一致
第二步:需要一个假的sdk http服务,使用django 中request.META去获取全部的请求头信息
演示1:
在不了解清楚的情况下,我是直接通过nginx 启用 ngx_http_realip_module 模块来尝试解决这个问题
nginx -V |grep http_realip_module # 尝试查看nginx配置是否有这个模块
如果没有这个模板的话,因为我nginx是编译安装的,所以我这里直接重新编译,如果nginx其他方式安装的话,请自行搜索解决方法
cd tengine-2.3.3/ # 切换到软件下载目录
./configure --with-stream --with-http_realip_module
make && make install
/usr/local/nginx/sbin/nginx -V # 查看realip模板是否安装好了
然后编辑nginx配置文件
# 启用 ngx_http_realip_module 模块
real_ip_header X-Forwarded-For;
set_real_ip_from IP_address;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
log_format nbwl_access '$remote_addr – $remote_user [$time_local] "$request" "$request_body" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"url=$http_host" "rt=$request_time" "us=$upstream_status" "uaddr=$upstream_addr" "urt=$upstream_response_time"';
access_log /data/logs/nginx/access.log nbwl_access;
加入以上配置
我们通过设置 real_ip_header
指令来告诉 Nginx 使用 X-Forwarded-For
请求头来获取真实 IP 地址。通常,代理服务器会将客户端的真实 IP 地址放置在 X-Forwarded-For
请求头中。
然后,我们使用 set_real_ip_from
指令指定允许从任何 IP 地址获取真实 IP。你可以根据需要进行适当的配置,以限制允许获取真实 IP 的 IP 地址范围,比如负载均衡ip地址段和高防ip地址段,多个ip地址段用逗号分隔
proxy_set_header 这个很好就是设置头信息, X-Real-IP remote_addr
修改完成后重载nginx
nginx -t # 查看配置语法是否正常
nginx -s reload # 重载
上述步骤可以提前现在测试环境中测试
然后我通过观察日志文件/data/logs/nginx/access.log,发现第一个列就是客户端真实请求ip,然后我就跟研发说,这个问题处理好了,结果研发试了,根本没好,因为此时我并不知道研发是从哪里获取到的用户真实ip
演示2:
按照上面思路的提示,我需要提前准备一个sdk测试域名,服务架构和正式sdk服务搞一样的,需要高防接入域名,添加dns解析,sdk服务器中添加nginx配置,还需要提前准备一个假的sdk服务(用django编写的)部署到sdk服务器上去
第一步:
本地用pycharm新建一个django项目
第二步:
修改项
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'index',
]
DEBUG = False
ALLOWED_HOSTS = ["*", ]
from django.contrib import admin
from django.urls import path
from index import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
]
在项目根目录下创建index包
from django.http import HttpResponse
def index(request): # 定义视图函数index()
s = ""
for k, v in request.META.items():
s += "{}:{}\n\n".format(k, v)
response = HttpResponse(content=s, content_type='text/plain')
return response
生成requirements.txt依赖包文件
第三步:将上面的项目目录给打包,上传到sdk测试服务器上
第四步:使用uwsgi启动项目
uwsgi是一个快速的,纯c语言开发的,自维护的,对开发者友好的wsgi服务器,旨在实现专业的python web应用开发和发布
unzip djangoProject.zip # 解压压缩包
mv djangoProject /var/www/ # 放入/var/www目录下
cd /var/www/djangoProject/ # 切入目录下
pip3 install virtualenv -i https://pypi.tuna.tsinghua.edu.cn/simple # 安装virtualenv
virtualenv venv # 生成虚拟环境目录
source venv/bin/activate # 激活虚拟环境
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 安装依赖包
pip install uwsgi # 安装uwsgi
ln -s /var/www/djangoProject/venv/bin/uwsgi /usr/bin/uwsgi
配置uwsgi配置文件
[uwsgi]
http=0.0.0.0:8001
chdir=/var/www/djangoProject
wsgi-file=/var/www/djangoProject/djangoProject/wsgi.py
uid=root
gid=root
master=true
processes=4
buffer-size=65535
vacuum=true
pidfile=/var/www/djangoProject/uwsgi.pid
daemonize=/var/www/djangoProject/uwsgi.log
启动项目
uwsgi --ini /var/www/djangoProject/uwsgi.ini
本地测试
curl http://localhost:8001/index/
添加nginx配置文件
然后将测试域名接入高防,添加dns解析,负载均衡进行80配置转发,就不多说了
第五步:通过访问测试域名/index 获取所有的请求头信息
通过头信息得知,要获取客户端真实ip的话可以拿HTTP_X_REAL_IP或HTTP_X_FORWARDED_FOR列表左边第一个值就是真实ip
不能拿REMOTE_ADDR这个值作为真实ip
解决
了解到研发从请求头REMOTE_ADDR这个值作为真实ip,我通过上面测试域名得到的结论建议研发去取HTTP_X_REAL_IP这个头部信息的值,如果这个头部信息的值没有的话,可以拿HTTP_X_FORWARDED_FOR ip列表第一个值就是真实ip,最后搞定了