blog_post/_posts/本地web服务内网穿透解决方案.md

303 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 本地web服务内网穿透解决方案
date: 2024-07-06 11:05:30
tags:
- frp
- nginx
- 内网穿透
- ssl
categories:
- 网络
index_img: http://cdn.7niu.begild.com/%E7%94%B5%E8%84%91%E8%BF%81%E7%A7%BB%E5%90%8E%E5%BC%80%E6%9C%BA%E6%97%A0%E6%98%BE%E7%A4%BA%E8%BE%93%E5%87%BA/%E6%90%AC%E5%AE%B6%E5%B7%A5%E4%BD%9C%E4%BD%8D%E7%BD%AE%E5%9B%BE.jpg
---
## 前言
前段时间搭了一个NAS
| 部件 | 渠道 | 参数 | 价格 |
| --------- | ---- | -------------------------------------- | ---- |
| 主板+机箱 | 淘宝 | 蜗牛星际的4盘位J1900 8G内存16G固态 | 385 |
| 硬盘 | 京东 | 两个4T西部数据红盘组一个RAID1阵列 | 1200 |
装了一个黑群晖7.X基本的网盘功能已经组建好了和手机电脑之间互通也没啥问题其他的基本也就没有继续捣鼓诸如什么影音之类的因为搬家投影仪卖了也没那么多时间去看。
但是话又说回来了不搞这些搞了其他的哎嘿。我搭了一个代码版本管理的server。用的是[gitea](https://about.gitea.com/)。本来之前自己写的一些自有的code不涉及公司的代码公司的代码都是内网的都是传到coding上的但是这玩意现在越来越恶心了并且公司也直接ban掉了访问的权限导致说给我的一些code的同步带来了阻碍。所以既然公的不行那就来私的在NAS上搭建了一个gitea server这样我就可以自由的进行异地的开发和同步了。
众所周知:
1. NAS是在自己家内网的。而运营商是ban掉了你的公网IP的宽带的出口仅仅是在运行商大的内网里面还是不通公网的所以百度查到的公网IP也并不是你独有的。
2. 写code的环境是在另外的一个内网比如公司的内网酒店的内网等等。
二者都没法直接联系到对方所以就请出了我们的工具内网穿透了。这也是本片所说的重点但是并不是讲内网穿透的原理仅仅是记录一下由于这个跨两个内网访问web服务带来的问题以及我的处理过程。
---
## 内网穿透方案
### 虚拟局域网
在搭建NAS的时候就已经使用了[ZeroTier](https://www.zerotier.com
)来组建了一个虚拟局域网也就是我说的手机PC笔记本等之间的访问。利用这个其实已经能够解决大部分的场景但是他必须依赖于每个设备都需要装一个zerotier客户端这就直接导致公司的电脑没有办法完成因为职员的账户是没有软件安装的权限的。只能走普通的web访问的路线。
### FRP
在看了一些资料后,我选择了[FRP](https://gofrp.org/zh-cn/)作为本次的主角,其使用是非常简单的, 随便搜一下教程即可。
这里主要是要有一台云服务器。因为之前博客备案而当时腾讯云搞活动所以直接99一年的服务器买了。有现成的服务器就好办了。
---
## 实施
### FRP
frp的配置网络都有教程。不过注意的是有很多教程比较老还停留ini的配置。新版的已经是toml了所以语法上稍有不同。我是参考的这个-> [Linux搭建frp服务](https://blog.csdn.net/u013250861/article/details/130750631)
核心就是:
1. 在服务端(云服务器)配置一个通信端口以监听来自客户端访问。
```bash
$ cat frps.toml
bindPort = 22700 #服务端监听的端口
```
2. 在客户端NAS配置
1. 要访问的服务器端口
2. 要反向代理的本地服务的端口像gitea默认的端口是8418所以直接填写8418即可
```bash
$ cat frpc.toml
serverAddr = "xxxx.com" #需要自行添加域名解析
serverPort = 22700 #服务端端口
[[proxies]]
name = "gitea-proxy"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8418 #代理本地的8418端口
remotePort = 44556 #服务器端使用44556端口对外提供服务
```
这里的 serverAddr 可以是域名也可以是云服务器IP因为不知道那天哪里有更便宜的服务器我就会迁过去所以我是填的域名这样我迁移服务器修改一下dns即可。
下面的 proxies 是个表数组可以定义多个代理copy改一改代理的内容即可。
配置完毕后启动两边的程序看到打印连接正常即可通过服务器IP加端口访问服务了。
记得把服务器的防火墙的 44556、22700 端口访问打开。
比如 http://43.56.67.12:44556。即可看到gitea主页面了。登录后即可看到自己的仓库等等。
为了方便持续运行异常恢复搭建一个system service来管理也是极好的所以
客户端的service 配置:
```bash
$ cat /etc/systemd/system/frpc.service
[Unit]
Description=frpc
After=network.target
Wants=network.target
[Service]
Restart=on-failure
RestartSec=5
ExecStart=/var/services/homes/work/run/frp/frpc -c /var/services/homes/work/run/frp/frpc.toml
[Install]
WantedBy=multi-user.target
```
服务端的service 配置:
```bash
$ cat /etc/systemd/system/frps.service
[Unit]
Description=frps
After=network.target
Wants=network.target
[Service]
Restart=on-failure
RestartSec=5
ExecStart=/home/ubuntu/frp/frp_0.58.1_linux_amd64/frps -c /home/ubuntu/frp/frp_0.58.1_linux_amd64/frps.toml
[Install]
WantedBy=multi-user.target
```
服务相关命令服务端的改成frps.service即可
```bash
systemctl daemon-reload #刷新服务
systemctl enable frpc.service # 开机自启
systemctl start frpc.service #启动
systemctl restart frpc.service #重启
systemctl status frpc.service #服务状态
systemctl is-enabled frpc.service #检查是否处于开机自启
```
http://43.56.67.12:44556。 这样你以为就结束了no no no。这样之后呢实际下来会有这么几个问题。
1. 访问的IP和端口不好记IP可以通过域名解析来解决但是端口不行时间长容易忘。
2. 非web端口(80/433)基本被公司防火墙干掉了,所以在公司电脑上还是无法访问,等于白搞!
所以还得继续搏斗
### NGINX
为什么浏览器可以访问其他的网页但是这个网页就不行呢明明我备案的博客网页也是在这个服务器上的在公司电脑也是能访问的所以防火墙肯定不是防IP也不是HTTP协议那大概率就是端口了
使用wireshark过滤下访问服务器IP的流量看下我发现了一个问题请求时建立TCP连接的握手在本机发送的SYN信号后正确发给了服务器的44556端口但是一直没有回应后面都是重试的错误所以应该就是端口被防火墙挡道了。
无所谓Nginx会出手通过Nginx对80端口的流量分流根据访问的域名分流到不通的后端来代理不同的业务服务。
因为备案的原因80端口必须要给到备案网页的web服务所以这里的域名就分流如下。
| 域名 | 服务 | 实际端口 |
| ----------------- | ----------- | -------- |
| xxx.com | 备案网页 | 80 |
| gitserver.xxx.com | gitea的网页 | 44556 |
当然这里的gitserver这个二级域名也需要域名解析是到服务器的IP。
```bash
$ sudo vim /etc/nginx/sites-available/gitea
server {
listen 80;
server_name gitserver.xxx.com; # 将此替换为你希望用来访问Gitea的域名或IP
location / {
proxy_pass http://localhost:44556;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
生效配置
```bash
$ sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl reload nginx
```
这样你就可以直接通过 http://gitserver.xxx.com 来访问到gitea server了。相比较通过 http://43.56.67.12:44556 是不是已经非常现代了!
并且在公司电脑的浏览器一样可以浏览仓库和代码因为防火墙只能看到的是Nginx对外提供的80端口在和他通信完全绕过了防火墙的限制
桥豆麻袋,桥豆麻袋,还没结束,正当我兴冲冲的打开终端, git pull 拉code的时候 WTF还是不行
### TLS终止代理
难道是git另外起了其他的端口去访问吗不应该啊我在家也试过可以的不需要额外的代理端口啊老方法wireshark上马筛选和服务器的流量看下发起git pull的时候会显示一个GIT协议的请求然后后面就不行了大意了原来是防火墙直接拦截掉了GIT的请求了难道就要这样结束了吗 不不不我还有一计祭出我的屠龙宝刀流量加密。既然你识别GIT协议那我就加密整个TCP流量你怎么识别
先看下gitea是否支持HTTPS是支持的不过貌似没有必要这么彻底我只要公司电脑到服务器是加密的即可后续无所谓。所以为了减少改动范围使用了Nginx来一手TLS终止代理即可。
随便用openssl生成了nginx可用的证书和公钥配置一下即可。
这里另外配置了重定向让http访问也重新向到https
```bash
$ sudo vim /etc/nginx/sites-available/gitea
server {
listen 80;
server_name gitserver.xxx.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name gitserver.xxx.com;
ssl_certificate /home/ubuntu/ssl/nginx/xxx.pem
ssl_certificate_key /home/ubuntu/ssl/nginx/xxx.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://localhost:44556;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
然后再次使用git pull code git push code。 OJBK**完全没有问题**!
到此已经完全无障碍的使用NAS上搭建的gitea服务了
不过是的还有一点小瑕疵那就是证书并不是CA颁发的无论是git 还是web都会报警告证书不受信很烦。虽然可以工作但是警告真的很烦所以工作还未结束
## 免费CA ssl证书
我的域名是在阿里买的他会给免费的证书的不过只有90天并且我实测下来下来的貌似也不受到认可不知道是不是我操作有问题但是即使他有用90天我就的换一下岂不是麻烦死了除非可以自动更新。哎还真有果然像我一样懒得还是很多的一开始我想是不是写一个脚本去自动获取但是已经有人做了这样得工具可以自动获取自动更新完美兄弟
[Certbot](https://certbot.eff.org/),一个免费的证书管理工具。他颁发的证书来自 [Let's Encrypt](https://letsencrypt.org/zh-cn/getting-started/) 。具体的工作原理可以看他们的文档这里主要是域名的所有权要保证是你的所以这里需要他支持查询的DNS服务上才能用。
参考官网进行了安装,我一开始在文档里并没有找到阿里还以为不支持,想着不应该啊阿里还是用户蛮多的,死马当活马医,试试看真有!
```bash
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-aliyun #我的域名dns提供商是阿里云的这里可以把aliyun删掉然后用table键看下都支持哪些dns提供商
```
它非常方便,自动检测nginx的配置并且生成和替换完全不用额外的命令。过程贴这里了
``` bash
$ sudo certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): xxxx@126.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in
order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
Account registered.
Which names would you like to activate HTTPS for?
We recommend selecting either all domains, or all domains in a VirtualHost/server block.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: gitserver.xxx.com # 我这里这里检测到两个域名。需要颁发给哪一个
2: yyy.xxx.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1 #我选择1
Requesting a certificate for gitserver.xxx.com
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/xxx.begild.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/xxx.begild.com/privkey.pem
This certificate expires on 2024-10-03.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate
Successfully deployed certificate for gitserver.xxx.com to /etc/nginx/sites-enabled/gitea
Congratulations! You have successfully enabled HTTPS on https://xxxx.begild.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```
这样浏览器就不会弹证书警告了默认证书就是90天有效期这个工具可以自动续期所以相当于就是无限期只要你确保域名在你手里。
测试续期是否能够成功
```bash
$ sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/gitserver.xxx.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Account registered.
Simulating renewal of an existing certificate for gitserver.xxx.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/gitserver.xxx.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```
当我配置完上面这一切之后不知不觉已经迭代了很多个版本呢。每个版本解决一个问题再上一个问题解决前我也不知道后面的问题就这样不断升级打怪我可能本身已经不仅仅是为了访问gitea的服务了转而变成了你不让我这样我就要试试看看我能不能成
希望我能一直保持有这种对于问题和未知源源不绝的兴趣!
## FRP 的优化
这是一个彩蛋上面说的gitea暴露给公网访问是通过frpfrp本身的流量其实并不是加密的一不做二不休做都做了来都来了阅读了frp相关的文档对其进行了一些优化修改
1. FRP的双向加密这样端到端之间都是加密的。
2. TCP改为QUIC提升速度。TCP的拥塞控制并不是很优秀。
这些都是小小菜就不展开说的,都是参考文档简单配置一下即可。
## 最后
最后贴一下整体的架构是这个样子:
![本地web服务内网穿透解决方案-arch](https://cdn.7niu.begild.com/%E6%9C%AC%E5%9C%B0web%E6%9C%8D%E5%8A%A1%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88-arch.svg)