关于 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 这种正规的包管理器,很多设计相当凑合。它有以下特点:
- Homebrew 本身和它的各个索引仓库(tap)是通过 Git 管理的
- 没有 GPG 签名,完整性检验靠的是索引的 SHA-256,因此索引是一个关键点
- 没有配置文件,配置全靠
git config
和环境变量
一般换源都是同时替换索引镜像和二进制预编译包镜像。洒家作为受迫害妄想症搞信息安全的,马上就想到这里面显然有供应链攻击的可能性。如果镜像被黑客劫持,或者镜像自身作恶,那么用户的电脑就白给了。
如果你:
也有受迫害妄想症重视安全- 有一个较快的梯子,但是还是不如镜像快
那么可以使用本文介绍的方法换源。
换源¶
总体来看,一共有 3 类资源需要分别处理:
- 包含 SHA-256 hash 的原始索引仓库:通过代理访问
- 二进制预编译文件:使用镜像下载
- 有些二进制预编译文件镜像里没有:还是需要一个 fallback 代理
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'
其中设置了一些个人认为有用的参数:
HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK
:禁止 bottle 下载失败时 fallback 到源码编译。个人认为程序干得太多也会过犹不及。假设暂时的网络波动导致 bottle 下载失败,从源码编译又要下载一大堆编译依赖,然后又会下载失败,这只会把事情搞得更糟。HOMEBREW_BOTTLE_DOMAIN
:设置二进制预编译包的镜像,此处设置为清华大学开源软件镜像站。HOMEBREW_CLEANUP_MAX_AGE_DAYS
:删除超过此天数的缓存文件,默认是 120 天。
PoC¶
下面来证明控制了索引和 bottle 源镜像就控制了一切。
环境版本信息:
- Homebrew:2.5.11-22-g66b6828
- macOS:Catalina 10.15.7-x86_64
模拟索引仓库被控制的场景,创建一个 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.
扩展阅读¶
- Homebrew 官方文档:Formula Cookbook,包含一个术语表(都和酒有关)
- Homebrew 官方文档:Bottles
-
“不降低”指的是换源后,安全性和不换源相同。原始源仓库被上传恶意代码、SHA-256 hash 碰撞攻击等场景则不在本文讨论范围之内。↩