- 创建者: 虚拟的现实,上次更新时间:11月 04, 2024 需要 5 分钟阅读时间
1. 背景
家中有一台 all in one 的 NAS 设备,希望实现在公网实现 https://a.example.com 自动对应 NAS 设备的 A 服务,b.example.com 访问 B 服务。
1.1. 存在的问题
- 家庭使用的是移动捆绑宽带套餐,不提供公网 IP 地址
- 注册的域名没有完成 ICP 备案,无法直接使用国内云服务器的 80 和 443 端口
- 市面上的内网穿透工具和服务不够问题,出了问题只能等(还在使用 cpolar)
- 使用第三方穿透工具配置 SSL 证书比较繁琐
- 成本和穿透工具套餐的带宽不匹配
1.2. 考虑的解决方案
- 注册顶级域名(费用不高)
- 使用 letsencrypt 申请泛域名的 SSL 证书(免费,3个月续一次)
- ucloud 云主机(固定 IP,1M带宽)
- 在云主机配置 frp 服务端,启用 http 端口
- 在云主机配置 Nginx 实现 80 和 443 解析
- 在客户端配置 frp 客户端,定义需要访问的 http 应用
- 通过 Nginx 转发访问请求到 frps 的 http 端口
2. 组件配置
2.1. 云服务器1台
名称 | 描述 |
---|---|
操作系统 | ubuntu 22 |
公网IP | 117.xx.xx.xx |
开放端口 | 80,443,8002,8003,8004,20001~20010 |
frp | 0.60.0 内网穿透 |
nginx | 1.26.1 网站服务 |
acme.sh | 3.0.7 SSL 域名证书申请 |
2.2. 域名服务 cloudflare
类型 | 内容 | 指向 |
---|---|---|
A | file | 117.xx.xx.xx |
A | file1 | 117.xx.xx.xx |
txt | 参照acme.sh 提示 | 参照acme.sh 提示 |
3. 申请 SSL 泛域名证书
SSL 泛域名证书支持多个二级域名仅配置一条证书记录,acme.sh 脚本支持基于 DNS 的泛域名证书申请
4. 防火墙配置
需要同时配置操作系统的防火墙以及云服务器端的防火墙策略
sudo ufw allow 80 sudo ufw allow 443 sudo ufw allow 8003:8004/tcp sudo ufw allow 20001:20010/tcp sudo ufw status
5. Frp 配置
5.1. Frps 配置
以下内容在云服务器端配置,详细的 Frp 配置说明请参考官网文档
wget https://github.com/fatedier/frp/releases/download/v0.60.0/frp_0.60.0_linux_amd64.tar.gz tar zxvf frp_0.60.0_linux_amd64.tar.gz sudo mv frp_0.60.0_linux_amd64 /usr/local/frp #配置文件 sudo cat > /usr/local/frp/frps.toml << EOF bindPort = 8003 webServer.addr = "127.0.0.1" webServer.port = 8002 webServer.user = "admin" webServer.password = "admin" log.to = "/tmp/frps.log" log.level = "info" log.maxDays = 30 log.disablePrintColor = false detailedErrorsToClient = true auth.method = "token" auth.token = "token" vhostHTTPPort = 8004 subdomainHost = "waringid.com" allowPorts = [{ start = 20000, end = 20010 }, { single = 3001 },{ single = 3003 },{ start = 4000, end = 50000 }] maxPortsPerClient = 5 udpPacketSize = 1500 natholeAnalysisDataReserveHours = 168 EOF #配置为服务实现自启动 sudo cat > /usr/lib/systemd/system/frps.service << EOF [Unit] Description=Frp Server Service After=network.target syslog.target Wants=network.target [Service] Type=simple User=nobody Restart=on-failure RestartSec=5s ExecStart=/usr/local/frp/frps -c /usr/local/frp/frps.toml [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl start frps systemctl enable frps systemctl status frps ss -tln
5.2. frpc 配置
以下内容在需要内网穿透的设备上配置,以 windows 配置为例。先下载 windows 版本
serverAddr = "117.xx.xx.xx" serverPort = 8003 auth.method = "token" auth.token = "token" log.to = "./frpc.log" [[proxies]] name = "rdp" type = "tcp" localIP = "127.0.0.1" localPort = 3389 remotePort = 20001 [[proxies]] name = "zyfeng" type = "http" localPort = 8000 localIP = "127.0.0.1" subdomain = "file"
5.3. 验证 frpc
完成配置并确保启动正常后在浏览器输入 http://file.waringid.com:8004 应该可以访问内网的 127.0.0.0:80 端口。
当前直接使用 frp 的配置,没有经过 nginx 也没有加载 SSL 证书
6. Nginx 配置
6.1. 安装 Nginx
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \ | sudo tee /etc/apt/preferences.d/99nginx sudo apt update sudo apt install nginx
6.2. Nginx.conf
user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; log_format waringid '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" "$http_x_real_ip"'; access_log /var/log/nginx/access.log main; proxy_headers_hash_bucket_size 1024; types_hash_bucket_size 1024; set_real_ip_from 10.0.0.0/8; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; map $http_upgrade $connection_upgrade { default upgrade; '' ''; } include /etc/nginx/conf.d/*.conf; }
6.2.1. 其它版本的 Nginx.conf
注意配置文件中 proxy_set_header Host $host:80;
server { listen 80; server_name *.yourdomain.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name *.yourdomain.com; ssl_certificate /usr/local/nginx/conf/ssl/yourdomain.com.crt; ssl_certificate_key /usr/local/nginx/conf/ssl/yourdomain.com.key; client_max_body_size 50m; client_body_buffer_size 256k; client_header_timeout 3m; client_body_timeout 3m; send_timeout 3m; proxy_connect_timeout 300s; proxy_read_timeout 300s; proxy_send_timeout 300s; proxy_buffer_size 64k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; proxy_ignore_client_abort on; location / { proxy_pass http://127.0.0.1:1234; proxy_redirect off; proxy_set_header Host $host:80; proxy_ssl_server_name on; 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; } }
6.3. proxy_params.conf
这个是代理的通用配置,用来配置实现 http 到 https 的重定向和反向代理的参数设置。该文件保存在 /etc/nginx/ 下。
set $host_fixed $http_host; if ($http_host = "") { set $host_fixed "default"; } set $test ""; if ($scheme = "http") { set $test "H"; } if ($test = H) { return 301 https://$host$request_uri; } proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_hide_header X-Powered-By; add_header X-Served-By $host; proxy_set_header Host $host; proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr;
6.4. default.conf
该文件在 /etc/nginx/conf.d/ 下面,该目录用来保存单独定义的配置文件。这里实现每个二级域名对应一个配置文件。default.conf 是默认配置文件,当访问的 URL 在其它个性化的配置文件中都没有匹配的时候就最终匹配该文件。
server { listen 80; server_name *.waringid.com default_server; return 301 https://$host$request_uri; }
6.5. file.conf
这个就是代理并展示内网设备的 web 页面配置,其中 SSL 证书的申请和配置请参考 letsencrypt 申请泛域名证书
server { listen 443 ssl; server_name file.waringid.com; access_log /var/log/nginx/file.log waringid; ssl_certificate_key cert/privkey.pem; ssl_certificate cert/fullchain.pem; ssl_trusted_certificate cert/chain.pem; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:8004; include proxy_params; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
6.6. dashboard.conf
这个是 frps 提供的管理 web 页面,在前面的配置中设置仅允许 127.0.0.1 的内部网络访问,该文件的大部分配置直接复用前面的内容。nginx 代理后可以在公网通过域名访问。
server { listen 443 ssl; server_name dashboard.waringid.com; access_log /var/log/nginx/dashboard.log waringid; ssl_certificate_key cert/privkey.pem; ssl_certificate cert/fullchain.pem; ssl_trusted_certificate cert/chain.pem; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:8002; include proxy_params; } }
提醒
验证成功后可以直接在云服务器的安全规则上禁止 8004 的端口开放,直接通过 Nginx 的内部转发即可实现访问访问
7. 进阶版
通过 frp 的 【vhostHTTPPort】选项的方式配置反向代理的模式还是不够安全和灵活。受云主机的配置所限不太可能启用高级的安全组件。能不能实现云主机将 http 访问请求直接转发到内网中的 WAF 主机,最终实现在云主机上仅需配置一次,后续公网访问的二级域名只在内网的 WAF 上配置即可。以下是上述需求的实现方式
7.1. frps 端的配置不变
7.2. frpc 增加 TCP 配置
将原来的 http 访问改为 tcp 的连接
[[proxies]] name = "home_web" type = "tcp" localIP = "192.168.77.12" localPort = 80 remotePort = 20002
7.3. 配置 nginx 的 default.conf
通过 default.conf 的配置实现按域名访问,如果没有输入正确的域名则返回403
server { listen 80 default_server; server_name _; return 403; }
7.4. 增加 ssl-ciphers.conf
将证书相关的配置整合到同一个文件,后续直接引用,简化配置文件
if ($scheme != "https") { return 301 https://$host$request_uri; } ssl_session_timeout 5m; ssl_session_cache shared:SSL:50m; # intermediate configuration. tweak to your needs. ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; ssl_prefer_server_ciphers off; ssl_certificate_key cert/privkey.pem; ssl_certificate cert/fullchain.pem; ssl_trusted_certificate cert/chain.pem;
增加 all-home.conf
通过该文件转发代理需求到 frp 设置的内网端口
server { listen 80; listen 443 ssl; server_name *.waringid.com; access_log /var/log/nginx/all.log waringid; include ssl-ciphers.conf; location / { proxy_pass http://127.0.0.1:20002; include proxy_params; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
8. 测试验证
http://file.waringid.com 会自动转为 https://file.waringid.com
http://dashboard.waringid.com 也类似 https://dashboard.warignid.com
泛域名验证
完成上述的 nginx 中 all.conf 配置并启用后直接在内网的 WAF 上增加 Host 记录就能实现2个域名同时正常访问了
8.1. Nginx 异常
Nginx 启动检测时出现以下的日志提醒
nginx: [warn] could not build optimal proxy_headers_hash, you should increase either proxy_headers_hash_max_size: 1024 or proxy_headers_hash_bucket_size: 64; ignoring proxy_headers_hash_bucket_size
nginx: [warn] could not build optimal proxy_headers_hash, you should increase either proxy_headers_hash_max_size: 1024 or proxy_headers_hash_bucket_size: 64; ignoring proxy_headers_hash_bucket_size
在 nginx.conf 的 http 段增加以下内容
proxy_headers_hash_bucket_size 1024; types_hash_bucket_size 1024;
- 无标签
添加评论