Nginx配合ssh/frp反向代理实现内网穿透
同步发布在个人笔记Nginx配合ssh/frp反向代理实现内网穿透
Nginx配合ssh/frp反向代理实现内网穿透
最近折腾了一个复古游戏机样式的树莓派外壳,搭配树莓派4b后感觉很适合做一个低负载的服务器玩玩,但是放在家里后就会面临在外面想“触碰”到它却没有公网ip的问题。本科的时候也有过类似的情况,当时采用了花生壳的内网穿透服务,但是体验下来免费版确实限制很多(流量低,域名难记),现在自己手头上有一个公网服务器(必需),也有域名(对于内网穿透不是必需的),于是尝试使用反向代理的方式来实现。
问题简单描述:在局域网外访问局域网内树莓派上的web页面。
1.0 内网穿透原理及工具选择
1.1 内网穿透原理
问题的症结就是当我们处在外网时,我们不知道我们服务器的“地址”也就是 ip ,毕竟内网服务器没有公网 ip 来着… 情况就像是这样:
#mermaid-svg-AOIj2Np2cP4epm2b {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-AOIj2Np2cP4epm2b .error-icon{fill:#552222;}#mermaid-svg-AOIj2Np2cP4epm2b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AOIj2Np2cP4epm2b .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-AOIj2Np2cP4epm2b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AOIj2Np2cP4epm2b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AOIj2Np2cP4epm2b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AOIj2Np2cP4epm2b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AOIj2Np2cP4epm2b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AOIj2Np2cP4epm2b .marker.cross{stroke:#333333;}#mermaid-svg-AOIj2Np2cP4epm2b svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AOIj2Np2cP4epm2b .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AOIj2Np2cP4epm2b .cluster-label text{fill:#333;}#mermaid-svg-AOIj2Np2cP4epm2b .cluster-label span{color:#333;}#mermaid-svg-AOIj2Np2cP4epm2b .label text,#mermaid-svg-AOIj2Np2cP4epm2b span{fill:#333;color:#333;}#mermaid-svg-AOIj2Np2cP4epm2b .node rect,#mermaid-svg-AOIj2Np2cP4epm2b .node circle,#mermaid-svg-AOIj2Np2cP4epm2b .node ellipse,#mermaid-svg-AOIj2Np2cP4epm2b .node polygon,#mermaid-svg-AOIj2Np2cP4epm2b .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AOIj2Np2cP4epm2b .node .label{text-align:center;}#mermaid-svg-AOIj2Np2cP4epm2b .node.clickable{cursor:pointer;}#mermaid-svg-AOIj2Np2cP4epm2b .arrowheadPath{fill:#333333;}#mermaid-svg-AOIj2Np2cP4epm2b .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AOIj2Np2cP4epm2b .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AOIj2Np2cP4epm2b .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-AOIj2Np2cP4epm2b .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-AOIj2Np2cP4epm2b .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AOIj2Np2cP4epm2b .cluster text{fill:#333;}#mermaid-svg-AOIj2Np2cP4epm2b .cluster span{color:#333;}#mermaid-svg-AOIj2Np2cP4epm2b div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AOIj2Np2cP4epm2b :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
外网
找不到服务!
用户
内网
内网服务器
那么有没有一种方法找到没有公网 ip 设备的方式呢?当然有,就是由设备向外主动发起连接,一旦这种连接建立了,就能顺着连接找到设备了。那么能不能让内网服务器主动来连接我们外部用户呢?实际上也不行,因为不止内网服务器没有公网 ip ,我们用户端也没有呀! 看来一切的问题就在于这个公网 ip 了,似乎有了这个就能解决问题了。
还真是,解决的方法就是找一个具有公网 ip 的服务器当中间人,内网服务器主动与公网服务器主动建立连接并保持,用户去找公网服务器要内网服务器的信息。由于公网服务器是有地址 ip 的,所以这两个连接都可以实现。
#mermaid-svg-1NHjoTGlHYb51U3h {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1NHjoTGlHYb51U3h .error-icon{fill:#552222;}#mermaid-svg-1NHjoTGlHYb51U3h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1NHjoTGlHYb51U3h .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-1NHjoTGlHYb51U3h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1NHjoTGlHYb51U3h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1NHjoTGlHYb51U3h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1NHjoTGlHYb51U3h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1NHjoTGlHYb51U3h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1NHjoTGlHYb51U3h .marker.cross{stroke:#333333;}#mermaid-svg-1NHjoTGlHYb51U3h svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1NHjoTGlHYb51U3h .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1NHjoTGlHYb51U3h .cluster-label text{fill:#333;}#mermaid-svg-1NHjoTGlHYb51U3h .cluster-label span{color:#333;}#mermaid-svg-1NHjoTGlHYb51U3h .label text,#mermaid-svg-1NHjoTGlHYb51U3h span{fill:#333;color:#333;}#mermaid-svg-1NHjoTGlHYb51U3h .node rect,#mermaid-svg-1NHjoTGlHYb51U3h .node circle,#mermaid-svg-1NHjoTGlHYb51U3h .node ellipse,#mermaid-svg-1NHjoTGlHYb51U3h .node polygon,#mermaid-svg-1NHjoTGlHYb51U3h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1NHjoTGlHYb51U3h .node .label{text-align:center;}#mermaid-svg-1NHjoTGlHYb51U3h .node.clickable{cursor:pointer;}#mermaid-svg-1NHjoTGlHYb51U3h .arrowheadPath{fill:#333333;}#mermaid-svg-1NHjoTGlHYb51U3h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1NHjoTGlHYb51U3h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1NHjoTGlHYb51U3h .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-1NHjoTGlHYb51U3h .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-1NHjoTGlHYb51U3h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1NHjoTGlHYb51U3h .cluster text{fill:#333;}#mermaid-svg-1NHjoTGlHYb51U3h .cluster span{color:#333;}#mermaid-svg-1NHjoTGlHYb51U3h div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1NHjoTGlHYb51U3h :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
外网
内网
索要信息
提供信息
用户
公网服务器
内网服务器
实际上这样的方式我们每天都在进行,考虑一下两个人通过微信聊天,实际上两个人都是没有公网 ip 的,那么我们是怎么把信息传给对方的呢?实际上我们根本没有直接连接到对方手机,我们只是都主动连接到微信的公网服务器,并且把信息传给这个公网服务器了,由公网服务器作为中间人来传递消息罢了。
实现了这个过程也就可以叫内网穿透。
1.2 工具选择
实现上面图里的两条线,需要用到专门的工具:
- nginx: 转发内网的消息给外网;
- ssh / frp: 内网服务器连接公网服务器。
现在我们假设我们的需求是这样的:
服务内网端口公网服务器 ip期望实现的公网端口http 网页应用80domain.com8080ftp 文件服务器90domain.com9090
这里我们用域名举例子,如果没有域名,后面使用 ip 就是了。域名如何解析到 ip 也可以参考我 blog 里的笔记。
最后实现的信息通路是这样,下面的配置都以这个域名和端口为例:
#mermaid-svg-2ejkhw40uIs5LW0a {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-2ejkhw40uIs5LW0a .error-icon{fill:#552222;}#mermaid-svg-2ejkhw40uIs5LW0a .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2ejkhw40uIs5LW0a .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-2ejkhw40uIs5LW0a .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2ejkhw40uIs5LW0a .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2ejkhw40uIs5LW0a .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2ejkhw40uIs5LW0a .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2ejkhw40uIs5LW0a .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2ejkhw40uIs5LW0a .marker.cross{stroke:#333333;}#mermaid-svg-2ejkhw40uIs5LW0a svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2ejkhw40uIs5LW0a .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2ejkhw40uIs5LW0a .cluster-label text{fill:#333;}#mermaid-svg-2ejkhw40uIs5LW0a .cluster-label span{color:#333;}#mermaid-svg-2ejkhw40uIs5LW0a .label text,#mermaid-svg-2ejkhw40uIs5LW0a span{fill:#333;color:#333;}#mermaid-svg-2ejkhw40uIs5LW0a .node rect,#mermaid-svg-2ejkhw40uIs5LW0a .node circle,#mermaid-svg-2ejkhw40uIs5LW0a .node ellipse,#mermaid-svg-2ejkhw40uIs5LW0a .node polygon,#mermaid-svg-2ejkhw40uIs5LW0a .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2ejkhw40uIs5LW0a .node .label{text-align:center;}#mermaid-svg-2ejkhw40uIs5LW0a .node.clickable{cursor:pointer;}#mermaid-svg-2ejkhw40uIs5LW0a .arrowheadPath{fill:#333333;}#mermaid-svg-2ejkhw40uIs5LW0a .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2ejkhw40uIs5LW0a .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2ejkhw40uIs5LW0a .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-2ejkhw40uIs5LW0a .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-2ejkhw40uIs5LW0a .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2ejkhw40uIs5LW0a .cluster text{fill:#333;}#mermaid-svg-2ejkhw40uIs5LW0a .cluster span{color:#333;}#mermaid-svg-2ejkhw40uIs5LW0a div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2ejkhw40uIs5LW0a :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
公网服务器 domain.com
内网服务器
ssh / frp
ssh / frp
port: 81
nginx 服务1
port: 8080
port: 91
ngixn 服务2
port: 9090
http 网页应用
port:80
ftp 文件服务器
port:90
用户
如果你的公网服务器交给管理商管理了,要注意哪些端口是开放的,以 cloudflare 为例,开放的端口详见文末。
2.0 Nginx配置
实际上如果只是为了实现内网穿透,只依靠 frp 也可,但是由于 nginx 可以实现更方便的管理和负载分配,我仍然推荐用 nginx 做一次端口转发, nginx 是配置在公网服务器上的:
nginx 的文件结构可以参考这个笔记Nginx 多域名访问多端口应用
新建一个Penetration.conf
server {#http 穿透服务
listen 8080;# 外部访问用端口
server_name domain.com;# 这里填你的域名,如果没有域名则填localhost
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;# 不进行重定向
proxy_pass domain.com:81;# 将穿透来到81口的信息代理到外部访问用的80口#注意此处,如果使用ssh穿透,则domain或者localhost均可;如果使用frp穿透,则必需和frp设置的vhttp端口一致,否则就不能访问。}}
server {#ftp文件服务器 穿透服务
listen 9090;# 外部访问用端口
server_name domain.com;# 这里填你的域名,如果没有域名则填localhost
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;# 不进行重定向
proxy_pass http://localhost:91;# 将穿透来到81口的信息代理到外部访问用的80口}}
重启 nginx
systemctl restart nginx
,这样 nginx 就配置好了。
3.0 反向代理配置
3.1 SSH 方式
首先修改公网服务器的 SSH 配置文件
/etc/ssh/sshd_config
里面有一项改为
GatewayPorts yes
这样可以把监听的端口绑定到任意 IP 0.0.0.0 上,否则只有本机 127.0.0.1 可以访问,然后重启服务
sudo systemctl restart sshd
。
在内网服务器上通过 ssh 的反向代理功能连接内网服务器与公网服务器,
xx.xx.xx.xx
是公网服务器 ip 。
ssh-CNR81:localhost:80 [email protected] -p22# 22是默认 ssh 端口ssh-CNR91:localhost:90 [email protected] -p22# 两个服务要开两个终端# 推荐挂载在后台运行nohupssh-CNR81:localhost:80 [email protected] -p22&nohupssh-CNR91:localhost:90 [email protected] -p22&
检验是否已经启动了可以使用
ps aux | grep ssh
指令来查看。
3.2 frp 方式
ssh 连接十分容易断,更稳定且方便管理的方式是 frp 工具。
再详细介绍笔记就太长了,再开一篇 frp 实现 http / tcp 内网穿透(穿透 wordpress ) 的笔记吧。
附录:
Cloudflare 默认代理发往下列 HTTP/HTTPS 端口的流量。
Cloudflare 支持的 HTTP 端口:
80
8080
8880
2052
2082
2086
2095
Cloudflare 支持的 HTTPS 端口:
443
2053
2083
2087
2096
8443
如果您的域的流量要发送到上面列出的端口以外的其他端口,则可以:
通过您 Cloudflare DNS 页面添加子域为灰色云记录,或者开启 Cloudflare Spectrum。
通过 WAF 规则 ID 100015 针对 Pro、Business 和 Enterprise 域阻止除 80 和 443 以外的其他端口上的流量:“Block requests to all ports except 80 and 443”。
只有端口 80 和 443 可兼容以下服务:
对于启用了中国网络的域名的中国境内数据中心 HTTP/HTTPS 流量,CloudflareApps 代理,以及Cloudflare缓存。Cloudflare Access 不支持 URL 中的端口号。 系统会从通过 Cloudflare Access 保护的 URL 请求中剥离端口号。
source:https://zhuanlan.zhihu.com/p/685240158#:~:text=Cloudflare%20%E6%94%AF%E6%8C%81%E7%9A%84,HTTP%20%E7%AB%AF%E5%8F%A3%EF%BC%9A%2080
版权归原作者 地衣君 所有, 如有侵权,请联系我们删除。