当前位置: 首页>后端>正文

外网域名映射到nginx nginx外网转内网

背景:
家里的 荣耀路由器 Pro 2 的 NAT 配置限制非常多,不能手动指定 IP 地址,只能选择设备。但是,这个设备还不能是路由器!
所以,我不能将请求路由给 LAN 口上的子路由器,导致子路由器上连接的设备无法从外部访问。
因此,我将内网中的树莓派4配置为 DMZ 主机,打算通过 iptables, nginx 等手段作为路由器的补充。


实现方案

  • 背景
  • 预期结果
  • Nginx 实现(简单)
  • 安装 Nginx (--with-stream)
  • 加载动态模块
  • 配置转发
  • 关于 UDP 转发配置 `proxy_responses`
  • 验证连接
  • 验证方式:`nc` 命令
  • 验证 TCP 连接
  • 验证 UDP 连接


背景

网络拓扑图如下:

外网域名映射到nginx nginx外网转内网,外网域名映射到nginx nginx外网转内网_nginx,第1张

网络结构非常简单,Server 作为 R1 的 DMZ 主机,如果要实现外网访问 Server,完全就是 R0 一条 NAT 的事情。

但是! R0 这类消费级的路由器,UI 缺少很多路由器本身能实现的功能,包括 静态路由表 等。更奇葩的是:

这个路由器的 NAT、DMZ 等功能不能选择路由器作为目标!且不能手动输入NAT、DMZ 的目标 IP 地址!

当前在线的终端如下:

外网域名映射到nginx nginx外网转内网,外网域名映射到nginx nginx外网转内网_linux_02,第2张

DMZ 可选设备完整列表如下(NAT可选设备列表相同):

外网域名映射到nginx nginx外网转内网,外网域名映射到nginx nginx外网转内网_外网域名映射到nginx_03,第3张

外网域名映射到nginx nginx外网转内网,外网域名映射到nginx nginx外网转内网_路由器_04,第4张

DMZ 和 NAT 都不可以选连接在 LAN 口上的路由器!

因此,本人尝试使用运行 Linux 系统的树莓派 RPi 实现 NAT 到子路由器的功能。

预期结果

RPi 作为 R0 的 DMZ 主机,只要 PC 能通过 RPi 连接 Server,外网就可以访问到 Server。

外网域名映射到nginx nginx外网转内网,外网域名映射到nginx nginx外网转内网_linux_05,第5张

Nginx 实现(简单)

通过 Nginx 实现 TCP、UDP 等协议的转发比较简单

安装 Nginx (–with-stream)

可以通过 apt、yum 等直接安装功能比较全的版本 nginx-extras,该版本包括了 stream 等模块

sudo apt install -y nginx-extras

通过以下命令,确认 nginx 带有 --with-stream 编译参数

nginx -V

输出结果,可以看到 --with-stream=dynamic

nginx version: nginx/1.14.2
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-v2a0Oa/nginx-1.14.2=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-headers-more-filter --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-cache-purge --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-ndk --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-echo --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-fancyindex --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/nchan --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-lua --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/rtmp --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-uploadprogress --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-v2a0Oa/nginx-1.14.2/debian/modules/http-subs-filter

加载动态模块

一般通过 apt 等安装 nginx-extras,模块已默认启用,可以跳过本章节。

--with-stream=dynamic--with-stream 的区别在于,前者可以在 nginx 的配置文件配置动态加载模块,不需要使用该模块时可以不加载。
模块的加载在 nginx 的配置文件中可以配置

在配置文件中使用 load_module 加载模块:

load_module modules/ngx_stream_module.so;

模块加载可以单独放在一个文件中,然后通过 include 引入到 nginx.conf
/etc/nginx/modules-enabled/50-mod-stream.conf

本人的 /etc/nginx/nginx.conf 文件节选:
第 4 行引入了模块相关配置

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

include /etc/nginx/conf.d/*.conf;

http {
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
        include /etc/nginx/sites-enabled/*;
}

配置转发

新建一个配置文件 /etc/nginx/conf.d/forward.conf

stream {
    server {
        listen 65000;
        proxy_pass 192.168.3.99:65000;
    }
    server {
        listen 65000 udp;
        proxy_pass 192.168.3.99:65000;
        # proxy_responses 1;
    }
}

通过 include 的方式引入 nginx.conf 中。
第 11 行引入了 /etc/nginx/conf.d/ 目录下所有配置文件,包括了刚才创建的 forward.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

include /etc/nginx/conf.d/*.conf;

http {
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
        include /etc/nginx/sites-enabled/*;
}

配置修改完成后,nginx 重新加载配置

sudo nginx -s reload

关于 UDP 转发配置 proxy_responses

使用 UDP 的时候,有一项参数 proxy_responses

参考官方文档 ngx_stream_proxy_module # proxy_responses

Syntax:	proxy_responses number;
Default:	—
Context:	stream, server
This directive appeared in version 1.9.13.

Sets the number of datagrams expected from the proxied server in response to a client datagram if the UDP protocol is used. The number serves as a hint for session termination. By default, the number of datagrams is not limited.

If zero value is specified, no response is expected. However, if a response is received and the session is still not finished, the response will be handled.

个人理解:

  • 如果客户端仅单向发送 UDP 数据包到服务端,不需要接收服务端数据,则可以配置该项为 0,即不保持 UDP 会话;
  • 如果使用类似于 RDP(远程桌面) 等工具,客户端与服务端之间有大量的双向 UDP 通讯,则这一项可以不配置,这样会话将持续保持一段时间,经过 proxy_timeout 时间后结束会话。

proxy_timeout 参考官方文档 ngx_stream_proxy_module # proxy_timeout

Syntax:	proxy_timeout timeout;
Default:	
proxy_timeout 10m;
Context:	stream, server
Sets the timeout between two successive read or write operations on client or proxied server connections. If no data is transmitted within this time, the connection is closed.

验证连接

验证方式:nc 命令

外网域名映射到nginx nginx外网转内网,外网域名映射到nginx nginx外网转内网_nginx,第1张

PC 与 Server 之间的连接,采用 nc 命令进行验证

nc 命令基本使用方式:

OpenBSD netcat (Debian patchlevel 1.195-2)
usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl]
          [-m minttl] [-O length] [-P proxy_username] [-p source_port]
          [-q seconds] [-s source] [-T keyword] [-V rtable] [-W recvlimit] [-w timeout]
          [-X proxy_protocol] [-x proxy_address[:port]]           [destination] [port]
        Command Summary:
                -4              Use IPv4
                -6              Use IPv6
                -b              Allow broadcast
                -C              Send CRLF as line-ending
                -D              Enable the debug socket option
                -d              Detach from stdin
                -F              Pass socket fd
                -h              This help text
                -I length       TCP receive buffer length
                -i interval     Delay interval for lines sent, ports scanned
                -k              Keep inbound sockets open for multiple connects
                -l              Listen mode, for inbound connects
                -M ttl          Outgoing TTL / Hop Limit
                -m minttl       Minimum incoming TTL / Hop Limit
                -N              Shutdown the network socket after EOF on stdin
                -n              Suppress name/port resolutions
                -O length       TCP send buffer length
                -P proxyuser    Username for proxy authentication
                -p port         Specify local port for remote connects
                -q secs         quit after EOF on stdin and delay of secs
                -r              Randomize remote ports
                -S              Enable the TCP MD5 signature option
                -s source       Local source address
                -T keyword      TOS value
                -t              Answer TELNET negotiation
                -U              Use UNIX domain socket
                -u              UDP mode
                -V rtable       Specify alternate routing table
                -v              Verbose
                -W recvlimit    Terminate after receiving a number of packets
                -w timeout      Timeout for connects and final net reads
                -X proto        Proxy protocol: "4", "5" (SOCKS) or "connect"
                -x addr[:port]  Specify proxy address and port
                -Z              DCCP mode
                -z              Zero-I/O mode [used for scanning]
        Port numbers can be individual or ranges: lo-hi [inclusive]

验证 TCP 连接

Server 端监听 TCP 65000 端口

nc -vlp 65000

PC 端使用 TCP 连接 RPi 65000 端口

nc -v 192.168.3.254 65000

PC 与 Server 各发一些数据:

Server 端输出:

Listening on [0.0.0.0] (family 2, port 65000)
Connection from rpi4 17150 received!
from server: hi, pc!
from PC: hi, server!

PC 端输出:

Connection to 192.168.3.254 65000 port [tcp/*] succeeded!
from server: hi, pc!
from PC: hi, server!

由此可见,TCP 转发成功

验证 UDP 连接

Server 端监听 UDP 65000 端口

nc -uvlp 65000

PC 端使用 UDP 连接 RPi 65000 端口

nc -uv 192.168.3.254 65000

PC 与 Server 各发一些数据:

Server 端输出:

Listening on [0.0.0.0] (family 2, port 65000)
Connection from rpi4 52981 received!
XXXXXhi, PC!
hi, Server!

PC 端输出:

Connection to 192.168.3.254 65000 port [udp/*] succeeded!
hi, PC!
hi, Server!

验证完成,达到预期效果。



https://www.xamrdz.com/backend/3dj1924681.html

相关文章: