Shipping reproducible binaries on github: repro-env

kpcyrd kpcyrd at archlinux.org
Sat Jul 8 21:18:11 UTC 2023


hello,

my initial interest in reproducible builds was "how do I distribute 
pre-compiled binaries on github without people raising security concerns 
about them".

I've cycled back to this original problem about 5 years later and built 
a tool that is meant to address this:

https://github.com/kpcyrd/repro-env

It works by adding two more files into your git repository that work 
together like Cargo.toml/Cargo.lock or package.json/package-lock.json:

- **repro-env.toml**: this describes the environment you're aiming for, 
eg. "the latest debian bookworm, with rust and gcc installed, everything 
fully upgraded"
- **repro-env.lock**: this is the resolved state from back then, with a 
list of .deb files, their version and sha256

The repro-env.lock is very similiar to buildinfo files in eg. Arch Linux 
or Debian.

For the operating system base it's using **podman** internally (with 
unprivileged user namespaces) and **repro-env.lock** also tracks a 
container image by its `@sha256:`.

The build directory is always mounted to /build/, most other things are 
provided by the pinned container image, eg the username or any 
environment variables.

Additional packages can be installed from these two archive services:

- https://archive.archlinux.org
- https://snapshot.debian.org

For example for repro-env itself the **repro-env.toml** looks like this 
(I found this environment works slightly better for static binaries than 
docker.io/library/rust, which of course can also be used):

```
[container]
image = "docker.io/library/archlinux"

[packages]
system = "archlinux"
dependencies = ["make", "musl", "rust-musl"]
```

The resolved dependency-lock file then may look something like this:

```
[container]
image = 
"docker.io/library/archlinux at sha256:6568d3f1f278827a4a7d8537f80c2ae36982829a0c6bccff4cec081774025472"

[[package]]
name = "binutils"
version = "2.40-6"
system = "archlinux"
url = 
"https://archive.archlinux.org/packages/b/binutils/binutils-2.40-6-x86_64.pkg.tar.zst"
sha256 = "b65fd16001578e10b602e577a8031cbfffc1164caf47ed9ba00c60d804519430"
signature = 
"iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZG6Rg18UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCge2AQD/LGBeHRaeO8xh4E/bAYfqd1O/OFqk2DrQBJ73cdKl2gD9EC8p4U/cXQK8V774m6LSS50usH5pxcQWEq/H0SF+FgM="

# [...]

[[package]]
name = "rust"
version = "1:1.69.0-3"
system = "archlinux"
url = 
"https://archive.archlinux.org/packages/r/rust/rust-1%3A1.69.0-3-x86_64.pkg.tar.zst"
sha256 = "b8eb31a2eb80efab27bb68beab80436ed3e1d235a217c3e24ba973936c95839e"
signature = 
"iIsEABYIADMWIQQGaHodnU+rCLUP2Ss7lKgOUKR3xwUCZExVKBUcaGVmdGlnQGFyY2hsaW51eC5vcmcACgkQO5SoDlCkd8fQkAD6AudRi2qP3WxSn38OOkSRSITciqRevPaVJgrz03JUBEAA/12h9z8dReD07Lqnltx9QTa3Cxppbv7VpJlTCQuavoMG"
```

This is usually sufficient to achieve reproducible builds. This tool, of 
course, does not prevent you from doing something silly that breaks 
reproducible builds, like recording `uname` output, the current time, or 
the order the kernel responds to readdir() syscalls. *wink*

There are 4 example projects here, with Rust, Golang and C twice: 
https://github.com/kpcyrd/repro-env/tree/main/examples

The overall concept works so well, github actions CI always builds all 4 
projects for every commit and verifies the sha256 still matches: 
https://github.com/kpcyrd/repro-env/blob/786e23e4f49e48bb42a2c911a0dfd870b8cf98ac/.github/workflows/rust.yml#L73-L88

The sha256 of repro-env v0.2.0 for x86_64 Linux is:

b78727a7b2f635504ab780bfa5dd7e7bbcfb9255607eaf815762e0893aad5663

It can be downloaded here:

https://github.com/kpcyrd/repro-env/releases/download/v0.2.0/repro-env

It can be built from this git commit:

0c8e630af20f6d7cee0949e1b78d3ea9c9e358fa

Let me know what you think. 🌈

cheers,
kpcyrd


More information about the rb-general mailing list