Maltrail Docker 部署指南:单镜像承载 Server 与 Sensor 的构建、权限模型与安全姿态
【免费下载链接】maltrailMalicious traffic detection system项目地址: https://gitcode.com/GitHub_Trending/ma/maltrail
本指南以仓库 docker/README.md 为主体,系统讲解 Maltrail 的容器化部署:一条命令用 Docker Compose 同时启动服务器(Server)与传感器(Sensor)、在非特权前提下通过精确的两个 Linux capabilities 完成抓包、解决 bind mount 与容器内非 root 用户之间的目录属主适配,并掌握仅运行 Server、仅运行 Sensor、部署自检、健康检查与镜像供应链验证等实战技能。阅读并实践本文后,你将能独立完成一套可对外接收远程 Sensor 上报、数据可持久化、权限最小化且可被 CI 验证的 Maltrail Docker 部署。
快速开始:一条命令启动全套
仓库根目录下的 docker/docker-compose.yml 定义了两个服务(server与sensor),共用同一个镜像。从仓库根目录执行:
docker compose -f docker/docker-compose.yml up -d该命令会构建一个同时包含服务器与传感器(Rust 实现)的镜像,并启动两者。构建上下文是仓库根目录(context: ..),因为 Dockerfile 需要整棵树来编译传感器并拷贝服务器代码。启动后:
- 报告界面(reporting UI)位于 http://localhost:8338;
- 服务器默认命令为
python3 server.py,监听 8338/tcp(报告 UI)与 8337/udp(事件接收); - 传感器运行
maltrail-sensor,使用network_mode: host并仅附加NET_RAW与NET_ADMIN两个 capability,不以privileged运行。
如需手动构建镜像(同样必须从仓库根目录执行,构建上下文需包含整个仓库树):
docker build -f docker/Dockerfile -t maltrail .镜像里到底跑什么
| 服务 | 命令 | 说明 |
|---|---|---|
server | python3 server.py | 报告 UI 监听 8338/tcp,事件接收监听 8337/udp |
sensor | maltrail-sensor | network_mode: host,NET_RAW+NET_ADMIN——非privileged |
传感器刻意使用宿主机网络命名空间:在默认 bridge 网络内,它只能看到容器自身的流量,而无法采集宿主机网络的真实流量。它不依赖privileged提权,抓包恰好需要且只需要两个 capability,镜像就只授予这两个(见 docker/docker-compose.yml 中cap_add注释:NET_RAW用于打开 AF_PACKET 套接字,NET_ADMIN用于混杂模式与 PACKET_FANOUT)。
镜像构建是两阶段完成的(见 docker/Dockerfile):第一阶段在rust:1-slim-bookworm中编译maltrail-sensor(先拷贝Cargo.toml/Cargo.lock以缓存依赖层,再拷贝源码并strip二进制);第二阶段基于python:3.12-slim-bookworm拷贝整个仓库与编译产物,安装libpcap0.8(仅运行库,不安装 -dev)与tini(保证信号能送达进程),并以tini作为 PID 1 入口。
配置与数据:bind mount 与命名卷
maltrail.conf以只读方式从仓库根目录 bind mount 进容器(compose 中的../maltrail.conf:/opt/maltrail/maltrail.conf:ro),因此配置直接在宿主机上编辑、重启容器即可生效。两个命名卷承载必须在重建镜像后仍存活的状态:
maltrail-logs→/var/log/maltrail(事件日志)maltrail-state→/var/lib/maltrail(trail 规则集)
请将配置中的TRAILS_FILE指向/var/lib/maltrail/trails.csv(maltrail.conf 第 488-489 行给出了 systemd 语境下的同一路径),这样每次容器启动时 trail 规则集不会被重复重建。关于该配置项,core/settings.py 中会将其规范化:未设置时回退到~/.maltrail/trails.csv,并经过expanduser与abspath处理;镜像内已通过ENV HOME=/var/lib/maltrail将默认路径指向状态卷。此外,docker/Dockerfile 特别注释了VOLUME的声明时机:VOLUME必须在chown之后声明,因为 Docker 会在声明VOLUME的瞬间对镜像内容做快照,过早声明会让后续层的属主修复永远无法进入卷——这一点是通过实际运行镜像验证的,而非阅读文档得出结论。
bind mount 与非特权用户:uid 自动适配
两个进程默认以uid/gid 10001(用户maltrail)运行,而非 root。但 bind mount 保留的是宿主机目录的属主,它会覆盖镜像内预先准备好的属主设置。过去-v ./logs:/var/log/maltrail配合由登录用户拥有的./logs会导致容器无法创建当天的日志文件——而由于日志文件只在第一条事件到达时才打开,容器表现为“启动了、UI 正常、却什么都没记录”的静默失败。
现在无需任何手动处理:docker/entrypoint.sh 以 root 身份运行几毫秒,判断哪个 uid 能写入日志与状态目录,然后在传感器或服务器启动前通过setpriv降权。其决策规则如下:
| 遇到的情况 | entrypoint 的做法 |
|---|---|
| 命名卷,或未挂载任何东西 | 以 10001(镜像自带用户)运行 |
| 属主为 uid 1000 的 bind mount | 以 1000 运行—— 你的文件仍是你的 |
属主为root:1000且组可写 | 保持 uid 10001,采用 gid 1000 |
| root 所有的 bind mount | 接管该目录属主,以 10001 运行 |
设置了PUID/PGID | 一律使用指定值,不管目录属主如何 |
给了--user且目录不可写 | 拒绝启动,并明确指出是哪个目录、为什么 |
因此docker run -v $PWD/logs:/var/log/maltrail ...无需在宿主机上执行任何chown,写入的事件归属你的用户,而不是需要sudo才能读取的系统 uid。这套行为由 docker/tests/entrypoint_test.sh 对真实 Docker daemon 逐行断言,并运行于 CI。
有两个值得记住的后果:
- 镜像没有
USER指令,因此docker exec进入的是 root。想以进程实际运行的用户身份获得 shell,请用docker exec -u maltrail(或-u 1000)。 --user会完全禁用这套自动适配——没有 root 就没有可用来适配的东西。此时容器只检查目录是否可写,并在不可写时响亮地失败,而不是带着一半功能启动。
entrypoint 的实现细节(docker/entrypoint.sh)值得一提:它用setpriv --reuid/--regid --clear-groups /usr/bin/test -w以“将要变成的用户”身份测试可写性,而不是去推断 mode 位,这样补充组、ACL 与只读挂载给出的答案与传感器实际运行时完全一致;对 root 拥有但组可写的目录(如root:staff 775),它只“加入该组”而不 chown;仅当目录内容确属镜像自建(全新命名卷,属主即默认 uid)时才递归 chown,对挂载进来的宿主机目录只改目录本身的属主,绝不chown -R别人(宿主)的树。
Trails 不烘焙进镜像
早期版本在构建时运行更新器并在容器内放一个 cron 任务,现在两者都不再做:把约 200 万个指标烘焙进镜像会使镜像体积巨大,且在发布那一刻就已过期;容器内的 cron 则是需要额外监管的第二个进程,没有任何收益。如今服务器与传感器都在启动时以及每个UPDATE_PERIOD(maltrail.conf 默认 86400 秒)自行刷新 trail,数据落入状态卷。这也解释了为什么TRAILS_FILE必须指向/var/lib/maltrail/trails.csv:只有落在持久卷上,冷启动后的首次 trail 构建(约一两分钟)才不会反复发生。
仅运行 Server:接收远程 Sensor 上报
docker run -d --name maltrail-server \ -p 8338:8338/tcp -p 8337:8337/udp \ -v /etc/maltrail.conf:/opt/maltrail/maltrail.conf:ro \ -v maltrail-logs:/var/log/maltrail \ -v maltrail-state:/var/lib/maltrail \ maltrail:latest这正是镜像的默认命令(CMD ["python3", "server.py"]),因此无需覆盖任何东西——也无需任何 capability:服务器不抓包,既不需要NET_RAW也不需要NET_ADMIN。镜像内置的HEALTHCHECK请求服务器自身的/ping端点并期待pong(响应常量定义于 core/settings.py 的PING_RESPONSE),因此无需上述 capability 也能通过健康检查。
仅运行 Sensor:对接已有服务器
docker run -d --name maltrail-sensor \ --network host --cap-add NET_RAW --cap-add NET_ADMIN \ -v /etc/maltrail.conf:/opt/maltrail/maltrail.conf:ro \ -v maltrail-state:/var/lib/maltrail \ maltrail:latest maltrail-sensor在该配置中将LOG_SERVER设为服务器 8337/udp 端口的地址(maltrail.conf 中支持 IPv4、带作用域的 IPv6 等形式),传感器即可把事件上报给远端服务器。
检查一次部署是否可用
docker compose -f docker/docker-compose.yml run --rm sensor maltrail-sensor -T-T(即--test-config)会校验配置、trails、白名单、日志目录、抓包过滤器和权限,若传感器无法正常工作则非零退出。sensor/src/main.rs 中该选项的语义是“不抓包即可校验一切,然后退出(0 = 可用,1 = 不可用)”。Compose 的run --rm会临时拉起一个执行完即销毁的传感器容器,非常适合作为部署后冒烟检查。
安全姿态
两个进程都以非特权用户maltrail(默认 uid 10001)运行,与 systemd unit 保持一致。二者都不需要 root:传感器获得CAP_NET_RAW与CAP_NET_ADMIN,服务器只绑定非特权端口。compose 文件不是privileged的。
只有 docker/entrypoint.sh 以 uid 0 运行,且只持续到它决定采用哪个 uid 为止;它通过setprivexec真正的命令,因此容器内没有任何残留的 root 进程。这也正是cap_add之所以能生效的关键:Docker 只会把请求的 capability 放进bounding集,而ambient集保持为空;ambient capability 只能由以 root 启动的进程提升。若把USER maltrail烘焙进镜像,--cap-add NET_RAW会让传感器的CapEff变成0000000000000000,抓包套接字以EPERM失败(entrypoint 脚本注释中给出了 3.1 镜像上的实测记录)。现在的实现是:entrypoint 在降权前解析/proc/self/status的CapBnd,仅当maltrail-sensor(或sensor.py)在命令行中时才通过setpriv --inh-caps/--ambient-caps携带net_raw(bit 13)与net_admin(bit 12),于是传感器以 uid 10001 获得CapEff: 0000000000003000——且只有传感器如此,服务器保持零 capability。
健康检查的设计同样值得展开:
- 镜像自带的
HEALTHCHECK询问服务器是否在服务(因为镜像默认命令是server.py):请求/ping并期待pong。它不运行maltrail-sensor -T——那是对服务器容器提出的错误问题:除非配置恰好设置了DISABLE_CHECK_SUDO,-T会因缺少服务器既不需要也不拥有的CAP_NET_RAW而失败,导致一个配置正确的 server-only 部署自报不健康(issue #19596 中实测仅此一项就 exit 1)。一个在正确配置下无法通过的健康检查比没有更糟——它会训练人们忽略健康状态。 - compose 文件中 sensor 服务覆盖了健康检查为
maltrail-sensor -T,于是“不健康”意味着传感器真正失去了检测能力——配置错误、trails 不可读、日志目录不可写——而不是“PID 1 还存在”。start-period: 180s覆盖首次 trail 构建。 HEALTHCHECK不会经过ENTRYPOINT,所以该覆盖显式调用/usr/local/bin/maltrail-entrypoint;否则-T将以 root 运行,而 root 能写任何日志目录——这恰恰是该检查要抓的问题之一。
发布镜像为多架构(linux/amd64、linux/arm64),并携带构建来源(build provenance)证明,可确认某个镜像是由此仓库的发布工作流构建的:
gh attestation verify oci://ghcr.io/stamparm/maltrail:3.0 --repo stamparm/maltrail小结
Maltrail 的 Docker 化设计围绕三条主线:最小权限(非 root 运行 + 恰好两个 capability + entrypoint 短暂提权后彻底降权)、数据与镜像解耦(trails 不入镜像、命名卷持久化、bind mount 属主自动适配)与可验证性(-T预检、针对服务器与传感器的差异化健康检查、针对 entrypoint 行为表的真实 daemon 测试)。对照 docker/Dockerfile、docker/entrypoint.sh、docker/docker-compose.yml 与 docker/tests/entrypoint_test.sh 阅读本文,即可把这套部署模型复用到你的监控网络中。
【免费下载链接】maltrailMalicious traffic detection system项目地址: https://gitcode.com/GitHub_Trending/ma/maltrail
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考