唠唠闲话

在管理内网和公网服务时,可能会遇到这样的问题:如何既保证服务的安全性,又能简化维护和管理流程? 最近实践了一种解决方案:使用 Nginx 结合双域名配置来管理内网服务。在深入讲解具体的配置步骤之前,让我们先来看看希望实现的效果是什么样的。

假设我们在内网部署了一个服务,正常情况下,只能在内网环境访问。现在,我们想在公网安全访问服务,而不需要将其迁移到公网上。经过下边的配置,每次更新或新增内网服务,公网无需做任何多余的操作,就能自然地访问到内网对应的服务。

举个例子,如果内网的维护地址为 next-web.local.wzhecnu.cn,公网用户则通过 next-web.wzhecnu.cn 访问同一个服务,这样的好处在于:

  1. 安全性提高:服务不直接暴露在公网上,减少了潜在的安全风险。
  2. 维护简化:所有的更新和维护工作仅需在内网进行,无需额外关注公网服务的配置。
  3. 访问便捷:无论用户身处内网还是外网,都可以通过相应的域名访问到服务。

接下来,我们将详细介绍每一步的操作过程,并提供详细的代码和操作指南。

实战演示

内容概要

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

在实操设置中,我们将使用两个不同的域名泛解析:*.local.wzhecnu.cn*.wzhecnu.cn。前者将指向我们的内网服务器,而后者则用于公网访问。

注:借助内网穿透,理论上公网服务器只需对外提供网络服务就足够了,不需要有很高的配置。

设置泛解析 DNS

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

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

配置 SSL 证书

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

这部分内容可以参考 『Let's Encrypt 域名证书增强网站安全

内网服务配置

以下是配置内网服务的步骤,作为示例,我们在 /var/www/hello 下放置了一个简单的静态网页,命名为 index.html

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
<title>Hello, World!</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>

然后配置 Nginx 以将流量代理到内网服务的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# HTTP 到 HTTPS 的重定向
server {
listen 80;
server_name test.local.wzhecnu.cn;
# 可考虑设置重定向
# return 301 https://$host$request_uri;
root /var/www/hello;
index index.html;
}

# HTTPS 配置
server {
listen 443 ssl;
server_name test.local.wzhecnu.cn;

ssl_certificate /etc/letsencrypt/live/local.wzhecnu.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/local.wzhecnu.cn/privkey.pem;

root /var/www/hello;
index index.html;
}

现在,测试一下是否能够通过 test.local.wzhecnu.cn 访问到内网服务。如果一切正常,我们就可以继续配置内网穿透服务。

1
2
curl http://test.local.wzhecnu.cn
curl https://test.local.wzhecnu.cn

穿透服务配置

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

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

  1. 在内网服务器上安装内网穿透工具。
  2. 配置穿透规则,指定本地和远程端口,以及流量的类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# frp 内网穿透配置
[[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。通过这种方式,外部流量可以通过特定的远程端口安全地访问内网服务。

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

当然,更详细的介绍可以参考另一篇文章:『内网穿透 | FRP 使用教程

接下来,我们将在公网服务器的 Nginx 配置中引用这些端口,以确保来自公网的请求能够正确地被转发到内网服务。

公网服务器配置

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

  1. 使用 map 指令动态地映射请求头中的 host。
  2. 配置 Nginx 服务器块以监听公网域名,并将请求转发到内网穿透的端口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 公网服务器配置
map $http_host $host_with_cn {
default $http_host; # 避免重定向时丢失原始域名
~^(.+)\.wzhecnu\.cn$ $1.local.wzhecnu.cn;
}

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

这段配置首先使用 map 指令将公网域名(如 something.wzhecnu.cn)映射到对应的内网域名(如 something.local.wzhecnu.cn)。然后在 server 块中,将所有匹配的公网域名请求转发到内网穿透指定的端口(此例中为 10080):

  • proxy_set_header Host $host_with_cn; 使用映射后的内网域名,确保内网服务能正确地处理请求
  • default $http_host; 避免重定向时丢失原始域名,也即防止公网服务重定向时,变成内网访问的域名

此外,这个配置下,即使内网 *.local.wzhecnu.cn 的解析不存在,外网链接也能访问。也就是说,如果我们希望服务仅通过公网访问,可以将 *.local.wzhecnu.cn 替换为任意域名,只要与公网端的配置相匹配即可。

p.s. 对于复杂的服务,比如 GitLab,Overleaf,可能需要为 Nginx 设置其他请求头,以确保服务能被正常转发。贴一段常用的配置,供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 设置代理服务器以正确地处理 HTTP/HTTPS 请求和 WebSocket 连接
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

location / {
... # 其他配置
proxy_ssl_server_name on;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}

或者 streamlit 的配置:

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;
}

总结

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

如果有任何疑问或想法,请随时在下方留言讨论!