inbjo/MirrorProxy

GitHub: inbjo/MirrorProxy

一个基于 Rust 的全栈自托管镜像加速平台,通过统一代理服务为数十种编程语言和操作系统的包管理器提供高速镜像拉取能力。

Stars: 32 | Forks: 0

# MirrorProxy [English](README.md) | [简体中文](README_CN.md) [![CI](https://img.shields.io/github/actions/workflow/status/inbjo/MirrorProxy/ci.yml?branch=main&style=flat-square&logo=githubactions&logoColor=white&label=CI)](https://github.com/inbjo/MirrorProxy/actions/workflows/ci.yml) [![Release](https://img.shields.io/github/v/release/inbjo/MirrorProxy?style=flat-square&logo=github&label=Release)](https://github.com/inbjo/MirrorProxy/releases/latest) [![License](https://img.shields.io/github/license/inbjo/MirrorProxy?style=flat-square&label=License)](LICENSE) [![Docker Pulls](https://img.shields.io/docker/pulls/kudang/mirrorproxy?style=flat-square&logo=docker&logoColor=white&label=Docker%20Pulls)](https://hub.docker.com/r/kudang/mirrorproxy) [![Clients](https://img.shields.io/badge/clients-Windows%20%7C%20macOS%20%7C%20Linux-2f81f7?style=flat-square)](#安装客户端) [![GitHub Stars](https://img.shields.io/github/stars/inbjo/MirrorProxy?style=flat-square&logo=github&label=Stars)](https://github.com/inbjo/MirrorProxy/stargazers) MirrorProxy 是一个使用 Rust 编写的自托管镜像代理平台。`mirrorproxy-server` 服务和 `mirrorproxy` 源管理客户端是独立的二进制文件。服务端内嵌了 React + Vite + Tailwind Web 控制台;独立客户端可在 Windows、macOS 和 Linux 上运行。 该项目使用基于适配器的代理核心,并且已经内置了针对 GitHub、Docker/OCI、Composer、npm、PyPI、Cargo、Go modules、主流语言仓库、开发者工具分发服务以及操作系统镜像的适配器。新的生态系统可以复用相同的路由、流式传输、安全、流量统计、配额和缓存基础设施。 ## 功能特性 - 内嵌公共和经过身份验证的管理 Web 控制台,位于 `/` - 健康检查端点位于 `/healthz` - 运行时公共配置端点位于 `/api/config` - 源目录端点位于 `/api/sources` - 基于 SQLite 的设置、管理员会话、审计日志、流量统计、月度配额、速率限制和有界磁盘缓存 - 独立的 Windows/macOS/Linux 客户端,支持源编辑、精确回滚以及 GitHub HTTPS Git URL 重写 - GitHub 代理,支持仓库页面、原始文件、release 资产、归档以及 Composer GitHub dist URL - Composer 代理位于 `/composer` - Docker/OCI 代理位于 `/v2/*`,支持 Docker Hub、GHCR、Quay 和 Kubernetes 公共镜像 - npm/yarn/pnpm 代理位于 `/npm` - 针对 NVM 的 Node.js 发行版代理位于 `/nvm` - opam 仓库代理位于 `/opam` - Go module 代理位于 `/goproxy` - Maven Central 代理位于 `/maven` - RubyGems 代理位于 `/rubygems` - Rustup 发行版代理位于 `/rustup` - NuGet v3 代理位于 `/nuget/v3/index.json` - CPAN 仓库代理位于 `/cpan` - CRAN 仓库代理位于 `/cran` - Hackage 仓库代理位于 `/hackage` - Julia package server 代理位于 `/julia` - LuaRocks 仓库代理位于 `/luarocks` - Clojars 仓库代理位于 `/clojars` - CocoaPods CDN 代理位于 `/cocoapods` - Dart / Flutter Pub 代理位于 `/pub` - Anaconda / Conda 代理位于 `/anaconda` - TeX Live 代理位于 `/texlive` - WinGet 源代理位于 `/winget` - GNU ELPA 代理位于 `/elpa` - Nix binary cache 代理位于 `/nix` - GNU Guix substitute cache 代理位于 `/guix` - Flatpak OSTree 代理位于 `/flatpak` - Homebrew bottle 代理位于 `/homebrew` - `/os` 下的白名单 Linux、BSD、MSYS2、OpenWrt、Termux、ROS 及相关操作系统仓库 - Cargo sparse registry 代理位于 `/crates-index` - pip/PyPI 代理位于 `/pypi/simple` - 流式传输上游响应,并过滤逐跳 (hop-by-hop) 头部 - 安全的默认设置,拒绝不受支持的绝对代理目标 ## 快速开始 ``` cargo run -p mirrorproxy-server --bin mirrorproxy-server -- --config config.example.toml ``` 打开: ``` http://selfhost.com ``` 检查健康状态: ``` curl http://selfhost.com/healthz ``` ## Docker 部署 服务端镜像以非 root 用户 UID/GID `10001` 运行,监听端口 `3000`,并将其 SQLite 数据库和可选缓存存储在 `/data` 卷下。 该仓库包含一个可以直接运行的 [compose.yaml](compose.yaml)。你也可以在一个空的部署目录中将以下内容另存为 `docker-compose.yaml`: ``` services: mirrorproxy: image: ${MIRRORPROXY_IMAGE:-kudang/mirrorproxy:latest} container_name: mirrorproxy restart: unless-stopped ports: - "${MIRRORPROXY_PORT:-3000}:3000" environment: MIRRORPROXY_PUBLIC_BASE_URL: ${MIRRORPROXY_PUBLIC_BASE_URL:-http://127.0.0.1:3000} MIRRORPROXY_QUOTA_TIMEZONE: ${MIRRORPROXY_QUOTA_TIMEZONE:-local} RUST_LOG: ${RUST_LOG:-mirrorproxy_server=info,tower_http=info} volumes: - mirrorproxy-data:/data healthcheck: test: ["CMD", "curl", "--fail", "--silent", "--show-error", "http://127.0.0.1:3000/healthz"] interval: 30s timeout: 5s start_period: 10s retries: 3 volumes: mirrorproxy-data: ``` 启动前,在 `.env` 文件中设置外部 URL 和可选的主机端口: ``` MIRRORPROXY_PORT=53000 MIRRORPROXY_PUBLIC_BASE_URL=https://mirror.example.com ``` ``` MIRRORPROXY_PUBLIC_BASE_URL=https://mirror.example.com docker compose up -d docker compose logs mirrorproxy curl http://127.0.0.1:3000/healthz ``` 首次启动日志包含一次性的随机 `admin` 密码。升级时请保留名为 `mirrorproxy-data` 的卷。如果不使用 Compose 运行: ``` docker run -d --name mirrorproxy --restart unless-stopped \ -p 3000:3000 \ -e MIRRORPROXY_PUBLIC_BASE_URL=https://mirror.example.com \ -v mirrorproxy-data:/data \ kudang/mirrorproxy:latest ``` 推荐使用命名卷。如果你使用主机绑定挂载(例如 `/srv/mirrorproxy/data:/data`)替换它,该目录必须可被容器的 UID/GID `10001:10001` 写入: ``` sudo install -d -o 10001 -g 10001 -m 0750 /srv/mirrorproxy/data sudo install -d -o 10001 -g 10001 -m 0750 /srv/mirrorproxy/data/cache ``` 在启用 SELinux 强制模式的主机上,在绑定挂载后附加 `:Z`。否则,SQLite 在启动时可能会失败并报错 `code: 14 unable to open database file`。 诸如 `MIRRORPROXY_ENABLED_PROXIES`、配额、缓存和速率限制设置等环境变量在容器中均有效。如需使用完整的 TOML 配置,请以只读方式挂载并显式设置其路径: ``` docker run -d --name mirrorproxy --restart unless-stopped \ -p 3000:3000 \ -e MIRRORPROXY_CONFIG=/etc/mirrorproxy/config.toml \ -e MIRRORPROXY_LISTEN_ADDR=0.0.0.0:3000 \ -e MIRRORPROXY_DB=/data/mirrorproxy.sqlite3 \ -v mirrorproxy-data:/data \ -v "$PWD/config.toml:/etc/mirrorproxy/config.toml:ro" \ kudang/mirrorproxy:latest ``` 将当前源码构建为本地 `linux/amd64` 镜像: ``` ./scripts/docker-build.sh ``` 如果 Docker Hub 缓慢或不可用,可通过 MirrorProxy 实例路由基础镜像拉取: ``` MIRRORPROXY_DOCKER_BASE_REGISTRY=sina.dev/library ./scripts/docker-build.sh ``` 对于本地多平台构建,请注册一次 ARM64 模拟(GitHub Actions 会自动执行此操作)。当 Docker Hub 不可用时,也可以通过 MirrorProxy 拉取相同的镜像: ``` docker run --privileged --rm sina.dev/tonistiigi/binfmt --install arm64 ``` 然后在 `docker login` 后发布 `linux/amd64` 和 `linux/arm64` manifest: ``` ./scripts/docker-build.sh --push --image /mirrorproxy ``` `Docker` GitHub Actions 工作流执行相同的多平台发布。设置仓库变量 `DOCKERHUB_USERNAME` 和仓库密钥 `DOCKERHUB_TOKEN`;推送 `v*` 标签会发布语义化版本和 `latest` 标签,而工作流调度支持指定明确的版本。 ## GitHub 代理 MirrorProxy 接受在你自己的域名下受支持的 GitHub 绝对 URL: ``` http://selfhost.com/https://github.com/inbjo/Conductor http://selfhost.com/https://github.com/inbjo/Conductor/releases/download/nightly/conductor-client-linux-amd64.deb ``` 此切片中允许的与 GitHub 相关的主机: - `github.com` - `api.github.com` - `raw.githubusercontent.com` - `objects.githubusercontent.com` - `codeload.github.com` ## Composer 代理 将 Composer 配置为使用 MirrorProxy: ``` composer config repo.packagist composer http://selfhost.com/composer composer require monolog/monolog ``` MirrorProxy 会代理 Packagist 元数据,并将常见的 GitHub/Packagist 下载 URL 重写为通过你的 MirrorProxy 公共基础 URL 进行回传。 ## Docker / OCI 代理 将你的 MirrorProxy 主机用作 Docker registry: ``` docker pull selfhost.com/nginx docker pull selfhost.com/user/image docker pull selfhost.com/ghcr.io/user/image docker pull selfhost.com/quay.io/org/image docker pull selfhost.com/registry.k8s.io/pause:3.8 ``` 映射规则: - `name` 映射到 Docker Hub 的 `library/name` - `user/image` 映射到 Docker Hub 的 `user/image` - `ghcr.io/user/image` 映射到 GHCR - `quay.io/org/image` 映射到 Quay - `registry.k8s.io/name` 映射到 Kubernetes registry 该代理处理公共的穿透拉取请求以及上游 Bearer token 质询。私有上游凭据可以按照[安全](#security)部分中的说明进行配置。 ## npm / yarn / pnpm 代理 将你的包管理器配置为使用 MirrorProxy: ``` npm config set registry http://selfhost.com/npm npm install react yarn config set npmRegistryServer http://selfhost.com/npm yarn add react pnpm config set registry http://selfhost.com/npm pnpm add react ``` MirrorProxy 会代理 npm 包元数据并重写 `dist.tarball` URL,以确保 tarball 下载流量继续通过 `/npm`。 ## Go Module 代理 将 MirrorProxy 用作 `GOPROXY`: ``` go env -w GOPROXY=http://selfhost.com/goproxy,direct go list -m github.com/gin-gonic/gin@latest ``` Go 适配器将 `@v/list`、`.info`、`.mod` 和 `.zip` 等 GOPROXY 协议路径转发到 `proxy.golang.org`。 ## Maven Central 代理 配置 Maven 的用户设置,通过 MirrorProxy 镜像 Central: ``` mirrorproxy http://selfhost.com/maven/ central ``` 将此内容保存在 `~/.m2/settings.xml` 下,或者让 CLI 在带有回滚保护的情况下写入它: ``` mirrorproxy set maven --mirror mirrorproxy --base-url http://selfhost.com mvn dependency:resolve ``` Maven 适配器以流式方式处理来自 Maven Central 的 Maven2 仓库路径,包括 POM、元数据、构件、校验和与签名。 ## RubyGems 代理 将 RubyGems 配置为使用 MirrorProxy 作为其源: ``` --- :sources: - http://selfhost.com/rubygems/ ``` 将此内容保存在 `~/.gemrc` 下,或者让 CLI 在带有回滚保护的情况下写入它: ``` mirrorproxy set rubygems --mirror mirrorproxy --base-url http://selfhost.com gem install rake ``` RubyGems 适配器以流式方式传输紧凑索引 (`/versions`, `/info/*`)、传统索引、API 响应和 `.gem` 下载,同时保留 Bundler 所使用的 Range 和 ETag 头部。 ## NuGet v3 代理 将 NuGet 配置为使用 MirrorProxy 作为 v3 包源: ``` ``` 在 Windows 上将其保存到 `%APPDATA%\NuGet\NuGet.Config`,或在 Linux/macOS 上保存到 `~/.nuget/NuGet/NuGet.Config`。CLI 会带有回滚保护地写入相同的文件: ``` mirrorproxy set nuget --mirror mirrorproxy --base-url http://selfhost.com dotnet restore ``` 该适配器将 NuGet v3 service-index 资源 URL 重写为 MirrorProxy,然后通过 `/nuget` 以流式方式传输 flat-container 包、注册表元数据、搜索结果和包下载。 ## CPAN 代理 使用 `cpanm` 配合 CPAN 静态镜像端点: ``` cpanm --mirror http://selfhost.com/cpan/ --mirror-only Moo ``` CLI 可以将带有回滚保护的 CPAN 镜像列表保存到 `~/.cpan/CPAN/MyConfig.pm`: ``` mirrorproxy set cpan --mirror mirrorproxy --base-url http://selfhost.com ``` 该适配器以流式方式传输 CPAN 索引和发行版(如 `modules/02packages.details.txt.gz` 和 `authors/id/...`),同时拒绝路径遍历。 ## CRAN 代理 将 R 的 CRAN 仓库设置为 MirrorProxy: ``` options(repos = c(CRAN = "http://selfhost.com/cran/")) install.packages("digest") ``` `mirrorproxy set cran --mirror mirrorproxy --base-url http://selfhost.com` 会写入一个受回滚保护的 `~/.Rprofile`。源索引、归档和平台二进制文件路径通过 `/cran` 以流式方式传输。 ## Hackage 代理 将 Cabal 的用户仓库配置为使用 MirrorProxy: ``` repository hackage.haskell.org url: http://selfhost.com/hackage/ secure: True ``` `mirrorproxy set hackage --mirror mirrorproxy --base-url http://selfhost.com` 会写入并可以恢复 `~/.cabal/config`。该适配器以流式方式传输包索引和包 tarball,同时拒绝路径遍历。 ## Rustup 发行版代理 将 Rustup 的发行版和自我更新根路径指向 MirrorProxy: ``` export RUSTUP_DIST_SERVER=http://selfhost.com/rustup export RUSTUP_UPDATE_ROOT=http://selfhost.com/rustup/rustup rustup update stable ``` 通道清单、组件、校验和与签名通过标准化路径从官方 Rust 发行版服务以流式方式传输。 ## Julia Package Server 代理 在运行 Julia 的包管理器之前设置 `JULIA_PKG_SERVER=http://selfhost.com/julia`。Registry 和 package-server 协议路径会被转发到配置的 Julia package server。 ## LuaRocks 代理 在 Linux 上,使用 `luarocks install --server=http://selfhost.com/luarocks/ ` 从 MirrorProxy 安装。 ## NVM / Node.js 发行版代理 在 Linux 上,在安装特定版本之前,将 NVM 指向被代理的 Node.js 发行文件: ``` export NVM_NODEJS_ORG_MIRROR=http://selfhost.com/nvm/ nvm install --lts ``` ## opam 代理 在 Linux 上,配置 `opam repository set-url default http://selfhost.com/opam/`。 ## Clojars 代理 配置 Clojure CLI 用户的 `deps.edn`,通过 MirrorProxy 路由 Clojars: ``` {:mvn/repos {"clojars" {:url "http://selfhost.com/clojars/"}}} ``` `mirrorproxy set clojars --mirror mirrorproxy --base-url http://selfhost.com` 会写入并可以恢复 `~/.clojure/deps.edn`。该适配器仅使用标准化的仓库路径以流式方式传输 Clojars 的 POM、元数据和 JAR。 ## CocoaPods CDN 代理 当通过 MirrorProxy 路由 CocoaPods CDN 时,在 Podfile 中使用 `source 'http://selfhost.com/cocoapods/'`。CDN 索引分片和 podspec 文件仅接受通过标准化路径的访问。 ## WinGet 源代理 将 MirrorProxy 端点添加为预索引的 WinGet 源: ``` winget source add --name mirrorproxy --arg http://selfhost.com/winget/ --type Microsoft.PreIndexed.Package --accept-source-agreements ``` 该适配器通过 `/winget` 以流式方式传输官方 WinGet 源索引和包元数据。 ## Pub / Flutter 代理 ``` PUB_HOSTED_URL=http://selfhost.com/pub/ flutter pub get ``` Pub 包元数据和官方归档保留在 MirrorProxy 上;归档 URL 仅针对官方 Google Cloud Storage 主机进行重写。 ## Anaconda / Conda 代理 将 MirrorProxy 用作 Conda channel 基础地址,例如 `http://selfhost.com/anaconda/main`。该适配器以流式方式传输 `repodata.json` 和包构件,同时拒绝路径遍历。 ## TeX Live 代理 使用 `http://selfhost.com/texlive/` 作为 TeX Live 网络安装程序镜像。该适配器仅使用标准化路径以流式方式传输 `tlpkg/texlive.tdb` 和归档文件。 ## GNU ELPA 代理 使用 `http://selfhost.com/elpa/` 作为 Emacs 包归档 URL。该适配器仅通过标准化路径以流式方式传输 `archive-contents` 和包归档。 ## Nix Binary Cache 代理 使用 `http://selfhost.com/nix/` 作为 Nix substituter。`.narinfo` 签名和相对缓存 URL 保持不变,因此 Nix 会继续像往常一样验证缓存签名。 ## GNU Guix Substitute Cache 代理 使用 `http://selfhost.com/guix/` 作为 Guix substitute URL,例如 `guix build --substitute-urls=http://selfhost.com/guix/ hello`。Narinfo 签名和 substitute payload 保持不变地进行流式传输,因此 Guix 会继续验证授权的缓存密钥。 ## Flatpak OSTree 代理 使用 `http://selfhost.com/flatpak/` 作为 Flatpak remote URL。OSTree 摘要和 GPG 签名保持原样进行流式传输,保留了客户端的仓库验证。 ## Homebrew Bottle 代理 在运行 `brew install` 之前设置 `HOMEBREW_BOTTLE_DOMAIN=http://selfhost.com/homebrew`。默认上游是 Homebrew 的公共 GHCR OCI bottle 仓库;manifest 和 blob 请求(包括 Range 请求)保持原样进行流式传输。 ## OS 静态仓库代理 使用固定的目标路径,例如 `http://selfhost.com/os/debian/`、`/os/ubuntu/`、`/os/fedora/`、`/os/archlinux/`、`/os/opensuse/`、`/os/void/`、`/os/gentoo/`、`/os/freebsd/`、`/os/alpine/`、`/os/openwrt/`、`/os/termux/`、`/os/kali/`、`/os/rocky/`、`/os/alma/`、`/os/manjaro/`、`/os/msys2/`、`/os/raspios/`、`/os/armbian/`、`/os/openeuler/`、`/os/anolis/`、`/os/deepin/`、`/os/linuxmint/`、`/os/solus/`、`/os/trisquel/`、`/os/linuxlite/`、`/os/ros/`、`/os/netbsd/` 或 `/os/openbsd/`。仅接受这些目标;每个目标都有可单独配置的上游。其他额外的 OS 目标使用 `[upstreams.additional_os]` TOML 映射。Linux Mint 默认使用 Kernel.org 的 HTTPS 包镜像;ROS 目标代理 ROS 2 Ubuntu APT 仓库;Solus 使用 `/os/solus/polaris/eopkg-index.xml.xz`。 ## Rust Crates 代理 将 Cargo 配置为使用 MirrorProxy 作为 sparse registry 镜像: ``` [source.crates-io] replace-with = "mirrorproxy" [source.mirrorproxy] registry = "sparse+http://selfhost.com/crates-index/" ``` 然后获取依赖: ``` cargo fetch ``` MirrorProxy 提供本地 sparse `config.json`,并通过 `/crates/api/v1/crates/{crate}/{version}/download` 代理 crate 下载。 ## pip / PyPI 代理 配置 pip 以使用 MirrorProxy: ``` pip config set global.index-url http://selfhost.com/pypi/simple/ pip install requests ``` MirrorProxy 代理 PyPI Simple API HTML,并将 files.pythonhosted.org 链接重写回 `/pypi/files`。 ## 配置 从 CLI 检查或安全地更新显式的 TOML 配置文件。`set` 在原子替换文件之前会创建一个同级的 `.bak` 备份;使用 `--dry-run` 可先检查更改。 ``` mirrorproxy-server --config ./config.toml config get public_base_url mirrorproxy-server --config ./config.toml config set public_base_url https://mirror.example mirrorproxy-server --config ./config.toml config set quota.monthly_gb 100 --dry-run ``` ## 安装客户端 Linux 和 macOS 共享同一个安装程序。它会检测操作系统和 CPU 架构,从最新的稳定版 GitHub Release 下载匹配的资产,验证其 SHA-256 校验和,并将 `mirrorproxy` 安装到 `/usr/local/bin` 下(仅在需要时使用 `sudo`)。 主机必须提供 `curl`、`tar`、`gzip`、`install`,以及 `sha256sum` 或 `shasum`;安装程序会在下载前报告缺少的先决条件。 使用以下命令安装: ``` curl -fsSL https://raw.githubusercontent.com/inbjo/MirrorProxy/main/scripts/install.sh | sh ``` 要通过 MirrorProxy 实例加速安装程序和发行资产,请传入 `--mirror`: ``` curl -fsSL https://sina.dev/https://raw.githubusercontent.com/inbjo/MirrorProxy/main/scripts/install.sh | sh -s -- --mirror https://sina.dev ``` Windows 使用单独的 PowerShell 安装程序。Windows 默认可能会阻止远程脚本,因此仅针对当前的 PowerShell 进程允许它们,然后运行安装程序: ``` Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force irm https://raw.githubusercontent.com/inbjo/MirrorProxy/main/scripts/install.ps1 | iex ``` 加速的 Windows 安装: ``` Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force $env:MIRRORPROXY_DOWNLOAD_MIRROR='https://sina.dev' irm https://sina.dev/https://raw.githubusercontent.com/inbjo/MirrorProxy/main/scripts/install.ps1 | iex ``` PowerShell 安装程序会将 `mirrorproxy.exe` 放置在当前用户的本地程序目录下,并将其添加到用户的 `PATH` 中。两个安装程序都接受 `MIRRORPROXY_VERSION` 用于指定特定的 tag,以及 `MIRRORPROXY_INSTALL_DIR` 用于自定义安装位置。`latest` 路由有意选择稳定版本,而不是滚动的 `nightly` 预发布版;它在第一个 `v*` 标签发布后变得可用。 ## 本地源 CLI `mirrorproxy` 是一个独立的客户端,不包含 Axum、数据库或嵌入式 Web 控制台。GitHub Releases 提供了 Windows、macOS 和 Linux 构建;独立的 `mirrorproxy-server` 构件专为 Linux 发布。 源命令使用 chsrc 风格的顶层 `set`、`get`、`reset`、`list` 和 `mirrors` 形式。为了向后兼容,依然接受传统的 `sources` 命名空间。 ``` mirrorproxy set bun --mirror mirrorproxy --base-url https://sina.dev --scope user ``` `set` 写入支持的用户级包管理器配置,包括 npm、pip、Cargo、GitHub HTTPS Git URL 重写、Go、Composer、Maven、RubyGems、NuGet、CPAN、CRAN、Hackage、Clojars 和 Anaconda,而无需调用包管理器的可执行文件。在首次更改之前,它会在平台原生的用户状态目录(Linux 上默认为 `~/.local/state/mirrorproxy/sources/`)中记录完整的上一个文件;`reset` 会恢复该确切文件。除非显式使用 `--force`,否则永远不会替换非空配置,并且 reset 同样会拒绝在命令执行后被更改过的文件。 ``` mirrorproxy set npm --mirror mirrorproxy --base-url http://selfhost.com mirrorproxy set cargo --mirror mirrorproxy --base-url http://selfhost.com mirrorproxy set github --mirror mirrorproxy --base-url http://selfhost.com mirrorproxy reset npm mirrorproxy reset github ``` `set github` 会在用户的 `~/.gitconfig` 中追加一条 `url..insteadOf` 规则,因此使用 `https://github.com/` 的 Git 克隆和包管理器 Git 依赖项会自动通过 MirrorProxy 进行。现有的 Git 配置会被保留,无需使用 `--force`,而 `reset github` 会恢复回滚记录的原始文件。SSH 格式的 GitHub URL 不会被重写。 在自动化或测试中使用 `--config-root /tmp/mirrorproxy-config` 进行隔离的配置根目录设置。APT、DNF/YUM、pacman 和 Docker 额外支持显式的 `--scope system`:MirrorProxy 仅管理相关的配置文件,并在 `/var/lib/mirrorproxy/sources/`(或提供的根目录)下保留回滚记录。APT 需要版本代号;系统写入通常需要 root 访问权限,并且仅在 Linux 主机上启用。 ``` mirrorproxy set apt --mirror tuna --scope system --distribution jammy mirrorproxy set apt --mirror mirrorproxy --base-url https://mirror.example --scope system --distribution debian/bookworm mirrorproxy reset apt --scope system mirrorproxy set alpine --mirror mirrorproxy --base-url https://mirror.example --scope system --distribution v3.21 mirrorproxy reset alpine --scope system mirrorproxy set xbps --mirror mirrorproxy --base-url https://mirror.example --scope system mirrorproxy reset xbps --scope system mirrorproxy set zypper --mirror mirrorproxy --base-url https://mirror.example --scope system --distribution distribution/leap/15.6 mirrorproxy reset zypper --scope system mirrorproxy set gentoo --mirror mirrorproxy --base-url https://mirror.example --scope system mirrorproxy reset gentoo --scope system mirrorproxy set docker --mirror mirrorproxy --base-url https://mirror.example --scope system mirrorproxy reset docker --scope system ``` Docker 将带有 `registry-mirrors` 的配置写入 `/etc/docker/daemon.json`。如果没有 `--force`,它绝不会替换现有的 daemon 配置,而 reset 会恢复确切的上一个文件。应用配置后请重启 Docker。 复制 `config.example.toml` 并为你的部署调整公共 URL: ``` listen_addr = "0.0.0.0:3000" public_base_url = "https://mirror.example.com" enabled_proxies = ["github", "composer", "oci", "npm", "nvm", "opam", "go", "maven", "rubygems", "rustup", "nuget", "cpan", "cran", "hackage", "julia", "luarocks", "clojars", "cocoapods", "pub", "anaconda", "texlive", "winget", "elpa", "nix", "guix", "flatpak", "homebrew", "os", "crates", "pypi"] [upstreams] github = "https://github.com" github_raw = "https://raw.githubusercontent.com" packagist = "https://repo.packagist.org" docker_hub = "https://registry-1.docker.io" ghcr = "https://ghcr.io" quay = "https://quay.io" kubernetes = "https://registry.k8s.io" npm = "https://registry.npmjs.org" nvm = "https://nodejs.org/dist" opam = "https://opam.ocaml.org" go_proxy = "https://proxy.golang.org" maven = "https://repo.maven.apache.org/maven2" rubygems = "https://rubygems.org" rustup = "https://static.rust-lang.org" nuget = "https://api.nuget.org" cpan = "https://cpan.metacpan.org" cran = "https://cloud.r-project.org" hackage = "https://hackage.haskell.org" julia = "https://pkg.julialang.org" luarocks = "https://luarocks.org" clojars = "https://repo.clojars.org" cocoapods = "https://cdn.cocoapods.org" pub_repository = "https://pub.dev" anaconda = "https://repo.anaconda.com/pkgs" texlive = "https://mirror.ctan.org/systems/texlive/tlnet" winget = "https://cdn.winget.microsoft.com" elpa = "https://elpa.gnu.org/packages" nix = "https://cache.nixos.org" guix = "https://ci.guix.gnu.org" flatpak = "https://dl.flathub.org/repo" homebrew = "https://ghcr.io/v2/homebrew/core" alpine = "https://dl-cdn.alpinelinux.org/alpine" openwrt = "https://downloads.openwrt.org" termux = "https://packages.termux.dev/apt/termux-main" debian = "https://deb.debian.org/debian" ubuntu = "https://archive.ubuntu.com/ubuntu" fedora = "https://download.fedoraproject.org/pub/fedora/linux" archlinux = "https://geo.mirror.pkgbuild.com" opensuse = "https://download.opensuse.org" void = "https://repo-default.voidlinux.org" gentoo = "https://distfiles.gentoo.org" freebsd = "https://pkg.freebsd.org" crates_index = "https://index.crates.io" crates_api = "https://crates.io" pypi_simple = "https://pypi.org/simple" pypi_files = "https://files.pythonhosted.org" ``` `public_base_url` 被 Web 控制台和元数据重写器使用。将其设置为外部可访问的 URL,特别是当 MirrorProxy 位于 Nginx、Caddy、Traefik 或其他反向代理之后时。 常见的环境变量覆盖: ``` MIRRORPROXY_CONFIG=/etc/mirrorproxy/config.toml MIRRORPROXY_DB=/var/lib/mirrorproxy/mirrorproxy.sqlite3 MIRRORPROXY_LISTEN_ADDR=0.0.0.0:3000 MIRRORPROXY_PUBLIC_BASE_URL=https://mirror.example.com MIRRORPROXY_ENABLED_PROXIES=github,composer,oci,npm,nvm,opam,go,maven,rubygems,rustup,nuget,cpan,cran,hackage,julia,luarocks,clojars,cocoapods,pub,anaconda,texlive,winget,elpa,nix,guix,flatpak,homebrew,os,crates,pypi MIRRORPROXY_REQUEST_TIMEOUT_SECS=60 MIRRORPROXY_RATE_LIMIT_ENABLED=true MIRRORPROXY_RATE_LIMIT_REQUESTS_PER_MINUTE=600 MIRRORPROXY_CACHE_ENABLED=true MIRRORPROXY_CACHE_DIRECTORY=/var/cache/mirrorproxy MIRRORPROXY_CACHE_MAX_ENTRY_MB=8 ``` MirrorProxy 在启动期间会验证 `public_base_url`、所有上游 URL、已启用的代理名称和超时值。无效的配置会直接报错并显示特定于字段的错误。 默认情况下禁用可选的磁盘缓存。启用后,它仅存储带有显式 `Content-Length` 且大小不超过 `cache.max_entry_mb` 的成功公共 GET 响应;`cache.max_total_mb` 限制磁盘使用量并淘汰最近最少使用的条目。携带 `Authorization`、`Cookie` 或 `Range` 的请求会绕过缓存。大型或未知长度的响应保持流式传输,绝不进行缓存缓冲。 在首次启动时,MirrorProxy 会创建其 SQLite 数据库,并在本地启动日志中为 `admin` 账户打印一个一次性的随机密码。将其与 `POST /api/admin/login` 一起使用,然后将返回的 token 作为 `Authorization: Bearer ` 发送给受保护的端点,例如 `GET /api/admin/config`。该密码仅以 Argon2 哈希存储;请妥善保管启动输出。 `PUT /api/admin/config` 接受经过验证的完整配置文档,并将其持久化到 SQLite 中并带有审计记录。公共 URL、已启用的适配器、上游、配额和速率限制设置会立即应用于新请求。更改 `timeout.request_secs` 会被持久化,但会报告为需要重启;`listen_addr` 和 `database_path` 必须在服务配置中进行更改,并且不能通过此 API 进行更改。 `GET /api/admin/stats` 返回当前已配置月份的摘要、剩余配额字节数、每日/每个目标的流量点,以及十个最繁忙的代理目标。它需要相同的管理员 Bearer token。 `POST /api/admin/password` 接受 `current_password` 和一个至少 12 个字符的新密码。更改成功后,将撤销每个管理员会话,包括发起请求的会话。 可以使用以下命令启用可选的全局速率限制: ``` [rate_limit] enabled = true requests_per_minute = 600 ``` 当超过限制时,MirrorProxy 会返回 `429 Too Many Requests` 并带有 `Retry-After` 头部。 ## 流量统计与月度配额 每个代理响应在其主体已流式传输给客户端后才进行统计;下载绝不会仅仅为了统计而发生缓冲。SQLite 保留每日每个目标的请求/字节/错误总数,以及每月的汇总字节总数。健康检查、Web 控制台和管理 API 不会被统计或阻止。 ``` [quota] enabled = true monthly_gb = 500 timezone = "Asia/Taipei" # or "local" on_exceeded = "stop_proxy" # use "throttle" for HTTP 429 instead ``` 一旦发送的主体总数达到月度限制,新的代理请求将收到 `503` (`stop_proxy`) 或 `429` (`throttle`),而公共和管理接口保持可用。在配置的时区中,新的日历月份会自动开始新的配额。 ## 开发 构建 Web 控制台: ``` cd web npm ci npm run build ``` 运行 Rust 测试: ``` cargo test ``` 运行完整的本地检查: ``` cargo fmt --check cargo clippy --workspace --all-targets -- -D warnings cargo test ``` GitHub Actions 会在 Linux、Windows 和 macOS 上运行格式化、clippy、Rust 测试、前端生产构建、浏览器端到端测试以及原生客户端测试。标记 `v*` 会构建三平台客户端构件以及 Linux 服务端构件,并发布包含每个构件校验和和 `SHA256SUMS` 的 GitHub release。 对于本地真实客户端协议检查(Git、npm/yarn/pnpm、Go、Cargo、pip、CPAN cpanm、RubyGems、Maven、NuGet、CRAN、Cabal/Hackage、LuaRocks 和 Composer,可选 Docker),运行: ``` ./scripts/smoke-clients.sh ``` 该脚本会启动一个临时本地服务器,使用临时的客户端主目录/缓存,并在退出时将其删除。 ### 已验证的平台和客户端 下表记录了实际运行过的检查;单纯的 HTTP GET/HEAD 探测并不作为原生客户端测试呈现。2026-07-16 的 OS 检查通过 `sina.dev` 拉取了它们的容器镜像和包仓库。能够运行 Linux 客户端的镜像还演练了单行安装程序、源更改、索引刷新以及真实的包下载。 | 验证级别 | 已验证的目标 | | --- | --- | | 原生语言/开发客户端 | Git、npm、Yarn、pnpm、Go modules、Cargo、pip、CPAN/cpanm、RubyGems、Maven、NuGet、CRAN/R、Cabal/Hackage、LuaRocks、Composer 和 Docker/OCI | | 在匹配的 OS 容器中的原生包管理器 | Debian 12 APT、Ubuntu 24.04 APT、Fedora 42 DNF、Arch Linux pacman、Alpine 3.21 apk、openSUSE Leap 15.6 zypper、Void Linux xbps、Gentoo emerge、Kali rolling APT、Rocky Linux 9 DNF、AlmaLinux 9 DNF、Manjaro pacman、openEuler 24.03 LTS DNF、Anolis OS 8.8 DNF、Deepin 23 APT、ROS 2 Jazzy APT、OpenWrt 24.10.5 opkg 和 Termux x86_64 APT | | 兼容包管理器的容器 | 通过 APT 的 Linux Mint、Trisquel 和 Linux Lite;通过 arm64 APT 索引和包的 Raspberry Pi OS 和 Armbian;通过其 mingw64 仓库使用 pacman 的 MSYS2 | | 仅限公共协议端点 | FreeBSD、Solus、NetBSD 和 OpenBSD;它们的原生 userland/kernel 无法在 Linux Docker daemon 上运行,因此这些未标记为原生包管理器测试 | OS 检至少下载一个真实的包,而不仅仅是仓库元数据:Debian 系列目标下载 `.deb`,Fedora/RHEL 系列目标下载 `.rpm`,Arch/Manjaro/MSYS2 下载 `.pkg.tar.zst`,Alpine 下载 `.apk`,OpenWrt 下载 `.ipk`,Void 下载 `.xbps`,而 Gentoo 则通过 `emerge --fetchonly` 下载 distfile。当最小镜像缺少 CA 证书、`tar` 或 `gzip` 时,在清除其源并测试 MirrorProxy 之前,只能从该镜像的原始仓库安装这些引导依赖项。 运行可重复的核心 OS 包管理器矩阵: ``` ./scripts/smoke-os-clients.sh ``` 默认矩阵涵盖 Debian、Ubuntu、Fedora、Arch Linux、Alpine、openSUSE、Void 和 Gentoo。使用 `MIRRORPROXY_OS_SMOKE_TARGETS` 选择一个子集。要验证未发布的客户端修复,请将 `MIRRORPROXY_OS_SMOKE_CLIENT_BINARY` 指向本地静态客户端。脚本依然会首先运行公共的单行安装程序,然后使用候选二进制文件进行源更改回归测试。 ## 静态 Linux 构建 在 Linux 上: ``` ./build.sh ``` 脚本首先构建 Web 控制台,然后为 `x86_64-unknown-linux-musl` 构建 `mirrorproxy-server` 和 `mirrorproxy` 的 release 二进制文件。 请先安装 `musl-tools`,以便使用 `musl-gcc`。 ## 反向代理部署 MirrorProxy 通常应在 TLS 反向代理后运行。将 `public_base_url` 设置为外部 HTTPS URL,而不是内部绑定地址。 Nginx 示例: ``` server { listen 443 ssl http2; server_name mirror.example.com; client_max_body_size 0; proxy_request_buffering off; proxy_buffering off; location / { proxy_pass http://selfhost.com; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; } } ``` Caddy 示例: ``` mirror.example.com { reverse_proxy selfhost.com { flush_interval -1 } } ``` 对于 Docker/OCI 和大型发行文件,请在反向代理中禁用请求缓冲,以便大型 blob 直接流式传输而不是被完全缓冲。 ## 安全说明 - MirrorProxy 不是开放代理。 - GitHub 绝对 URL 代理仅限于少量的 GitHub 相关主机白名单。 - 过滤 hop-by-hop 头部。 - 私有上游 registry 可以使用服务 TOML 中 `upstream_auth` 配置的静态 Basic 或 Bearer 凭据。凭据仅匹配配置的上游主机,绝不会通过管理 API 或 SQLite 暴露,并且客户端提供的 `Authorization` 和 `Cookie` 头部永远不会被转发。 - 要将客户端自身的 GitHub、npm 或 PyPI token 与其匹配的上游一起使用,请设置 `forward_client_authorization = true`。默认禁用此项;已配置的静态 `upstream_auth` 凭据始终具有优先权。 - 请求级诊断事件默认保留 30 天;设置 `quota.request_event_retention_days`(或 `MIRRORPROXY_REQUEST_EVENT_RETENTION_DAYS`)以调整保留期限。 ## 路线图 版本 1.0 已经包含多生态系统和 OS 仓库适配器、基于 SQLite 的管理和流量统计、全局月度配额、速率限制、有界磁盘缓存、原生客户端发布、嵌入式 Web 控制台、文档化的原生客户端和公共协议冒烟矩阵以及 Docker 部署支持。 计划中的 v1.x 工作: - 维护带有 SBOM 和 provenance 证明的签名多架构 Docker Hub 镜像。 - 添加基于用户或基于子域名的流量所有权和独立的配额。 - 为剩余的目录目标扩展真实的原生客户端冒烟覆盖范围,特别是 Windows、macOS 和较少见的语言生态系统。 - 添加 Prometheus/OpenTelemetry 指标、结构化请求追踪和报警示例,且不记录凭据。 - 为剩余的目录目标完善特定于包管理器的源编辑和回滚行为。 - 评估高可用性元数据存储,同时保留 SQLite 作为零依赖的默认选项。 ### Spark 志愿者镜像网络 计划中的 **Spark** 网络将允许运维人员自愿贡献公共的 MirrorProxy 容量,同时客户端可以在节点之间进行发现和故障转移,而无需依赖单一的镜像端点: - `spark-mirrors.sina.dev` DNS TXT 记录仅发布一小部分带有版本控制的核心引导节点;DNS 记录并不是全局节点列表。 - 引导节点通过基于 Kademlia 发现、Identify、Ping 和可选 Gossipsub 事件的 libp2p 控制平面,向客户端介绍已签名的节点广播。 - 本地 MirrorProxy agent 将根据健康状况、延迟、声明的容量和最近的请求成功率对符合条件的节点进行评分,然后在进行熔断和故障转移的情况下路由包管理器请求。 - 志愿者节点仍将限制为 MirrorProxy 适配器,而不是任意的 URL 转发代理。节点身份、过期广播、完整性检查、带宽限制和操作员控制的配额是必需的。 Spark 特意将目标定为可直接访问的服务器。每个志愿节点必须具有公共域名、有效且受公共信任的 HTTPS 证书,并且开放来自 Internet 的所需入站端口。位于 NAT 或 CGNAT 后的节点不在范围内;路线图不包括中继带宽、UPnP、NAT-PMP、打洞或 NAT 穿透数据平面。
标签:DevOps工具, Docker, React, Rust, Syscalls, 可视化界面, 安全防御评估, 版权保护, 网络流量审计, 自托管, 请求拦截, 通知系统, 镜像加速