如何使用两个域名分别绑定内外网服务,方便内网用户和外网用户访问。举个例子:内网地址为:share.local.codejerry.cn,公网则通过share.codejerry.cn访问同一个服务。

内容概要

  1. 设置泛解析 DNS:讨论如何通过泛解析来简化 DNS 管理,使任何子域名都能解析到指定的 IP。
  2. 配置 SSL 证书:介绍使用 Certbot 和 Let’s Encrypt 来自动化 SSL 证书的申请和续期过程。
  3. 内网服务配置:如何设置内网服务以通过 HTTPS 提供服务。
  4. 内网穿透设置:讲述如何使用内网穿透工具将公网流量安全地转发到内网服务。
  5. 公网服务器配置:最后,我们将看到如何配置公网服务器,以便通过公网域名访问内网服务。

实现过程

设置泛解析 DNS

泛解析 DNS 允许我们将任何子域名解析到指定的 IP 地址。这意味着,无论何时我们需要添加新的服务或页面,都不需要为它们单独配置 DNS 记录。

1
2
*.local.codejerry.cn    => 内网服务器 IP
*.codejerry.cn => 公网服务器 IP

配置 SSL 证书

为了确保通过 HTTPS 访问的安全性,我们需要为我们的域名配置 SSL 证书。利用 Certbot 和 Let’s Encrypt,我们可以自动化这个过程,简化证书的申请和续期。以下是配置 SSL 证书的步骤:
这部分内容可以参考 Lets Encrypt配置泛域名证书

内网服务配置

内网服务配置的目的是确保内网服务能够通过 HTTPS 安全提供服务,并且对外开放。以下是配置内网服务的步骤:

  1. 在 Nginx 配置文件中设置一个新的服务器块。
  2. 配置监听 80 端口的 HTTP 服务,并将其重定向到 HTTPS。
  3. 设置监听 443 端口的 HTTPS 服务,并配置 SSL 证书路径。

安装nginx:

1
2
sudo apt update
sudo apt install nginx

然后编辑配置文件:

1
2
mkdir -p /etc/nginx/sites-available/single
vim /etc/nginx/sites-available/single/upload.conf

添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 80;
server_name upload.local.codejerry.cn;
client_max_body_size 0;
location /{
proxy_pass http://内网实际服务的 IP:端口;
}
}

server {
listen 443 ssl;
server_name upload.local.codejerry.cn;
client_max_body_size 0;
ssl_certificate /etc/letsencrypt/live/local.codejerry.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/local.codejerry.cn/privkey.pem;
location / {
proxy_pass http://内网实际服务的 IP:端口;
}
}

然后将上面的文件导入nginx生效的文件中

1
2
3
4
5
6
7
8
sudo vim /etc/nginx/sites-available/default
# 在文件最后添加如下:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

include sites-available/single/*.conf;

single文件夹里边可以存放很多其他服务,比如test.conf
然后保存退出,重新加载nginx

1
2
sudo nginx -t
sudo nginx -s reload

穿透服务配置

内网穿透是一个关键步骤,它允许我们的内网服务能够通过特定的穿透服务,在公网安全地访问。

我们将使用内网穿透工具(如frp)来实现这一点。具体来说,我们需要在内网服务器上配置相应的穿透规则,以便将来自公网的请求转发到内网服务。以下是具体配置的步骤:

  1. 在内网服务器上安装内网穿透工具。
  2. 配置穿透规则,指定本地和远程端口,以及流量的类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
[[proxies]]
name = "web 80"
type = "tcp"
localIP = "127.0.0.1"
localPort = 80
remotePort = 10080

[[proxies]]
name = "web 443"
type = "tcp"
localIP = "127.0.0.1"
localPort = 443
remotePort = 10443

这个配置创建了两个代理:web 80web 443web 80 代理将内网的 80 端口(通常用于 HTTP 流量)映射到远程端口 10080;而 web 443 代理则将内网的 443 端口(通常用于 HTTPS 流量)映射到远程端口 10443。通过这种方式,外部流量可以通过特定的远程端口安全地访问内网服务。

注:远程端口 10080 和 10443 可以不对外开放,它们只用于内网穿透服务,只能在远程服务器的内部访问。

具体FRP穿透可以参考博主的另一篇文章:内网穿透之FRP,域名访问本地服务【只看这篇就够了,收藏必备】

公网服务器配置

公网服务器的配置旨在使得外部用户能够通过公网域名访问内网服务。关键在于设置 Nginx 以正确地转发来自公网的请求到内网穿透服务。以下是配置公网服务器的步骤:
vim /etc/nginx/sites-available/serve.conf

  1. 使用 map 指令动态地映射请求头中的 host。
  2. 配置 Nginx 服务器块以监听公网域名,并将请求转发到内网穿透的端口。
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
## 公网服务器配置
map $http_host $host_with_local {
~^(.+)\.codejerry\.cn$ $1.local.codejerry.cn;
}

server {
listen 80;
server_name ~^(.*)\.codejerry\.cn$; # 使用正则表达式匹配所有子域名
# return 301 https://$host$request_uri;
location / {
proxy_pass http://127.0.0.1:10080; # 转发到内网穿透的端口
proxy_set_header Host $host_with_local; # 映射后的内网域名
}
}

server {
listen 443 ssl;
server_name ~^(.*)\.codejerry\.cn$; # 使用正则表达式匹配所有子域名
client_max_body_size 0;
ssl_certificate /etc/letsencrypt/live/codejerry.cn/fullchain.pem; # domain
ssl_certificate_key /etc/letsencrypt/live/codejerry.cn/privkey.pem; # .key
location / {
proxy_pass https://127.0.0.1:10443; # 转发到内网穿透的端口
proxy_set_header Host $host_with_local; # 映射后的内网域名
}
}

使用 map 指令将公网域名(如 upload.codejerry.cn)映射到对应的内网域名(如 upload.local.codejerry.cn)。然后在 server 块中,将所有匹配的公网域名请求转发到内网穿透指定的端口(此例中为 10080)。proxy_set_header Host $host_with_cn; 这一行确保内网服务通过请求头中的 host 来识别原始请求的域名。

此外,这种配置方式允许通过公网域名访问内网服务,即使 *.local.codejerry.cn 并未开启解析。这意味着,如果我们希望服务仅通过公网访问,可以将 *.local.codejerry.cn替换为任意域名,只要与公网端的配置相匹配即可。

增加404界面

由于浏览器的缓存机制,如果访问不存在的二级域名,也会跳转到已经正常的服务。为了避免这个情况,在/etc/nginx/sites-available/single/下新建一个404.conf,添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
server{
listen 80;
server_name *.local.codejerry.cn;
return 404;
}

server{
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/local.codejerry.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/local.codejerry.cn/privkey.pem;
server_name *.local.codejerry.cn;
return 404;
}

更新nginx配置

更复杂情况

对于Streamlit,Studio,GitLab,Overleaf等,可能在Nginx重设置其他请求头,以确保服务正常转发。上代码供参考:

1
2
3
4
5
6
7
8
9
10
11
location / {
... # 其他配置
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_set_header X-Forwarded-Host $server_name;
}

调试

以上过程如果出现问题,在调试时,逐步进行。可参考如下过程进行:

  1. 首先在/var/www/下新建一个helloworld文件夹,里边新建一个index.html,添加如下内容

    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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>我的简单 HTML 页面</title>
    </head>
    <body>

    <header>
    <h1>欢迎来到我的网站</h1>
    </header>

    <section>
    <h2>介绍</h2>
    <p>这是一个简单的 HTML 页面示例,用于演示基本的结构。</p>
    </section>

    <section>
    <h2>列表</h2>
    <ul>
    <li>项目 1</li>
    <li>项目 2</li>
    <li>项目 3</li>
    </ul>
    </section>

    <footer>
    <p>© 2024 我的网站. 版权所有.</p>
    </footer>

    </body>
    </html>
  2. 添加对应的nginx解析,在/etc/nginx/sites-available/single/下新建一个test.conf,添加如下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    server {
    listen 80;
    server_name test.local.codejerry.cn;
    root /var/www/helloworld;
    index index.html;
    }

    server {
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/local.codejerry.cn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/local.codejerry.cn/privkey.pem;

    server_name test.local.codejerry.cn;
    root /var/www/helloworld;
    index index.html;
    }
  3. 本地测试及访问curl http://test.local.codejerry.cn

  4. 测试内网穿透frp

    • frpc/frps --version
    • 云服务端,页面正常访问
  5. 云服务端配置 80 映射,以及 curl localhost(本地) curl localhost:穿透端口

一步一步来,莫急。

注:如果遇到几十上百个线程占用着80端口,如果想全部kill,执行下面的操作:

1
sudo kill -9 `sudo lsof -i:80 -t`

总结

通过以上步骤,使用双域名和内网穿透技术来管理服务系统。这种方法不仅提高了安全性,还极大简化了服务的维护和管理工作。这对于那些需要频繁更新和维护服务的人来说,无疑是一个非常实用的解决方案。