secopdotdev/OpeNirvana

GitHub: secopdotdev/OpeNirvana

OpeNirvana是一个安全优先的生产就绪自托管应用栈,通过单命令部署集成WAF、SIEM、SSO、运行时安全和网络监控等完整安全防护层,为安全研究人员和隐私敏感用户提供从边缘到应用的全栈防护方案。

Stars: 1 | Forks: 0

# 统一堆栈 单命令自托管基础:只需一个 `docker compose up` 即可启动 Caddy(包含 Crowdsec、Coraza WAF、forward-auth、Souin 缓存、Brotli、L4 代理的定制版本)、Tailscale 入口 sidecar、共享的 Postgres + Redis、Wazuh SIEM、Crowdsec LAPI、Falco 运行时安全监控、Zeek 网络安全监控、Authentik SSO、Nextcloud,以及可选的媒体和生产力技术栈——所有这些都可以通过 `*.secop.dev`(公共网络,经由 Cloudflare)和 `*.neon-lenok.ts.net`(Tailnet)访问。 **优先级顺序:** 功能性 > 安全性 > 效率 > 稳定性。 **配置 Profiles:** - *(无 profile)* — 核心技术栈:Authentik、Nextcloud、Wazuh、Falco、Zeek、Crowdsec、Caddy - `--profile media` — Jellyfin、Jellyseerr、Prowlarr、Radarr、Sonarr、Lidarr、qBittorrent、FlareSolverr(均通过 ProtonVPN) - `--profile apps` — ntfy、Tandoor、Vikunja、AFFiNE ## 架构图 ### 网络拓扑 ``` flowchart TB subgraph Internet CF[Cloudflare edge] TN[Tailnet peers] end subgraph host["Docker host (Ubuntu)"] subgraph ingress["ingress 10.0.10.0/24"] TS[tailscale-ingress
10.0.10.200 ★multi-homed] CADDY[caddy
shares netns] CS1[crowdsec alias
10.0.10.21] end subgraph auth["auth 10.0.11.0/24"] AUTH_S[authentik-server
.20] AUTH_W[authentik-worker
.21] AUTH_P[authentik-proxy
.22] end subgraph data["data 10.0.12.0/24"] PG[postgres
.30] RD[redis
.31] end subgraph obs["observability 10.0.13.0/24"] CS2[crowdsec
.20] SPRO2[socket-proxy-ro
.21] SPRW[socket-proxy-rw
.22] AH[autoheal
.23] FL[falco
.24] WM[wazuh-manager
.30] WI[wazuh-indexer
.31] WD[wazuh-dashboard
.32] end subgraph apps["apps 10.0.14.0/24"] NC[nextcloud
.20] JF[jellyfin
.21 media] JS[jellyseerr
.22 media] GT[gluetun
.30 ProtonVPN WG] end subgraph prod["productivity 10.0.15.0/24"] NT[ntfy
.20 apps] TD[tandoor
.21 apps] VK[vikunja
.22 apps] AF[affine
.23 apps] end ZK[zeek
host netns] CT[coturn
host netns] end CF --> TS TN --> TS TS --- CADDY CADDY --> AUTH_P & WD & NC & JF & JS CADDY --> NT & TD & VK & AF CADDY --> GT AUTH_P --> AUTH_S AUTH_S --> PG & RD AUTH_W --> PG & RD AUTH_P --> RD NC --> PG & RD TD --> PG VK --> PG AF --> PG & RD CADDY --> CS1 CS1 -.same process.- CS2 FL -.docker API.-> SPRO2 AH --> SPRW ZK -.taps all networks.-> host CT -.TURN/STUN 3478.-> Internet GT -.WireGuard tunnel.-> Internet ``` ### 请求流(公共) ``` sequenceDiagram autonumber participant C as Client participant CF as Cloudflare edge participant UFW as Host UFW participant CAD as Caddy participant CS as Crowdsec participant CRZ as Coraza WAF participant AK as Authentik participant APP as Target app C->>CF: HTTPS request (app.secop.dev) CF->>UFW: forwarded (CF IP → 443) UFW->>CAD: ACCEPT (CF IP, 443) CAD->>CAD: CF allowlist check CAD->>CS: bouncer: is this IP banned? CS-->>CAD: allowed CAD->>CRZ: WAF inspection (CRS rules) CRZ-->>CAD: anomaly score under threshold CAD->>AK: forward-auth: session valid? AK-->>CAD: 200 + headers CAD->>APP: reverse_proxy APP-->>CAD: response CAD-->>C: HTTPS response ``` ### 启动顺序 ``` flowchart LR subgraph init["Bootstrap (docker compose up --build)"] TS[tailscale-ingress] --> CAD PG[postgres + initdb hook
provisions app DBs] --> AKM RD[redis] --> AKM AKM[authentik-migration] --> AKS AKS[authentik-server] --> AKW[authentik-worker] AKS --> CAD[caddy] WI[wazuh-indexer] --> WM[wazuh-manager] WM --> WDB[wazuh-dashboard] SPR[socket-proxy-rw] --> AH[autoheal] CSC[crowdsec] --> CAD end ``` ### 日志管道 ``` flowchart LR CAD[caddy
access.log JSON] --> WA CS[crowdsec
decisions.log JSONL] --> WA FL[falco
events.log JSON] --> WA ZK[zeek
conn/dns/ssl/notice .log JSON] --> WA WA[Wazuh Agent
host-level, file tail] --> WM[wazuh-manager] WM --> WI[wazuh-indexer] WI --> WD[wazuh-dashboard
UI @ wazuh.secop.dev] ``` ## 宿主机目录结构 ``` /dock/ ├── conf/ # configuration (RO in most containers) │ ├── caddy/{Caddyfile, snippets/, coraza/, data/, logs/, souin/} │ ├── crowdsec/{config.yaml, acquis.yaml, profiles.yaml, notifications/, db/} │ ├── authentik/{media/, custom-templates/, certs/} │ ├── wazuh/{manager/, indexer/, dashboard/, certs/, decoders/, rules/} │ ├── falco/{falco.yaml, rules.d/, events.log} │ ├── zeek/{local.zeek, node.cfg, networks.cfg, intel/, logs/current/} │ ├── jellyfin/ # (profile: media) │ ├── jellyseerr/ # (profile: media) │ ├── qbittorrent/qBittorrent/qBittorrent.conf # pre-seeds LocalHostAuth=false │ └── ntfy/ # (profile: apps) ├── data/ # application data (non-DB) │ ├── authentik/ │ ├── nextcloud/ # owned 33:33 (www-data inside container) │ ├── wazuh/{indexer/, manager/} │ ├── jellyfin/ # (profile: media) │ ├── ntfy/{cache/, data/} # (profile: apps) │ ├── tandoor/{media/, static/} # owned 1000:1000 (profile: apps) │ ├── vikunja/ # owned 1000:1000 (profile: apps) │ └── affine/{config/, storage/} # (profile: apps) ├── db/ # databases │ ├── postgres/{data/, init.d/} │ └── redis/ ├── tail/ │ └── ingress/ └── backups/ ├── postgres/ # pg-backup.sh output └── redis/ ``` 所有路径的所有者设置为 `docktaetor:media (1010:1010)`,模式为 `770`(数据库目录为 `700`)。 ## 安全模型 ### 入口路径 ``` Internet → Cloudflare → Host UFW → Caddy (via Tailscale netns) → @cloudflare matcher → Crowdsec bouncer → Coraza WAF → Authentik forward-auth → App container Parallel observation: Falco (runtime) + Zeek (network) ``` ### 威胁 × 缓解措施矩阵 | 威胁 | 主要缓解措施 | 备用方案 | |---|---|---| | 公共 DDoS | Cloudflare 边缘节点 | CF 白名单丢弃非 CF 流量 | | 凭证撞库 | Authentik 速率限制 + MFA | Crowdsec auth-brute 场景 | | 零日 Web 漏洞利用 | Coraza OWASP CRS | 出口隔离(数据层 + 应用层分离) | | 容器逃逸(未知) | Falco: terminal-shell, write-below-root, unexpected-privileged | 宿主机 UFW + 内核加固 | | 恶意 DNS 数据外泄 | Zeek dns.log + Intel 命中 → Wazuh 关联分析 | Crowdsec 出口黑名单场景 | | TLS 指纹 C2 信标 | Zeek ssl.log JA3/JA4 异常 | Cloudflare 入站 WAF | | 供应链(安装后阶段) | Falco: 从 /tmp 执行,异常出站流量 | 镜像固定版本,审查更新 | | Socket-proxy 滥用 | Falco: 未授权 Docker API;RO/RW 代理分离 | 代理权限环境变量 | | .env 泄露至 git | .gitignore .env + CI 密钥扫描 | 密钥可通过 docker-host-config.sh 重新生成 | | 容器逃逸(通用) | user:1010:1010, read_only, cap_drop ALL, no-new-privileges, seccomp | Falco + 宿主机 UFW | | 数据库数据外泄 | 数据层隔离,每个应用独立 DB/角色,不暴露端口 | 针对外部 :5432 的 Wazuh 规则 | | 横向移动 | 应用间绝不共享层;仅 Caddy 多宿主 | Crowdsec 内部网络 + Zeek conn.log | | 日志篡改 | 日志绑定挂载至宿主机,Wazuh Agent 在 Docker 外读取 | 仅允许从宿主机追加内容 | | 静默备份失败 | pg-backup.sh level-12 告警;失败时跳过修剪 | 保留底线 = 仅限手动删除 | | Tailscale 密钥泄露 | 临时密钥 + 禁用重用 | 重新签发 + 重启 sidecar | | Authentik 宕机导致管理员锁定 | 通过 API 使用 AUTHENTIK_BOOTSTRAP_TOKEN | 直接 psql 重置流程 | ## 快速入门 **前提条件**(Ubuntu 22.04+,2+ 核,8+ GB RAM): - 注册并使用 Cloudflare 账号管理您的 `PUBLIC_FQDN` 区域;API token 需具有 `Zone:Read` + `DNS:Edit` 权限。 - 注册 Tailscale 账号并已将该宿主机加入网络;从管理控制台获取 authkey。 - 路由器端口转发至该宿主机: | 端口 | 协议 | 用途 | |------|----------|---------| | 80 | TCP | Caddy HTTP→HTTPS 重定向 + ACME HTTP-01 备用 | | 443 | TCP + UDP | HTTPS (TLS) + HTTP/3 (QUIC) | | 3478 | UDP + TCP | TURN/STUN — Nextcloud Talk WebRTC NAT 穿越(直连,绕过 Cloudflare) | | 49152–49200 | UDP | TURN 媒体中继 — Nextcloud Talk(直连,绕过 Cloudflare) | **操作步骤:** 1. 克隆仓库并运行宿主机引导脚本(创建 `/dock/` 目录树,安装 Docker,设置所有权): git clone ~/git/finnsbeincaddy sudo ~/git/finnsbeincaddy/unified-stack/docker-host-config.sh 2. 从示例文件创建 `.env` 并设置两个外部密钥: cd ~/git/finnsbeincaddy/unified-stack cp .env.example .env # 编辑 .env — 设置 TAILSCALE_AUTHKEY 和 CLOUDFLARE_API_TOKEN。 # 所有其他密钥将在下一步中生成。 # 取消注释与您的宿主机相匹配的资源限制层级(tier)块。 3. 生成所有密钥(Postgres、Redis、Wazuh、Authentik、Nextcloud、CrowdSec bouncer): bash scripts/gen-secrets.sh .env 4. 启动技术栈: sudo docker compose up -d 5. 访问(将 `secop.dev` 替换为您的 `PUBLIC_FQDN`): - `https://auth.secop.dev` — Authentik(首次运行:设置 MFA,创建用户) - `https://wazuh.secop.dev` — Wazuh(由 Authentik 门禁) - `https://cloud.secop.dev` — Nextcloud(由 Authentik 门禁) **开机自启:** `sudo systemctl enable --now compose-stack.service` **媒体技术栈**(可选 — Jellyfin、Jellyseerr、Prowlarr、Radarr、Sonarr、Lidarr、FlareSolverr、qBittorrent 通过 ProtonVPN): ``` # 1. 将 PROTONVPN_WIREGUARD_PRIVATE_KEY 添加到 .env(来自 ProtonVPN dashboard → WireGuard config)。 # 2. 确保 MEDIA_PATH 和 DOWNLOADS_PATH 在挂载了存储的 host 上存在。 # 3. 在 Cloudflare 中添加 DNS CNAMEs:media/requests/prowlarr/radarr/sonarr/lidarr/qbit → @(proxied)。 # 4. 启动 media profile: sudo docker compose --profile media up -d ``` Jellyfin 直接提供媒体服务(不走 VPN — 供用户访问)。所有的 *arr 和 qBittorrent 的 出站流量(索引器请求、 torrent 节点)均通过 ProtonVPN 隧道传输。FlareSolverr 仅限 内部访问;在 Prowlarr 中将其配置为 `http://localhost:8191`。Jellyfin 和 Jellyseerr 使用 它们自身的认证 —— 无需 Authentik forward-auth 门禁(媒体播放器 API 客户端需要直接访问)。 **生产力技术栈**(可选 — ntfy、Tandoor、Vikunja、AFFiNE): ``` # 1. 验证 productivity DB vars 是否已在 .env 中设置(由 gen-secrets.sh 填充)。 # 2. 在 Cloudflare 中添加 DNS CNAMEs:ntfy/recipes/tasks/affine → @(proxied)。 # 3. 启动 apps profile: sudo docker compose --profile apps up -d ``` Tandoor 和 AFFiNE 受 Authentik forward-auth 门禁保护。ntfy 和 Vikunja 使用它们 自身的认证(API/移动客户端需要直接的 token 访问)。 ## 各层服务索引 | 层级 | 服务 | 角色 | |---|---|---| | 入口 | tailscale-ingress | Tailnet 网络呈现 | | 入口 | caddy | TLS、WAF、拦截器、forward-auth | | 认证 | authentik-server | SSO UI + API | | 认证 | authentik-worker | 后台任务 | | 认证 | authentik-proxy | Forward-auth 哨兵(Caddy → Authentik 门禁) | | 认证 | authentik-migration | 一次性 DB 迁移 | | 数据 | postgres | 共享 Postgres 集群 (pgvector) | | 数据 | redis | 共享 Redis(逻辑数据库) | | 可观测性 | crowdsec | 行为封禁 + 黑名单 | | 可观测性 | socket-proxy-ro | 只读 Docker API | | 可观测性 | socket-proxy-rw | 读写 Docker API(仅限 Autoheal) | | 可观测性 | autoheal | 重启不健康的容器 | | 可观测性 | falco | 运行时容器安全 | | 可观测性 | wazuh-manager | SIEM 事件接入 | | 可观测性 | wazuh-indexer | OpenSearch 索引 | | 可观测性 | wazuh-dashboard | Web UI | | 宿主网络 | zeek | 网络安全监控 | | 宿主网络 | coturn | Nextcloud Talk WebRTC 的 TURN/STUN 服务器 | | 应用 | nextcloud | 文件同步 + 协作 | | 应用 | gluetun | ProtonVPN WireGuard 网关 *(profile: media)* | | 应用 (经由 gluetun) | prowlarr | 索引器管理器 *(profile: media)* | | 应用 (经由 gluetun) | radarr | 电影管理 *(profile: media)* | | 应用 (经由 gluetun) | sonarr | 电视剧管理 *(profile: media)* | | 应用 (经由 gluetun) | lidarr | 音乐管理 *(profile: media)* | | 应用 (经由 gluetun) | flaresolverr | 索引器的 Cloudflare 绕过 — 仅限内部 *(profile: media)* | | 应用 (经由 gluetun) | qbittorrent | BitTorrent 客户端 *(profile: media)* | | 应用 | jellyfin | 媒体服务器 — GPU 转码 *(profile: media)* | | 应用 | jellyseerr | 媒体请求管理 *(profile: media)* | | 生产力 | ntfy | 推送通知服务器 *(profile: apps)* | | 生产力 | tandoor | 菜谱管理器 *(profile: apps)* | | 生产力 | vikunja | 任务/项目管理 *(profile: apps)* | | 生产力 | affine | 协作工作区 *(profile: apps)* | ## 故障排除 - **Caddy 卡在 ACME 阶段**:检查 `docker logs caddy`;验证 Cloudflare token 是否具有该区域的 `Zone:read + DNS:edit` 权限。 - **Wazuh 索引器 OOM(内存溢出)**:检查层级块设置;MED/LOW 层级会将 JVM 堆内存减半;或者增加 swap。 - **Falco eBPF 驱动加载失败**:在 `.env` 中设置 `FALCO_DRIVER=ebpf`(旧版探针)并重启 falco。 - **Authentik 管理员被锁定**:`docker exec -it authentik-server ak shell` → `from authentik.core.models import User; u = User.objects.get(username='akadmin'); u.set_password('newpass'); u.save()`。 - **Postgres 备份失败告警**:检查 `/var/log/pg-backup.log`;修剪操作将暂停,直到下一次成功备份。 - **Zeek 未记录日志**:`docker exec zeek zeekctl status`;如果状态为 `crashed`,请检查 `/dock/conf/zeek/logs/current/stderr.log`。 - **GlueTUN 无法连接**:检查 `docker logs gluetun`;验证 `PROTONVPN_WIREGUARD_PRIVATE_KEY` 是否为原始的 base64 密钥(而不是配置文件路径)。如果已连接但存在泄漏:确认已设置 `FIREWALL_OUTBOUND_SUBNETS=10.0.0.0/8`。 - **\*arr 无法连接索引器 / qBittorrent 无做种**:VPN 隧道可能已断开。运行 `docker exec gluetun wget -qO- https://ifconfig.io` —— 如果返回的是您 ISP 的 IP 而不是 VPN IP,则 GlueTUN 需要重新连接。 - **Nextcloud Talk TURN 测试失败**:确认路由器已将 3478/UDP+TCP 和 49152–49200/UDP 端口转发至宿主机 IP。检查 `docker logs coturn` 是否有认证错误。验证 `.env` 中的 `COTURN_SECRET` 是否与 Talk 管理设置中输入的密钥一致。 - **Jellyfin 无法进行 GPU 转码**:确保宿主机具有 `/dev/dri`(Intel: `intel-media-va-driver`,AMD: `mesa-va-drivers`)。检查 `docker logs jellyfin` 是否有关于 `/dev/dri/renderD128` 的权限错误。 - **Tandoor 动时报 500 错误**:`docker logs tandoor` —— 通常是因为缺少 `SECRET_KEY` 或数据库尚未初始化。运行 `gen-secrets.sh` 并确认已设置 `TANDOOR_DB_NAME/USER/PASSWORD`。 - **Vikunja 提示 "JWT secret must be set"**:`VIKUNJA_JWT_SECRET` 为空。运行 `bash scripts/gen-secrets.sh .env` 来填充它。 - **AFFiNE WebSocket 断开 / CORS 错误**:`AFFINE_SERVER_EXTERNAL_URL` 必须与浏览器中的 URL 完全匹配(包含协议 + 主机名,无尾部斜杠)。更新 `.env` 并重启 affine。 - **ntfy 推送未送达**:检查 `docker logs ntfy`。确保 `NTFY_BASE_URL` 与公共 URL 匹配。确认该主题的订阅者已连接,且带有正确 token 的 `NTFY_AUTH_DEFAULT_ACCESS=deny-all` 规则正在生效。 ## 添加新应用(第 3 阶段及以后) 1. 在 `.env` 中添加新的配置段,包含 `_DB_NAME`、`_DB_USER`、`_DB_PASSWORD`(密码留空 —— `docker-host-config.sh` 在下次运行时会自动填充)。 2. 在 `templates/caddy/Caddyfile` 中的两个配置节(stanzas)里添加新的 handle 块: @newapp host newapp.{$PUBLIC_FQDN} handle @newapp { import authentik-forward-auth reverse_proxy newapp: } 3. 在 `docker-compose.yml` 中添加该应用的服务。加入其自身所在的层级(如需要则创建一个:`10.0.14.0/24` 用于媒体,`10.0.15.0/24` 用于生产力等)+ `data` 层以访问 Postgres/Redis。 4. 添加 tailscale-ingress 多宿主:将新的层级添加到其 `networks:` 列表中。 5. 重启:`docker compose up -d --build`。
标签:AppImage, Authentik, Caddy, CISA项目, Cloudflare, CrowdSec, DevSecOps, Docker Compose, Falco, Ingress, Jellyfin, MITRE ATT&CK, Nextcloud, PE 加载器, ProtonVPN, Rootkit, SSO, Tailscale, VPN, WAF, Wazuh, Web应用防火墙, Web截图, Zeek, 上游代理, 单点登录, 反向代理, 可视化架构, 媒体服务器, 子域名字典, 安全信息与事件管理, 安全架构, 容器安全, 应用栈, 开源云盘, 搜索引擎查询, 搜索引擎爬取, 敏感词过滤, 服务器运维, 流量捕获, 测试用例, 版权保护, 生产就绪, 统一部署, 网络拓扑, 自动化运维, 自托管, 零信任网络