nginx配合ssh/frp反向代理实现内网穿透

Nginx配合ssh/frp反向代理实现内网穿透

最近折腾了一个复古游戏机样式的树莓派外壳,搭配树莓派4b后感觉很适合做一个低负载的服务器玩玩,但是放在家里后就会面临在外面想“触碰”到它却没有公网ip的问题。本科的时候也有过类似的情况,当时采用了花生壳的内网穿透服务,但是体验下来免费版确实限制很多(流量低,域名难记),现在自己手头上有一个公网服务器(必需),也有域名(对于内网穿透不是必需的),于是尝试使用反向代理的方式来实现。

问题简单描述:在局域网外访问局域网内树莓派上的web页面

1.0 内网穿透原理及工具选择

1.1 内网穿透原理

问题的症结就是当我们处在外网时,我们不知道我们服务器的“地址”也就是 ip ,毕竟内网服务器没有公网 ip 来着… 情况就像是这样:

那么有没有一种方法找到没有公网 ip 设备的方式呢?当然有,就是由设备向外主动发起连接,一旦这种连接建立了,就能顺着连接找到设备了。那么能不能让内网服务器主动来连接我们外部用户呢?实际上也不行,因为不止内网服务器没有公网 ip ,我们用户端也没有呀! 看来一切的问题就在于这个公网 ip 了,似乎有了这个就能解决问题了。
还真是,解决的方法就是找一个具有公网 ip 的服务器当中间人,内网服务器主动与公网服务器主动建立连接并保持,用户去找公网服务器要内网服务器的信息。由于公网服务器是有地址 ip 的,所以这两个连接都可以实现。

实际上这样的方式我们每天都在进行,考虑一下两个人通过微信聊天,实际上两个人都是没有公网 ip 的,那么我们是怎么把信息传给对方的呢?实际上我们根本没有直接连接到对方手机,我们只是都主动连接到微信的公网服务器,并且把信息传给这个公网服务器了,由公网服务器作为中间人来传递消息罢了。
实现了这个过程也就可以叫内网穿透

### 1.2 工具选择
实现上面图里的两条线,需要用到专门的工具:

  • nginx: 转发内网的消息给外网;
  • ssh / frp: 内网服务器连接公网服务器。

现在我们假设我们的需求是这样的:

服务 内网端口 公网服务器 ip 期望实现的公网端口
http 网页应用 80 domain.com 8080
ftp 文件服务器 90 domain.com 9090

这里我们用域名举例子,如果没有域名,后面使用 ip 就是了。域名如何解析到 ip 也可以参考我 blog 里的笔记。
最后实现的信息通路是这样,下面的配置都以这个域名和端口为例:

如果你的公网服务器交给管理商管理了,要注意哪些端口是开放的,以 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 -CNR 81:localhost:80 [email protected] -p 22     # 22是默认 ssh 端口
ssh -CNR 91:localhost:90 [email protected] -p 22     # 两个服务要开两个终端

# 推荐挂载在后台运行
nohup ssh -CNR 81:localhost:80 [email protected] -p 22 &
nohup ssh -CNR 91:localhost:90 [email protected] -p 22 &

检验是否已经启动了可以使用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