NGINX反向代理https
架构说明
我的实践是构建 docs.cloud-atlas.io :
HTTPS反向代理部署在阿里云上: 因为阿里云有稳定的公网带宽
HTTP服务器部署在 Raspberry Pi Cluster : 部署在家庭内部的微型 Raspberry Pi 系统
核心服务器完全是自己部署,可以自由扩展服务器集群规模而没有云计算的高昂费用
所有软硬件都由自己打造,可以充分锻炼
一个人的数据中心
技术堆栈个人家庭宽带没有公网IP,通过 CTunnel 打通阿里云公网服务器到家庭内部集群的通道
安装Nginx
Debian 系安装Nginx:
Arch Linux 安装Nginx:
# 需要安装certbot-nginx插件,以便能够让Certbot配置nginx
pacman -S nginx certbot-nginx
配置Nginx
备注
需要简单配置Nginx的服务域名 Nginx virtual host配置 这样后续执行 certbot
会自动修订配置完成TLS/SSL配置修订。
警告
如果服务器在墙内云上,如果没有备案, Cetbot
配置时反向访问HTTP 80端口会被云厂商拦截导致配置失败。请备案后执行,或者在海外服务器上配置后再迁移。
配置
/etc/nginx/conf.d/cloud-atlas.io.conf
( 这个文件命名只需要以.conf
结尾即可包含在/etc/nginx/nginx.conf
中,具体根据nginx版本发行版提供的/etc/nginx.conf
而定 ):
/etc/nginx/nginx.conf
包含的 Nginx virtual host配置 配置server {
listen 80;
listen [::]:80;
root /var/www/cloud-atlas.io;
index index.html index.htm ;
server_name cloud-atlas.io alias docs.cloud-atlas.io;
location / {
try_files $uri $uri/ =404;
}
}
在
/var/www/cloud-atlas.io
目录下创建一个测试index.html
内容如下:
index.html
<html>
<head>
<title>Welcome to Cloud Atlas</title>
</head>
<body>
<h1>Success! The cloud-atlas.io server block is working!</h1>
</body>
</html>
启动 Nginx ,并使用浏览器访问 http://cloud-atlas.io 确保看到测试页面内容
Let's Encrypt证书
备注
首先需要确保域名( docs.cloud-atlas.io
)已经指向了反向代理服务器,也就是我的阿里云公网服务器IP
这个步骤非常重要: Let's Encrypt就是通过域名指向的服务器来确保分发证书是合法的
安装
Certbot
:
apt-get update
apt-get install software-properties-common
add-apt-repository ppa:certbot/certbot
apt-get update
apt-get install python3-certbot-nginx
Certbot
dnf install epel-release
dnf install certbot python3-certbot-nginx
# 如果是Apache,则使用
# dnf install certbot python3-certbot-apache
运行
Certboot
:
certbot
为Nginx生成证书certbot --nginx
执行的输出信息
certbot
为Nginx生成证书...
Which names would you like to activate HTTPS for?
We recommend selecting either all domains, or all domains in a VirtualHost/server block.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: cloud-atlas.io
2: docs.cloud-atlas.io
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1 2
Requesting a certificate for cloud-atlas.io and docs.cloud-atlas.io
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/cloud-atlas.io/fullchain.pem
Key is saved at: /etc/letsencrypt/live/cloud-atlas.io/privkey.pem
This certificate expires on 2025-03-24.
These files will be updated when the certificate renews.
Deploying certificate
Successfully deployed certificate for cloud-atlas.io to /etc/nginx/nginx.conf
Successfully deployed certificate for docs.cloud-atlas.io to /etc/nginx/nginx.conf
Congratulations! You have successfully enabled HTTPS on https://cloud-atlas.io and https://docs.cloud-atlas.io
NEXT STEPS:
- The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
检查 /etc/nginx/conf.d/cloud-atlas.io.conf
可以看到 certbot
修订了配置:
Certbot
自动修订了 /etc/nginx/nginx.conf
包含的 Nginx virtual host配置 配置server {
server_name cloud-atlas.io alias docs.cloud-atlas.io;
root /var/www/cloud-atlas.io;
index index.html index.htm ;
location / {
try_files $uri $uri/ =404;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/cloud-atlas.io/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/cloud-atlas.io/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = docs.cloud-atlas.io) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = cloud-atlas.io) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name cloud-atlas.io alias docs.cloud-atlas.io;
return 404; # managed by Certbot
}
现在访问 https://cloud-atlas.io 或者 https://docs.cloud-atlas.io 就可以看到HTTPS的加密已经生效,并且证书是Let's Encrypt 签发的。
证书更新
certbot
提供了自动更新证书的能力,简单执行以下命令可以检查:
# 测试证书更新
certbot renew --dry-run
配置一个定时任务
sudo crontab -e
:
0 0,12 * * * python -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew --quiet
警告
我还没有实践,待补充完善
反向代理
上述已经完成HTTPS基本配置,接下来修订转发规则:
server {
server_name cloud-atlas.io alias docs.cloud-atlas.io;
#root /var/www/cloud-atlas.io;
index index.html index.htm ;
location / {
#try_files $uri $uri/ =404;
proxy_pass http://127.0.0.1:24180;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/cloud-atlas.io/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/cloud-atlas.io/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = docs.cloud-atlas.io) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = cloud-atlas.io) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name cloud-atlas.io alias docs.cloud-atlas.io;
return 404; # managed by Certbot
}
注意,这里必须要传递 header
,也就是 proxy_set_header
传递参数非常关键,没有这些参数,后端WEB服务器无法分辨访问域名,也就无法使用 Nginx virtual host配置 来提供合适的返回。
后端服务器
后端服务器运行在一个 Raspberry Pi 主机 192.168.7.241
上,简单的Nginx配置实现一个WEB服务器配置:
server {
listen 80;
listen [::]:80;
server_name cloud-atlas.io alias docs.cloud-atlas.io;
root /var/www/cloud-atlas.io;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
备注
我在实践完成后发现一个非常困扰的问题,safari经常正常访问网站。我最初以为是自己的配置问题,反复排查,但是最后发现问题在于阿里云对没有"网站备案"的HTTP/HTTPS会TCP reset。这个问题困扰的是只有Safari会出现异常(chrome和firefox正常)。
这个问题的根源在于阿里云对HTTPS的握手 SNI(Server Name Indication) 进行检查,因为Safari目前(2025年中)不支持 ESNI(encrypted SNI) 和 ECH (Encrypted Client Hello) ,导致明文的SNI泄露了客户端访问的域名。任何没有备案的域名访问都会被TCP RESET!
而Chrome和Firefox已经默认启用了 ECH (Encrypted Client Hello) 支持,所以访问网站时不会泄露SNI,所以始终正常。
目前(Safari不支持加密SNI),没有很好的解决方案:
我的域名是 cloud-atlas.dev ,这个
.dev
顶级域名工信部不提供备案,也就是无法在大陆租用的云主机上使用海外的VM价格太贵而且反向访问速度太慢,不太可能作为homelab的网关
我最终决定将方案改为使用 Cloudflare Tunnel 输出 cloud-atlas.dev ,构建自己的互联网集群。