]> git.ipfire.org Git - thirdparty/freeradius-server.git/log
thirdparty/freeradius-server.git
2 months agoadd tmpl corpus
Alan T. DeKok [Wed, 27 May 2026 01:49:56 +0000 (21:49 -0400)] 
add tmpl corpus

2 months agoadd fuzzer corpus for base16_32_64 tests
Alan T. DeKok [Wed, 27 May 2026 01:49:00 +0000 (21:49 -0400)] 
add fuzzer corpus for base16_32_64 tests

2 months agoadd tmpl fuzzer
Alan T. DeKok [Wed, 27 May 2026 01:47:19 +0000 (21:47 -0400)] 
add tmpl fuzzer

2 months agooss-fuzz: Add new fuzzer targetting base16/32/64 encode/decode
Arthur Chan [Fri, 23 Jan 2026 13:07:16 +0000 (13:07 +0000)] 
oss-fuzz: Add new fuzzer targetting base16/32/64 encode/decode

Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
With manual merges for new location of the fuzzer

2 months agocatch more corner cases of constant strings when tokenizing
Alan T. DeKok [Tue, 26 May 2026 02:10:49 +0000 (22:10 -0400)] 
catch more corner cases of constant strings when tokenizing

update to commit b17b28a02

2 months agoreturn 0 for pop case
Alan T. DeKok [Mon, 25 May 2026 21:15:20 +0000 (17:15 -0400)] 
return 0 for pop case

the caller already checks for this.  Previously, all paths would
return 1, which doesn't make sense

2 months agomake cf_section_dup() iterative
Alan T. DeKok [Mon, 25 May 2026 17:30:52 +0000 (13:30 -0400)] 
make cf_section_dup() iterative

and add assertions that we can't copy the inputs back to the
inputs

2 months agomore stringent checks on references
Alan T. DeKok [Mon, 25 May 2026 17:28:56 +0000 (13:28 -0400)] 
more stringent checks on references

we can't refer to any parent, otherwise we end up copying ourselves

2 months agoinvalid control characters are errors
Alan T. DeKok [Mon, 25 May 2026 16:22:52 +0000 (12:22 -0400)] 
invalid control characters are errors

2 months agotrack reference braces.
Alan T. DeKok [Mon, 25 May 2026 15:24:52 +0000 (11:24 -0400)] 
track reference braces.

so we can tell the difference between "close the at reference"
and "close the parent section"

2 months agoturn assert into run-time check
Alan T. DeKok [Mon, 25 May 2026 15:10:29 +0000 (11:10 -0400)] 
turn assert into run-time check

2 months agoAdd connection subsection to rlm_sql sample config
Nick Porter [Mon, 25 May 2026 15:41:04 +0000 (16:41 +0100)] 
Add connection subsection to rlm_sql sample config

And correct comment about `start` - failure to open connections no
longer stops the server from starting.

2 months agofix end conditions on fr_skip_*() functions
Alan T. DeKok [Mon, 25 May 2026 14:41:25 +0000 (10:41 -0400)] 
fix end conditions on fr_skip_*() functions

2 months agoadd cf_file_read_buffer(), and use it in the fuzzer
Alan T. DeKok [Mon, 25 May 2026 14:11:53 +0000 (10:11 -0400)] 
add cf_file_read_buffer(), and use it in the fuzzer

2 months agobuild CF and xlat fuzzers, but don't run them in CI
Alan T. DeKok [Mon, 25 May 2026 12:41:51 +0000 (08:41 -0400)] 
build CF and xlat fuzzers, but don't run them in CI

so that we can at least try to see what's up

2 months agowe should call tmpl_global_init(), too
Alan T. DeKok [Mon, 25 May 2026 12:36:41 +0000 (08:36 -0400)] 
we should call tmpl_global_init(), too

2 months agodisable xlat fuzzer
Alan T. DeKok [Mon, 25 May 2026 12:21:36 +0000 (08:21 -0400)] 
disable xlat fuzzer

there are still issues with the code, and the fuzzer failures are
causing other patches to not be merged.

2 months agomake xlat fuzzer use the common framework
Alan T. DeKok [Mon, 25 May 2026 11:57:42 +0000 (07:57 -0400)] 
make xlat fuzzer use the common framework

and update it so that it actually runs

2 months agoclarify errors and clean up checks for invalid name
Alan T. DeKok [Mon, 25 May 2026 11:55:54 +0000 (07:55 -0400)] 
clarify errors and clean up checks for invalid name

the fuzzer showed that inputs like "[0]" were parsed, but the
tmpl_t wasn't properly updated.

This change makes the errors less opaque, and tightens the
restrictions on invalid names.

2 months agomake "util" use the common framework
Alan T. DeKok [Mon, 25 May 2026 02:04:34 +0000 (22:04 -0400)] 
make "util" use the common framework

which is simpler, and less code

2 months agomove common code to common function
Alan T. DeKok [Mon, 25 May 2026 01:44:01 +0000 (21:44 -0400)] 
move common code to common function

2 months agowe need to call tmpl_global_init before parsing tmpls
Alan T. DeKok [Mon, 25 May 2026 03:36:23 +0000 (23:36 -0400)] 
we need to call tmpl_global_init before parsing tmpls

2 months agoif the tmpl is unresolved, set the type to UNRESOLVED
Alan T. DeKok [Mon, 25 May 2026 03:31:39 +0000 (23:31 -0400)] 
if the tmpl is unresolved, set the type to UNRESOLVED

2 months agomerge adjacent constant strings in xlat_tokenize_input()
Alan T. DeKok [Mon, 25 May 2026 02:58:15 +0000 (22:58 -0400)] 
merge adjacent constant strings in xlat_tokenize_input()

2 months agoremove unnecessary rule
Alan T. DeKok [Mon, 25 May 2026 01:40:48 +0000 (21:40 -0400)] 
remove unnecessary rule

2 months agotypo
Alan T. DeKok [Mon, 25 May 2026 01:40:41 +0000 (21:40 -0400)] 
typo

2 months agofree on error
Alan T. DeKok [Sun, 24 May 2026 13:29:56 +0000 (09:29 -0400)] 
free on error

2 months agotighten restrictions on decoding
Alan T. DeKok [Sun, 24 May 2026 13:17:21 +0000 (09:17 -0400)] 
tighten restrictions on decoding

* reject indefinite length forms for all tags
* reject lengths which are non minimal

2 months agobe more precise for time parsing
Alan T. DeKok [Sun, 24 May 2026 13:02:21 +0000 (09:02 -0400)] 
be more precise for time parsing

* if there's a '.' but no subseconds, that's an error
* if there are subseconds, check that there's a trailing 'Z'

2 months agoclean up vp on error path
Alan T. DeKok [Sun, 24 May 2026 12:15:16 +0000 (08:15 -0400)] 
clean up vp on error path

it's in tmp_ctx, so it's not leaked. but it's still good to lower
peak memory usage

2 months agoput data for different roots into named subdirectories
Alan T. DeKok [Sun, 24 May 2026 12:12:45 +0000 (08:12 -0400)] 
put data for different roots into named subdirectories

so that we can fuzz different parts of DER

2 months agohoist "create fuzzer dir" code into separate function
Alan T. DeKok [Sat, 23 May 2026 20:49:45 +0000 (16:49 -0400)] 
hoist "create fuzzer dir" code into separate function

2 months agoseparate "base" fuzzer_dir from "current" or "proto" dir
Alan T. DeKok [Sat, 23 May 2026 18:09:29 +0000 (14:09 -0400)] 
separate "base" fuzzer_dir from "current" or "proto" dir

in preparation for allowed DER to have different bases

2 months agorename variable holding FD to be fuzzer_fd
Alan T. DeKok [Sat, 23 May 2026 17:56:50 +0000 (13:56 -0400)] 
rename variable holding FD to be fuzzer_fd

there's already a fuzzer_dir which is a string

2 months agosimplify definitions of allowed_chars
Alan T. DeKok [Sat, 23 May 2026 17:52:09 +0000 (13:52 -0400)] 
simplify definitions of allowed_chars

to make them clearer, and to have more re-use

2 months agopause subsequent connections from dynamic clients
Alan T. DeKok [Thu, 21 May 2026 23:41:44 +0000 (18:41 -0500)] 
pause subsequent connections from dynamic clients

while the first one is being defined

and then either allow or deny all subsequent connections

2 months agoRevert "fix multiple connections from dynamic clients"
Alan T. DeKok [Thu, 21 May 2026 22:50:26 +0000 (17:50 -0500)] 
Revert "fix multiple connections from dynamic clients"

This reverts commit 01748d3363b6a38f92eef3cf7ff8c638a6e06962.

2 months agomulti-server: bump radenv to 1.0.2
Arran Cudbard-Bell [Fri, 22 May 2026 13:11:06 +0000 (09:11 -0400)] 
multi-server: bump radenv to 1.0.2

The published package is now named 'radenv' and lives under
pypi.inkbridge.io/radenv/. Script entrypoints follow the same rename:
multi-server-test{,-config} -> radenv{,-config}.

See InkbridgeNetworks/radenv@v1.0.2 for the upstream rename.

2 months agomulti-server: install radenv from inkbridge pypi instead of git clone
Arran Cudbard-Bell [Fri, 22 May 2026 03:46:55 +0000 (23:46 -0400)] 
multi-server: install radenv from inkbridge pypi instead of git clone

Drop the github.com/InkbridgeNetworks/radenv.git checkout + 'make
configure' dance and install the framework as a pip package from the
inkbridge index instead. Pinned version stays in RADENV_VERSION so
bumps are a one-line change.

  --extra-index-url https://pypi.inkbridge.io/ freeradius-multi-server==1.0.1

The inkbridge host already serves the PEP 503 simple listing at its
root, so no /simple/ suffix. --extra-index-url keeps the default
PyPI fallback for transitive deps.

Switches the python3 -m src.<module> invocations over to the
multi-server-test / multi-server-test-config entrypoints that the
sdist exposes via [project.scripts]; the cd / venv-activate dance
in front of every call goes away.

Also bumps actions/upload-artifact from v4 (Node.js 20, deprecated
2026-06-02) to v6 (Node.js 24) so ci-multi-server-tests stops
emitting the deprecation warning on every run.

Closes #5860

2 months agoprofiling image: stage /docker-entrypoint.sh so it's a drop-in for service
Arran Cudbard-Bell [Fri, 22 May 2026 03:20:51 +0000 (23:20 -0400)] 
profiling image: stage /docker-entrypoint.sh so it's a drop-in for service

src/tests/multi-server/environments/kafka.yml.j2 (and the other env
templates) override the Dockerfile entrypoint with a bash wrapper that
still execs /docker-entrypoint.sh. The service image stages that file
via service.{deb,rpm}.m4 but the profiling image didn't, so the
kafka-producer1 container exited 127 ("command not found") in
profiling-mode multi-server runs.

Add the same COPY + chmod to common.freeradius-profile-build.m4. Leave
the per-distro profiling.{deb,rpm}.m4 EXPOSE / CMD (sleep loop) alone
since compose overrides them anyway.

2 months agomulti-server / docker: cleaner path layout and plain-ascii arrows
Arran Cudbard-Bell [Fri, 22 May 2026 03:17:38 +0000 (23:17 -0400)] 
multi-server / docker: cleaner path layout and plain-ascii arrows

Reorder the profiling-results path so branch / commit / run-index
come before the test name:

  before: prof-results/<test>/<mode>/<branch>/<commit>/<run-index>
  after:  prof-results/<branch>/<commit>/<run-index>/<test>/<mode>

The run-index now counts at the branch/commit level instead of per
(test, mode), so a single CI invocation's parallel tests share an
index and a re-run of the same commit gets the next index.

Also swap the unicode '->' arrows in docker.mk's DOCKER_BUILD header
for plain '>' to match the BUILD echo style.

2 months agoprofiling image: install with --prefix=/usr to match deb/rpm layout
Arran Cudbard-Bell [Fri, 22 May 2026 03:08:14 +0000 (23:08 -0400)] 
profiling image: install with --prefix=/usr to match deb/rpm layout

The profiling Dockerfile chain used the default ./configure prefix
(/usr/local), so radclient / radiusd / etc. ended up under
/usr/local/bin and /usr/local/sbin while the service image (deb/rpm
install) puts them under /usr/bin and /usr/sbin. Test fixtures had
to either drop absolute paths or branch on mode.

Pass --prefix=/usr so the source build lands binaries at the same
absolute paths as the production packages. Update the radiusd ->
freeradius alias symlink to match. Restore /usr/bin/radclient in the
proxy-accept and proxy-multihop-accept test templates now that both
modes resolve it the same way.

2 months agomulti-server: invoke radclient from PATH; surface docker build log on failure
Arran Cudbard-Bell [Fri, 22 May 2026 03:03:55 +0000 (23:03 -0400)] 
multi-server: invoke radclient from PATH; surface docker build log on failure

The proxy-accept and proxy-multihop-accept test templates were calling
/usr/bin/radclient directly. That path is correct for service mode
(deb-installed FR) but the profiling image builds from source with the
default ./configure prefix, so radclient lands at /usr/local/bin/radclient
and the exec failed with 'No such file or directory'. Drop the absolute
path and rely on $PATH so both modes work.

Also add the same Show-docker-build-log failure step to
ci-multi-server-tests.yml that docker-refresh / docker-crossbuild /
docker-service already have, so the apt errors from a failed
crossbuild / profiling-deps build aren't trapped in a per-job runner
log file.

scripts/docker/docker.mk: switch the PULL echo from 'latest <- registry'
to 'latest < registry' to match the BUILD echo's plain redirect style.

2 months agocommon.deb.dbgsym: tolerate ddebs version skew per-package
Arran Cudbard-Bell [Fri, 22 May 2026 02:11:45 +0000 (22:11 -0400)] 
common.deb.dbgsym: tolerate ddebs version skew per-package

ddebs.ubuntu.com and debian-debug rebuild dbgsyms slower than the
matching main package gets a security update. On any given day a
handful of entries are in skew: dbgsym pinned to -buildN while main
is at -ubuntuN.M.K, so an apt-get install of the full list fails with
unmet dependencies. Recent refresh runs caught 9 packages mid-skew on
jammy in one snapshot.

Switch back to per-package install with a WARNING fallback. Skewed
entries are logged loudly so missing symbols stay visible, and the
remaining ~15 of ~24 dbgsyms still land. The next refresh picks up
the previously-skewed ones once ddebs catches up.

2 months agodocker-refresh: build crossbuild from upstream OS, run freeradius in parallel with...
Arran Cudbard-Bell [Fri, 22 May 2026 01:23:23 +0000 (21:23 -0400)] 
docker-refresh: build crossbuild from upstream OS, run freeradius in parallel with CI bases

process-freeradius-images was overriding crossbuild's FROM to
self-hosted-ci-<distro> via CB_FROM_* env vars to reuse the slim CI
toolchain layer. The reuse saved a minute or two of apt-install per
distro at the cost of a hard dependency on process-ci-images
(crossbuild's FROM had to exist in the registry before the build).

Drop the CB_FROM_* block so crossbuild builds from the upstream
ubuntu/debian/rocky base directly, same path service uses. Trades a
modest build-time increase for parallelism with process-ci-images
and removes the cross-job FROM-chain that broke during the
self-hosted-{deb,rocky} -> self-hosted-ci rename.

2 months agodocker-refresh: serialise concurrent refresh runs
Arran Cudbard-Bell [Fri, 22 May 2026 01:19:48 +0000 (21:19 -0400)] 
docker-refresh: serialise concurrent refresh runs

Add a workflow-level concurrency group with cancel-in-progress: false
so a second refresh trigger (push to master + a manual workflow_dispatch,
two manual triggers in quick succession, or the nightly schedule
overlapping a push) queues behind the running one instead of starting
a second parallel refresh.

cancel-in-progress is left false: an in-flight refresh that's halfway
through publishing :latest tags shouldn't be torn down by the next
trigger; let it finish, then run the queued one which re-publishes
everything cleanly.

2 months agodocker-refresh: chain freeradius-images on ci-images
Arran Cudbard-Bell [Fri, 22 May 2026 01:16:56 +0000 (21:16 -0400)] 
docker-refresh: chain freeradius-images on ci-images

process-freeradius-images was running in parallel with
process-ci-images, both gated on process-docker-cli. crossbuild
FROMs self-hosted-ci-<distro>:latest and resolved that via the
previous nightly's registry copy. The rename from
self-hosted-{deb-,rocky}<distro> to self-hosted-ci-<distro> means
no previous nightly has published the new tag yet, so the parallel
crossbuild step now fails with 'manifest not found' on the FROM.

Serialise: freeradius-images needs ci-images. One extra wait per
refresh in exchange for a working FROM chain through any future
rename.

2 months agodocker-refresh: rename CI bases to self-hosted-ci-<distro>, surface build log on...
Arran Cudbard-Bell [Fri, 22 May 2026 01:05:24 +0000 (21:05 -0400)] 
docker-refresh: rename CI bases to self-hosted-ci-<distro>, surface build log on failure

The slim CI build bases (built from scripts/docker/build/<distro>/Dockerfile.ci
and consumed by ci-deb.yml / ci-rpm.yml) were named inconsistently:
the rpm side ran as self-hosted-rocky{9,10} and the deb side as
self-hosted-deb-{debian12,debian13,debiansid,ubuntu22,ubuntu24,ubuntu26}.
Unify on self-hosted-ci-<distro> across both families. self-hosted-ubuntu24
(no -ci-) stays as the GH runner host image so the two roles are
distinguishable.

Updates the matrix entries in docker-refresh.yml, the CB_FROM_* env
in process-freeradius-images and docker-crossbuild.yml, and the
BUILD_IMAGE strings in ci-deb.yml / ci-rpm.yml.

Also re-adds the Show docker build logs failure step to
process-freeradius-images (got lost in an earlier rewrite). Without
it the actual apt errors from the dbgsym install stay hidden in
build/docker/build.<os>.profiling-deps on the runner.

2 months agodocker-refresh: force bash for container-based jobs
Arran Cudbard-Bell [Fri, 22 May 2026 00:45:43 +0000 (20:45 -0400)] 
docker-refresh: force bash for container-based jobs

When a GH Actions job declares a container:, the per-step run: shell
defaults to /bin/sh (dash on the cli image's debian base). The Create
build dependency package step uses $(stat -c'%u:%g' .git) inside an
unquoted heredoc, and dash fails to parse that with a cryptic
'Syntax error: "(" unexpected'. Pin shell: bash for both
process-ci-images and process-freeradius-images.

Also renames process-images to process-ci-images for clarity now
that it only covers the CI runner / build bases plus pull-through
caches.

2 months agodocker-refresh: rebuild freeradius-build-deps.deb for self-hosted-ubuntu24
Arran Cudbard-Bell [Fri, 22 May 2026 00:32:22 +0000 (20:32 -0400)] 
docker-refresh: rebuild freeradius-build-deps.deb for self-hosted-ubuntu24

scripts/ci/docker/Dockerfile COPYs /freeradius-build-deps.deb and
/freeradius-build-deps-extra.deb into the runner host image. After the
matrix split the build step was orphaned in process-host-bases so the
files were never generated, and the docker build failed with a 'cache
key not found' on the COPY. Re-add the dep-pkg step in
process-host-bases, gated on a needs_build_deps flag on the ubuntu24
matrix entry.

2 months agodocker-refresh: build dind + cli first, run the rest under fresh dind
Arran Cudbard-Bell [Fri, 22 May 2026 00:23:34 +0000 (20:23 -0400)] 
docker-refresh: build dind + cli first, run the rest under fresh dind

Split docker-refresh into a four-stage pipeline so the freshly built
dind sidecar and docker-cli job-container are in the registry before
anything else uses them.

  process-host-bases    raw host, parallel: self-hosted-ubuntu24
                        (alias self-hosted) and self-hosted-docker-dind
  process-docker-cli    raw host, needs process-host-bases: builds
                        self-hosted-docker-cli, which FROMs self-hosted
  process-images        dind + cli, needs process-docker-cli, parallel:
                        the remaining CI bases (rocky/deb-*) plus the
                        mariadb / postgres / 389ds pull-through caches
  process-freeradius-images
                        dind + cli, needs process-docker-cli, parallel:
                        the freeradius4-{crossbuild,profiling-deps,
                        service} chain per distro

Stages 3 and 4 use fresh dind + cli from this run instead of the
previous cycle's copies. Stages 1 and 2 stay on the raw self-hosted
runner because they're building the very dind/cli they'd otherwise
sit inside.

Also switches all internal-registry logins to docker/login-action@v4
for consistency with the existing Docker Hub login.

2 months agodocker-refresh: parallelise jobs and fix dbgsym install terminator
Arran Cudbard-Bell [Thu, 21 May 2026 23:58:49 +0000 (19:58 -0400)] 
docker-refresh: parallelise jobs and fix dbgsym install terminator

Drop the needs: chain in docker-refresh.yml so process-images,
process-docker-cli and process-freeradius-images all start in
parallel. Each job's container and FROM dependencies resolve from
the previous cycle's :latest, so a one-cycle staleness is acceptable.
Rename process-derived-images to process-docker-cli since it builds
exactly one image.

Fix common.deb.dbgsym.m4 to terminate the apt-get install reliably:
move libc6-dbg to the tail as the unconditional last token. On sid
and resolute the libxml2/libidn12/python ifelse blocks all evaluate
to empty, which left the previous-to-last package's trailing
backslash joining the apt-get install command with the next RUN.
Buildkit fed "RUN git clone FlameGraph ..." to apt as a package name
and exited 100. Also corrects the codename test from forky to sid to
match Dockerfile.m4's OS_CODENAME mapping for debiansid.

2 months agoMerge profiling-multi-server-fixes into developer/arr2036
Arran Cudbard-Bell [Thu, 21 May 2026 22:46:31 +0000 (18:46 -0400)] 
Merge profiling-multi-server-fixes into developer/arr2036

Pulls in the docker-refresh, profiling-deps split, /etc/freeradius
standardisation, ddebs version-skew tolerance, and matrix display
name work from profiling-multi-server-fixes. Resolves conflicts in
docker.mk and dockerfile.mk by keeping the cleaner per-type list
factoring from developer/arr2036 and extending it with profiling-deps.

Adds display: fields to every docker-refresh.yml matrix entry so the
GH Actions UI shows the actual image identity instead of the entry
dict serialisation, renames the workflow to "Docker refresh", and
labels the FR-image chain job as
freeradius4-{crossbuild,profiling-deps,service}-<os>.

2 months agofix multiple connections from dynamic clients
Alan T. DeKok [Thu, 21 May 2026 21:59:46 +0000 (16:59 -0500)] 
fix multiple connections from dynamic clients

this is a bit of a work-around for now.  the better fix is to
instead have a list of pending connections, and add the later
connections to that list.

2 months agoadd --profile=file options
Alan T. DeKok [Thu, 21 May 2026 21:39:15 +0000 (16:39 -0500)] 
add --profile=file options

which sets CPUPROFILE=file, so that we can profile the program
we're executing, and not jlibtool itself

2 months agoallow for unit_test_module to use profiling, too
Alan T. DeKok [Thu, 21 May 2026 21:28:23 +0000 (16:28 -0500)] 
allow for unit_test_module to use profiling, too

2 months agomost things don't need the gperftools header
Alan T. DeKok [Thu, 21 May 2026 21:23:03 +0000 (16:23 -0500)] 
most things don't need the gperftools header

it's only needed if the files call the perftools APIs.  And that's
only radmin and dependency.c

2 months agoremove tests which is racy, and causes CI failures.
Alan T. DeKok [Thu, 21 May 2026 17:43:30 +0000 (12:43 -0500)] 
remove tests which is racy, and causes CI failures.

depending on timing, the output is either "2" or "3".  and the
framework isn't smart enough (yet) to double-check that. So we
just remove the test.

2 months agoMove more image building into docker-refresh
Arran Cudbard-Bell [Thu, 21 May 2026 20:00:40 +0000 (16:00 -0400)] 
Move more image building into docker-refresh

2 months agocommon.deb.dbgsym: tolerate apt-get update flakiness for ddebs sources
Arran Cudbard-Bell [Thu, 21 May 2026 19:01:18 +0000 (15:01 -0400)] 
common.deb.dbgsym: tolerate apt-get update flakiness for ddebs sources

The dbgsym RUN step did `apt-get update && for pkg in ...`. When the
update step fell over - typically because ddebs.ubuntu.com or
debug.mirrors.debian.org returned a 5xx mid-fetch despite the
Acquire::Retries=3 we set in the toolchain template - the && killed
the whole RUN with exit 100 before any of the per-package installs
ran. The per-package loop already prints a WARNING and continues
for any individual package the cache can't resolve, so letting the
update itself fail soft means partial cache + best-effort installs
still happen rather than tipping the whole image build over.

Concretely: docker.profiling.ubuntu24 stopped building on the
docker-server workflow each time ddebs hiccupped, even though every
dbgsym package is technically optional and FR ships fine without
them.

2 months agoredhat: install config under /etc/freeradius to match the debian layout
Arran Cudbard-Bell [Thu, 21 May 2026 18:46:24 +0000 (14:46 -0400)] 
redhat: install config under /etc/freeradius to match the debian layout

The RPM build used the historical /etc/raddb path while the debian
package installs to /etc/freeradius. Everything downstream that
mounts FR config (the multi-server compose files, the profiling
image's m4 macro) was already on /etc/freeradius - the RPM was the
odd one out, leaving operators of rocky9/10-derived images to deal
with a different config path than ubuntu/debian operators.

The configure call gains --with-raddbdir=%{_sysconfdir}/freeradius
so FR's compile-time raddbdir matches what the spec actually
packages. All the hardcoded %{_sysconfdir}/raddb paths in the %files
manifest and the post-install bootstrap, the systemd unit's
ReadOnlyDirectories, and the sysv init script's config_dir default
all flip to %{_sysconfdir}/freeradius / /etc/freeradius.

Operators upgrading an existing RPM install will need to move (or
symlink) /etc/raddb to /etc/freeradius before reinstall - the
%config(noreplace) entries won't migrate files across paths, and
the post-install certificate bootstrap reads from the new location.

2 months agomulti-server: standardise compose mounts on /etc/freeradius
Arran Cudbard-Bell [Thu, 21 May 2026 18:40:46 +0000 (14:40 -0400)] 
multi-server: standardise compose mounts on /etc/freeradius

The kafka, proxy, and proxy-multihop env files mounted configs at
/etc/raddb/, while the four new accept / ldap / mysql / pap-auth env
files mounted at /etc/freeradius/. Both paths resolve to the same
underlying directory (the freeradius4-profiling image has an
/etc/raddb -> /etc/freeradius symlink configured by
common.freeradius-profile-build.m4, and the standard debian package
that backs freeradius4-service installs config under /etc/freeradius
with raddbdir pointing there), but FR's semaphore association uses
the literal path it was started with - so kafka-producer1 and the
proxy homeserver containers were associating semaphores against a
file path that didn't quite match what FR ultimately opened, tripping
ENOENT on -n-prefixed runs.

Rewrite every /etc/raddb/... bind target to /etc/freeradius/...
across all the multi-server env files (and the start_valgrind_profiling.sh
script's mount target) so the kafka and proxy suites work against
both the service image and the profiling image without per-image
divergence. ${raddbdir} references inside the rendered radiusd.conf
fragments stay as-is - that's FR's compile-time variable, not a
literal path, and it already resolves to /etc/freeradius on both
images.

2 months agoci: retarget docker prune at the internal registry, not the host daemon
Arran Cudbard-Bell [Thu, 21 May 2026 18:30:03 +0000 (14:30 -0400)] 
ci: retarget docker prune at the internal registry, not the host daemon

The previous prune walked the runner host's docker daemon looking for
images carrying a ci-ttl label, but the entire CI pipeline builds
inside per-job DIND sidecars - those images live in dind, never on
the host daemon, and go away when each job ends. The prune ran every
30 minutes and reliably reported `removed=0 kept=0` because the place
it was looking was always empty. Meanwhile the actual disk pressure
is on the internal registry, where docker-server.yml and
docker-crossbuild.yml accumulate one freeradius4-<type>/<image>:<sha>
per commit until something cleans them up.

The new job runs inside the self-hosted-docker-cli container,
authenticates to docker.internal.networkradius.com with the
DOCKER_REPO_UPDATE_* secrets, walks the catalog for repos under the
freeradius4-* namespaces, pulls each tag's manifest + image config
to recover the ci-ttl label and creation timestamp, and issues
`DELETE /v2/<name>/manifests/<digest>` for anything past its
deadline. The label TTL semantics, log shape, and 30-minute cron
are unchanged. Disk reclamation still needs `registry
garbage-collect` to run on the registry host afterwards; that's not
in scope here.

2 months agoversion.sh: silence git describe stderr on branches without release/branch tags
Arran Cudbard-Bell [Thu, 21 May 2026 17:49:44 +0000 (13:49 -0400)] 
version.sh: silence git describe stderr on branches without release/branch tags

`version.sh commit_depth` runs
`git describe --tags --match 'branch_*' --match 'release_*'`
to count commits since the last branch/release tag. On feature
branches and freshly-checked-out CI workspaces neither tag class is
reachable from HEAD, so git describe exits non-zero and emits
"fatal: No names found, cannot describe anything." to stderr. The
existing `|| echo "${commit_depth}"` fallback already covers the
exit code; redirect stderr to /dev/null on the git describe call so
the message stops leaking into CI build logs (it was showing up
twice per docker-server.yml step plus once inside the in-container
`make deb`).

2 months agoci: docker prune script logs the actual image being pruned
Arran Cudbard-Bell [Thu, 21 May 2026 17:18:18 +0000 (13:18 -0400)] 
ci: docker prune script logs the actual image being pruned

The remove / skip lines used to identify the image by sha256 hash
only, which is useless when you're trying to figure out which build
of which type just disappeared at 02:14am. Pull the docker inspect
output forward to include {{join .RepoTags ","}} and render each
decision as `<repo:tag,repo:tag,...> (sha256:first-19-chars)`.
Falls back to "<dangling>" when RepoTags is empty (orphaned cache
layers). Added a corresponding keep line so the log shows the full
picture of what the prune considered each pass, not just the
deletions, and surfaced the parsed ci-ttl value in each message so
the cron / TTL pairing is visible without cross-referencing the
labels yourself.

2 months agoci: re-add nightly schedule trigger to multi-server
Arran Cudbard-Bell [Thu, 21 May 2026 17:14:51 +0000 (13:14 -0400)] 
ci: re-add nightly schedule trigger to multi-server

Restore the nightly cron entry that was lost when the workflow was
narrowed to workflow_run-only. The schedule path runs the full
multi-server test set instead of the per-commit CI subset (the
original split before the gating change). It fires independently of
the upstream Docker crossbuild images workflow, so the matching
crossbuild image may not be in the internal registry at trigger time
(e.g. after a registry prune) - docker.mk's stamp dependency chain
handles that: a pull-through miss on the crossbuild image cascades
into a local crossbuild docker build via the existing fallback
branch, then the profiling layer is built on top. Slow path but
self-sufficient.

The job-level if: gate now lets schedule events through (the prior
workflow_run.conclusion check would have skipped them). The checkout
ref: falls back to github.sha when event.workflow_run.head_sha is
unset (schedule has no upstream workflow_run context). The test
command split keys on matrix.mode + event_name so service / profiling
each get the full target on schedule and the .ci suffix otherwise.

2 months agoci: rename docker / crossbuild workflows to reflect what they publish
Arran Cudbard-Bell [Thu, 21 May 2026 17:11:19 +0000 (13:11 -0400)] 
ci: rename docker / crossbuild workflows to reflect what they publish

docker.yml -> docker-server.yml ("Docker service images") and
crossbuild.yml -> docker-crossbuild.yml ("Docker crossbuild images")
so the workflow names match the freeradius4-<type>/<image>:<sha>
namespaces they push to the internal registry. The workflow_run
trigger in ci-multi-server-tests.yml, the failure-diagnosis filter
in claude-ci-failure-diagnosis.yml, and the comment refs in
docker-refresh.yml all updated to the new names.

The dead `if [ "\$GITHUB_EVENT_NAME" = "schedule" ]` branches in the
multi-server test step are gone too; multi-server is now
workflow_run-only, so the schedule case never fires.

2 months agoci: multi-server tests trigger via Check Crossbuild only
Arran Cudbard-Bell [Thu, 21 May 2026 17:02:57 +0000 (13:02 -0400)] 
ci: multi-server tests trigger via Check Crossbuild only

Drop the push / pull_request / schedule / workflow_dispatch triggers
left over from the previous shape. Each of them would race ahead of
Check Crossbuild and find no freeradius4-crossbuild/<image>:<sha> in
the internal registry to consume, falling all the way back to a
local rebuild and defeating the whole pull-through chain.

workflow_run is now the only entry point. The job-level if: gate
simplifies to a straight conclusion == 'success' check and the
checkout step pins straight to event.workflow_run.head_sha without
the prior fallback for the now-deleted triggers.

2 months agoci: chain multi-server tests after crossbuild and use push-capable creds
Arran Cudbard-Bell [Thu, 21 May 2026 17:00:13 +0000 (13:00 -0400)] 
ci: chain multi-server tests after crossbuild and use push-capable creds

The push steps in crossbuild.yml + docker.yml reused the existing
DOCKER_REPO_USERNAME / DOCKER_REPO_PASSWORD secrets, which only carry
read access to docker.internal.networkradius.com (enough for pulling
the self-hosted base images). Each push 401'd against nginx with
"Authorization Required". Both workflows now do a second docker login
right before the push using DOCKER_REPO_UPDATE_USERNAME /
DOCKER_REPO_UPDATE_PASSWORD, which override dind's auth config for the
upload only - the earlier read-only login remains for the pull paths.

ci-multi-server-tests.yml now fires off Check Crossbuild completion
via workflow_run instead of on every push. The job skips when the
upstream crossbuild run didn't succeed (workflow_run.conclusion gate),
and the checkout pins to the SHA the crossbuild run was triggered
against (event.workflow_run.head_sha) so the multi-server pass tests
the same commit the crossbuild image was built from. PR / schedule /
manual dispatch triggers are untouched.

2 months agomulti-server: switch ldap suite to dinkel/openldap
Arran Cudbard-Bell [Thu, 21 May 2026 14:51:55 +0000 (10:51 -0400)] 
multi-server: switch ldap suite to dinkel/openldap

osixia/openldap fights docker bind-mounts at every granularity: its
slapd-bootstrap chowns the LDIF mount on startup (fails read-only),
post-init rms the parent dir (fails when the dir is bind-mounted),
and runs `sed -i` on the LDIF via a temp-file rename (fails on a
file-level bind-mount because rename can't replace the target). No
mount shape survives all three rewrites without baking the LDIF into
a derived image.

dinkel/openldap reads `/etc/ldap/prepopulate/*.ldif` and slapadds them
once on first start, never touching the mount. The image takes
SLAPD_DOMAIN / SLAPD_PASSWORD via env, listens on the default port
389, and our existing configs/openldap/bootstrap/01-testuser.ldif
works as-is. Healthcheck uses bash's /dev/tcp because dinkel doesn't
ship ldap-utils.

2 months agomulti-server: use \$PROFILING not \${PROFILING} in test templates
Arran Cudbard-Bell [Thu, 21 May 2026 04:58:01 +0000 (00:58 -0400)] 
multi-server: use \$PROFILING not \${PROFILING} in test templates

radenv's events/CommandEvents.run_command runs every shell snippet
through a `\${name}` -> `<project>-<name>-1` regex before docker exec,
treating any \${...} as a container reference. That silently rewrote
our profiling-mode branch's `if [ "\${PROFILING}" = "yes" ]` to a
container-name comparison, always taking the non-profiling branch and
running freeradius without valgrind. Bash accepts the unbraced
\$PROFILING form and the radenv regex doesn't match it.

With this in place test.multi-server.profiling.ci produces real
callgrind output (~21 per-thread files per suite) for all four new
suites.

2 months agoutil: make libbacktrace recipe a grouped target and use a relative HEADERS path
Arran Cudbard-Bell [Thu, 21 May 2026 14:48:59 +0000 (10:48 -0400)] 
util: make libbacktrace recipe a grouped target and use a relative HEADERS path

libfreeradius-util.mk drove libbacktrace via a regular two-target rule
that called the libbacktrace sub-make. GNU make >=4.3 expands a
multi-target rule as one independent rule per target sharing the same
recipe text, so parallel make can schedule the recipe twice when both
outputs are needed in the same invocation. Two concurrent sub-makes
land in the same .a / .libs/ directory; ar mid-writing libbacktrace.a
(with mmapio.o as a member) while a peer ranlib reads it surfaces as
"ranlib: .libs/libbacktrace.a: error reading mmapio.o: file truncated"
under -j>1. The grouped-target form (`&:`, added in GNU make 4.3) tells
make a single recipe invocation produces both outputs and only
schedules it once.

The HEADERS entry for backtrace.h was `$(top_srcdir)/.../backtrace.h`.
The install rule does `$(addprefix ${SRC_INCLUDE_DIR}/,$(HEADERS))`,
so an absolute path produces a double-slash install target like
`${SRC_INCLUDE_DIR}//abs/path/backtrace.h`. The pattern rule
`${SRC_INCLUDE_DIR}/%.h: src/include/%.h` still matches that target,
but its derived prereq `src/include//abs/path/backtrace.h` doesn't
exist, so `make install` fails with "No rule to make target". This
breaks every build where $(top_srcdir) resolves to an absolute path,
i.e. inside docker builds where @abs_top_srcdir@ is /usr/local/...
The relative `backtrace/backtrace.h` form reaches the same file via
the src/include/backtrace -> ../lib/backtrace symlink set up just
below.

2 months agobuild,profiling-image: gate dsymutil on apple-darwin so libbacktrace links survive ELF
Arran Cudbard-Bell [Thu, 21 May 2026 14:48:12 +0000 (10:48 -0400)] 
build,profiling-image: gate dsymutil on apple-darwin so libbacktrace links survive ELF

scripts/boiler.mk:702 used to set DSYMUTIL = $(shell which dsymutil)
whenever AC_HAVE_BACKTRACE was set, with no target-platform check.
On Linux that picked the LLVM dsymutil up off PATH (now reliably
present in the profiling image because kcachegrind/inferno pull in
the LLVM toolchain), and jlibtool then invoked it against every
linked .so. dsymutil only understands Mach-O, so the first link of
libfreeradius-make-dlopen.so under -g3 aborted with "cannot parse
the debug map for build/lib/.libs/libfreeradius-make-dlopen.so:
The file was not recognized as a valid object file" and the make
plugin's recursive build never produced the .so the parent make
tried to load. Restricting the binary lookup to apple-darwin
TARGET_SYSTEM falls back to the existing `DSYMUTIL := echo` no-op
on every other platform, so the recipe step stays harmless.

scripts/docker/m4/common.freeradius-profile-build.m4 now wipes any
host build artefacts that survived the COPY before invoking
autoconf/make so the container builds from a clean state. The
libbacktrace submodule in particular tends to carry host-absolute
paths in its generated Makefile / libtool, which broke the
in-container configure when the host tree had been built natively
on a different architecture.

2 months agomulti-server: dedup load-generator-packets across all suites and ignore host build/
Arran Cudbard-Bell [Thu, 21 May 2026 03:52:38 +0000 (23:52 -0400)] 
multi-server: dedup load-generator-packets across all suites and ignore host build/

The proxy / proxy-multihop / kafka suites all carried their own copy of
the same load-generator packet.conf, byte-identical to what the new
accept/ldap/mysql/pap-auth suites now read from configs/freeradius/
common/proto-load/. Drop the per-suite copies and repoint the compose
envs at the shared one. The unused common/proto-load/template.d
placeholder is removed (no radiusd.conf.j2 or env mount referenced it).

.dockerignore now also excludes the local build/ and prof-results/
directories so docker builds that do `COPY . .` don't drag a polluted
host tree into the image and confuse in-container Makefile installs
with absolute host paths.

2 months agomulti-server: dedup proto_load scaffolding into common/proto-load
Arran Cudbard-Bell [Thu, 21 May 2026 03:45:23 +0000 (23:45 -0400)] 
multi-server: dedup proto_load scaffolding into common/proto-load

The proto_load packet config, stats placeholder, packet template
fragment, and load-generator-packets/packet.conf were byte-identical
across accept/ldap/mysql/pap-auth. Lift them to configs/freeradius/
common/proto-load/ and point each suite's compose env at the common
mount path. A one-line change to the load profile now touches one file
instead of four.

2 months agomulti-server: fold profiling into standard suites with a MODE matrix
Arran Cudbard-Bell [Thu, 21 May 2026 03:30:01 +0000 (23:30 -0400)] 
multi-server: fold profiling into standard suites with a MODE matrix

Renames prof-accept/prof-ldap/prof-mysql/prof-pap-auth to accept/ldap/
mysql/pap-auth so they sit alongside proxy-accept and kafka-produce as
ordinary multi-server suites. Each suite is now driven once with MODE=
service (default, freeradius-build:latest, exec the server directly)
and once with MODE=profiling (freeradius-prof:latest, source the
valgrind/callgrind wrapper). The compose envs reference
${FREERADIUS_IMAGE} and ${PROFILING}, and the per-test recipe in
all.mk exports those alongside PROF_RESULTS_PATH based on MODE. The
bespoke Dockerfile.multi-server-prof and the build_image.sh /
run_container.sh wrappers are gone; the standard
freeradius4-profiling/<image>:<sha> image (with FreeRADIUS now compiled
in by common.freeradius-profile-build.m4) is just re-tagged as
freeradius-prof:latest by the new freeradius-prof.image target.

ci-profiling.yml is deleted; ci-multi-server-tests.yml grows a
matrix.mode = [service, profiling] so both passes share one job
template, one set of dind/cli images, and one workflow_dispatch entry.
The mariadb fixture now loads the canonical schema from
raddb/mods-config/sql/main/mysql/schema.sql via an additional ordered
mount and only seeds the testuser row locally; the openldap fixture
switched to osixia/openldap:1.5.0 plus an LDIF bootstrap; mariadb data
moved to tmpfs so init.sql re-applies on every compose-up.

2 months agomulti-server: early review fixes on PR 5848
Arran Cudbard-Bell [Thu, 21 May 2026 03:19:44 +0000 (23:19 -0400)] 
multi-server: early review fixes on PR 5848

Pulled in via merge from marc-casavant/profiling-multi-server-tests, then
addressed the structural and bug review feedback before the larger suite
restructure: the bogus crossbuild.ubuntu24.profile.* and docker.openldap.prof
target references in all.mk are gone, the multi-server prof Dockerfile now
FROMs the standard freeradius4-profiling/ubuntu24 image via build-arg, and
the configure/make/install pass with callgrind-friendly CFLAGS is lifted
into a shared common.freeradius-profile-build.m4 so every distro's
Dockerfile.profiling produces an image with FreeRADIUS already installed.

Bug fixes in start_valgrind_profiling.sh: VALGRIND_PID now captures the
valgrind process (was tee, leaving the fallback kill paths hitting the
wrong PID), the dead PROFILE_DURATION string-arithmetic line is removed,
and the eval-wrapped callgrind_annotate call is replaced with a direct
invocation. 5min.test.yml files now use test_verify_timeout to match the
template (was test_state1_verify_timeout, undefined). prof-ldap/prof-mysql
no longer mount mods-config/files/authorize, neither config loads files.
README path under scripts/profiling/ corrected. Openldap fixture switched
to osixia/openldap:1.5.0 with an LDIF bootstrap under
configs/openldap/bootstrap/ instead of the missing docker.openldap.prof
target.

2 months agoWIP: Removed unnecessary build steps
Marc-Andre Casavant [Thu, 14 May 2026 16:41:03 +0000 (12:41 -0400)] 
WIP: Removed unnecessary build steps

2 months agoWIP: Fixed typo
Marc-Andre Casavant [Thu, 14 May 2026 15:46:33 +0000 (11:46 -0400)] 
WIP: Fixed typo

2 months agoWIP: Removed previous location of packet.conf
Marc-Andre Casavant [Thu, 14 May 2026 15:45:03 +0000 (11:45 -0400)] 
WIP: Removed previous location of packet.conf

2 months agoWIP: CI profiling workflow is now split into two jobs; build-profiling-image to build...
Marc-Andre Casavant [Thu, 14 May 2026 14:56:44 +0000 (10:56 -0400)] 
WIP: CI profiling workflow is now split into two jobs; build-profiling-image to build profiling images and push to docker registry, profiling-tests that pulls images from registry and runs tests.

2 months agoWIP: New CI profiling workflow
Marc-Andre Casavant [Thu, 14 May 2026 14:30:35 +0000 (10:30 -0400)] 
WIP: New CI profiling workflow

2 months agoWIP: Add test.multi-server.profiling and test.multi-server.profiling.ci make targets
Marc-Andre Casavant [Thu, 14 May 2026 14:27:52 +0000 (10:27 -0400)] 
WIP: Add test.multi-server.profiling and test.multi-server.profiling.ci make targets

2 months agoWIP: new PROF_MODE makefile argument to generate prof-results with different director...
Marc-Andre Casavant [Thu, 14 May 2026 14:10:58 +0000 (10:10 -0400)] 
WIP: new PROF_MODE makefile argument to generate prof-results with different directory structures for ci and dev

2 months agoWIP: use server's default pap config
Marc-Andre Casavant [Thu, 14 May 2026 14:09:29 +0000 (10:09 -0400)] 
WIP: use server's default pap config

2 months agoWIP: additional updates to simplify proto-load test env
Marc-Andre Casavant [Thu, 14 May 2026 13:06:56 +0000 (09:06 -0400)] 
WIP: additional updates to simplify proto-load test env

2 months agoWIP: consolidating common proto_load configs for profiling tests starting with the...
Marc-Andre Casavant [Wed, 13 May 2026 19:45:00 +0000 (15:45 -0400)] 
WIP: consolidating common proto_load configs for profiling tests starting with the prof-ldap test

2 months agoFour new valgrind profiling multi-server tests
Marc-Andre Casavant [Thu, 23 Apr 2026 13:47:28 +0000 (09:47 -0400)] 
Four new valgrind profiling multi-server tests

2 months agoci: retrigger ci-deb against refreshed self-hosted-deb-* images
Arran Cudbard-Bell [Wed, 20 May 2026 18:20:27 +0000 (14:20 -0400)] 
ci: retrigger ci-deb against refreshed self-hosted-deb-* images

2 months agoci: retrigger ci-deb against refreshed self-hosted-deb-* images
Arran Cudbard-Bell [Wed, 20 May 2026 18:20:27 +0000 (14:20 -0400)] 
ci: retrigger ci-deb against refreshed self-hosted-deb-* images

2 months agoadd warning for WITH_VERIFY_PTR
Alan T. DeKok [Thu, 21 May 2026 14:25:39 +0000 (09:25 -0500)] 
add warning for WITH_VERIFY_PTR

so that people know that it kills performance if used in production
networks

Manual port of commit df330a6867b

2 months agoadd notes on coding style
Alan T. DeKok [Thu, 21 May 2026 13:04:14 +0000 (08:04 -0500)] 
add notes on coding style

2 months agomake variable unsigned
Alan T. DeKok [Wed, 20 May 2026 20:27:35 +0000 (15:27 -0500)] 
make variable unsigned

because it's also used in an array lookup (with cast), and is
also cast to (size_t).  Where negative numbers would result in
gigantic size_t values

2 months agouse more fr_sbuff_uint8() when passing results to tolower()
Alan T. DeKok [Wed, 20 May 2026 20:23:54 +0000 (15:23 -0500)] 
use more fr_sbuff_uint8() when passing results to tolower()

2 months agodocker: drive per-type wiring from a single TYPES list
Arran Cudbard-Bell [Wed, 20 May 2026 19:46:20 +0000 (15:46 -0400)] 
docker: drive per-type wiring from a single TYPES list

docker.mk and dockerfile.mk both had a hand-written stanza per type:
four DOCKER_BUILD calls, four DOCKERFILE_RULE calls, four umbrellas.
Adding a fifth type meant editing every block. Collapse them into a
single DOCKER_TYPES / DOCKERFILE_TYPES list plus a double foreach over
images and types.

Per-type quirks (profiling's --build-arg=from=crossbuild and crossbuild
stamp dependency, the optional CB_FROM_$(IMG) override on crossbuild)
move into DOCKER_BUILD_ARGS_<type> / DOCKER_BUILD_DEPS_<type> recursive
variables, so the $(IMG) inside them resolves at the right point in the
foreach. Adding a new type is now: append to the list, drop in the m4
files, optionally set the two lookup vars.

2 months agodocker: put the build-log path back on the BUILD echo line
Arran Cudbard-Bell [Wed, 20 May 2026 19:41:06 +0000 (15:41 -0400)] 
docker: put the build-log path back on the BUILD echo line

2 months agoupdate CHAP howto
Alan T. DeKok [Wed, 20 May 2026 19:08:26 +0000 (14:08 -0500)] 
update CHAP howto

2 months agodocs-v4: update howto/modules/CHAP for v4, added diagram, and separated doc
nolade [Tue, 21 Apr 2026 17:28:41 +0000 (13:28 -0400)] 
docs-v4: update howto/modules/CHAP for v4, added diagram, and separated doc
into pages for increased readability.
Formatting, typo, and nav fixes (2nd commit).

2 months agominor tweaks to tacacs
Alan T. DeKok [Wed, 20 May 2026 16:56:25 +0000 (11:56 -0500)] 
minor tweaks to tacacs

2 months agoci: surface the docker build log when docker.yml fails
Arran Cudbard-Bell [Wed, 20 May 2026 18:50:53 +0000 (14:50 -0400)] 
ci: surface the docker build log when docker.yml fails

DOCKER_BUILD redirects buildkit output to build/docker/build.<image>.<type>
to keep parallel builds from interleaving on stdout. When CI fails,
the workflow log shows the BUILD echo then several silent minutes
followed by Post job cleanup, with no clue what broke. Add an
if-failure cat step in both job variants so the underlying error
surfaces in the run page.