如何安全地为 Homebrew 替换中国大陆镜像源

关于 Homebrew 在国内换源的文章一搜一大把,洒家并不想复制镜像的帮助页面再制造一篇,而是想讨论一下如何在不降低1安全性的情况下使用镜像源。

TL;DR

首先你需要有个梯子,本文假设开启代理服务为 socks5h://127.0.0.1:1080

第 1 步,运行下面的命令修改 Git 全局配置,让 Git 通过代理访问 github.com

git config --global http.https://github.com.proxy socks5h://127.0.0.1:1080

第 2 步,根据使用的 shell 类型和配置,将下面一行添加到 ~/.zshrc~/.bash_profile 等文件末尾,设置 shell alias。重新打开终端,运行 alias 命令,确认 alias 设置成功。

alias brew='HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK=1 HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles HOMEBREW_CLEANUP_MAX_AGE_DAYS=30 no_proxy=".cn" http_proxy="socks5h://127.0.0.1:1080" https_proxy="${http_proxy}" ALL_PROXY="${http_proxy}" /usr/local/bin/brew'

到此全部设置完毕,可以开始使用 Homebrew 了。

概述

“Homebrew”的意思是家酿啤酒。毕竟是家酿的,Homebrew 还是比不上 APT 这种正规的包管理器,很多设计相当凑合。它有以下特点:

一般换源都是同时替换索引镜像二进制预编译包镜像。洒家作为受迫害妄想症搞信息安全的,马上就想到这里面显然有供应链攻击的可能性。如果镜像被黑客劫持,或者镜像自身作恶,那么用户的电脑就白给了。

如果你:

那么可以使用本文介绍的方法换源。

换源

总体来看,一共有 3 类资源需要分别处理:

Git 设置代理

运行 man git-config 查阅文档,并动手实验验证,可以证实不存在 https.proxy 配置项,只需配置 http.proxy 即可同时控制 HTTP 和 HTTPS 协议的代理,网络流传的一些方法是错误的。

首先使用原版方式安装 Homebrew,不要替换 Git 仓库的 origin。此时 Git 仓库的 origin 是 GitHub,需要给它们加上代理。以下两种方式任选一种即可。

全局设置

一般我们可以全局设置 Git 使用 HTTP/HTTPS 协议访问 GitHub 时使用代理。假设开启代理服务为 socks5h://127.0.0.1:1080,运行下面的命令即可。

git config --global http.https://github.com.proxy socks5h://127.0.0.1:1080

逐个仓库设置

如果你不想修改全局设置,也可以逐个仓库设置代理。

列出所有需要处理的 Git 仓库:

brew --repo
for repo in $(brew tap)
do
    brew --repo "${repo}"
done

假设开启代理服务为 socks5h://127.0.0.1:1080,逐个 cd 进去,运行:

git config --local http.proxy socks5h://127.0.0.1:1080

配置 Homebrew

上文提到,Homebrew 没有配置文件,我们要用到的各种配置全都需要通过环境变量设置。假设开启代理服务为 socks5h://127.0.0.1:1080,根据使用的 shell 类型和配置,将下面一行添加到 ~/.zshrc~/.bash_profile 等文件末尾,设置 shell alias。重新打开终端,运行 alias 命令,确认 alias 设置成功。

alias brew='HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK=1 HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles HOMEBREW_CLEANUP_MAX_AGE_DAYS=30 no_proxy=".cn" http_proxy="socks5h://127.0.0.1:1080" https_proxy="${http_proxy}" ALL_PROXY="${http_proxy}" /usr/local/bin/brew'

其中设置了一些个人认为有用的参数:

PoC

下面来证明控制了索引和 bottle 源镜像就控制了一切。

环境版本信息:

模拟索引仓库被控制的场景,创建一个 formula 文件 /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/formula/phuker-test.rb,内容为:

class PhukerTest < Formula
  desc "PoC"
  homepage "https://www.example.com/"
  url "http://127.0.0.1:8181/phuker-test-1.0.0.tar.gz"
  sha256 "0000000000000000000000000000000000000000000000000000000000000000"

  bottle do
    rebuild 1
    sha256 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" => :catalina
  end

  def install
    system "make", "install"
  end
end

创建一个简单的 .tar.gz bottle 文件,此文件的 SHA-256 hash 需要填到 .rb formula 文件的对应位置。文件结构为:

$ cd /tmp/brew/homebrew-bottles/bottles/
$ tar -tvf phuker-test-1.0.0.catalina.bottle.1.tar.gz
drwxr-xr-x  0 phuker   wheel       0 11 18 09:59 phuker-test/
drwxr-xr-x  0 phuker   wheel       0 11 18 09:59 phuker-test/1.0.0/
drwxr-xr-x  0 phuker   wheel       0 11 18 09:59 phuker-test/1.0.0/bin/
-rwxr-xr-x  0 phuker   wheel      23 11 18 09:59 phuker-test/1.0.0/bin/phuker-test

.tar.gz bottle 文件中的 phuker-test/1.0.0/bin/phuker-test 可执行文件内容为:

#!/bin/sh

echo 'Pwn!'

启动 Web 服务,模拟 bottle 镜像源:

$ cd /tmp/brew
$ python3 -m http.server --bind 0.0.0.0 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
127.0.0.1 - - [18/Nov/2020 10:00:04] "GET /homebrew-bottles/bottles/phuker-test-1.0.0.catalina.bottle.1.tar.gz HTTP/1.1" 200 -

安装并执行命令:

$ export HOMEBREW_NO_AUTO_UPDATE=1
$ export HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK=1
$ export HOMEBREW_BOTTLE_DOMAIN=http://127.0.0.1/homebrew-bottles
$ /usr/local/bin/brew install phuker-test
==> Downloading http://127.0.0.1/homebrew-bottles/bottles/phuker-test-1.0.0.catalina.bottle.1.tar.gz
======================================================================== 100.0%
==> Pouring phuker-test-1.0.0.catalina.bottle.1.tar.gz
🍺  /usr/local/Cellar/phuker-test/1.0.0: 2 files, 878B
$ phuker-test
Pwn!

Q.E.D.

扩展阅读

Footnotes
  1. “不降低”指的是换源后,安全性和不换源相同。原始源仓库被上传恶意代码、SHA-256 hash 碰撞攻击等场景则不在本文讨论范围之内。


Comments

您可以匿名发表评论,无需登录 Disqus 账号,勾选“我更想匿名评论”后,姓名和电子邮件分别填写“匿名”和“someone@example.com”然后发表评论即可。您也可以登录 Disqus 账号后发表评论。您的评论可能需要经过我审核后才能显示。点赞投票按钮(Reactions)无需登录即可点击。Disqus 评论系统在中国大陆可能无法正常加载和使用。

License

Creative Commons License

本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).

Top