izm1chael/mailhook

GitHub: izm1chael/mailhook

一个自托管的邮件安全网关,通过多引擎扫描流水线监控 IMAP 邮箱并自动隔离或删除恶意邮件。

Stars: 0 | Forks: 0

# MailHook [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/izm1chael/mailhook/actions/workflows/ci.yml) [![Release](https://static.pigsec.cn/wp-content/uploads/repos/cas/42/42ba98a60a0bb3b0ad908f024db145f9c5b831eb7df822f56ac578ee7d7215b3.svg)](https://github.com/izm1chael/mailhook/actions/workflows/release.yml) [![Latest Release](https://img.shields.io/github/v/release/izm1chael/mailhook?logo=github&label=release)](https://github.com/izm1chael/mailhook/releases/latest) [![Container](https://img.shields.io/badge/ghcr.io-mailhook-blue?logo=docker&logoColor=white)](https://github.com/izm1chael/mailhook/pkgs/container/mailhook) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/izm1chael/mailhook/badge)](https://scorecard.dev/viewer/?uri=github.com/izm1chael/mailhook) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12986/badge)](https://www.bestpractices.dev/projects/12986) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Go](https://img.shields.io/badge/Go-1.26-00ADD8?logo=go&logoColor=white)](app/go.mod) 一个自托管的邮件安全网关。MailHook 通过 IDLE 监控 IMAP 邮箱,将 每封收到的邮件经过一个多引擎扫描 pipeline 处理,并自动 隔离或删除威胁。它提供了一个 Web dashboard,用于审查、放行、审计 以及管理允许/阻止列表。 MailHook 作为一个单独的 Go 二进制文件发布(YARA 使用了 CGO),并与 Rspamd 和 ClamAV 一起运行。本 README 是完整的文档:涵盖了功能、架构、所有 受支持的安装和部署方式、配置、运维和开发。 ## 目录 1. [概述](#overview) 2. [功能](#features) 3. [架构](#architecture) 4. [要求](#requirements) 5. [生成必要的密钥](#generating-the-required-secrets) 6. [安装与部署](#installation-and-deployment) - [方法 1:Docker Compose(推荐)](#method-1-docker-compose-recommended) - [方法 2:带有 AI 扫描层的 Docker Compose](#method-2-docker-compose-with-the-ai-scanner-tier) - [方法 3:多合一单容器](#method-3-all-in-one-single-container) - [方法 4:Debian / Ubuntu (.deb) 结合 systemd](#method-4-debian--ubuntu-deb-with-systemd) - [方法 5:RHEL / Fedora (.rpm) 结合 systemd](#method-5-rhel--fedora-rpm-with-systemd) - [方法 6:从源码构建并运行二进制文件](#method-6-build-from-source-and-run-the-binary) 7. [配置参考](#configuration-reference) 8. [AI 扫描层(可选)](#the-ai-scanner-tier-optional) 9. [在反向代理后运行](#running-behind-a-reverse-proxy) 10. [运维](#operations) 11. [使用 Dashboard](#using-the-dashboard) 12. [开发](#development) 13. [项目结构](#project-layout) 14. [安全模型](#security-model) 15. [故障排除](#troubleshooting) 16. [许可协议](#license) ## 概述 MailHook 连接到一个或多个 IMAP 账户,使用 IMAP IDLE 监视收件箱,并 在收到新邮件时进行处理。每封邮件都由一组引擎并发扫描, 由判定引擎汇总结果,然后 MailHook 执行一个 IMAP 操作: 将邮件保留在原处、对其打标、将其移动到隔离文件夹,或者将其删除。 操作员可以通过内置的 Web dashboard 审查和管理被隔离的邮件。 所有数据都存储在本地的 SQLite 中。没有外部数据库,也没有云 依赖,尽管可以通过 API key 启用可选的声誉服务(VirusTotal、AbuseIPDB)。 ## 功能 - 针对一个或多个邮箱的 IMAP IDLE 接入,并带有周期性的恢复扫描,可以 捕获重连期间遗漏的任何邮件。 - 每封邮件运行一个并发的、多引擎扫描 pipeline: - Rspamd 用于垃圾邮件评分,ClamAV 用于恶意软件,YARA 用于自定义规则。 - URL 威胁情报源:URLhaus、OpenPhish、PhishTank、ThreatFox(保存在内存索引中)。 - 使用 SSRF 安全的 dialer 跟踪重定向链来进行 URL 还原(去除短链接)。 - 通过 RDAP 检测新注册域名(NRD)。 - IP 信誉(AbuseIPDB),以及 VirusTotal 和 MalwareBazaar 哈希查询。 - HTML 走私和隐藏文本/零字号字体启发式检测。 - 一个可选的 ONNX AI 层(DistilBERT 钓鱼检测加上 DGA CNN),位于 `ai` 构建标签后。 - 一个优先级的判定引擎,将结果映射为通过、打标、隔离或删除, 并在关键扫描器不可用时采取失败即关闭策略(隔离以供人工审查)。 - 隔离工作流:移动到隔离区、放行回收件箱、删除和 重新扫描。IMAP 状态和本地数据库保持同步。 - 一个使用 Tailwind 和 Alpine.js 构建的 Web dashboard,由二进制文件提供服务,通过 Server-Sent Events 提供实时更新、隔离管理、允许/阻止列表、统计 和审计日志。 - 默认安全:使用 AES-256-GCM 加密存储的 IMAP 凭据和 API key, 带有速率限制登录的 bcrypt 管理员身份验证、CSRF 保护,以及严格的 基于 nonce 的 Content Security Policy,没有 `unsafe-inline` 且没有 `unsafe-eval`。 - 回溯性威胁情报源追溯,根据 更新的情报源重新评估最近的安全邮件,并隔离新识别出的威胁。 ## 架构 ``` IMAP (IDLE) +-------------+ mailbox ------------------> MailHook --->| Rspamd | | | +-------------+ quarantine / delete <---------+ +------->| ClamAV | (IMAP actions) scanners +-------------+ | +-- SQLite (scans, audit, allow/block lists) +-- Web dashboard (HTTP, CSP, SSE) ``` MailHook 通过 HTTP 与 Rspamd 通信,通过 clamd TCP 协议与 ClamAV 通信。在 推荐的 Docker 部署中,它们作为私有网络上分离的容器运行。在 多合一镜像中,它们作为受监控的进程在单个容器内运行。在 裸机宿主机上,它们作为各自的服务运行。 ## 要求 - 一台可以运行 Docker(推荐)或 Linux 服务的宿主机。 - MailHook 可以访问的 Rspamd 和 ClamAV。 - 对于源码构建:Go 1.26 或更高版本,一个 C 工具链,以及 libyara 4.3 或更高版本。 - 一个支持带有 IDLE 的 IMAP 的邮箱。 默认端口:Web dashboard `8080`,Rspamd `11333`,ClamAV `3310`。 ## 生成必要的密钥 MailHook 在未设置三个密钥的情况下拒绝启动,并且它会拒绝 示例中提供的占位符值。生成它们一次: ``` # CSRF signing key(必须至少为 32 个字符) openssl rand -hex 32 # Database encryption key(必须正好是 64 个十六进制字符 = 32 bytes) openssl rand -hex 32 # 作为 bcrypt hash 的管理员密码(cost 12) htpasswd -nbBC 12 admin 'your-strong-password' | cut -d: -f2 ``` 这些对应于 `MAILHOOK_CSRF_SECRET`、`MAILHOOK_DB_ENCRYPTION_KEY` 和 `MAILHOOK_ADMIN_PASSWORD_BCRYPT`。Makefile 目标 `make setup-password` 可以为你生成 bcrypt 哈希。 ## 安装与部署 支持六种方法。大多数操作员应该使用方法 1。 ### 方法 1:Docker Compose(推荐) 这会在私有网络上启动 MailHook、Rspamd 和 ClamAV 三个强化容器,并 仅在 loopback 上发布 dashboard。 ``` git clone https://github.com/izm1chael/mailhook.git cd mailhook cp .env.example .env # then fill in the three secrets above cp config.example.yaml config.yaml # then add your IMAP account(s) docker compose up -d --build ``` Dashboard 随后可通过 `http://127.0.0.1:8080` 访问。ClamAV 在首次 启动时下载其病毒数据库,这可能需要几分钟时间,因此在完成之前,`/health` 可能会将 ClamAV 报告为 不可用。 有用的包装脚本: ``` make up # docker compose up -d --build make down # docker compose down make logs # follow logs ``` ### 方法 2:带有 AI 扫描层的 Docker Compose AI 层增加了 DistilBERT 钓鱼检测和 DGA CNN。它需要模型文件 (参见 [AI 扫描层](#the-ai-scanner-tier-optional)),并通过 override 文件进行叠加: ``` docker compose -f docker-compose.yml -f docker-compose.ai.yml up -d --build ``` ### 方法 3:多合一单容器 `app/Dockerfile.allinone` 将 MailHook、Rspamd 和 ClamAV 打包到一个容器中, 由 s6-overlay 监控。这对于单节点宿主机和测试非常方便。 它比单独运行 MailHook 消耗更多的内存,因为 ClamAV 会将其特征 数据库保留在 RAM 中。 ``` docker build -f app/Dockerfile.allinone -t mailhook:allinone . docker run -d --name mailhook \ -p 127.0.0.1:8080:8080 \ --env-file .env \ -v "$PWD/config.yaml:/etc/mailhook/config.yaml:ro" \ -v mailhook-data:/data \ mailhook:allinone ``` ### 方法 4:Debian / Ubuntu (.deb) 结合 systemd 使用 nfpm 构建原生包并安装。该包将二进制文件安装到 `/usr/local/bin/mailhook`,一个强化的 systemd unit,以及 `/etc/mailhook` 下的配置。它 声明 `clamav-daemon`、`clamav-freshclam` 和 `rspamd` 为依赖项,以便它们被 自动安装。 ``` cd app && make package-deb # produces ../dist/mailhook__.deb sudo apt install ./dist/mailhook_*.deb ``` 安装后: ``` # 编辑环境文件并添加这三个 secrets(见上文)。 sudoedit /etc/mailhook/mailhook.env # 添加你的 IMAP account(s)。 sudoedit /etc/mailhook/config.yaml sudo systemctl enable --now mailhook systemctl status mailhook journalctl -u mailhook -f ``` systemd unit 以专用的 `mailhook` 用户身份运行,并启用了 强化功能(`NoNewPrivileges`、`ProtectSystem=strict`、`ProtectHome`、空 capability 集以及 system-call 过滤器)。运行时数据位于 `/var/lib/mailhook`,情报源缓存位于 `/var/cache/mailhook`。 ### 方法 5:RHEL / Fedora (.rpm) 结合 systemd RPM 等同于 DEB,并依赖于 `clamd` 和 `rspamd`。 ``` cd app && make package-rpm # produces ../dist/mailhook-..rpm sudo dnf install ./dist/mailhook-*.rpm ``` 然后配置 `/etc/mailhook/mailhook.env` 和 `/etc/mailhook/config.yaml` 并 完全按照方法 4 中的方式启用服务。AI 变体包可通过 `make package-deb-ai` 和 `make package-rpm-ai` 获取。 ### 方法 6:从源码构建并运行二进制文件 ``` cd app make build # standard binary at ../bin/mailhook # 或者:make build-ai # 包含 ONNX AI tier (-tags ai) ``` 使用通过环境变量提供的配置运行它,并将其指向你 自己的 Rspamd 和 ClamAV: ``` export MAILHOOK_ADMIN_PASSWORD_BCRYPT='...' export MAILHOOK_CSRF_SECRET='...' export MAILHOOK_DB_ENCRYPTION_KEY='...' export MAILHOOK_RSPAMD_URL='http://127.0.0.1:11333' export MAILHOOK_CLAMAV_ADDR='127.0.0.1:3310' export MAILHOOK_CONFIG='./config.yaml' ../bin/mailhook ``` 源码构建需要安装 libyara(4.3 或更高版本)。提供的 Dockerfile 从源码编译 YARA 以确保可重现性,并且原生包静态链接 它,因此不需要运行时 libyara 依赖。 ## 配置参考 全局设置来自环境变量。每个账户的 IMAP 设置来自 YAML 文件(`config.yaml`)。有关完整的 带注释的集合,请参见 `.env.example` 和 `config.example.yaml`。最重要的变量: | 变量 | 默认值 | 用途 | |---|---|---| | `MAILHOOK_ADMIN_USER` | `admin` | Dashboard 用户名 | | `MAILHOOK_ADMIN_PASSWORD_BCRYPT` | (必填)| 管理员密码的 Bcrypt 哈希 | | `MAILHOOK_CSRF_SECRET` | (必填)| CSRF token 的 HMAC key,32 个字符以上 | | `MAILHOOK_DB_ENCRYPTION_KEY` | (必填)| 用于静态加密密钥的 64 位十六进制字符 AES-256 key | | `MAILHOOK_LISTEN` | `0.0.0.0:8080` | Web 监听地址 | | `MAILHOOK_RSPAMD_URL` | `http://rspamd:11333` | Rspamd endpoint | | `MAILHOOK_CLAMAV_ADDR` | `clamav:3310` | ClamAV (clamd) 地址 | | `MAILHOOK_YARA_RULES_DIR` | `/rules` | `.yar` 规则文件目录 | | `MAILHOOK_SPAM_SCORE` | `5.0` | 达到/超过此 Rspamd 分数时隔离邮件 | | `MAILHOOK_REJECT_SCORE` | `15.0` | 达到/超过此 Rspamd 分数时删除邮件 | | `MAILHOOK_VT_API_KEY` | empty | VirusTotal key(为空则禁用) | | `MAILHOOK_ABUSEIPDB_KEY` | empty | AbuseIPDB key(为空则禁用) | | `MAILHOOK_DATA_DIR` | `/data` | 存储的 EML 和数据库位置 | | `MAILHOOK_DB_PATH` | `/data/mailhook.db` | SQLite 数据库路径 | | `MAILHOOK_RETENTION_DAYS` | `30` | 清洁邮件 EML 的保留时间(如果保留的话) | | `MAILHOOK_EML_QUARANTINE_RETENTION_DAYS` | `90` | 隔离 EML 的保留时间 | | `MAILHOOK_TRUSTED_PROXIES` | empty | 其 `X-Forwarded-For` 受信任的 CIDR | | `MAILHOOK_METRICS_ALLOWED_CIDRS` | `127.0.0.1/32,::1/128` | 允许访问 `/metrics` 和 `/api/scan` 的 CIDR | | `MAILHOOK_TRUSTED_AUTHSERV_ID` | empty | 其 `Authentication-Results` 受信任的 authserv-id | | `MAILHOOK_REDACT_WEBHOOK_PII` | `false` | 在 ntfy/webhook payload 中掩盖发件人和主题 | | `MAILHOOK_INSECURE_COOKIES` | `false` | 禁用 Secure cookie 标志(仅限本地 HTTP 开发) | | `MAILHOOK_LOG_LEVEL` / `MAILHOOK_LOG_FORMAT` | `info` / `json` | 日志记录 | 每个账户的 IMAP 配置(`config.yaml`): ``` accounts: - name: primary # unique label, no spaces or slashes host: imap.example.com port: 993 user: security@example.com pass: app-password mailbox: INBOX # folder to monitor quarantine: Quarantine # folder threats are moved to (created if missing) tls_skip_verify: false # set true only for self-signed test servers ``` `config.yaml` 中的账户密码会在启动时迁移到数据库中,并在静态 使用 `MAILHOOK_DB_ENCRYPTION_KEY` 加密。也可以在运行时从 Dashboard 的“设置”页面管理账户。 ## AI 扫描层(可选) 默认构建中排除了 AI 层。要使用它,请使用 `-tags ai` (`make build-ai`)构建并提供模型文件。辅助脚本: ``` cd app make models-dl # fetch the Tranco greylist make models-bert # export the DistilBERT phishing model to ONNX make models-dga # build the DGA CNN model (see scripts/export_dga_onnx.py) make build-ai # build the AI-enabled binary ``` 缺少模型不是致命的:该子扫描器将被跳过并记录在日志中,而 pipeline 的其余部分 继续运行。 ## 通过纯 HTTP 访问(无反向代理) 默认情况下,Session cookie 会设置 `Secure` 标志,这会指示浏览器 仅在 HTTPS 上发送它们。当你从 **非 localhost** 地址直接通过纯 HTTP 访问 MailHook 时(例如,在局域网 IP 上发布的 Docker 或虚拟机的私有 地址),浏览器会默默丢弃 Session cookie,并且在登录后你会被立即重定向 回登录页面。 要解决此问题,请在你的 `.env` 文件中设置 `MAILHOOK_INSECURE_COOKIES=true`: ``` MAILHOOK_INSECURE_COOKIES=true ``` ## 在反向代理后运行 Dashboard 使用纯 HTTP 通信,旨在在生产环境中位于终止 TLS 的反向 代理(Caddy、nginx、Traefik)之后。当你这样做时: 1. 将 `MAILHOOK_TRUSTED_PROXIES` 设置为代理的 CIDR,以便正确从 `X-Forwarded-For` 读取客户端 IP(用于登录速率限制和审计日志)。 2. 保持 `MAILHOOK_METRICS_ALLOWED_CIDRS` 的严格限制。它默认仅允许 loopback,并对 `/metrics` 和基准测试 `/api/scan` endpoint 同时进行控制。 3. 不要将 `/metrics` 或 `/api/scan` 转发给不受信任的客户端。 Dashboard 发送 HSTS 和严格的 CSP,因此它期望通过 HTTPS 提供服务。 ## 运维 Endpoints: - `GET /healthz` 是容器健康检查使用的未经身份验证的存活探针。 - `GET /health`(需身份验证)返回完整的组件健康状况和今天的统计数据,并 在关键组件出现降级时返回 503。 - `GET /metrics` 暴露 Prometheus metrics,受 `MAILHOOK_METRICS_ALLOWED_CIDRS` 限制。 维护在本地时间 03:00 自动运行:EML 保留修剪、IP 信誉 缓存清理、完整性检查以及压缩的数据库备份(VACUUM INTO)。威胁 情报源按 `MAILHOOK_FEED_REFRESH_INTERVAL`(默认 6h)设置的时间间隔刷新,并 可以从“设置”页面按需刷新。 备份:SQLite 数据库和数据目录下的 `emls` 目录保存了所有状态。 备份 `MAILHOOK_DATA_DIR`(并妥善保管 `MAILHOOK_DB_ENCRYPTION_KEY`,因为没有它将无法解密存储的 凭据)。 更新 Docker 部署: ``` git pull docker compose up -d --build ``` ## 使用 Dashboard - Dashboard:最近的扫描、判定和状态,以及处理新邮件时的实时更新。 - 隔离区:审查被拦截的邮件,在 sandboxed frame 中预览净化的 HTML,放行 到收件箱、删除、重新扫描或学习为垃圾邮件。支持批量操作。 - 列表:按地址或域管理允许和阻止列表,包括批量导入。 - 统计:随时间变化的各个判定和各个发件人的细分。 - 审计:每一个自动化和手动操作的记录。 - 设置:阈值、API key、通知、扫描器开关、endpoint、账户 以及密码更改。 ## 开发 ``` cd app make build # standard binary make build-ai # AI-enabled binary make test # full test suite make test-race # race detector make test-cover # coverage profile make vet # go vet make lint # golangci-lint (if installed) make simulate # verdict-engine scenario comparison (standard vs AI) make bench # pipeline benchmarks ``` Docker 镜像构建:`make docker-build`、`make docker-build-ai`、 `make docker-build-standard`。 ## 项目结构 ``` app/ Go source pipeline/ message parsing, scanner fan-out, verdict engine scanners/ rspamd, clamav, yara, urlcheck, urlunshorten, nrd, vt, ... imap/ IMAP listener, actions, recovery, manager web/ HTTP server, handlers, templates, embedded static assets db/ models, migrations, SQLite access, at-rest encryption config/ configuration loading and validation notify/ ntfy and webhook notifications cmd/ seed, simulate, bench, soak helpers Dockerfile standard image Dockerfile.ai AI-variant image Dockerfile.allinone single-container image (s6-overlay) rules/ YARA rules rspamd-config/ Rspamd local.d overrides packaging/ nfpm DEB/RPM config, systemd unit, all-in-one assets docker-compose.yml main stack (mailhook + rspamd + clamav) docker-compose.ai.yml AI-variant override docker-compose.bench.yml benchmarking stack ``` ## 安全模型 - 管理员身份验证使用 bcrypt,并带有基于 IP 的登录速率限制和锁定机制。 - Session 是 HttpOnly、SameSite=Strict 的 cookie;CSRF 使用签名的双重提交 token。 - Dashboard 设置了基于 nonce 的 CSP,没有 `unsafe-inline` 且没有 `unsafe-eval`, 加上 `X-Content-Type-Options`、`X-Frame-Options`、`Referrer-Policy` 和 HSTS。 - IMAP 凭据和 API key 使用 AES-256-GCM 进行静态加密。 - 出站 URL 解析(还原短链接、RDAP、webhook)使用 SSRF 安全的 dialer,它 仅解析一次并拒绝非公开地址。 - 隔离的邮件 HTML 已经过净化,并且仅在 sandboxed iframe 中预览。 在 HTTPS 后运行 MailHook,将受信任的代理和 metrics CIDR 设置为符合你的 部署,并在上线前轮换示例密钥。 ## 故障排除 - `/health` 在首次启动时显示 ClamAV 不可用:ClamAV 仍在下载其 特征数据库。请等待几分钟。 - 启动时立即退出并抱怨密钥:三个必需密钥之一 缺失或仍设置为其占位符值。 - 本地测试期间通过纯 HTTP 登录状态无法保持:设置 `MAILHOOK_INSECURE_COOKIES=true`(生产环境中切勿如此)。 - `/metrics` 或 `/api/scan` 返回 403:调用者的 IP 不在 `MAILHOOK_METRICS_ALLOWED_CIDRS` 中,或者由于 未设置 `MAILHOOK_TRUSTED_PROXIES` 导致 `X-Forwarded-For` 不受信任。 ## 许可协议 在 [MIT License](LICENSE) 下发布。版权所有 (c) 2026 izm1chael。
标签:CNCF毕业项目, EVTX分析, Go语言, IMAP监控, 垃圾邮件过滤, 日志审计, 程序破解, 自托管, 请求拦截, 邮件安全网关