repod-ce/repod

GitHub: repod-ce/repod

Repod 是一个内置恶意软件和 CVE 漏洞扫描功能的自托管 APT/RPM/APK 私有软件包仓库管理器。

Stars: 0 | Forks: 0

Repod

Repod — 社区版

内置安全扫描的私有 APT/RPM/APK 仓库管理器

License Python FastAPI React Tailwind CSS PostgreSQL Docker nginx

backend image frontend image apt-repo image rpm-nginx image

文档 · 网站 · 企业版 · 请求演示

## 主要功能 / Fonctionnalites principales | EN | FR | |----|----| | Host DEB, RPM and APK packages in a single instance | Hebergez des paquets DEB, RPM et APK dans une seule instance | | ClamAV antivirus scan on every upload (blocking) | Scan antivirus ClamAV a chaque upload (bloquant) | | Grype CVE scan with configurable policy (block/review/warn/allow) | Scan CVE Grype avec politique configurable (block/review/warn/allow) | | GPG auto-signing (Release, repomd.xml, APKINDEX) | Signature GPG automatique (Release, repomd.xml, APKINDEX) | | 5 RBAC roles (admin, maintainer, uploader, auditor, reader) | 5 roles RBAC (admin, maintainer, uploader, auditor, reader) | | Append-only audit trail (JSONL) | Journal d'audit immuable (JSONL) | | Package import from upstream APT/RPM/APK mirrors | Import de paquets depuis sources APT/RPM/APK amont | | FastAPI REST API with JWT auth | API REST FastAPI avec auth JWT | | React + Tailwind web dashboard | Dashboard web React + Tailwind | | Download statistics | Statistiques de telechargement | | Health monitoring dashboard | Dashboard de surveillance | | CVE review/promotion workflow (pending_review queue) | Workflow de revue/promotion CVE (file pending_review) | | Custom roles & groups admin | Administration des roles personnalises et groupes | | Email notification templates | Modeles d'e-mails de notification | | Prometheus `/metrics` endpoint | Point de terminaison Prometheus `/metrics` | | Self-hosted, air-gap ready | Auto-heberge, compatible air-gap | ## 架构 ``` +-------------------+ | Frontend (React) | | nginx :3003 | +--------+----------+ | /api/* v +-------------------+ | Backend (FastAPI) | | :8000 | +--------+----------+ | +-------------+----+----+-------------+ | | | | +-----+-----+ +----+----+ +--+---------------+ | ClamAV | | Grype | | PostgreSQL :5432 | | Antivirus | | CVE DB | | (users, manifests,| +-----------+ +---------+ | package index…) | +--+-----------------+ | +-------------+-------------+ | | +-----+-----+ +-----+-----+ | apt-repo | | rpm-repo | | nginx :80 | | nginx :8080 | | .deb (reprepro) + | .rpm | | .apk (under /apk/) | (createrepo_c) | +-----------+ +-----------+ ``` 5 个 Docker 服务(默认的 `docker-compose.yaml`,`REPO_FORMAT=all` — 同时支持 APT + RPM + APK):`db` (PostgreSQL,容器 `repod-db`) · `backend` (FastAPI,容器 `backend-api`,:8000) · `frontend` (nginx/React,容器 `frontend-ui`,默认 :3003) · `apt-repo` (纯 nginx 静态文件服务器,容器 `depot-apt`,:80 — 同时提供 Alpine `.apk` 的 `/apk/` 路径访问) · `rpm-repo` (纯 nginx 静态文件服务器,容器 `depot-rpm`,默认 :8080,仅限 `.rpm`)。`reprepro` 和 `createrepo_c` 自身在 `backend` 容器内运行,直接操作共享的 `/repos` 卷 — `apt-repo`/`rpm-repo` 仅负责提供生成的文件,它们不运行任何仓库工具。 ## REPO_FORMAT 模式 (apt / rpm / apk / all) 后端与格式无关,在启动时读取一次 `REPO_FORMAT` (`services/format_router.py`) 以决定加载哪个验证器/分发 后端: | `REPO_FORMAT` | 提供的包 | 仓库工具 | |---|---|---| | `apt` (未设置时的默认值) | 仅 `.deb` | reprepro | | `rpm` | 仅 `.rpm` | createrepo_c | | `apk` | 仅 Alpine `.apk` | `apk index` | | `all` | 同时包含 `.deb` + `.rpm` + `.apk` | 全部三种 | **默认的 `docker-compose.yaml` 硬编码了 `REPO_FORMAT: all`** (在 `backend.env.example` 中不作为可覆盖的环境变量公开)— 每次全新执行 `docker compose up -d` 都会开箱即用地同时运行 APT、RPM 和 APK,并且全部集中管理(一个数据库,一个仪表板,一个 RBAC/审计跟踪)。 ## 前置条件 / Prerequis ### 系统时钟 / Horloge systeme ### 推荐磁盘空间 / Dimensionnement disque recommande | Component / Composant | Path | Typical size | Notes | |---|---|---|---| | Docker images (all 5 services) | Docker's own storage | ~2–3 GB | One-time, grows slowly across upgrades | | ClamAV signature DB | `./repos/clamav-db/` | ~300–500 MB | Fixed, updated in place | | Grype vulnerability DB (NVD feed) | `./repos/grype-db/` | ~1–2 GB | Fixed, updated in place | | PostgreSQL (users, manifests index, package-index search) | `postgres_data` volume | Few hundred MB → low GB | Grows slowly with package/user/audit-history count, not with package *file* size | | Audit logs (JSONL, one file/day) | `./repos/audit/` | A few MB/day at moderate usage | Subject to `retention_daily` cleanup — see `settings.json["retention"]` | | **Package pool (`.deb`/`.rpm`/`.apk`)** | `./repos/pool/`, `./repos/rpm/`, `./repos/apk/` | **Highly variable — the dominant factor** | Every uploaded/imported/mirrored version is retained (no automatic pruning beyond `snapshots.py`'s configurable version-count limit); size = Σ(package size × retained versions) | ## 快速开始 / Demarrage rapide ``` # 1. Clone the repository / Cloner le depot git clone https://github.com/repod-ce/repod.git && cd repod # 2. Configure environment / Configurer l'environnement cp .env.example .env cp backend.env.example backend.env # Edit .env : POSTGRES_PASSWORD, JWT_SECRET_KEY, CORS_ORIGINS (REQUIRED / OBLIGATOIRE en prod # — with the default docker-compose.yaml, backend.env's own copies of these three # are silently ignored, see the "Environment variables" section below) # Edit backend.env : SETTINGS_ENCRYPTION_KEY, REPOD_LICENSE_VENDOR_KEY, WEBHOOK_SECRET (REQUIRED / OBLIGATOIRE en prod) # 3. Pull published images and start / Tirer les images publiees et demarrer docker compose pull docker compose up -d ``` ### 已发布的镜像 / Images publiees | Image | Pull command | |-------|-------------| | Backend (FastAPI) | `docker pull ghcr.io/repod-ce/backend:latest` | | Frontend (React/nginx) | `docker pull ghcr.io/repod-ce/frontend:latest` | | APT repo (nginx) | `docker pull ghcr.io/repod-ce/apt-repo:latest` | | RPM repo (nginx) | `docker pull ghcr.io/repod-ce/rpm-nginx:latest` | ## 部署后设置 / Configuration post-deploiement 1. **Create the first admin / Créer le premier admin** — 打开 `http://:3003`;首次运行设置向导会自动出现 (参见下方的 [安全警告](#security-warning--avertissement-securite))。 2. **Generate a GPG signing key / Générer une clé GPG de signature** — Settings 页面 → GPG 部分 → **"Générer une nouvelle clé"**(仅限管理员,`POST /settings/gpg/generate`)。请务必在**上传或导入第一个包之前**执行此操作。 3. **Sync sources / update security databases / Synchroniser les sources — mettre à jour les bases de sécurité** — Sources 页面 → **"Synchroniser"** (`POST /import/sync/start`, maintainer+ )。这将在一次操作中刷新 Grype CVE 数据库、CISA KEV 目录、FIRST.org EPSS 分数,以及上游 APT/RPM/APK 安全源包索引。它也会每天自动运行一次 (`security_sync_daily`,默认 03:00,可在 Settings 页面配置),但在全新安装时,您肯定不想等到凌晨 3 点才获取准确的 CVE 数据。 4. **Verify / Vérifier** — 完成上述步骤后,Supervision 页面 (`/supervision`,或 `GET /health`) 应该报告整体 `"status": "healthy"`(特别要注意 `gpg.ok: true` — 如果仍为 `false`,请参见步骤 2 中的说明)。 ## TLS 部署 / Deploiement TLS ### 自签名证书 / Certificat auto-signe ``` bash scripts/gen-selfsigned-certs.sh docker compose -f docker-compose.yaml -f docker-compose.tls.yml up -d ``` ### Let's Encrypt (需要公共域名 / domaine public requis) ``` export REPOD_DOMAIN=repod.example.com export CERTBOT_EMAIL=admin@example.com docker compose -f docker-compose.yaml -f docker-compose.tls.yml \ -f docker-compose.letsencrypt.yml up -d docker compose -f docker-compose.yaml -f docker-compose.tls.yml \ -f docker-compose.letsencrypt.yml run --rm certbot certonly ``` ## 环境变量 / Variables d'environnement 两个文件,在首次使用前会被复制到其实际(被 gitignored 忽略)的对应文件: `.env.example` → `.env` (由 `docker compose` 自身读取) 和 `backend.env.example` → `backend.env` (仅限后端的密钥/配置,绝不能被 `docker-compose.yaml` 自身读取)。完整参考 — 包含两个文件中的所有变量,而不仅仅是精选的子集。**没有任何变量在两个文件中重复声明** — 下面的每一行都原样以 `KEY=value` 的形式仅出现在其中一个 示例文件中;另一个文件在相关情况下仅包含指回该行的解释性注释。 ### `.env` — 由 `docker compose` 自身读取 | Variable | Purpose | |---|---| | `POSTGRES_PASSWORD` | PostgreSQL 密码 — 这是数据库密码的真正事实来源;它会同时提供给 `db` 服务(使用它初始化 Postgres)和 `backend` 服务的 `DATABASE_URL`(从中重构),从而保持两者自动同步。**在生产环境中请务必更改默认值** | | `JWT_SECRET_KEY` | Token 签名密钥 — **生产环境中必填** (`docker-compose.yaml` 硬编码了 `ENV=production`,如果保留为 `change-me-in-production`,后端将拒绝启动)。这里是其生效的位置 — 请参见上面的优先级说明 | | `CORS_ORIGINS` | 以逗号分隔的允许来源。同上文的 `JWT_SECRET_KEY` 优先级说明 | | `REACT_APP_API_URL` | 除非从源代码构建前端 (`docker-compose.build.yml`),否则保留为空 — 非空值会将绝对 URL 打包进 JS bundle 中并破坏跨主机访问 | | `REACT_APP_REPO_URL` | 同上“仅限从源代码构建”的注意事项 — 仓库的公共 URL (apt-repo),用于在 UI 中构建客户端安装说明 | | `REACT_APP_RPM_REPO_URL` | 同上“仅限从源代码构建”的注意事项,相当于 RPM 仓库的 `REACT_APP_REPO_URL` | | `REPOD_VERSION` | 要拉取的镜像标签 (`latest`, `v1.2.3`…) | | `BIND_HOST` | `0.0.0.0` (默认,端口可从外部访问) 或 `127.0.0.1` (反向代理设置 — 端口仅限主机访问) | | `FRONTEND_PORT` | 前端的主机端口 (默认 `3003`) | | `BACKEND_PORT` | 后端 API 的主机端口 (默认 `8000`) | | `APT_PORT` | APT 仓库的主机端口 (默认 `80`) | | `APT_TLS_PORT` | 启用 `docker-compose.tls.yml` 时的 APT 仓库端口 (默认 `8085`) — 在该模式下,端口 80 会被 TLS 反向代理重新占用 | | `RPM_REPO_PORT` | RPM 仓库的主机端口 (默认 `8080`) | | `REPOD_DOMAIN` / `CERTBOT_EMAIL` | 可选 — 仅被 `docker-compose.letsencrypt.yml` 使用 (公共域名 + ACME 联系邮箱) | | `REPOS_NFS_MOUNT` | 可选 — 仅被 `docker-compose.ha.yml` 使用 (用于多副本 HA 的共享 NFS/EFS 挂载点,必须已经在每台主机上挂载) | `WEBHOOK_SECRET` *不*在此声明 — `docker-compose.yaml` 从不 重定义它,因此 `backend.env` 的值会原样应用;请参阅下文的 `backend.env` 表格(在两个文件中都声明它只会重新引入上述的 `env_file` 顺序陷阱)。 ### `backend.env` — 仅限后端,通过 `env_file` 使用 (绝不会被 `docker-compose.yaml` 自身读取) | Variable | Purpose | |---|---| | `JWT_EXPIRE_MINUTES` | Token 有效期,以分钟为单位 (默认 `60`) | | `SETTINGS_ENCRYPTION_KEY` | 加密 `settings.json` 中的机密 (SMTP/LDAP 密码,OIDC `client_secret`);如果未设置,则回退到 `JWT_SECRET_KEY` (不推荐) | | `REPOD_LICENSE_VENDOR_KEY` | 签名/验证企业版许可证密钥 — **生产环境中必填** (如果未设置或为默认值,启动会立即失败;在开发模式下仅发出警告) | | `ADMIN_USERNAME` / `ADMIN_PASSWORD_HASH` | 预先配置第一个管理员,跳过设置向导 — 参见下方的 [安全警告](#security-warning--avertissement-securite) | | `SETUP_TOKEN` | 可选 — 在容器启动到创建第一个管理员之间的时间窗口内保护 `POST /api/v1/setup (需要请求头 `X-Setup-Token`) | | `AUTH_RATELIMIT_PER_MINUTE` | 登录速率限制,每个 IP 每分钟的请求数 (默认 `10`) | | `LOGIN_MAX_ATTEMPTS` / `LOGIN_LOCKOUT_MINUTES` | 连续 N 次登录失败后锁定账户 (默认 `5`),持续 M 分钟 (默认 `15`) — 按账户持久化,独立于上述基于 IP 的速率限制 | | `SSH_HOST` / `SSH_USER` | 可选 — **不是**企业版的集群资产清单功能。支撑 Packages 页面上的 "Télécharger depuis Internet" 按钮:后端通过 SSH 连入 **Docker 宿主机** (而不是远程集群客户端) 并通过 `apt` 在那里运行 `download-package-dep.sh`。将两者留空可禁用该按钮 (会返回明确的错误,没有其他影响)。 | | `SSH_KEY_PATH` | 后端容器内用于上述连接的 Ed25519 私钥路径 (默认 `/home/appuser/.ssh/id_ed25519`) | | `SSH_PORT` | 相同连接的 SSH 端口 (默认 `22`) | | `WEBHOOK_SECRET` | 用于验证 `/webhooks/github` 和 `/webhooks/kev` 上的 `X-Hub-Signature-256` 的 HMAC 密钥 — **生产环境中必填** | | `WEBHOOK_SIGNATURE_SKIP` | 绕过该签名检查 — **仅限开发/测试使用,切勿在生产环境中启用** (默认 `false`) | | `METRICS_TOKEN` | 可选的 Bearer token,用于保护 `GET /metrics` (Prometheus) — 未设置表示不进行身份验证 | | `MANIFEST_CACHE_TTL` | 内存中的 `.deb`/`.rpm`/`.apk` 元数据缓存 TTL,以秒为单位 (默认 `30`) | | `SQL_ECHO` | 记录由 SQLAlchemy 执行的每一条 SQL 语句 — 仅限调试,非常冗长 | | `EMAIL_TEMPLATES_DIR` / `AUTH_DIR` | 电子邮件模板覆盖/密码重置 token (默认 `/repos/templates/email`, `/repos/auth`)。⚠ 在默认的 `docker-compose.yaml` 中两者都没有专属的卷 — 除非您添加自己的绑定挂载,否则在重建 `backend` 容器时内容将无法保留 | | `REPREPRO_DISTS` / `REPREPRO_CONTAINER` | 高级选项,仅限 APT — 当未明确传入参数时,`remove_package()` 使用的默认发行版列表 (默认 `jammy,noble,focal,bookworm`,如果更改,请与 `services/distributions_apt.py:VALID_CODENAMES` 保持同步),以及仅在通过 `via_docker=True` 调用它时使用的容器名称 (仅限开发环境,例如 `docker-compose.dev.yml`) | `POOL_DIR`, `MANIFEST_DIR`, `STAGING_INCOMING`, `STAGING_QUARANTINE`, `AUDIT_DIR`, `INDEX_PATH`, `ADD_DEB_SCRIPT`, `ADD_RPM_SCRIPT`, `IMPORTS_DIR`, `CLAMAV_DB_DIR`, `SETTINGS_PATH`, `GRYPE_DB_CACHE_DIR`, `SECURITY_CACHE_DIR`, `NGINX_LOGS_DIR`, `GNUPG_HOME`, `REPREPRO_BASE`, `DISTS_DIR`, `CONF_DIR`, `REPO_BASE`, `APK_REPO_BASE`, `TRUSTED_PROXIES` 完全**不**在 `backend.env.example` 中 — 它们是硬编码在 `docker-compose.yaml` 的 `environment:` 块中的内部路径,与其自身的卷声明一对一耦合。只能在 `docker-compose.yaml` 中直接修改这些路径,并与其卷保持同步。 ## 卸载与重装 / Desinstallation et reinstallation ### 卸载 / Désinstallation ``` # 1. Stop and remove containers, network, and the named Postgres volume docker compose down -v --remove-orphans # 2. Remove every Repod image (forces a fresh pull on next install) docker images --format '{{.Repository}}:{{.Tag}}' | grep '^ghcr.io/repod-ce/' | xargs -r docker rmi -f # 3. Wipe persisted data and local config sudo rm -rf repos/* rm -f .env backend.env ``` ### 重装 / Réinstallation ``` # Optional — only if you also want to update Repod itself git pull cp .env.example .env cp backend.env.example backend.env # Edit both files — see "Environment variables" above. For a real fresh # install, generate brand-new secrets (don't reuse old ones, unless you # deliberately kept the PostgreSQL volume/repos data from step 3 above and # need continuity — in that case POSTGRES_PASSWORD in particular MUST match # the value Postgres was originally initialized with, or the backend won't # be able to authenticate). docker compose pull docker compose up -d ``` 然后重复上方的[部署后设置](#post-deployment-setup--configuration-post-deploiement) — 创建第一个管理员,生成 GPG 密钥(并重新初始化发行版),同步源 — 一个全新的数据库意味着所有这些都要从零开始。 ## 安全警告 / Avertissement securite 对于自动化部署,请在 `backend.env` 中通过 `ADMIN_USERNAME` / `ADMIN_PASSWORD_HASH` 预先配置管理员(参见 `backend.env.example`)。 ``` # Generate a bcrypt hash / Generer un hash bcrypt docker run --rm python:3.10-slim python3 -c \ "from passlib.context import CryptContext; print(CryptContext(schemes=['bcrypt']).hash('YourPass1!'))" ``` ## 文档 | | EN | FR | |---|---|---| | 完整指南 | [docs.getrepod.com](https://docs.getrepod.com/) | [docs.getrepod.com/fr](https://docs.getrepod.com/fr/) | | 架构 | [Architecture](https://docs.getrepod.com/explanation/architecture/) | [Architecture](https://docs.getrepod.com/fr/explanation/architecture/) | | 快速开始 | [Getting started](https://docs.getrepod.com/getting-started/) | [Demarrage rapide](https://docs.getrepod.com/fr/getting-started/) | | 角色与权限 | [Roles](https://docs.getrepod.com/reference/roles/) | [Roles](https://docs.getrepod.com/fr/reference/roles/) | | 安全流水线 | [Security pipeline](https://docs.getrepod.com/explanation/security-pipeline/) | [Pipeline de securite](https://docs.getrepod.com/fr/explanation/security-pipeline/) | ## 许可证 / Licence Repod 源代码(后端和前端)采用 **GNU Affero General Public License v3.0 (AGPL-3.0-only)** 授权 -- 参见 [LICENSE](./LICENSE)。 我们也提供不受 AGPL 义务约束的商业许可 -- 参见 [LICENSE-COMMERCIAL.md](./LICENSE-COMMERCIAL.md)。 Le code source de Repod (backend et frontend) est distribue sous la **GNU Affero General Public License v3.0 (AGPL-3.0-only)** -- voir [LICENSE](./LICENSE). Une licence commerciale sans les obligations de l'AGPL est disponible -- voir [LICENSE-COMMERCIAL.md](./LICENSE-COMMERCIAL.md). ### 第三方组件 / Composants tiers | Component | License | Usage | |-----------|---------|-------| | [reprepro](https://salsa.debian.org/brlink/reprepro) | GPL v2 | APT repo management (subprocess) | | [ClamAV](https://www.clamav.net/) | GPL v2 | Antivirus scanning (Unix socket) | | [Grype](https://github.com/anchore/grype) | Apache 2.0 | CVE vulnerability scanning | | [FastAPI](https://fastapi.tiangolo.com/) | MIT | Backend web framework | | [React](https://react.dev/) | MIT | Frontend UI library | | [Tailwind CSS](https://tailwindcss.com/) | MIT | Frontend CSS framework | | [PostgreSQL](https://www.postgresql.org/) | PostgreSQL License | Relational database | | [nginx](https://nginx.org/) | BSD-2-Clause | Reverse proxy & static file serving | reprepro 和 ClamAV 作为**独立进程**被调用(分别为 subprocess 执行和 Unix socket),且**未与** Repod 的代码发生静态或动态**链接**。 完整的第三方归因说明请参见 [NOTICES](./NOTICES),完整的许可证文本请参见 [LICENSES/](./LICENSES/)。
标签:AV绕过, FastAPI, Linux包管理, PostgreSQL, React, Syscalls, 安全扫描, 时序注入, 测试用例, 请求拦截, 软件源仓库, 逆向工具