过去我的服务通过frp + nginx-proxy-manager的方案实现公网访问。但是面板形式的npm经常会遇到一些细枝末节的地方遇上小bug,不影响正常运行但总觉得有些膈应。另外任何暴露在公网上的面板都会有被爆破的风险。于是趁着时间充足,我打算退回最原始的nginx反代方案,手工配置conf文件并注册证书。
我的内网穿透方案总体没变,还是保持了如下结构:
阿里云跳板机:
frps:开内网穿透服务
nginx:负责反代
acme.sh:注册并续签证书
内网服务器:
frpc:与跳板机建立连接
各种内网服务
完成部署后,访问链路仍然不变:
用户 => 二级域名 => Nginx反代 => 本地映射端口 => frp => 内网服务
阿里云配置
阿里云控制面板需要配置三处地方。首先需要将域名指向云服务器IP,其次设置防火墙打开对应的监听端口,最后申请一个 accessKey 用于泛域名证书申请。
其中,在控制台中至少设置下面两个域名解析:
1@ A VPS_IP
2* A VPS_IP同时,accessKey面板处申请一个新的accessKey,申请后妥善保存:
1Ali_Key="AccessKeyId"
2Ali_Secret="AccessKeySecret"FRP配置
我们没有特殊需求,因此frps只需要做最简单的配置:
1bindPort = 1234
2auth.token = "auth_token"使用systemd创建守护进程,frps.service配置如下:
1# /etc/systemd/system/frps.service
2[Unit]
3Description = frp server
4After = network.target syslog.target
5Wants = network.target
6
7[Service]
8Type = simple
9ExecStart = /home/palemoons/frps/frps -c /home/palemoons/frps/frps.toml
10
11[Install]
12WantedBy = multi-user.targetfrpc照旧配置:
1serverAddr = "example.com"
2serverPort = 1234
3auth.token = "auth_token"
4
5[[proxies]]
6name = "proxy_name"
7type = "tcp"
8localPort = 443
9localIP = "127.0.0.1"
10remotePort = 4000unraid没有默认的守护进程,打包成一个docker容器又有点小题大做,因此我现在使用的是nohup挂在后台运行:
1cp /boot/config/frpc/frpc /usr/local/bin/frpc
2chmod +x /usr/local/bin/frpc
3
4/usr/local/bin/frpc -c /boot/config/frpc/frpc_ssh.toml > /var/log/frpc_ssh.log 2>&1 &
5/usr/local/bin/frpc -c /boot/config/frpc/frpc_app.toml > /var/log/frpc_app.log 2>&1 &证书申请
使用 acme.sh 作为 ACME 客户端,通过阿里云 DNS API 完成 DNS-01 验证。由于默认的 ZeroSSL 方案在续期的时候会遇上网络问题,因此需要手动设置申请 Let’s Encrypt 泛域名证书。证书覆盖根域名 example.com 和所有二级域名 *.example.com,无需为每个反向代理服务单独申请证书。
如果跳板机还没有 acme.sh,需要先安装:
1curl https://get.acme.sh | sh
2source ~/.bashrc设置阿里云 accessKey:
1export Ali_Key="AccessKeyId"
2export Ali_Secret="AccessKeySecret"申请根证书:
1acme.sh --set-default-ca --server letsencrypt
2acme.sh --issue --server letsencrypt --dns dns_ali \
3 -d example.com \
4 -d '*.example.com' \
5 --keylength ec-256安装证书:
1acme.sh --install-cert -d example.com --ecc \
2 --key-file /etc/nginx/ssl/example.com/key.pem \
3 --fullchain-file /etc/nginx/ssl/example.com/fullchain.pem \
4 --reloadcmd "sudo nginx -t && sudo systemctl reload nginx"后续各站点配置统一引用该目录下的证书文件:
1ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;
2ssl_certificate_key /etc/nginx/ssl/example.com/key.pem;NGINX配置
nginx在这里用于处理各种域名请求,接着将请求转发到对应端口上。我们需要用到的配置目录结构如下:
1/etc/nginx/
2 nginx.conf
3 snippets/
4 proxy-common.conf
5 sites-available/
6 default
7 example.com.conf
8 sub1.example.com.conf
9 sub2.example.com.conf
10 sub3.example.com.conf
11 ...
12 sites-enabled/
13 default => /etc/nginx/sites-available/default
14 example.com.conf => /etc/nginx/sites-available/example.com.conf
15 ...
16 ssl/
17 example.com.conf/
18 fullchain.pem
19 key.pem下面简单说明一下各文件的配置内容。
nginx.conf
主配置文件,负责定义全局运行参数,不直接配置具体网站或反代服务,只负责加载其他配置。
1user www-data;
2worker_processes auto;
3pid /run/nginx.pid;
4
5events {
6 worker_connections 1024;
7}
8
9http {
10 include /etc/nginx/mime.types;
11 default_type application/octet-stream;
12
13 sendfile on;
14 tcp_nopush on;
15 tcp_nodelay on;
16
17 keepalive_timeout 65;
18 types_hash_max_size 2048;
19
20 server_tokens off;
21
22 access_log /var/log/nginx/access.log;
23 error_log /var/log/nginx/error.log;
24
25 gzip on;
26 gzip_disable "msie6";
27
28 map $http_upgrade $connection_upgrade {
29 default upgrade;
30 '' close;
31 }
32
33 include /etc/nginx/sites-enabled/*;
34}具体各二级域名如何配置我们放在/etc/nginx/sites-enabled/*中。
snippets
放置一些通用的配置片段,这里我们只设置了一个proxy-common.conf,设置好反向代理的通用片段:
1proxy_http_version 1.1;
2
3proxy_set_header Host $host;
4proxy_set_header X-Real-IP $remote_addr;
5proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
6proxy_set_header X-Forwarded-Proto $scheme;
7proxy_set_header X-Forwarded-Host $host;
8
9proxy_set_header Upgrade $http_upgrade;
10proxy_set_header Connection "upgrade";
11
12proxy_buffering off;
13proxy_request_buffering off;
14
15client_max_body_size 1024m;
16proxy_read_timeout 3600s;
17proxy_send_timeout 3600s;sites-available
存放所有可用的站点配置文件,但如果不链接至sites-enabled处,则不生效。
1sites-available/
2 default
3 example.com.conf
4 sub1.example.com.conf
5 sub2.example.com.conf
6 sub3.example.com.conf
7 ...其中default是兜底的回退配置,example.conf是根域名主页配置,剩余的为其他子域名配置。
default
目标是将其他未设置的域名或uri访问重定向至根目录,同时禁用 http 访问。
1server {
2 listen 80 default_server;
3 listen [::]:80 default_server;
4
5 server_name _;
6
7 return 301 https://c/;
8}
9
10server {
11 listen 443 ssl default_server;
12 listen [::]:443 ssl default_server;
13 http2 on;
14
15 server_name _;
16
17 ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;
18 ssl_certificate_key /etc/nginx/ssl/example.com/key.pem;
19
20 return 301 https://example.com/;
21}example.com.conf
用于配置根域名网页配置,响应文件为/var/www/example.com/index.html。
1server {
2 listen 80;
3 listen [::]:80;
4
5 server_name example.com www.example.com;
6
7 return 301 https://example.com/;
8}
9
10server {
11 listen 443 ssl;
12 listen [::]:443 ssl;
13 http2 on;
14
15 server_name www.example.com;
16
17 ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;
18 ssl_certificate_key /etc/nginx/ssl/example.com/key.pem;
19
20 return 301 https://example.com/;
21}
22
23server {
24 listen 443 ssl;
25 listen [::]:443 ssl;
26 http2 on;
27
28 server_name example.com;
29
30 ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;
31 ssl_certificate_key /etc/nginx/ssl/example.com/key.pem;
32
33 root /var/www/example.com;
34 index index.html;
35
36 location = / {
37 try_files /index.html =404;
38 }
39
40 location / {
41 return 301 https://example.com/;
42 }
43}sub.example.com.conf
配置各二级域名,首先禁用 http 访问,然后将对该域名的访问转发至真正监听的端口上。
1server {
2 listen 80;
3 server_name sub.example.com;
4 return 301 https://$host$request_uri;
5}
6
7server {
8 listen 443 ssl;
9 http2 on;
10 server_name sub.example.com;
11
12 ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;
13 ssl_certificate_key /etc/nginx/ssl/example.com/key.pem;
14
15 location / {
16 proxy_pass http://127.0.0.1:1234;
17 include /etc/nginx/snippets/proxy-common.conf;
18 }
19}sites-enabled
存放实际生效的站点配置,通过刚刚说的软链接引用sites-available文件,例如:
1ln -s /etc/nginx/sites-available/sub1.example.com.conf /etc/nginx/sites-enabled/若要停用某站点,移除该文件夹中的软链即可。