Copyparty 结合 Authentik 与 Traefik 的容器化 IdP 认证部署方案详解
本文以仓库中的 docs/examples/docker/idp-authentik-traefik 示例文件夹为主体,完整解析如何用 Docker Compose 将 copyparty 放到 Traefik 反向代理之后、并通过 goauthentik 作为身份提供方(Identity Provider)实现容器化的单点登录认证。读完你将掌握:整套 compose 文件中每个服务的职责与关键参数、cpp/copyparty.conf 中基于 ${u}/${g} 的动态卷权限体系,以及 copyparty 侧 IdP 头校验的源码级实现原理。需要特别注意:该示例作者在 README 中明确声明“无法保证其质量、安全与安全性,内容组合自网上找到的示例”,并标注“does not work yet”,因此本文将其定位为“架构参考与学习素材”,生产环境请自行完善后使用。
一、示例文件夹结构与总体架构
该示例文件夹(docs/examples/docker/idp-authentik-traefik/README.md)包含以下核心文件:
| 文件 | 作用 |
|---|---|
| docker-compose.yml | 六合一编排:copyparty、traefik、postgresql、redis、authentik_server、authentik_worker |
| cpp/copyparty.conf | copyparty 的 IdP 模式配置文件,挂载进容器 /cfg |
| based-on/docker-compose-authentik.yml | 参考来源:Authentik 官方 docker-compose 示例的存档 |
| based-on/docker-compose-traefik.yml | 参考来源:Authentik 官方 Traefik outpost 集成文档的示例存档 |
整体架构是:外部请求先到 Traefik(80 端口),Traefik 通过 Docker Provider 读取容器 label 发现路由;认证由 Authentik 完成,认证通过后由反向代理在请求头中注入用户名/组信息,copyparty 读取这些头完成授权。这与 docs/idp.md 中描述的通用 IdP 接入模型一致:copyparty 不做登录,只信任由反向代理注入的指定头。
示例 README 说明其基于 Authentik 官方的 docker-compose 示例与 Traefik 集成文档,并列出(不完全)的改动清单:
- 支持以 root 在 Fedora 上用 podman 运行(
:z卷标志、label:disableSELinux 选项); - 其他改动未完全列出。
二、docker-compose.yml 逐服务解析
完整编排文件见 docs/examples/docker/idp-authentik-traefik/docker-compose.yml,以下按服务展开。
2.1 copyparty 服务
copyparty:
image: copyparty/ac
container_name: idp_copyparty
restart: unless-stopped
user: "1000:1000" # should match the user/group of your fileshare volumes
volumes:
- ./cpp/:/cfg:z # the copyparty config folder
- /srv/pub:/w:z # host path shared online
ports:
- 3923
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.fs.rule=Host(`fs.example.com`)'
- 'traefik.http.routers.fs.entrypoints=http'
#- 'traefik.http.routers.fs.middlewares=authelia@docker' # TODO: ???
stop_grace_period: 15s
environment:
LD_PRELOAD: /usr/lib/libmimalloc-secure.so.NOPE
PYTHONUNBUFFERED: 1
关键点:
user: "1000:1000"必须与宿主机共享目录属主的 uid/gid 一致,否则容器内无权限读写数据卷;./cpp/挂载为容器内/cfg,即 copyparty 的配置目录;/srv/pub映射为容器内/w,这就是 cpp/copyparty.conf 中所有卷都指向/w/...的原因;:z卷后缀与label:disable是 SELinux/podman 场景下放行共享所需的写法;stop_grace_period: 15s:注释说明这是给缩略图生成器在收到关闭信号后继续收尾用的;LD_PRELOAD指向.NOPE是禁用状态,把NOPE改为2可启用 mimalloc(注释称会提速但占用双倍内存);- 那行被注释掉的
traefik.http.routers.fs.middlewares=...带# TODO: ???注释——这正是 README 所说“尚未跑通”的直接体现:认证中间件尚未挂到 fs 路由上,即请求还没有被强制经过 IdP 认证。
2.2 traefik 服务
traefik:
image: traefik:v2.11
container_name: traefik
volumes:
- /var/run/docker.sock:/var/run/docker.sock # WARNING: full root-access to host
security_opt:
- label:disable
ports:
- 80:80
command:
- '--api'
- '--providers.docker=true'
- '--providers.docker.exposedByDefault=false'
- '--entrypoints.web.address=:80'
要点:使用 Docker Provider 监听容器 label 做服务发现,exposedByDefault=false 表示只有显式带 traefik.enable=true 的服务才会暴露(本文件中仅 copyparty 服务带该 label);挂载 docker.sock 会授予 Traefik 对宿主机的 root 级访问,compose 中的 WARNING 注释已明确提示此风险;label:disable 用于在 Fedora/SELinux 环境下禁用对 docker.sock 的 SELinux 拦截。
2.3 数据层:postgresql 与 redis
postgresql使用postgres:12-alpine,健康检查pg_isready(start_period 20s / interval 30s / retries 5);数据目录为命名卷database;环境里硬编码了示例账号authentik/postgrass,并声明env_file: .env——注意该.env文件并未包含在仓库中,实际部署需自行准备;redis使用redis:alpine,启动参数--save 60 1 --loglevel warning,健康检查redis-cli ping | grep PONG,数据存于命名卷redis。
两者都是 Authentik 的运行时依赖(PostgreSQL 存认证数据,Redis 做缓存/会话)。
2.4 authentik_server 与 authentik_worker
两个服务都使用 ghcr.io/goauthentik/server:2024.2.1,分别以 command: server(HTTP 服务,对外 9000/9443 端口)与 command: worker(任务执行器)方式运行,环境变量 AUTHENTIK_REDIS__HOST、AUTHENTIK_POSTGRESQL__* 指向同网络内的服务名。worker 服务保留了几处值得注意的配置:
user: root加/var/run/docker.sock挂载是可选的,用于 Authentik 的 docker outpost 自动管理集成;注释同时提醒:若移除user: root,需自行保证./media、./certs、./custom-templates等挂载目录权限(默认 1000:1000);- 这些
./media、./certs、./custom-templates目录同样没有包含在仓库中,首次运行前需创建。
based-on/ 两个存档文件说明了本编排的原始出处:based-on/docker-compose-authentik.yml 是 Authentik 官方 compose 的存档(原版用 ${PG_PASS:?database password required} 强制从 .env 取密码,本示例改成了硬编码,安全性更差,仅作演示);based-on/docker-compose-traefik.yml 则展示了认证真正闭环时应该长什么样——它包含一个 authentik-proxy outpost 容器,并通过 Traefik label 定义 forwardauth 中间件:
traefik.http.routers.authentik.rule: Host(`app.company`) && PathPrefix(`/outpost.goauthentik.io/`)
traefik.http.middlewares.authentik.forwardauth.address: http://authentik-proxy:9000/outpost.goauthentik.io/auth/traefik
traefik.http.middlewares.authentik.forwardauth.trustForwardHeader: true
traefik.http.middlewares.authentik.forwardauth.authResponseHeaders: X-authentik-username,X-authentik-groups,...
即:outpost 认证成功后,会把 X-authentik-username、X-authentik-groups 等响应头回写给下游。对照当前 docker-compose.yml 中 copyparty 服务缺失的 middlewares 配置和那个 # TODO: ???,可以推断:要跑通该示例,需要引入类似 authentik-proxy 的 outpost 服务,并把 fs 路由挂上对应的 forwardauth 中间件,同时让 copyparty 读取 X-authentik-username/X-authentik-groups(或统一改名为 X-IdP-User/X-IdP-Group)。这正是作者标注“尚未跑通”的工作缺口。
三、cpp/copyparty.conf:IdP 模式下的动态卷与权限体系
容器内 copyparty 读取 docs/examples/docker/idp-authentik-traefik/cpp/copyparty.conf,这是本示例最有参考价值的部分。文件头注释解释了整体思路:不再使用 copyparty 内置账号认证,而是期望反向代理在 HTTP 头中提供用户名(以及可选组名),用于可选的组级访问控制。
3.1 [global] 段:声明 IdP 头
[global]
e2dsa # enable file indexing and filesystem scanning
e2ts # enable multimedia indexing
ansi # enable colors in log messages
idp-h-usr: x-idp-user
idp-h-grp: x-idp-group
idp-h-usr 指定承载用户名的头名(此处为 X-IdP-User),idp-h-grp 指定组名头(X-IdP-Group)。在源码中,这两个选项对应 copyparty/main.py 中的命令行参数 --idp-h-usr(可重复使用)与 --idp-h-key,且 --idp-h-usr 的帮助文本自带警告:“如果你启用这个,务必确保客户端无法自行指定该头;必须由反向代理将其洗掉并替换”。
补充安全参数(见 docs/idp.md,本示例的 conf 未全部启用):
xff-src:限定合法请求来源子网,例如xff-src: 10.88.0.0/24或xff-src: lan(所有私有 IP),防止外部伪造代理来源;idp-h-key: <头名>:要求反向代理额外注入一个“秘密头”,缺省时其余 IdP 头一律不被信任——这是 copyparty 侧对 IdP 头信任链的主护栏。
3.2 静态根卷
[/]
/w
accs:
rw: * # 所有人可读
rwmda: @su # 组 "su" 获得读/写/移动/删除/管理权限
/ 卷直接分享容器数据卷 /w(宿主机 /srv/pub),所有登录用户可读,仅管理员组 su 可写。
3.3 基于 ${u} / ${g} 的动态卷
[/u/${u}] # 每个用户在 /u/username 拥有自己的家目录
/w/u/${u}
accs:
r: * # 任何人可读
rwmda: ${u}, @su # 该用户本人 + su 组可写
[/u/${u}/priv] # 每个用户的私有区域
/w/u/${u}/priv
accs:
rwmda: ${u}, @su # 仅本人 + su 组,无 r:* 匿名读
[/lounge/${g}] # 每个组一个共享卷
/w/lounge/${g}
accs:
r: *
rwmda: @${g}, @su
[/lounge/${g}/priv] # 每个组也有私有区域
/w/lounge/${g}/priv
accs:
rwmda: @${g}, @su
${u} 与 ${g} 是占位符,分别匹配请求头中的用户名与组名,URL 路径即按登录者身份动态解析出不同的文件系统路径。权限语法中:r 只读、rwmda = 读/写/移动/删除/管理,* 表示匿名/所有,@组名 表示组,${u}/${g} 表示身份本身。更复杂的条件选择器(如 ${u%+su} 仅 su 成员、${u%-su} 非 su 成员)在完整版示例 docs/examples/docker/idp/copyparty.conf 的尾部有演示,可作为本示例的进阶补充。
3.4 “战略性”兜底卷
[/u]
/w/u
accs:
rwmda: @su
[/lounge]
/w/lounge
accs:
rwmda: @su
这两个父级卷的作用是兜底安全:若 IdP 的用户/组数据库丢失,动态卷暂时无法“复活”时会继承父卷权限。父卷只授予 @su,意味着私有任何人不可读——这正是 docs/idp.md 中“IdP 卷默认在重启后被遗忘,复活前继承父卷权限”一节的推荐做法:把动态卷放在一个权限足够保守的父卷之下。
四、copyparty 侧 IdP 机制的源码印证
结合源码可以确认 IdP 头信任的完整链路:
- 参数解析:copyparty/main.py 定义
--idp-h-usr(可重复)、--idp-h-key(秘密头,可选但推荐)、以及帮助文档中反复出现的--idp-hm-usr等配套头;--hdr-au-usr是--idp-h-usr的兼容别名(见 copyparty/main.py 中的参数迁移表)。 - 请求头校验:copyparty/httpcli.py 实现了核心防线——当配置了
idp-h-key而请求中缺失该秘密头时,日志会明确拒绝:“the idp-h-key header is not present in the request; will NOT trust the other headers saying that the client's username is …”,即直接忽略用户名/组头,请求退回未认证状态;copyparty/httpcli.py 附近的注释也点明“IdP 的主要保护手段是--idp-h-key”。 - 身份持久化:copyparty/authsrv.py 按
idp-store级别处理身份缓存:级别 1(默认)把用户记入数据库但不真正“记住”;级别 2 记住用户名;级别 3 记住用户名及其组。默认不持久化的原因是:你从 IdP 服务器删除某用户后,期望 copyparty 也随之忘记此人;若开了持久化,则需手动在控制面板view idp cache中清理。相关状态在 copyparty/svchub.py 中与服务启动流程联动。
另外,若 IdP 部署后要连接 WebDAV 客户端(如 rclone),docs/idp.md 给出了已知解法:将目标域的策略设为单因素认证,然后在 rclone 配置里用 headers = Proxy-Authorization,basic <base64(用户名:密码)> 注入代理认证头。
五、使用前提、限制与加固建议
适用前提与限制(均来自该文件夹 README 与 compose 文件本身的注释,务必知悉):
- 示例作者声明无法保证质量、安全与安全性,内容组合自网上示例;并明确标注 does not work yet——当前 compose 中 copyparty 的 fs 路由没有挂载认证中间件(见 2.1 节中的 TODO 注释),认证闭环尚未打通;
postgresql、authentik_server、authentik_worker均引用env_file: .env,但.env、./media、./certs、./custom-templates均未包含在仓库中,部署前需自行补齐,且应把硬编码的POSTGRES_PASSWORD换回基于.env的${PG_PASS}写法(可参照 based-on/docker-compose-authentik.yml 的${PG_PASS:?database password required}强制写法);- Traefik 与 authentik_worker 都挂载了
/var/run/docker.sock,等于向容器暴露宿主 root 权限,属于示例的已知风险点,生产环境可考虑 docker socket-proxy 等收敛方案(姊妹示例 docs/examples/docker/idp-authelia-traefik/README.md 就采用了 socket-proxy,并把安全加固清单(如缓存服务移入私有网络、只暴露 Traefik 公开端口)列了出来,可供借鉴); - 该编排基于 Traefik v2(v2.11),中间件语法为
traefik.http.*,不要混用 Traefik v3 语法。
要跑通认证闭环,最小修改方向(基于 based-on/ 参考文件的推断):
- 增加
authentik-proxyoutpost 容器(参考 based-on/docker-compose-traefik.yml),配置AUTHENTIK_HOST与 outpost token; - 在 copyparty 服务的 label 中启用 fs 路由的 forwardauth 中间件,并把 outpost 回写的
X-authentik-username/X-authentik-groups头与 copyparty 侧idp-h-usr/idp-h-grp的头名对齐(或统一为X-IdP-User/X-IdP-Group); - 在 cpp/copyparty.conf 的
[global]中补上xff-src(限定代理来源子网)与idp-h-key(秘密头),这两项在 docs/idp.md 中被分别描述为“必需”与“推荐”; - 按 3.4 节保留父级兜底卷,并为
su组配置对应的 Authentik 组映射。
六、小结
docs/examples/docker/idp-authentik-traefik 提供了“copyparty + Traefik + Authentik”容器化 IdP 部署的完整骨架:compose 侧定义了六个服务的编排、数据卷与健康检查,cpp/copyparty.conf 侧用 ${u}/${g} 动态卷 + 组级 accs + 兜底父卷构建了可落地的权限模型,而 copyparty 源码(__main__.py 的参数、httpcli.py 的头校验、authsrv.py 的 idp-store)则保证了“头不可伪造、信任可关闭”的安全语义。由于该示例自述尚未跑通,建议将其视为结构与思路参考,配合 docs/idp.md 的机制说明与 based-on/ 中的官方 outpost 集成写法完成最后一公里,并对照姊妹示例 docs/examples/docker/idp-authelia-traefik/README.md 中已跑通的 Authelia 方案进行交叉验证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00