通过 mDNS Gateway 为家庭 NAS 中的服务配置容易记住的内网地址。例如,把可道云的 http://192.168.50.30:2030 改为通过 http://media.local/ 访问,把 Media Harbor 的入口整理为 http://harbor.local/。

本次继续使用 Ubuntu 上的 Docker Engine,配置文件与数据放在 SSD 上。部署方式参考项目的 README 和 Compose 示例 ,使用发布镜像启动,不需要在 NAS 上编译源码。

为什么使用 mDNS Gateway

不用记住每个服务的端口

前面已经部署了可道云、下载器和 Media Harbor。服务一多,访问入口也跟着变多。IP 地址虽然相同,端口却各不相同。

使用 mDNS Gateway 后,可以给常用服务取一个直观的名字:

服务 原来的入口 配置后的入口
可道云 http://192.168.50.30:2030 http://media.local/
Media Harbor http://192.168.50.30:2050 http://harbor.local/

这里的地址是部署示例,实际使用时替换为自己的 NAS 地址与服务端口。

把名称解析和反向代理放在一起管理

仅给 NAS 配置一个主机名,访问应用时仍然需要带上端口。这个项目同时管理 mDNS 和 OpenResty:客户端先把 .local 域名解析到网关,再由网关按域名将请求转发到对应的服务。

浏览器访问 http://media.local/
        ↓ mDNS 解析
网关 192.168.50.30:80
        ↓ OpenResty 转发
可道云 192.168.50.30:2030

同一局域网中,支持 mDNS 的客户端通常不需要逐台修改 hosts,也不需要专门在路由器上维护这些名称。服务地址或端口变化时,只需在管理后台调整目标配置。

适合家庭内网的统一入口

项目提供 Web 管理界面,支持 HTTP、WebSocket 和 SSH 转发。保存站点时会先验证代理配置,再发布生效,减少手工维护多份配置的工作。

如果只部署一两个服务,继续使用 IP 和端口也很方便;当服务数量增加,或者希望家里的其他设备直接通过名称访问时,统一入口会更容易使用。

需要区分的是:这里解决的是内网名称解析与服务转发。mDNS 主要在同一网络链路内传播,跨 VLAN、访客 Wi-Fi 或不同网段可能无法发现;外网访问仍然使用前面配置的 Cloudflare Tunnel 等方案。项目目前也不包含 HTTPS 和证书管理。

部署前准备

本例假设 NAS 的局域网 IPv4 为 192.168.50.30,管理后台使用 TCP 2051。先查看真实网卡地址:

ip -4 -br addr

选择与客户端同一局域网的网卡 IPv4,后面填入 GATEWAY_IPV4。不要填写 Docker bridge 地址,也不要把其他目标设备的 IP 当作网关地址。建议在路由器中保留 NAS 的 DHCP 地址,避免重启后变化。

端口 用途
TCP 80 HTTP 与 WebSocket 代理入口
TCP 2051 本例的 Web 管理后台
UDP 5353 mDNS 查询与应答
TCP 2222 等 创建 SSH 站点时指定的独立转发端口

TCP 80 和管理端口需要未被其他服务占用。防火墙开启时,应按需允许局域网客户端访问这些端口。

准备目录

拉取镜像,创建配置与数据目录:

docker pull ghcr.io/leapsea/mdns-gateway:latest

mkdir -p /mnt/ssd/docker/compose/mdns-gateway
mkdir -p /mnt/ssd/docker/data/mdns-gateway
sudo chown 1000:2000 /mnt/ssd/docker/data/mdns-gateway

/data 保存 SQLite 数据库 gateway.db、主密钥 master.key 和 OpenResty 配置发布记录。数据库与主密钥需要一起保留;丢失 master.key 后,已有的 TOTP 密文无法恢复。

本例沿用项目镜像的默认运行身份,不额外设置 user,也不套用前文应用的 1000:2000 配置。数据目录包含认证信息,因此单独限制访问权限。

创建配置文件

Compose 配置

创建 /mnt/ssd/docker/compose/mdns-gateway/compose.yaml:

services:
  mdns-gateway:
    image: ${IMAGE:-ghcr.io/leapsea/mdns-gateway:latest}
    network_mode: host
    restart: unless-stopped
    volumes:
      - ${DATA_PATH}:/data
    environment:
      GATEWAY_LISTEN: ":${GATEWAY_HTTP_PORT:-2060}"
      GATEWAY_DATA_DIR: /data
      GATEWAY_OPENRESTY_BIN: openresty
      GATEWAY_IPV4: "${GATEWAY_IPV4}"
    healthcheck:
      test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1:${GATEWAY_HTTP_PORT:-2060}/health || exit 1"]
      interval: 30s
      timeout: 3s
      start_period: 15s

项目推荐原生 Linux 主机使用 network_mode: host,让 mDNS 组播通过真实网卡进入局域网。容器共享主机网络,因此这里没有 ports 映射,端口也直接占用宿主机端口。

管理后台的端口可以调整,HTTP/WebSocket 代理入口仍然使用 80。镜像自带健康检查默认访问 8080,这里同步覆盖为本例的管理端口。

环境变量

在同目录下创建 .env:

IMAGE=ghcr.io/leapsea/mdns-gateway:latest
DATA_PATH=/mnt/ssd/docker/data/mdns-gateway

GATEWAY_HTTP_PORT=2060
# 所在服务器IP
GATEWAY_IPV4=192.168.50.30

按实际环境修改 GATEWAY_IPV4。多网卡主机尤其需要明确指定广播所使用的局域网 IPv4。

启动服务

进入配置目录并启动:

cd /mnt/ssd/docker/compose/mdns-gateway
docker compose up -d --wait
docker compose logs --tail=100 mdns-gateway

日志中的 mdns interface selected 应显示真实局域网网卡与填写的 IP。检查后台健康接口:

curl http://127.0.0.1:2060/health

然后在浏览器访问 http://192.168.50.30:2060/。

初始化管理员

首次打开管理后台,按页面提示初始化管理员,并用验证器扫描 TOTP 二维码完成绑定。恢复码只显示一次,需要妥善保存。

管理员认证用于保护网关的管理后台,不会自动给被代理的应用增加登录验证。可道云、Media Harbor 等服务仍需使用各自的访问控制。

添加站点

HTTP 服务

在后台点击“添加站点”,以可道云为例填写:

配置项 示例值
名称 可道云
域名 media.local
目标主机 192.168.50.30
端口 2030
协议 HTTP
配置方式 自动生成
启用站点 勾选

保存后,在同一局域网的客户端打开 http://media.local/。再用相同方式添加 harbor.local,目标端口填写 2050,即可访问前面部署的 Media Harbor。

目标主机可以是网关所在的 NAS,也可以是网关能访问到的其他内网设备。.local 域名解析到的是网关,而不是直接解析到目标服务。

WebSocket 服务

应用需要 WebSocket 长连接时,选择 WebSocket 协议。网关会生成对应的 Upgrade 转发配置,入口仍使用 TCP 80,不需要额外设置网关监听端口。

SSH 服务

SSH 使用独立的网关端口,不能像 HTTP 那样仅靠域名区分多个服务。以转发 NAS 自身的 SSH 为例:

配置项 示例值
名称 NAS SSH
域名 shell.local
目标主机 192.168.50.30
端口 22
协议 SSH
网关端口 2222

SSH 站点使用自动生成配置,网关端口需要未被其他服务占用。保存后连接:

ssh -p 2222 ubuntu@shell.local

账号、密钥和主机指纹仍由目标 SSH 服务提供,网关只负责转发连接。

验证访问

在同一局域网的另一台客户端执行:

curl --noproxy '*' http://media.local/

如果无法访问,可以先绕过 mDNS 解析,只测试代理:

curl --noproxy '*' --resolve media.local:80:192.168.50.30 http://media.local/

如果第二条命令可以正常返回,而第一条失败,重点检查客户端的 mDNS 支持、UDP 5353、访客网络与 AP 隔离。Ubuntu 客户端需要系统解析器支持 mDNS,跨网段访问需要额外的 mDNS 转发设施。

如果代理请求本身也失败,再检查站点的目标地址、目标端口与网关日志。管理后台能打开、健康检查通过,都不能单独证明其他设备已经能够解析 .local 地址。

维护与备份

备份时先停止容器,再保存完整的 /mnt/ssd/docker/data/mdns-gateway 目录和部署配置,之后重新启动。数据库与 master.key 必须一起备份,不要只保留数据库。

需要更新镜像时,在配置目录执行:

docker compose pull
docker compose up -d --wait

升级前查看项目说明并备份数据。当前程序不会自动迁移旧数据库结构,不兼容时会明确报错;不要把更换镜像当作已完成数据升级。也不要让两个网关实例同时使用同一个数据目录。

丢失验证器时,优先使用恢复码。需要通过 CLI 重置管理员验证器时执行:

docker compose exec mdns-gateway mdns-gateway user reset-2fa admin

其中 admin 替换为实际管理员账户。重置后应立即在可信局域网中完成重新绑定;绑定完成前,知道账户名且能访问后台的人也可能抢先绑定。