当前位置: 首页 > news >正文

Nginx 网关别只会反代:Docker 部署 Nginx Proxy Manager,给家庭服务加一层安全边界

Nginx 网关别只会反代:Docker 部署 Nginx Proxy Manager,给家庭服务加一层安全边界

家里小主机跑的服务一多,端口很快就乱了:面板一个端口、监控一个端口、下载器一个端口、文档工具一个端口。为了图省事,有人会把每个服务端口都映射出去,甚至把 NAS 后台、Docker 管理面板、数据库端口一起暴露到公网。

这不是“方便”,这是把家里的管理入口摊开给公网看。

更稳的做法是:在家庭服务器前面加一层统一网关。内部服务继续跑在各自容器里,对外只露出必要入口;需要临时外网演示或访问时,再用 cpolar 暴露这个入口,而不是把每个服务端口逐个打出去。

这篇用 Docker 部署 Nginx Proxy Manager(下面简称 NPM),再准备一个whoami示例服务做反代测试。最后用 cpolar 临时映射 NPM 的演示入口,并把管理后台和真正的管理类服务留在内网。

Nginx Proxy Manager 是什么

Nginx Proxy Manager 可以理解为“带 Web 管理界面的 Nginx 反向代理”。它适合放在家庭服务器、小主机、NAS 旁边,统一管理多个 Web 服务入口。

它能帮你做几件事:

  • 给多个内部服务配置统一入口,例如grafana.hometools.homewhoami.home
  • 把外部访问转发到内部容器、局域网 IP 或指定端口;
  • 在 Web 界面里管理 Proxy Host,不用每次手写 Nginx 配置;
  • 给服务配置 HTTPS 证书;
  • 给入口加 Access List、Basic Auth、IP 访问控制;
  • 处理 WebSocket 转发,例如面板、终端、实时监控页面常用到的连接。

要注意:NPM 不是安全银弹。它只是把入口收拢起来,并提供更清晰的访问控制位置。真正的边界仍然来自:只暴露必要服务、关闭默认账号、强密码、日志审计、用完关闭临时隧道。

本文最终结构

本文会搭出这样一条链路:

外部临时访问者 ↓ cpolar 公网地址(临时暴露 8080) ↓ 家庭服务器:NPM 代理入口 8080 ↓ Docker 内部网络:whoami 示例服务 80

同时保留一个关键边界:

NPM 管理后台 81 端口只绑定 127.0.0.1,不公开到公网

也就是说,外部只访问被你允许的业务入口,不访问 NPM 管理后台。

环境约定

本文命令在 Linux 服务器上执行,适用于常见 Ubuntu、Debian、树莓派系统和多数 x86/ARM 小主机。已经安装 Docker 和 Docker Compose 插件。

先确认 Docker 可用:

docker version docker compose version

本文使用这些端口:

用途宿主机端口说明
NPM HTTP 代理入口8080后面用 cpolar 临时映射这个端口
NPM HTTPS 代理入口8443给真实域名 HTTPS 场景使用
NPM 管理后台127.0.0.1:81只允许本机访问,不公开
whoami 示例服务不映射宿主机只在 Docker 内部网络访问

如果你的服务器 8080 已被占用,可以把 Compose 文件里的8080:80改成其他宿主机端口,例如18080:80。后续 curl 和 cpolar 命令也要同步改端口。

第一步:创建 NPM 项目目录

先创建目录:

sudo mkdir -p /opt/npm-gateway cd /opt/npm-gateway sudo mkdir -p data letsencrypt mysql

目录用途:

/opt/npm-gateway/data # NPM 配置数据 /opt/npm-gateway/letsencrypt # 证书数据 /opt/npm-gateway/mysql # MariaDB 数据

第二步:编写 Docker Compose 文件

/opt/npm-gateway下创建docker-compose.yml

cd /opt/npm-gateway sudo tee docker-compose.yml > /dev/null <<'EOF' services: npm-db: image: jc21/mariadb-aria:latest container_name: npm-db restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: npm_root_password_change_me MYSQL_DATABASE: npm MYSQL_USER: npm MYSQL_PASSWORD: npm_db_password_change_me volumes: - ./mysql:/var/lib/mysql networks: - npm-net npm: image: jc21/nginx-proxy-manager:latest container_name: npm restart: unless-stopped depends_on: - npm-db ports: - "8080:80" - "8443:443" - "127.0.0.1:81:81" environment: DB_MYSQL_HOST: npm-db DB_MYSQL_PORT: 3306 DB_MYSQL_USER: npm DB_MYSQL_PASSWORD: npm_db_password_change_me DB_MYSQL_NAME: npm DISABLE_IPV6: "true" volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt networks: - npm-net whoami: image: traefik/whoami:latest container_name: whoami-demo restart: unless-stopped networks: - npm-net networks: npm-net: name: npm-net EOF

这份 Compose 做了三件事:

  1. 启动 NPM;
  2. 启动 MariaDB 保存 NPM 配置;
  3. 启动whoami示例服务,用来测试反代。

这里没有把whoami的端口映射到宿主机,它只能在npm-netDocker 网络里被访问。NPM 后面会通过容器名whoami-demo转发到它。

第三步:启动服务并检查容器状态

执行:

cd /opt/npm-gateway sudo docker compose up -d

查看容器:

sudo docker compose ps

正常会看到npmnpm-dbwhoami-demo都处于 running 状态。

再看日志:

sudo docker logs --tail=80 npm sudo docker logs --tail=80 npm-db

确认宿主机端口监听:

sudo docker port npm

输出里应包含:

80/tcp -> 0.0.0.0:8080 443/tcp -> 0.0.0.0:8443 81/tcp -> 127.0.0.1:81

这个结果很关键:81只绑定在127.0.0.1,说明 NPM 管理后台没有直接暴露到局域网和公网。

第四步:初始化登录 NPM,并立刻修改默认账号

在服务器本机打开:

http://127.0.0.1:81

如果你是在另一台电脑上操作服务器,可以用 SSH 本地端口转发打开管理后台:

ssh -L 8181:127.0.0.1:81 user@your-server-ip

然后在本机浏览器访问:

http://127.0.0.1:8181

NPM 默认账号是:

Email: admin@example.com Password: changeme

第一次登录后,按页面提示立刻修改:

  • 管理员邮箱;
  • 管理员昵称;
  • 管理员密码。

密码建议至少 16 位,包含大小写字母、数字和符号,不要复用 NAS、路由器、邮箱、Git 平台的密码。

修改完成后,退出再用新账号登录一次,确认默认账号已经不能继续使用。

第五步:添加 Proxy Host,反代 whoami 示例服务

进入 NPM 后台,打开:

Hosts → Proxy Hosts → Add Proxy Host

在 Details 页填写:

Domain Names: whoami.local.test Scheme: http Forward Hostname / IP: whoami-demo Forward Port: 80 Cache Assets: Off Block Common Exploits: On Websockets Support: On

说明一下这里的关键点:

  • Domain Names是访问这个入口时使用的域名;
  • Forward Hostname / IP填容器名whoami-demo,因为 NPM 和 whoami 在同一个 Docker 网络;
  • Forward Port80,这是 whoami 容器内部服务端口;
  • Websockets Support建议打开,后续反代面板、监控、终端类应用时能少踩坑;
  • Block Common Exploits打开,可以拦截一部分常见恶意请求。

保存后,在服务器上测试:

curl -H 'Host: whoami.local.test' http://127.0.0.1:8080/

能看到类似输出:

Hostname: whoami-demo IP: 127.0.0.1 IP: 172.20.0.3 RemoteAddr: 172.20.0.2:xxxxx GET / HTTP/1.1 Host: whoami.local.test

这说明访问已经经过 NPM,再转发到了 whoami 容器。

第六步:给入口加访问控制或 Basic Auth

家庭服务不要默认裸奔。即使只是工具页,也建议在 NPM 层加一层访问控制。

打开:

Access Lists → Add Access List

创建一个访问列表,例如:

Name: demo-basic-auth Satisfy Any: Off

在 Authorization 页添加账号:

Username: demo_user Password: 换成你自己的强密码

在 Access 页可以继续加 IP 规则。如果只允许某个固定公网 IP 访问,可以添加:

Allow: 203.0.113.10/32 Deny: all

如果你没有固定公网 IP,就先只使用 Basic Auth。不要为了省事把管理后台、数据库、NAS 后台直接暴露出去。

然后回到刚才的 Proxy Host:

Hosts → Proxy Hosts → Edit → Access List

选择:

demo-basic-auth

保存后再次访问:

curl -i -H 'Host: whoami.local.test' http://127.0.0.1:8080/

会看到401 Unauthorized,说明 Basic Auth 生效。

带上账号密码测试:

curl -u 'demo_user:你的强密码' -H 'Host: whoami.local.test' http://127.0.0.1:8080/

能返回 whoami 内容,就说明反代和 Basic Auth 都正常。

第七步:配置 SSL 的正确姿势

NPM 的 SSL 配置在 Proxy Host 的SSL页。

如果你有自己的域名,并且域名已经解析到这个 NPM 入口,可以选择:

SSL Certificate: Request a new SSL Certificate Force SSL: On HTTP/2 Support: On HSTS Enabled: On

再勾选同意 Let's Encrypt 服务条款,填写邮箱,保存即可。

如果你只是用本文的whoami.local.test做本地测试,不申请 Let's Encrypt 证书。这个域名只用于本地 Host 头测试,不是公网可验证域名。

如果你使用 cpolar 的 HTTPS 公网地址访问,浏览器到 cpolar 公网入口这一段已经是 HTTPS;cpolar 到本机 NPM 这一段转发到8080的 HTTP 入口。临时演示、Webhook 调试、短时间外部访问可以这样做。长期公开服务建议使用自己的域名、固定地址和完整 HTTPS 配置。

第八步:用 cpolar 临时映射 NPM 演示入口

现在只映射 NPM 的代理入口8080,不映射81管理后台。

启动临时 HTTP 隧道:

cpolar http 8080

终端会输出公网访问地址,格式类似:

https://xxxx.cpolar.top

复制其中的域名部分,例如:

xxxx.cpolar.top

回到 NPM 后台,编辑刚才的 Proxy Host,在Domain Names里添加这个 cpolar 域名:

whoami.local.test xxxx.cpolar.top

保存后,在外部浏览器打开:

https://xxxx.cpolar.top

如果前面配置了 Basic Auth,浏览器会先弹出账号密码框。输入demo_user和你的强密码后,页面会显示 whoami 返回的信息。

这就是本文推荐的 cpolar 使用方式:只暴露必要的业务入口或演示入口。NPM 管理后台不公开,NAS 后台不公开,数据库不公开,Docker 管理端不公开。

临时访问结束后,在运行 cpolar 的终端按:

Ctrl+C

隧道关闭后,公网地址不再转发到你的本机服务。

为什么不直接暴露每个服务端口

假设你家里有这些服务:

服务内部端口风险
NAS 后台5000/5001管理权限过高,不应直接公网暴露
Docker 面板9000能操作容器和挂载目录
Grafana3000监控数据和内部地址不适合直接对公网展示
Home Assistant8123涉及家居设备控制
数据库3306/5432不应作为 Web 服务暴露
临时演示页80/8080可以单独加认证后短期开放

把每个端口都暴露出去,等于把攻击面拆成很多入口。入口越多,越难统一审计,也越容易忘记关闭。

NPM 的价值是把入口收拢:

  • 外部只看到你允许的域名和路径;
  • 每个入口都能单独加 Basic Auth 或访问列表;
  • 后端服务可以继续留在 Docker 内部网络;
  • 日志集中在 NPM 和具体服务里;
  • 临时外网访问只需要开一个 cpolar 隧道。

安全边界清单

部署完成后,按这张清单检查:

  • NPM 默认账号admin@example.com / changeme已经改掉;
  • 管理员密码使用强密码,且没有复用其他平台密码;
  • NPM 管理后台81端口只绑定127.0.0.1
  • cpolar 只映射8080代理入口,不映射81管理后台;
  • NAS 后台、数据库、Docker 面板、路由器后台不直接暴露到公网;
  • 对外入口配置了 Basic Auth 或 Access List;
  • WebSocket 类服务已打开Websockets Support
  • 需要长期公开的服务使用自己的域名和 HTTPS;
  • 临时演示结束后关闭 cpolar 隧道;
  • 定期查看 NPM 日志和后端服务日志;
  • 不把真实密码、Token、数据库连接串写在公开页面里。

再强调一次:NPM 管理后台不要长期公开。你真正需要暴露的是被代理后的业务入口,而且应该是必要、低权限、可关闭、可审计的入口。

常见问题排查

1. 容器启动失败

先看状态:

cd /opt/npm-gateway sudo docker compose ps

再看日志:

sudo docker compose logs --tail=120 npm sudo docker compose logs --tail=120 npm-db sudo docker compose logs --tail=120 whoami

如果数据库密码改过,确认npm-db里的MYSQL_PASSWORDnpm里的DB_MYSQL_PASSWORD完全一致。

如果数据目录权限异常,执行:

cd /opt/npm-gateway sudo chown -R 1000:1000 data letsencrypt mysql sudo docker compose restart

2. 端口冲突

如果80808443已被占用,启动时会报 bind 失败。

查看占用:

sudo ss -lntp | grep -E ':8080|:8443|:81'

macOS 上可以用:

sudo lsof -iTCP:8080 -sTCP:LISTEN sudo lsof -iTCP:8443 -sTCP:LISTEN sudo lsof -iTCP:81 -sTCP:LISTEN

把 Compose 里的端口改成空闲端口,例如:

ports: - "18080:80" - "18443:443" - "127.0.0.1:81:81"

然后重启:

cd /opt/npm-gateway sudo docker compose up -d

后续访问和 cpolar 命令也要改成新端口:

cpolar http 18080

3. 反代返回 502

502 通常表示 NPM 找不到后端服务,按顺序查:

sudo docker inspect npm --format '{{json .NetworkSettings.Networks}}' sudo docker inspect whoami-demo --format '{{json .NetworkSettings.Networks}}'

确认两个容器都在npm-net网络。

再从同一个 Docker 网络里访问后端:

sudo docker run --rm --network npm-net curlimages/curl:8.8.0 -sS http://whoami-demo:80/

能返回 whoami 内容,说明后端可达。然后检查 Proxy Host:

Scheme: http Forward Hostname / IP: whoami-demo Forward Port: 80

不要把Forward Hostname / IP写成127.0.0.1。在 NPM 容器里,127.0.0.1指的是 NPM 容器自己,不是宿主机,也不是 whoami 容器。

4. WebSocket 异常

表现通常是页面能打开,但终端、实时日志、监控刷新、通知连接不正常。

在对应 Proxy Host 里打开:

Websockets Support: On

保存后重新访问。如果后端应用还有自己的 WebSocket 路径或反代说明,按应用文档补充对应的自定义 Nginx 配置。

5. SSL 证书申请失败

Let's Encrypt 证书申请失败时,先检查三点:

  1. Domain Names填的是公网真实域名,不是whoami.local.test
  2. 域名解析到了你的公网入口;
  3. 证书验证请求可以到达 NPM 的 HTTP 入口。

查看 NPM 日志:

sudo docker logs --tail=120 npm

如果只是临时使用 cpolar 的 HTTPS 地址,不在 NPM 里给xxxx.cpolar.top申请证书;直接使用 cpolar 提供的 HTTPS 公网地址访问。

6. cpolar 地址打不开

先确认本地 NPM 入口正常:

curl -I http://127.0.0.1:8080/

再确认 cpolar 映射的是代理入口端口:

cpolar http 8080

不要写成:

cpolar http 81

81是 NPM 管理后台,不应该公开。

如果浏览器打开 cpolar 地址后显示 NPM 的默认页面或 404,说明 Host 没匹配到 Proxy Host。把 cpolar 域名添加到对应 Proxy Host 的Domain Names里,再保存。

7. 源站地址填错

这是家庭 Docker 反代里最常见的问题。

如果后端服务和 NPM 在同一个 Docker 网络,优先填容器名:

Forward Hostname / IP: whoami-demo Forward Port: 80

如果后端服务跑在局域网其他机器上,填那台机器的局域网 IP:

Forward Hostname / IP: 192.168.1.50 Forward Port: 3000

如果后端服务跑在宿主机本机但不在 Docker 网络里,Linux Docker 环境可以先给 NPM 增加 host gateway:

extra_hosts: - "host.docker.internal:host-gateway"

然后在 NPM 里填:

Forward Hostname / IP: host.docker.internal Forward Port: 3000

改完 Compose 后重启:

cd /opt/npm-gateway sudo docker compose up -d

收个尾:网关不是为了炫技,是为了少暴露

家庭服务器越折腾,越需要一个清晰的边界。

NPM 负责把多个服务入口收拢起来:哪个能访问、转发到哪里、要不要 Basic Auth、要不要 WebSocket、要不要 HTTPS,都放在同一个地方管理。cpolar 负责在需要外部临时访问时,给必要入口开一条临时通道。

关键不是“能不能从外网打开”,而是“只打开该打开的那一个”。

如果你照着做,建议先用whoami跑通反代链路,再接入真正的家庭服务。遇到问题也更好定位:是 Docker、Proxy Host、502、SSL、WebSocket、外网访问,还是安全边界设计出了问题。

你现在卡在哪一步?可以在评论区留一句:Docker、Proxy Host、502、SSL、WebSocket、外网访问,还是安全边界。我会按你的具体报错继续拆。

http://www.gsyq.cn/news/1455643.html

相关文章:

  • 低功耗蓝牙广播
  • AI工具如何撬动用户LTV?揭秘智能积分系统的3层数据闭环设计
  • 国内评价高的斜管沉淀池厂选哪家,水处理一体化设备/一体化废水的处理装置/污水处理厂设备,斜管沉淀池生产厂家选哪家 - 品牌推荐师
  • 星月工具箱:轻量集成,多功能离线应用,为电脑日常维护打造的高效助手
  • B站视频转文字:从技术实现到学习效率的革命性提升
  • 5分钟掌握Pulover‘s Macro Creator:Windows自动化神器的终极指南
  • ChatGPT也能“看图说话“?揭秘多模态大模型如何输入图片输出视频!
  • 解锁FLUX.1-dev模型权重:下载、配置与优化技巧大公开
  • 基于D882晶体管的水位报警器DIY:从原理到实战防溢水
  • 深信服AD负载均衡实战:从交换机VLAN划分到链路聚合,一次搞定多线接入
  • Apex Legends智能压枪终极指南:三像素检测技术的精准射击革命
  • 2026北京继承律师排行出炉:专业调解成新趋势,榜首实至名归 - GrowthUME
  • 【Claude Code】Invalid API key 密钥无效错误排查 + 凭证源冲突解决
  • 通达信缠论插件ChanlunX:3分钟实现股票走势智能识别,告别手动画线烦恼
  • 2026苏州建筑修缮行业优选榜单|专业外墙屋面渗漏治理企业 - 苏易修缮
  • 南京本地免砸砖防水修缮优选推荐|2026资质齐全服务商排行榜 - 苏易修缮
  • 快速上手Arduino Audio Tools:零基础构建专业级嵌入式音频应用的终极指南
  • 实时更新招投标信息网站排行 5家平台实测对比 - 互联网科技品牌测评
  • DolphinDB自定义聚合函数:UDAF详解
  • C#零基础通关第十四篇:吃透反射机制,看懂框架底层、实现动态编程与项目解耦
  • 6.3
  • AI工具与智能订阅整合失效真相大起底(93%团队忽略的3个协议层断点)
  • 数控机床CNC集中监控运维管理平台方案
  • 旧笔记本与树莓派改造:打造动态魔法相框的完整硬件与软件指南
  • 别只跑Demo了!用ONNX Runtime部署BGE嵌入模型,打造你的本地语义搜索服务
  • 6款论文降AI率平台亲测:键清零AI痕迹,这款性价比封神 - 降AI小能手
  • 井下昼夜施工利器,鼎讯 DXA-3S 光纤熔接机性能详解
  • 500张真实火情图像数据集,含火焰与烟雾双类别YOLO+VOC标注
  • 绝区零自动化脚本终极指南:从零开始掌握全自动游戏助手
  • 2026年 东莞视觉螺丝机源头工厂推荐榜:高精度定位与智能锁付技术实力之选! - 品牌企业推荐师(官方)