0%

使用 cloudflare tunnel 暴露/保护服务器

nas 上有多个服务对应着多个端口. 本文将介绍三种方法来隐藏端口.

环境

  1. 广东电信
  2. 群晖 7.0
  3. cloudflare

方案1: 群晖自动的反向代理

这个方法最简单, 先按需注册不同二级域名[1]

然后通过群晖自带的 ngnix 配置反向代理即可, 如下图所示.

image-20220514233322175

但该方案缺点很明显且致命: 除非备案, 不然 443 端口和 80 端口默认是封的. 即, 依然需要在域名后加端口来访问 nas.

方案2: 使用 cloudflare workers 代理

该方案是在方案1基础上改装的. 没有 443 端口, 就得想办法怎么把非标准端口转为 443 端口, 经过一番搜索后找到了这个解决方案

sequenceDiagram
    Client->Cloudflare: 443 request
    Cloudflare->nas: 6443 request
    nas-->Cloudflare: 6443 response
    Cloudflare-->Client: 443 response

如上图所示, nas 暴露出来一个端口, cloudflare 负责把用户的请求发往这个端口, 再把这个端口返回的内容原封不动发回给用户. 所以 nas 上基本不用做什么, 仅需配置好 worker

按下图新建 worker

image-20220514235940732

image-20220515000014240

image-20220515000101103

image-20220515000136142

到这里把下面的代码粘贴上去并点击 Save and Deploy, 至此就配置完成了, 可以在右边的测试窗里输入域名测试.(注意这代码是我根据原代码[2]改的, 使用前请参考原 readme原文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
let Svr = {
"Host": "your host name",
"Port": your port,
"Protocol": "https"
};
let directDomain = ["subdomains that route directly, must end with '.'"]
addEventListener(
"fetch",
(e) => {
let Url = new URL(e.request.url);
Url.host = Url.hostname;
Url.port = Svr.Port;
Url.protocol = Svr.Protocol + ":";
let Req = new Request(Url,e.request);
Req.headers.set('X-Real-Host', e.request.headers.get('Host'));
let Res = fetch(Req).then((d)=>{
for(dd in directDomain){
let hostname = Url.hostname.toLowerCase()
if(hostname.startsWith(dd)){
return d
}
}
if(d.status < 500){
return d;
} else {
return new Response(d.status + " " + d.statusText, {
headers: {
"content-type": "text/html;charset=UTF-8",
},
status: d.status,
statusText: d.statusText
});
}
});
e.respondWith(Res);
}
)

这个方案理论上可以完美运行的, 但不知道为什么我这边在局域网内就正常, 但到了广域网就连不上. 而在广域网通过 workers 的编辑界面可以测通. 也不知道是哪里配错或少配了, 请大佬们不吝赐教.

方案3: 使用 cloudflare tunnel 做内网穿透

以上两种方案都无法完美实现, 最终在 clarkzjw 大佬的推荐下使用了 cloudflare tunnel 做穿透, 这是一个优缺点都很明显的方案.

查看 cloudflare tunnel 官方文档 可以发现他的实现方式和 frp 很相似, 都是通过一个公网服务来实现内网穿透. 硬要说不同的话应该是两者属于不同的层级. tunnel 原理图如下(来自官网)

Tunnel Diagram

以下内容参考视频[3] 实现. 会省略部分操作, 如果有疑问请参考原视频, 里面演示了完整的一套操作.

  1. 创建文件夹 ~/docker/cloudflare

image-20220515125338601

  1. 从 docker 下载 cloudflare 映像

    image-20220515125126852

    映射存储空间

    docker/cloudflare /home/nonroot/.cloudflared

    image-20220515125533806

    勾选 使用与 Docker Host 相同的网络

    image-20220515125713676

    命令填写 tunnel login

    image-20220515125911193

    运行, 进入 容器 -> 详情 查看日志

    image-20220515130201155

    复制链接到浏览器中打开完成登录, 再次查看日志可以看到下图则说明登录成功

    image-20220515130314264

    且可发现 ~/docker/cloudflare 中多了一个 cert.pem 文件

  2. 导出上面的容器到 ~/docker/cloudflare 文件夹中, 并重命名为 xxx_create, 如下图

    image-20220515130806918

    编辑文档内 cmd 属性, 要记住你的 tunnel 名, 后面还要用到

    image-20220515131030320

    导入该配置文件到 docker 中, 并运行. (这里就不截日志的图了). 运行后可以发现 ~/docker/cloudflare 文件夹中多了一个 json 文件, 里面包含了调用 tunnel 所需凭证

    image-20220515131640011

  3. 创建 dns 记录, 这里你可以参考视频里的步骤, 也可以直接用上一步获得的 json 文件的文件名去创建 CNAME 记录, 按需创建. 比如我用了 foto 和 blog-pics 等, 都指向 tunnel 地址.

    Type Name Target Proxy Status
    CNAME foto (json文件名).cfargotunnel.com 勾选

    image-20220515131853608

  4. 创建 tunnel 配置文件 config.yml, 其中 tunnel 填写第三步获取的 json 文件文件名, credentials-file 填写第三步获取到的 json 文件在 docker 中的路径 (即 /home/nonroot/.cloudflared/文件名.json)

    image-20220515132321665

    1
    2
    3
    4
    5
    6
    7
    8
    tunnel: 文件名
    credentials-file: /home/nonroot/.cloudflared/文件名.json

    ingress:
    - hostname: foto.fm7077.it
    service: http://localhost:端口 # 注意, 这里一定要用 http, 而不是 https 不然会因为证书不符导致 502 错误
    # ~~~~ 其他你要暴露的服务
    - service: http_status:404
  5. 复制第三步导出的 json 文件并重命名为 xxx_run.

    image-20220515132607397

    导入 docker 中, 设置 启用自动重新启动, 运行, 顺利的话就能看到如下图日志.

    image-20220515132903116

    至此配置完成, 试着从外网通过域名访问 nas.

从上面的步骤可以看到, 使用该方案不需要公网 ip, 也不需要把服务器完整地暴露在公网上, 而且在没有 443 端口的情况下能隐藏服务器端口, 可以说非常完美了.

但缺点也很明显:

 1. 在国内访问会比较慢.
 2. 如果 docker 挂了, 而人又不在家就 gg 了. (这点可以通过直接暴露 nas 的方案<sup id="fnref:1"><a href="#fn:1" rel="footnote"><span class="hint--top hint--error hint--medium hint--rounded hint--bounce" aria-label="[给 nas 配置多个域名并配置 ssl | 一颗牛肉丸 (fm7077.it)](https://blog.fm7077.it/2022/05/05/set-ssl-for-multi-domain-names/)

“>[1]解决)
1. 不是缺点的缺点: 依赖免费的 tunnel.

总结

三个方案各有优缺点, 实现难度依次递增, 除了第二个没能完整验证外, 其他两个都能在特定环境下实现.

方案 优点 缺点
方案一 操作简单 需要公网 ip 的443 端口没被封
方案二 不需要公网 443 端口 因不明原因无法在公网上正常使用
方案三 不需要暴露服务器到公网上 部署相对复杂. 在国内访问会比较慢, 如果 docker 挂了, 而人又不在家就 gg 了. (这点可以通过直接暴露 nas 的方案<span class=”hint–top hint–error hint–medium hint–rounded hint–bounce” aria-label=”[给 nas 配置多个域名并配置 ssl
“>[1]解决) 不是缺点的缺点: 依赖免费的 tunnel

希望这篇文章能帮到你.

参考链接