Settle the open points of the Ubuntu and nixpkgs image #42

Merged
clawbot merged 1 commits from issue-38-image-open-points into next 2026-10-04 02:42:58 +02:00
2 changed files with 79 additions and 27 deletions
+9 -4
View File
@@ -291,10 +291,15 @@ stand for the app's own options:
```bash
#!/usr/bin/env bash
set -euo pipefail
sleep 1
exec chpst -u app:app /usr/local/bin/app \
--listen 127.0.0.1:8081 \
--trusted-proxies 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/32,::1/128
main() {
sleep 1
exec chpst -u app:app /usr/local/bin/app \
--listen 127.0.0.1:8081 \
--trusted-proxies 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/32,::1/128
}
main "$@"
```
- The image's entrypoint, `runsvinit`, has runit start `smallwebwaf` and the app
+70 -23
View File
@@ -1155,16 +1155,29 @@ The image holds:
- Ubuntu 26.04 LTS, the newest long-term support release of Ubuntu, pinned by
digest. The image moves to the next LTS release when that ships.
- Three packages from Ubuntu, `ca-certificates`, `nix-bin` and `runit`,
installed from a dated snapshot of Ubuntu's archive and checked by hash (see
"Packages from Ubuntu" below). The Ubuntu image has no CA certificates, and
they come with `nix-bin` only because a library it uses recommends them, so
`ca-certificates` is installed by name. Without it Nix cannot download
packages, and `smallwebwaf`, unable to reach GeoJS, would count every visitor
as coming from an unknown country.
- Nix, the package manager, from Ubuntu's own `nix-bin` package, and nixpkgs,
the collection of packages Nix installs from, fixed at one commit (see
"Packages from nixpkgs" below). Root uses Nix directly, and no Nix daemon runs
in the container.
- runit, from Ubuntu's own `runit` package, and `runsvinit` as the entrypoint.
Neither Ubuntu nor nixpkgs packages `runsvinit`, so the image builds it from
its source (`github.com/peterbourgon/runsvinit`) at a version fixed by hash.
`runsvinit` starts runit's `runsvdir`, which starts a `runsv` for each
directory under `/etc/service`; each `runsv` runs the `run` script in its
directory, and runs it again whenever it exits. Ubuntu's runit looks for
in the container. Nix run by root expects a group of build users, `nixbld`,
which `nix-bin` does not create, so the image writes `build-users-group =` to
`/etc/nix/nix.conf`, and root's builds then run without build users.
- runit, from Ubuntu's own `runit` package, and `runsvinit` as the entrypoint,
as the owner's code style guide asks for service containers. Neither Ubuntu
nor nixpkgs packages `runsvinit`, so the image builds it from its source
(`github.com/peterbourgon/runsvinit`) at a fixed commit hash. Its repository
is archived and has not changed since 2015, and its last tag is `v2.0.0`. It
has no `go.mod`, and `go build` of its directory needs one, so the build
writes one; since `runsvinit` uses only Go's standard library, that file names
nothing else. `runsvinit` starts runit's `runsvdir`, which starts a `runsv`
for each directory under `/etc/service`; each `runsv` runs the `run` script in
its directory, and runs it again whenever it exits. Ubuntu's runit looks for
services in `/etc/service` too, so when `docker stop` has `runsvinit` stop
each service with runit's `sv`, `sv` finds it.
- The `smallwebwaf` binary, and a user of its own, `smallwebwaf` (uid and gid
@@ -1179,8 +1192,9 @@ The image holds:
(`HEALTHCHECK`).
Every `run` script, the app's included, is a bash script that starts with
`#!/usr/bin/env bash` and `set -euo pipefail`, as the owner's code style guide
asks; Ubuntu ships bash.
`#!/usr/bin/env bash` and `set -euo pipefail` and puts its code in a `main`
function, called on its last line, as the owner's code style guide asks; Ubuntu
ships bash.
Beyond its `FROM` line, the app's Dockerfile adds:
@@ -1219,23 +1233,56 @@ with `app.run` beside the Dockerfile:
```bash
#!/usr/bin/env bash
set -euo pipefail
sleep 1
exec chpst -u app:app /usr/local/bin/app \
--listen 127.0.0.1:8081 \
--trusted-proxies 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/32,::1/128
main() {
sleep 1
exec chpst -u app:app /usr/local/bin/app \
--listen 127.0.0.1:8081 \
--trusted-proxies 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/32,::1/128
}
main "$@"
```
Packages from Ubuntu: the image installs `ca-certificates`, `nix-bin` and
`runit` from Ubuntu's snapshot service, which serves the archive as it was at a
given moment, rather than from the archive itself, whose packages change with
every update. The image's Dockerfile names that moment, in apt's
`--snapshot 20261001T000000Z` form, on both `apt-get update` and
`apt-get install`. That moment is never earlier than the date of the pinned
Ubuntu image, since packages from an older snapshot can need older versions of
packages the Ubuntu image already holds, and it moves forward whenever that
image's digest does. `apt-get update` keeps the snapshot's `InRelease` files,
which apt checks against the archive's signature, in `/var/lib/apt/lists/`; each
lists the SHA-256 hash of the package lists it covers, and each package list the
hash of every package in it. The Dockerfile also names the SHA-256 hash of each
`InRelease` file apt uses, and the build checks them after `apt-get update` and
before `apt-get install`, so every package apt installs is checked, through
those files, against hashes the Dockerfile names. The snapshot service is
reached over HTTPS, and the Ubuntu image has no CA certificates of its own, so
this one install uses those of the Go image that `smallwebwaf` is built in,
which is pinned by digest too: apt's `Acquire::https::CaInfo` option names that
image's CA certificate file, `/etc/ssl/certs/ca-certificates.crt`, copied into
the build.
Packages from nixpkgs: nixpkgs is fixed at one commit of its newest release
branch, `nixos-26.05` today. The image's Dockerfile names the commit and the
hash of its contents, and the build checks that hash. nixpkgs is set up for root
under the name `nixpkgs`, so the app's Dockerfile installs a package with
`nix-env -iA nixpkgs.<name>`, and whatever it installs is on the `PATH` of every
service. Because nixpkgs stays at that commit, an app built on the same
`smallwebwaf` image gets the same packages each time it is built. A newer commit
of the branch, with its security fixes, comes with a newer `smallwebwaf` image,
as do Ubuntu's own fixes; an app takes them by changing the digest in its `FROM`
line. When nixpkgs makes its next release, every six months, the image moves to
that release's branch.
branch, `nixos-26.05` today. For each commit of the branch that has passed its
tests, the Nix project publishes a release on `releases.nixos.org`, such as
`nixos-26.05.11045.774debe7a0d1`, and the image takes nixpkgs from that
release's file `nixexprs.tar.xz`, not from a GitHub archive of the commit, whose
bytes can change. The image's Dockerfile names the release and the SHA-256 hash
of that file, which the release's page lists, and the build checks the hash
before unpacking it. nixpkgs is set up for root under the name `nixpkgs`, so the
app's Dockerfile installs a package with `nix-env -iA nixpkgs.<name>`, and
whatever it installs is on the `PATH` of every service. Because nixpkgs stays at
that commit, an app built on the same `smallwebwaf` image gets the same packages
each time it is built. Unpacked, nixpkgs takes about 500 MiB of disk, more on
some filesystems such as ZFS, and each package an app installs from it adds its
own size, with everything it depends on. A newer commit of the branch, with its
security fixes, comes with a newer `smallwebwaf` image, as do Ubuntu's own
fixes; an app takes them by changing the digest in its `FROM` line. When nixpkgs
makes its next release, every six months, the image moves to that release's
branch.
The two processes: