这是一个面向 Linux NAS 的 dsh 部署模板:使用 Docker Engine、Docker Compose V2、Bash/GNU 工具和 host 网络,在 NAS 本地可写目录中运行 dsh、Authelia 与 Caddy。
它不是通用 NAS 方案。支持边界见支持范围。
多数 NAS 反代教程的做法是「容器监听 0.0.0.0 + 反代挂 Basic Auth」。对本项目不够,原因:
| 维度 | Basic Auth 方案 | 本方案 |
|---|---|---|
| 认证因子 | 单因子密码,浏览器常驻保存,每个请求都携带明文凭据 | Authelia 双因素(TOTP);会话 1 小时上限、5 分钟无操作失效,勾选「记住我」延长为 1 个月(可配置收紧) |
| 凭据泄露后果 | 密码即全部权限,无法按设备撤销 | 单个会话可单独登出;连续失败 5 次锁定 5 分钟 |
| 应用暴露面 | 容器监听 0.0.0.0,局域网可绕过反代直连应用 | dsh 只监听 127.0.0.1,绕过认证无路径;部署后硬校验实际 listener |
| AI Agent 风险 | 常见网页升级教程给容器挂 Docker socket,等价于交出宿主 root | 主容器无 socket、无 Docker CLI;升级走宿主机命令行,带快照与自动回滚 |
dsh 是能执行任意命令的 AI Agent,凭据一旦被拿走就是整台 NAS。所以这个模板的安全边界按「应用本身必然会被攻破」设计:认证是独立的双因素层,应用本体只绑回环,宿主控制权不出 NAS 命令行。
- Linux 主机、rootful Docker Engine ≥ 20.10、Docker Compose V2(
docker compose插件或 V2 独立版) deploy.sh会检查 Docker daemon 是否运行、Engine 版本是否达标(≥ 20.10)以及 Compose 是否为 V2;检测到问题时在交互终端提供自动补救选项——安装/升级 Engine(官方 get.docker.com 脚本)、启动 daemon、安装 compose 插件(apt)、把当前用户加入 docker 组——确认执行后自动重查,非交互环境则直接报错退出- 代理也是交互选项:Docker 环境检查通过后(以及同意自动安装/升级前)询问是否使用代理,选择持久化到
.env的DSH_PROXY(空值 = 直连);--proxy-host参数或.env已配置时不再询问。直连模式下构建不注入代理 build-arg,第 6 节跳过代理监听检测。 - 宿主机不需要安装 Node.js/npm——所有 Node 相关步骤(dsh 安装、patch、版本核验)都在容器内执行
- Bash 与 GNU
sed/grep/awk/stat - x86_64 和 ARM64
network_mode: host- 项目目录、
data/、authelia/data/、caddy/位于本地可写文件系统 - 容器内 dsh 以 UID 1000 运行,相关目录必须允许 UID 1000 写入
- TrueNAS CORE、非 Linux Docker 主机
- rootless Docker、不支持 host 网络的容器平台
- 仅提供 BusyBox、缺少 Bash/GNU 工具的环境
- 不能使用 Docker Compose V2 的 NAS 管理界面
- 没有域名且没有可用前置 TLS 代理的部署
公网浏览器
│
├─ 直连 80/443 或 443-only ──> Caddy ──> Authelia 127.0.0.1:9091
│ └─> dsh 127.0.0.1:3080
│
└─ 前置反代/Tunnel ──> Caddy(同机 127.0.0.1:13080,或跨机 NAS_IP:13080)
├─> Authelia 127.0.0.1:9091
└─> dsh 127.0.0.1:3080
dsh 出站请求 ──> .env 中配置的 HTTP/mixed 代理,或直连
- 三个容器都用 host 网络;容器内
127.0.0.1就是 NAS 宿主机。 - dsh 由
entrypoint.sh强制绑定127.0.0.1,局域网无法绕过 Caddy + Authelia 直连 3080。 deploy.sh启动后校验 dsh/Authelia/Caddy 的实际 listener,关键安全检查失败即非零退出。- dsh 容器不挂载 Docker socket,运行镜像不含 Docker CLI/Compose。
- 重启与升级都在 NAS 命令行执行:
docker compose restart dsh、sudo ./deploy.sh --upgrade。 - 必须使用域名 + Authelia 双因素;公网 TLS 可由 Caddy 直连模式或前置反代/Tunnel 终结。
三个模式的内部链路完全相同:Caddy → Authelia(双因素)→ dsh;Caddy 到 Authelia/dsh 都走宿主回环。唯一的区别是:谁来当公网入口、TLS 在哪里终结。
| 1. 直连 443-only(向导默认) | 2. 直连 80/443 | 3. 前置反代/Tunnel | |
|---|---|---|---|
| 公网入口 | NAS 上的 Caddy | NAS 上的 Caddy | lucky / CF Tunnel 等 |
| 需要 NAS 有公网 IP | 是(IPv4 或 IPv6) | 是(IPv4 或 IPv6) | 否 |
| 需要开放入站端口 | 仅 443 | 80 + 443 | 跨机时 DSH NAS 的 13080 仅允许 Lucky 来源;同机回环无需开放此端口 |
| 证书由谁管 | Caddy 自动(TLS-ALPN-01) | Caddy 自动(ACME) | 前置入口 |
| 访问地址 | https://dsh.<domain> |
https://dsh.<domain> |
https://dsh.<domain>[:端口] |
配置向导会写入 # dsh-nas-entry-mode: ... 标记,部署脚本据此恢复模式并执行对应的端口与 listener 校验。
流量路径:浏览器 →(公网)→ NAS:443 → Caddy → Authelia/dsh
- 需要公网 IP(IPv4 或 IPv6 都行——没有公网 v4 时用 AAAA 记录走 IPv6 也算直连),但只要求 443 可达,80 被占用或被封也能用
- Caddy 只监听 443;不提供 HTTP→HTTPS 跳转,浏览器必须显式输入
https:// - 证书走 TLS-ALPN-01(只需 443 可达);只能 DNS-01 的环境需自行配置 Caddy DNS provider
- 部署后 Caddy 必须只报告
:443
流量路径:浏览器 →(公网)→ NAS:80/443 → Caddy → Authelia/dsh
- Caddy 自己就是公网入口:域名 A/AAAA 记录指向 NAS 的公网地址(动态 IP 配 DDNS),路由器把 80、443 转发进 NAS
- 80 用于 HTTP→HTTPS 自动跳转和 ACME HTTP-01 证书验证
- 如果运营商封禁 80,建议在向导中选择模式 1(443-only);脚本不会自动切换入口模式
- 部署后 Caddy 必须只报告
:80、:443
流量路径:浏览器 →(公网)→ lucky/CF 等前置入口(在这里终结 TLS)→ DSH NAS 的 Caddy 内部入口 → Authelia/dsh
- Caddy 不接触公网:
auto_https off,同机模式监听127.0.0.1:13080;跨机模式监听 DSH NAS 的具体局域网地址(例如192.168.123.131:13080) - 同机 Lucky 后端填
http://127.0.0.1:13080;跨机 Lucky 后端填http://192.168.123.131:13080 - 跨机模式的 Caddy
auth与dsh两个站点都用remote_ip仅允许 Lucky 的实际 TCP 来源(例如192.168.123.1),并且 NAS 防火墙只允许该来源访问 TCP 13080;禁止使用 X-Forwarded-For 作为 ACL - Lucky 的公网 HTTPS 由 Lucky 终结;Lucky→Caddy 默认是明文 HTTP,因此必须确保局域网可信,敏感环境应改用加密的内部链路
- NAS 不需要公网 IP,但跨机模式需要开放受限的入站 TCP 13080;CF Tunnel 纯出站时仍可使用同机回环模式
- 两个前置入口都要把
dsh.<domain>和auth.<domain>转发到 Caddy,并设置X-Forwarded-Proto: https - 前置监听的非标公网端口(如 16666)会写进 Authelia URL 和访问地址
选择顺序(编号与向导一致):有公网 IP 且 80/443 都可达、空闲 → 模式 2;有公网 IP、443 可达但 80 不可用 → 模式 1;没有公网 IP,或 NAS 上已经有 lucky/CF 入口 → 模式 3。
-
出站网络(代理可选):可以直连时,在向导中选择不使用代理,保存为
.env的DSH_PROXY=。使用 HTTP/mixed 代理时,脚本优先建议探测到的 NAS 局域网 IP 加:7890;探测失败才回退到127.0.0.1:7890,但回环地址不适合镜像构建。验证示例(请替换为实际代理地址):curl -x http://192.168.1.10:7890 -sI https://api.deepseek.com
可用
./deploy.sh --proxy-host 192.168.1.10:7890指定代理,并确保代理允许局域网访问。即使代理装在同一台 NAS,构建时也需使用构建容器可达的局域网地址;以上宿主 curl 检查不能代替构建容器的连通性验证。 -
域名和 DNS:需要
dsh.<domain>与auth.<domain>两个主机名。直连模式解析到 NAS;前置反代模式解析到公网 TLS 入口。 -
本地目录:项目目录和运行数据须位于本地可写文件系统(非只读挂载、可
chown)。容器内 UID 1000 需要写入:mkdir -p data/dsh data/workspace \ caddy/data caddy/config sudo chown -R 1000:1000 data/dsh data/workspace
不要对整个
data/做chown -R:data/本身保持部署用户所有,容器只需要写入上面两个子目录。往
data/workspace放入文件(SSH/SMB/文件管理器拷贝)后,属主通常不是 UID 1000,agent 只读不可写;拷贝完成后执行sudo chown -R 1000:1000 data/workspace修正(该目录为容器专属,整目录递归 chown 安全)。在 dsh 网页里新建的目录以 UID 1000 创建,无此问题。Authelia/Caddy 配置文件只读挂载;
authelia/data/、caddy/data/、caddy/config/由对应容器写入,deploy.sh会做实际写入测试。 -
SSH 和 Docker 权限:能在 NAS 上执行 Docker 命令。
推荐只用部署脚本:启动前执行 Compose、Authelia 配置和 Caddyfile 语法校验,再执行目录、端口、健康和 listener 检查。
cd dsh-nas
chmod +x deploy.sh
./deploy.sh首次运行时向导会:
- 写出站代理地址到
.env; - 询问根域名、证书邮箱和 Authelia admin 密码;
- 选择三种 Caddy 入口模式;
- 自动生成 Authelia 密钥、用户密码哈希和 Caddyfile(被改写文件备份
.bak); - 询问是否启用 DSH 反代域名 patch;启用时将 hostname 保存到
.env,构建阶段以 root patch DSH bundle;不启用则保持原始 loopback-only 行为; - 检查文件、目录、端口、代理连通性;
- 构建前交互选择 dsh 版本:展示 Dockerfile 锁定版、npm
latest正式版、npmnext/alpha预览版四个选项(各带版本号),选择后写回Dockerfile的ARG DSH_VERSION;回车默认保持锁定版,非交互或 registry 不可达时自动按锁定版继续; - 构建并启动,等待健康检查并校验 listener;全部通过后只清理带本项目专用标签的 dangling 旧 dsh 镜像,不再执行宿主全局镜像清理。
sudo ./deploy.sh --upgrade # 交互选择 dsh 版本(锁定版/latest/next/alpha)后重建
sudo ./deploy.sh --latest # 自动选择 npm latest 后重建;仍交互询问 patch
sudo ./deploy.sh --alpha # 自动选择 npm alpha 预览版后重建;仍交互询问 patch- 升级需 root(事务快照与升级锁在 root-only 的
/var/lib/dsh-nas-upgrade)。 - 升级时若
127.0.0.1:3080被非预期进程/容器占用(如残留容器),脚本会列出占用者并询问:手动停止后重跑,或由脚本自动停止(容器stop+rm、宿主进程kill);释放后才能继续升级。 - 跳过 Caddy/Authelia 配置向导,但仍交互询问是否启用/更新 DSH 反代域名 patch;构建前还会交互选择 dsh 版本(
--latest直接取 npm latest,--alpha直接取 npm alpha,二者均跳过选择);构建前保存配置、.env、旧镜像 ID 和容器状态,版本选择发生在快照之后,失败回滚仍恢复旧版本号。 flock防并发;版本文件原子替换。- 构建、启动、健康或 listener 校验失败时自动恢复旧版本文件、旧镜像和旧 dsh 服务;恢复失败仍非零退出。这是单机回滚保护,不是蓝绿发布。
- 运行数据在
data/,不会因重建丢失。
./deploy.sh --skip-build # 使用已有 dsh 镜像启动
./deploy.sh --proxy-host 192.168.1.10:7890 # 设置构建和运行时代理
./deploy.sh --setup # 重新选择域名、入口或密码
./deploy.sh --cookie-max-age 90 # dsh Web 会话 cookie 有效期(天;写入 .env,无需重建镜像)
./deploy.sh url # 打印最新一条 dsh web 启动 URL,并自动拼好公网地址(https://dsh.example.com[:公网端口]/?token=…)
sudo ./deploy.sh update-script # 升级本脚本(git 快进拉取;自动保护本地配置与 Dockerfile 版本)脚本自更新提示:
update-script保持原有自动更新流程,不增加确认或自动备份。更新前的git checkout -- .会丢弃未受保护的已跟踪文件的未暂存修改(例如手工修改的脚本、Compose 文件或 Dockerfile);即使后续快进合并失败,这些修改也不会自动恢复。未跟踪文件不会被此 checkout 删除,已暂存修改可能阻止合并。Caddyfile/Authelia 沿用skip-worktree保护,但它不等于备份;.env与data/的保护依赖项目原有的未跟踪约定。Dockerfile 的版本选择仅在合并成功后尝试写回,其它未暂存修改不保留。如果手工改过项目文件,请在运行此命令前自行备份。
dsh v0.1.2-alpha.1 起,Web 界面接入应用层浏览器会话认证(对应官方「网络访问 Web 界面时启用链接中的一次性 token 认证鉴权」):
- 每个 dsh 进程生成随机启动令牌;
dsh web启动时打印一次带?token=…的 URL。首次打开该 URL 会签发一个 30 天(可配)签名 cookie 并 303 跳回干净地址;此后所有 Web 会话(含/apiRPC)都要求该 cookie,缺失/过期一律 401。Authelia 双因素层不受影响,仍在其前端。 - 必须的配套改动:compose 健康检查已改为「任何 HTTP 响应都算存活」(未带 cookie 的
/返回 401 是正常行为,不再导致 unhealthy)。 - 每月/续期操作:cookie 密钥持久化在
data/dsh/.credentials.yaml,重启/重建不影响已签发 cookie;过期后取令牌有两种方式——SSH 执行./deploy.sh url(自动从日志取最新 token 并拼好https://dsh.example.com[:公网端口]/?token=…公网地址,直接复制打开),或直接看 NAS 官方 Docker 管理界面里 dsh 容器日志的dsh web:行,复制?token=段自己拼到公网地址后打开(自动 303 回干净地址,cookie 重新起算)。部署/升级脚本跑完的「结果」摘要里也会直接打印这条会话链接。多个浏览器各自独立持 cookie、独立起算;无痕窗口关闭即失效。 - 调整有效期:
./deploy.sh --setup向导会询问,或直接--cookie-max-age DAYS;保存在.env的DSH_COOKIE_MAX_AGE_DAYS,容器 entrypoint 据此生成--patchoverlay(覆盖 connection 行cookieMaxAgeDays),不需要重建镜像,docker compose up -d dsh重建容器即生效。留空 = dsh 默认 30 天。改时长只对新签发的 cookie 生效,已有浏览器需重新打开一次 token URL 才能拿到新时长。 - trusted-domain patch 仍保留:客户端
connection.isLoopback依旧只看页面 hostname,设置页持久化与「在 NAS 上打开文件」等特权面仍依赖DSH_TRUSTED_DOMAINpatch;该 patch 与一次性 token 认证互不冲突(认证在全域生效,patch 只影响浏览器权限面判定)。 - 若要升级到 alpha 预览版,可使用
sudo ./deploy.sh --alpha;该参数读取 npmalphadist-tag,当前不受latest正式版发布节奏影响。预览版升级仍建议先备份data/dsh/并完成旧会话、Remote 和 WebSocket 恢复验收。
- dsh 版本唯一来源是
Dockerfile的ARG DSH_VERSION。 - 交互部署在构建前从 npm dist-tags 端点拉取
latest(正式版)与next/alpha(预览版)版本号,连同 Dockerfile 锁定版一起列出供选择(直连失败自动走代理重试);选定后原子写回Dockerfile。--skip-build不构建故不询问,--latest/--alpha已自动选定对应 dist-tag 不再询问。 - 版本号查询不受缓存影响:脚本不经 npm 本地缓存(纯 HTTP 直读 registry),每次请求追加唯一时间戳参数并携带
Cache-Control: no-cache请求头,穿透转发代理/CDN 等中间层可能按 URL 缓存的旧响应。 - 代理地址的构建/运行差异:构建容器有独立网络命名空间,
127.0.0.1在构建阶段是构建容器自身、代理不可达;运行时容器是 host 网络,127.0.0.1与局域网地址都可达。因此单一地址取交集 = NAS 局域网地址(代理需允许局域网访问,如 Clashallow-lan)。脚本自动探测出口网卡 IP(ip route get,兜底过滤后的hostname -I)作为默认建议值;坚持填127.0.0.1会警告构建不可达但照常保存(仅适合--skip-build场景)。 - 构建阶段的 apt/npm 下载走部署脚本传入的代理;直连模式(
DSH_PROXY=空值)下构建不注入代理参数。 - 运行时代理来自
.env的DSH_PROXY(首次部署交互选择,--proxy-host覆盖,空值 = 直连);compose 用-插值区分「未设置(回落默认 127.0.0.1:7890)」和「显式空(直连)」,entrypoint 对显式空值 unset 代理变量。NO_PROXY至少含127.0.0.1,localhost。 - 可选的
.env键DSH_TRUSTED_DOMAIN只接受纯 hostname(如dsh.example.com),不含协议、端口或路径;非空时 Dockerfile 在 root 构建阶段 patch DSH 的浏览器与 Host API loopback 判定。修改此值必须重新构建 dsh 镜像;执行--upgrade/--latest时脚本会再次询问并保留或修改选择。 - patch 过程可见性:patch 在构建的 RUN 步骤内执行,BuildKit 进度 UI 会折叠其输出;构建完成后(以及
--skip-build启动前)脚本会进入镜像实测核验并在部署日志打印结论——两个 bundle 是否接受反代域名、镜像内 dsh 版本是否与 Dockerfile 锁定一致,不一致则拒绝启动。需逐行查看 patch 原始输出可运行BUILDKIT_PROGRESS=plain docker compose build dsh。 - 首次构建需编译 native 依赖,耗时取决于 NAS CPU 和代理速度。
.dockerignore保证构建上下文不含运行数据、密钥和部署脚本;构建还需要patch-trusted-domain.mjs。
向导会自动完成;手动配置时参考:
-
替换
authelia/configuration.yml、authelia/users_database.yml和 Caddyfile 中的域名为真实域名; -
用随机值替换
CHANGE_ME_*密钥; -
生成 argon2id 密码哈希:
docker run --rm authelia/authelia:4.39 \ authelia crypto hash generate argon2 --password '你的密码'
-
docker compose --profile auth up -d启动; -
打开
https://auth.<你的根域>[:公网端口],用户名为admin,密码为向导设置的密码;按提示注册手机动态验证码(TOTP)。注册验证通知见authelia/data/notifications.txt,它不是手机之后持续生成的动态码。 -
完成双因素认证后,打开部署结果中的 dsh 会话链接;也可执行
./deploy.sh url获取链接。旧版没有 token 认证时使用普通 dsh 访问地址。
Authelia 登录会话与 dsh 浏览器 cookie 是两层独立认证:dsh cookie 的默认 30 天不替代 Authelia 的会话时限,token 链接也不替代用户名、密码和双因素登录。
以 Lucky 在 192.168.123.1、DSH NAS 在 192.168.123.131 为例:
- 在 Lucky 配置公网 HTTPS listener 和证书;
dsh.<domain>和auth.<domain>都转发到http://192.168.123.131:13080;不要填 DSH 的3080;- DSH 配置向导选择“前置反代”→“Lucky 在其它机器”,Caddy 监听
192.168.123.131:13080,来源限制填写192.168.123.1; - DSH NAS 防火墙只允许
192.168.123.1 → 192.168.123.131:13080/TCP,拒绝其它来源; - dsh 规则开启 WebSocket;
- 两条规则都设置
X-Forwarded-Proto: https; - 向导中的公网端口与 Lucky 实际监听端口一致;
- Caddy 使用
remote_ip检查 Lucky 的直接 TCP 对端,不使用client_ip、X-Forwarded-For、PROXY protocol 做来源 ACL。
如果 Lucky 与 DSH 同机,则选择同机模式并使用 http://127.0.0.1:13080。跨机 Lucky→Caddy 默认是明文 HTTP,必须确保局域网可信;需要加密时应在网络层提供隔离或加密链路。
部署、健康及安全检查全部通过后,脚本自动清理带标签 io.github.gehennawu.dsh-nas.cleanup=dsh 的悬空镜像。标签只写入 Dockerfile 的最终 dsh 运行镜像,不写入基础镜像或中间构建阶段。
- 清理仍使用 Docker
image prune的引用保护,不加-a;有标签的镜像,以及被运行中或已停止容器引用的镜像均保留。 - 未带专用标签的历史 dsh 镜像不会补标或自动删除;首次更新后旧镜像可能继续占用空间,这是保守行为。
- Caddy、Authelia 及未使用此专用标签的其它项目镜像不在清理范围内。
- 标签是项目归属约定,不是权限边界;其它项目不要复用它。同一宿主上的多个 dsh-nas 副本使用同一标签,属于同一项目清理范围。
- 清理失败只显示警告,不撤销已成功的部署;前后数量统计可能受同时进行的 Docker 操作影响。
- 只有之后重新构建的 dsh 镜像才会带标签,
--skip-build不会修改旧镜像。无需为补标签立即重建或重启服务。
部署过程按 [1/8] 至 [8/8] 展示阶段和累计耗时;健康等待首次、状态变化或每约 15 秒输出 dsh/Authelia/Caddy 状态。约 240 秒为原有轮询等待窗口,不是包含 Docker 命令执行时间的严格截止时间。本次仅优化展示,不改变等待次数、失败判断或回滚流程。
结果页展示成功/失败、检查计数和总耗时,并将查看日志、重启服务命令分行。提前中止或升级回滚仍走原有报错路径,不保证出现最终结果页。密钥与 token 的显示方式保持不变。
颜色仅在支持的交互终端启用;非终端输出、TERM=dumb 或设置非空 NO_COLOR 时使用无 ANSI 颜色的文本。无需安装终端 UI 工具。例如 NO_COLOR=1 ./deploy.sh --help 可查看无颜色帮助。
docker compose ps
docker compose logs -f dsh
docker compose restart dsh社区插件用 dsh CLI 管理:
docker exec -it dsh dsh plugin --profile web add <插件包名>
docker exec -it dsh dsh plugin --profile web remove <插件名>
docker compose restart dsh| 现象 | 处理 |
|---|---|
| 配置向导拒绝继续 | 检查域名、密钥、用户哈希和 Caddyfile 模式标记;必要时 ./deploy.sh --setup。 |
| 80 被占用 | 443 空闲选 443-only;443 也被占用选前置反代模式。 |
| 443-only 证书申请失败 | 检查 A/AAAA、IPv6 防火墙和 443 可达性;看 docker compose logs caddy。 |
| 前置反代 502 /「后端访问被拒绝」 | 同机 Lucky 后端填 http://127.0.0.1:13080;跨机 Lucky 后端填 DSH NAS 的 http://192.168.123.131:13080,不能填 DSH 的 3080。同时检查 Lucky 的实际来源 IP、Caddy remote_ip ACL 和 NAS 防火墙。 |
| 部署报告 Caddy listener 或 ACL 不符合预期 | 同机检查 default_bind 127.0.0.1;跨机检查 default_bind 192.168.123.131、两个站点的 remote_ip 192.168.123.1 和 TCP 13080 防火墙规则;不要使用 X-Forwarded-For/client_ip 做 ACL。 |
| 未登录 401 或跳转循环 | 检查 Authelia URL、cookie domain、X-Forwarded-Proto 和 forward_auth 块。 |
| dsh 反复重启或 profiles 不可写 | sudo chown -R 1000:1000 data/dsh data/workspace,确认挂载在可写本地目录。 |
| 模型请求超时 | 检查 DSH_PROXY、代理监听地址和 docker compose logs dsh。 |
| 流式输出或 WebSocket 异常 | 检查前置代理的 WebSocket 设置。 |
| CLI 升级失败 | 脚本会尝试恢复快照、旧镜像和 dsh 服务;若恢复失败,按提示检查 Dockerfile、dsh:local 和 Compose 状态。 |
- 不要把 3080 暴露到局域网或公网;即使使用已有一次性 token 应用层认证的新版 dsh,也必须保持回环监听,由 Caddy + Authelia 保护公网入口。
- 不要给 dsh 容器挂载 Docker socket。
- 公网 TLS 必须由 Caddy 直连模式或前置代理/Tunnel 终结;跨机 Lucky→Caddy 的明文 HTTP 仅适用于可信局域网。
- 443-only 不会自动把 HTTP 跳转到 HTTPS;使用正确的
https://URL。
dsh-nas/
├── Dockerfile # 构建 dsh 镜像;唯一版本来源 ARG DSH_VERSION
├── deploy.sh # 部署/升级/回滚脚本
├── entrypoint.sh # 容器入口:代理、DSH_HOME 自检、回环绑定启动
├── patch-trusted-domain.mjs # 可选:构建阶段 patch DSH loopback 判定
├── docker-compose.yml # 三容器 host-network 编排与健康检查
├── .dockerignore # 构建上下文排除运行数据、密钥和部署配置
├── caddy/
│ ├── Caddyfile # 向导按入口模式生成
│ ├── data/ # 证书与运行数据
│ └── config/ # 配置状态
├── authelia/
│ ├── configuration.yml # 宿主维护,容器只读挂载
│ ├── users_database.yml # 用户库与 argon2id 哈希,容器只读挂载
│ └── data/ # SQLite 与通知文件,容器写入
├── data/
│ ├── dsh/ # → /home/node/.dsh,UID 1000
│ └── workspace/ # → /workspace,UID 1000
└── .env # deploy.sh 生成的 DSH_PROXY
升级事务快照与锁位于 /var/lib/dsh-nas-upgrade(root-only)。
MIT(见 LICENSE)。