neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
Make the Dockerfile actually build the apt NIF
9049eaf · Gabriel Morell · 2026-09-12 02:48
Message
{commit_body(@commit)}
Files changed
modified
.dockerignore
+6
−0
@@ -39,6 +39,12 @@
| 39 | 39 | # Generated on crash by the VM |
| 40 | 40 | erl_crash.dump |
| 41 | 41 | |
| 42 | +# Rust build artifacts for the NIF crates. `COPY native native` would | |
| 43 | +# otherwise drag well over a gigabyte of host-built output into the | |
| 44 | +# context and into the builder layer, where cargo ignores it anyway — | |
| 45 | +# it rebuilds against the image's toolchain and glibc. | |
| 46 | +/native/*/target/ | |
| 47 | + | |
| 42 | 48 | # Static artifacts - These should be fetched and built inside the Docker image |
| 43 | 49 | # https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Release.html#module-docker |
| 44 | 50 | /assets/node_modules/ |
modified
Dockerfile
+8
−2
@@ -24,11 +24,17 @@ FROM ${BUILDER_IMAGE} AS builder
| 24 | 24 | |
| 25 | 25 | # install build dependencies. |
| 26 | 26 | # * build-essential, git — Elixir / mix build chain |
| 27 | −# * pkg-config, libssl-dev — gix transitively links against system openssl | |
| 27 | +# * pkg-config, libssl-dev — gix transitively links against system openssl, | |
| 28 | +# and gitgud_apt's sequoia-openpgp uses its OpenSSL crypto backend | |
| 29 | +# * libclang-dev — `openssl-sys` generates its bindings with bindgen, | |
| 30 | +# which dlopens libclang. Build-stage only: the final image never | |
| 31 | +# sees it, and it is the one package the apt signing NIF adds | |
| 32 | +# anywhere. Every sequoia backend that binds C needs this; only the | |
| 33 | +# pure-Rust one avoids it, and that one is variable-time. | |
| 28 | 34 | # * curl, ca-certificates — needed to bootstrap rustup |
| 29 | 35 | RUN apt-get update \ |
| 30 | 36 | && apt-get install -y --no-install-recommends \ |
| 31 | − build-essential git pkg-config libssl-dev curl ca-certificates \ | |
| 37 | + build-essential git pkg-config libssl-dev libclang-dev curl ca-certificates \ | |
| 32 | 38 | && rm -rf /var/lib/apt/lists/* |
| 33 | 39 | |
| 34 | 40 | # Rust via rustup, pinned. apt's rust on trixie is 1.85, but recent |
modified
README.md
+20
−8
@@ -469,14 +469,26 @@ Rustler, and also carries xz and zstd, since a `.deb`'s control member
| 469 | 469 | is `control.tar.xz` on anything since squeeze and `:erl_tar` knows only |
| 470 | 470 | gzip. |
| 471 | 471 | |
| 472 | −Its crypto backend is chosen so the NIF adds **no package to either | |
| 473 | −container image**: sequoia's default Nettle backend would pull | |
| 474 | −`libnettle` and `libgmp` into the runtime image, and the pure-Rust | |
| 475 | −backend is gated behind `allow-variable-time-crypto`, which is the | |
| 476 | −wrong trade for code holding a private key. The OpenSSL backend links | |
| 477 | −`libssl`/`libcrypto`, which the builder already installs for gix and | |
| 478 | −the runner already installs for the Erlang runtime. The compression | |
| 479 | −crates are pure Rust for the same reason. | |
| 472 | +Its crypto backend is sequoia's **OpenSSL** one, which costs exactly | |
| 473 | +one package in the builder image and nothing in the runtime image: | |
| 474 | + | |
| 475 | +| backend | builder needs | runtime needs | crypto | | |
| 476 | +|---|---|---|---| | |
| 477 | +| `crypto-openssl` | `libclang-dev` (`libssl-dev` already there for gix) | `libssl` — from `openssl`, named in the Dockerfile | constant-time, audited | | |
| 478 | +| `crypto-nettle` (default) | `libclang-dev`, `nettle-dev`, `libgmp-dev` | libnettle/libgmp — present only transitively, via `git` | constant-time, audited | | |
| 479 | +| `crypto-rust` | nothing | nothing | **variable-time**, gated behind `allow-variable-time-crypto` | | |
| 480 | + | |
| 481 | +`libclang-dev` is there because `openssl-sys` generates its bindings | |
| 482 | +with bindgen, which dlopens libclang. Every sequoia backend that binds | |
| 483 | +C needs it; only the pure-Rust one avoids it, and that one is the | |
| 484 | +wrong trade for code holding a signing key. The builder is a separate | |
| 485 | +stage, so the runtime image never sees it. | |
| 486 | + | |
| 487 | +This table is measured, not reasoned: the first version of this NIF | |
| 488 | +built fine locally and then failed in the builder, because the | |
| 489 | +developer's machine happened to have clang installed. `podman build -f | |
| 490 | +Dockerfile .` is the only thing that settles it. The compression | |
| 491 | +crates are pure Rust, so they cost nothing either way. | |
| 480 | 492 | |
| 481 | 493 | ## What's not done |
| 482 | 494 |
modified
lib/git_gud/packages/apt/native.ex
+21
−10
@@ -16,16 +16,27 @@ defmodule GitGud.Packages.Apt.Native do
| 16 | 16 | |
| 17 | 17 | ## Why these backends |
| 18 | 18 | |
| 19 | − `sequoia-openpgp` defaults to Nettle, which would put `libnettle` | |
| 20 | − and `libgmp` in the runtime image. This project ships k8s manifests; | |
| 21 | − a NIF that changes the image's package list is a deployment change | |
| 22 | − wearing a dependency's clothes. The pure-Rust backend needs no | |
| 23 | − system libraries at all, but sequoia gates it behind | |
| 24 | − `allow-variable-time-crypto`, and this code holds a private key. The | |
| 25 | − OpenSSL backend links libssl and libcrypto, which are already in | |
| 26 | − both images — the builder installs `libssl-dev` for gix and the | |
| 27 | − runner installs `openssl` for the Erlang runtime — and is | |
| 28 | − constant-time. So: OpenSSL, and no image change at all. | |
| 19 | + This project ships k8s manifests, so a NIF that changes an image's | |
| 20 | + package list is a deployment change wearing a dependency's clothes. | |
| 21 | + The cost was measured by building the Dockerfile, not reasoned about: | |
| 22 | + | |
| 23 | + crypto-openssl builder: libclang-dev runtime: — (libssl already named) | |
| 24 | + crypto-nettle builder: libclang-dev, runtime: — (but libnettle/libgmp | |
| 25 | + nettle-dev, are present only as a | |
| 26 | + libgmp-dev transitive dep of git) | |
| 27 | + crypto-rust builder: — runtime: — variable-time | |
| 28 | + | |
| 29 | + `libclang-dev` is unavoidable for any backend that binds C: | |
| 30 | + `openssl-sys` generates its bindings with bindgen, which dlopens | |
| 31 | + libclang. Only the pure-Rust backend escapes it, and sequoia gates | |
| 32 | + that behind `allow-variable-time-crypto` — the wrong trade for code | |
| 33 | + holding a signing key. | |
| 34 | + | |
| 35 | + So: OpenSSL. One package, in the build stage only, for constant-time | |
| 36 | + audited crypto. The runtime image is unchanged. | |
| 37 | + | |
| 38 | + The compression crates (`lzma-rs`, `ruzstd`) are pure Rust and cost | |
| 39 | + nothing either way. | |
| 29 | 40 | |
| 30 | 41 | The compression crates (`lzma-rs`, `ruzstd`) are pure Rust for the |
| 31 | 42 | same reason: no new system libraries. |
modified
native/gitgud_apt/Cargo.toml
+13
−8
@@ -18,19 +18,24 @@ rustler = "0.34"
| 18 | 18 | # |
| 19 | 19 | # Backend choice, which is the load-bearing decision here: |
| 20 | 20 | # |
| 21 | −# * `crypto-nettle` is the default and pulls libnettle + libgmp into | |
| 22 | −# the *runtime* image. This project ships k8s manifests, and its | |
| 23 | −# runner image installs no such packages — a NIF that dragged them | |
| 24 | −# in would be a deployment change disguised as a dependency. | |
| 21 | +# * `crypto-nettle` is the default and needs nettle-dev + libgmp-dev | |
| 22 | +# in the *builder* image on top of libclang-dev, and the builder | |
| 23 | +# installs none of them. This project ships k8s manifests, and a | |
| 24 | +# NIF that changes an image's package list is a deployment change | |
| 25 | +# disguised as a dependency. (The runtime image does carry | |
| 26 | +# libnettle/libgmp already, but only as a transitive dependency of | |
| 27 | +# git — not something to hang signing on.) | |
| 25 | 28 | # * `crypto-rust` builds with no system libraries at all, but sequoia |
| 26 | 29 | # gates it behind `allow-experimental-crypto` and |
| 27 | 30 | # `allow-variable-time-crypto`. This code holds a private key; a |
| 28 | 31 | # variable-time implementation is not the right place to save a |
| 29 | 32 | # package. |
| 30 | −# * `crypto-openssl` links libssl/libcrypto, which are already in | |
| 31 | −# both images — the builder installs libssl-dev for gix, and the | |
| 32 | −# runner installs openssl for the Erlang runtime. No new packages | |
| 33 | −# in either, and constant-time audited crypto. | |
| 33 | +# * `crypto-openssl` links libssl/libcrypto, which both images | |
| 34 | +# already name outright — libssl-dev in the builder for gix, | |
| 35 | +# openssl in the runner for the Erlang runtime. It costs one | |
| 36 | +# build-stage package, libclang-dev, because openssl-sys generates | |
| 37 | +# its bindings with bindgen; every C-binding backend needs that, | |
| 38 | +# so it is not a reason to prefer another. Constant-time, audited. | |
| 34 | 39 | # |
| 35 | 40 | # So: openssl. `default-features = false` is what actually keeps nettle |
| 36 | 41 | # out; naming a backend alone does not. |
Parents: cac71de