问题: 通过nps或者frp访问内网服务时获取不到客户端的真实ip

解决思路: frp支持Proxy Protocol, 但是nps不支持, nginx支持Proxy Protocol

1.使用frp, 内网服务通过nginx或者雷池代理

2.使用nps, 由于nps不支持Proxy Protocol, 需要在前面加一层haproxy, 内网服务通过nginx或者雷池代理

以上

使用frp

在公网服务器部署frps, 在内网服务器部署frpc, 假设公网的ip为1.1.1.1,内网ip为10.10.0.2

目标: 通过公网服务器的443端口访问内网的10.10.0.2:7070应用

20240223截屏2024022316.35.54.png

frp配置文件示例

公网frps配置

bindPort = 7000

webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "xxx"
webServer.password = "xxx"

内网frpc配置

serverAddr = "1.1.1.1"
serverPort = 7000

webServer.addr = "0.0.0.0"
webServer.port = 7400
webServer.user = "xxx"
webServer.password = "xxx"
webServer.pprofEnable = false

[[proxies]]
name = "https"
type = "tcp"
localIP ="10.10.0.2"
localPort = 8443 # 该端口为雷池或nginx的应用端口
remotePort = 443 # 该端口为公网frps监听端口
transport.proxyProtocolVersion = "v2" # 开启Proxy Protocol,这是获取客户端真实ip的关键

雷池或nginx配置示例

由于雷池界面没有开启Proxy Protocol的功能, 需要手动修改配置文件

前往雷池的nginx目录: ~/safeline/resources/nginx/sites-enabled/

其中每个IF_backend_*文件都要修改

以下是一个文件的示例, 其他文件类似

只需在server中加上注释的4点即可, 其他nginx应用同理

upstream backend_9 {
    server 10.10.0.2:7070;
    keepalive 128;
    keepalive_timeout 75;
}
map $scheme $hsts_header {
        https   "max-age=5184000";
}
server {
    # 1.这里加上proxy_protocol
    listen 0.0.0.0:8443 ssl http2 proxy_protocol; 
    server_name xx.xx.com;
    ssl_certificate /etc/nginx/certs/cert_3.crt;
    ssl_certificate_key /etc/nginx/certs/cert_3.key;

    # 2.排除本地ip
    set_real_ip_from 192.168.0.0/24;
    set_real_ip_from 10.0.0.0/24;
    # 3.开启排除IP功能
    real_ip_recursive on;
    # 4.真实IP使用proxy_protocol协议
    real_ip_header proxy_protocol;

    ...省略
}

问题: 雷池目前是不支持自定义server内容的, 每次重启配置文件都会重置, 而且添加的网站很多的话, 修改起来也比较麻烦

解决办法: 使用脚本一键修改所有IF_backend_*文件

在目录~/safeline/resources/nginx/sites-enabled/下新建脚本modify.sh

#!/bin/bash

# 修改文件内容在`ssl http2`后面加上` proxy_protocol`, 在`gateway_timeout_page;`下一行加上4行内容:
# \n```
# set_real_ip_from 192.168.0.0/16;
# set_real_ip_from 10.0.0.0/8;
# real_ip_recursive on;
# real_ip_header proxy_protocol;
# ```
find ./ -name "IF_backend_*" | while read filename; do

  if grep -q "proxy_protocol" "$filename"; then
    echo "文件 $filename 已包含 proxy_protocol,无需修改。"
  else

    # 解锁
    chattr -i "$filename"

    sed -i 's/ssl http2/ssl http2 proxy_protocol/g' "$filename"  # 有换行
    sed -i '/gateway_timeout_page;/a \
    set_real_ip_from 192.168.0.0/16;\
    set_real_ip_from 10.0.0.0/8;\
    real_ip_recursive on;\
    real_ip_header proxy_protocol;' "$filename"  # 有换行
  fi

  # 为了避免雷池重启后配置文件被重置需要锁定文件
  chattr +i "$filename"
done

docker exec -it safeline-tengine nginx -t

docker exec -it safeline-tengine nginx -s reload

上面说的4点配置通过脚本一键修改, 修改后使用chattr +i锁住文件,防止文件被修改, 在雷池上修改或者添加站点时记得解锁哦

在目录~/safeline/resources/nginx/sites-enabled/下新建解锁脚本unlock.sh

#!/bin/bash

find ./ -name "IF_backend_*" | while read filename; do
    # 解锁
    chattr -i "$filename"
done

在雷池修改或添加站点之前运行unlock.sh脚本, 修改完后运行modify.sh脚本

总结

通过在frp上开启Proxy Protocol, 和在nginx开启Proxy Protocol即可在应用中通过header里的X-Real-IP获取客户端的真实IP

使用nps

在公网服务器部署haproxynps, 在内网服务器部署npc, 假设公网的ip为1.1.1.1,内网ip为10.10.0.2

目标: 通过公网的443端口访问内网的10.10.0.2:7070应用

20240223截屏2024022316.40.16.png

haproxy配置示例

安装haproxy

  • ubuntu/debian
sudo apt update && apt upgrade && apt install haproxy
  • centos
yum install epel-release -ysudo yum update -y && yum install haproxy -y

编辑haproyx的配置文件vim /etc/haproxy/haproxy.cfg

在配置文件的最下方加上:

其中443是haproxy监听端口, 4545是nps TCP隧道的服务端端口

listen web
 bind 0.0.0.0:443
 mode tcp
 option forwardfor
 server web1 localhost:4545 send-proxy check inter 3000 fall 3 rise 5

完整配置:

global
	log /dev/log	local0
	log /dev/log	local1 notice
	chroot /var/lib/haproxy
	stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
	stats timeout 30s
	user haproxy
	group haproxy
	daemon

	# Default SSL material locations
	ca-base /etc/ssl/certs
	crt-base /etc/ssl/private

	# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
	log	global
	mode	http
	option	httplog
	option	dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
	errorfile 400 /etc/haproxy/errors/400.http
	errorfile 403 /etc/haproxy/errors/403.http
	errorfile 408 /etc/haproxy/errors/408.http
	errorfile 500 /etc/haproxy/errors/500.http
	errorfile 502 /etc/haproxy/errors/502.http
	errorfile 503 /etc/haproxy/errors/503.http
	errorfile 504 /etc/haproxy/errors/504.http

listen web
 bind 0.0.0.0:1443
 mode tcp
 option forwardfor
 server web1 localhost:444 send-proxy check inter 3000 fall 3 rise 5

nps配置

20240223截屏2024022316.47.49.png

雷池或nginx配置示例

同上