Nginx 获取真实IP进行访问控制

326次阅读
没有评论

共计 2824 个字符,预计需要花费 8 分钟才能阅读完成。

背景

​​​Nginx 获取真实IP进行访问控制​​​

对于部分不能直接对公网暴露的敏感服务,我在下游 nginx 2 中使用 auth_basic​ 实现访问控制。

但是实际用起来频繁输入账号密码很烦人。希望在某些固定的场所访问时实现无需 auth_basic​访问控制。

例如当我在家里访问和在公司访问时,IP都是固定的。于是想到下述方案:

  • 首先判断访问IP来源
    • 若IP在受信任列表中,则不进行 auth_basic​ 校验,直接可访问。
    • 若IP不在受信任列表中,进行 auth_basic​ 校验,需要输入账号密码才可访问。

我在 nginx 2 中 添加了如下配置:

# cat ***.conf
    allow 1.1.1.1/32;
    allow 2.2.2.2/32;
    allow 192.168.0.0/16;
    satisfy any;
    auth_basic "Please input user and password.";
    auth_basic_user_file /etc/nginx/password;

这里假定 1.1.1.1​ 和 2.2.2.2​ 是受信任的外部公网IP(公司出口公网IP);192.168.0.0/16​ 是受信任的内网IP段(家庭内网CIDR)。

配置解释:

  • allow 1.1.1.1/32;​ 允许IP地址为 1.1.1.1 的请求访问。
  • allow 2.2.2.2/32;​ 允许IP地址在 2.2.2.2 网段范围内的请求访问。
  • allow 192.168.0.0/16;​ 允许IP地址为 192.168.0.0/16 的请求访问。
  • satisfy any;​ 表示只要满足任意一个allow条件,就允许访问,不需要进行身份验证。
  • auth_basic "Please input user and password.";​ 启用HTTP基本身份验证,并设置验证时的提示信息。
  • auth_basic_user_file /etc/nginx/password;​ 指定存储用户名和密码的文件路径。

然而配置完毕后验证效果却发现,在 1.1.1.1​ 和 2.2.2.2​ 发起请求仍需要经过 auth_basic​ 验证,而在 192.168.0.0/16​ 访问时不需要。

不符合预期,为什么 allow 1.1.1.1/32;​ 和 allow 2.2.2.2/32;​ 不生效呢?

思路

Nginx 的 access 模块在进行访问控制时,默认使用的是$remote_addr​变量,该变量表示客户端的真实IP地址。如果 Nginx服务器 前面有还代理或负载均衡,那么$remote_addr​取得的则是上一跳设备的IP。

查看日志

从 1.1.1.1 发起请求时,观察 nginx access.log​:

1.1.1.1 - https [22/May/2024:11:06:17 +0800] "GET /_next/static/chunks/app/(main)/(mobile)/me/profile/page-8d73e05285eddb8b.js HTTP/1.1" 200 4148 "https://***.opshub.cn/sw.js" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" "1.1.1.1" "***.opshub.cn" "192.168.2.11:3210" "-" 0.002 0.003

可以看到日志中的确是获取到了 1.1.1.1 IP,那为什么还需要经过 auth_basic​ 验证?

查看日志格式相关配置:

    log_format  main  '$http_x_real_ip - $scheme [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$host" "$upstream_addr" "$upstream_cache_status" $request_time $upstream_response_time';

到这儿我们可以发现,1.1.1.1 是 $http_x_real_ip​ 的值。

我们将日志配置中的 $http_x_real_ip​ 改为 $remote_addr​ 后,再次观察日志:

1.2.3.4 - https [22/May/2024:11:07:19 +0800] "GET /_next/static/chunks/app/(main)/(mobile)/me/profile/page-8d73e05285eddb8b.js HTTP/1.1" 200 4148 "https://***.opshub.cn/sw.js" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" "1.2.3.4" "***.opshub.cn" "1.2.3.41:3210" "-" 0.002 0.003

可以看到 IP 由 1.1.1.1 变为了 1.2.3.4。

到这儿就问题已经比较明显,在下游 nginx2 中使用 allow​ 进行访问控制时,默认使用的是$remote_addr​变量进行校验。

由于 $remote_addr​ 取得上一跳设备 nginx1 的IP:1.2.3.4​,所以不会匹配上 allow 1.1.1.1/32;​。

解决方案

$http_x_real_ip​ 才是客户端的真实IP,在 nginx 1 中已经正确配置将其了传递到 nginx2 中。

站内文章:Nginx 准确获取真实IP

nginx 1 配置:

proxy_set_header X-Real-IP  $remote_addr;

那么现在需要在 nginx2 中,使用 ngx_http_realip_module​ 模块来获取并替换 $remote_addr​ 变量的值为真实的客户端 IP。

nginx 2 配置:

    set_real_ip_from  1.2.3.4/32;
    real_ip_header    X-Real-IP;

    allow 1.1.1.1/32;
    allow 2.2.2.2/32;
    allow 192.168.0.0/16;
    satisfy any;
    auth_basic "Please input user and password.";
    auth_basic_user_file /etc/nginx/password;

配置解释:

  • set_real_ip_from 1.2.3.4/32;​ 用于指定可信的 IP 地址或 CIDR 范围,以确保只有受信任的上游 nginx1 服务器可以设置 X-Real-IP​ 请求头。
  • real_ip_header X-Real-IP;​ 将 $remote_addr​ 变量的值设置为 X-Real-IP​ 请求头的值。

配置完毕后,效果符合预期。

本文属于专题:Nginx

引用链接

正文完
 
pengyinwei
版权声明:本站原创文章,由 pengyinwei 2024-05-22发表,共计2824字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处:https://www.opshub.cn
评论(没有评论)