mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-05 19:18:25 +00:00
Package FIPS for RPM-based distributions
Add an RPM package for Fedora and RHEL. `make -C packaging rpm` builds it from packaging/rpm/fips.spec, and the release workflow attaches it beside the .deb and the systemd tarball. - The package is named `fips-mesh`, because Fedora already ships an unrelated `fips` (a FITS image viewer) that owns /usr/bin/fips. The spec declares `Conflicts: fips`. - It installs the same files, units and fips group as the .deb. fips.service and fips-dns.service are enabled but not started on install; fips-firewall and fips-gateway stay opt-in. - An upgrade queues `systemctl --no-block try-restart` of fips, fips-dns and fips-gateway, and reloads fips-firewall rather than restarting it. - The binaries come from the pinned build image via build-deb-container.sh, so the RPM passes the same glibc floor and dependency checks as the .deb. rpmbuild runs in FIPS_RPM_BUILD_IMAGE, AlmaLinux 9 pinned by digest. - The glibc floor is now 2.34 project-wide, the lowest supported RPM distribution (RHEL 9). testing/check-rpm-floor.sh fails a package that requires a newer glibc. RHEL 8 and openSUSE are not supported. - Erase removes the DNS drop-ins fips-dns-setup wrote and restarts or reloads the resolver whose file it removed, as the Debian postrm does. /etc/fips is kept, since rpm has no purge. - Dev builds are versioned 0.6.0-0.dev.git<date>.<sha>. Tested on Fedora 44 and in AlmaLinux 9 containers with systemd: the package requires GLIBC_2.34 and passes the floor check; install leaves both units enabled and inactive; upgrade returns immediately and the daemon restarts on the new binary; erase removes the units and DNS files and keeps /etc/fips; host-built binaries (GLIBC_2.39) fail the floor check; dnf refuses to install alongside the FITS viewer. The erase branch's resolver restart and reload were exercised in AlmaLinux 9 with systemctl stubbed. No install-test suite covers the RPM yet; it is only built.
This commit is contained in:
committed by
Johnathan Corgan
parent
0d77dbe2de
commit
17ca52e694
@@ -199,6 +199,44 @@ jobs:
|
|||||||
done
|
done
|
||||||
rm -rf "$UNPACK"
|
rm -rf "$UNPACK"
|
||||||
|
|
||||||
|
# The RPM is packaged from the binaries the .deb shipped, so all three
|
||||||
|
# Linux artifacts carry the same objects and the floor check that has
|
||||||
|
# already passed on the .deb covers them. The script runs rpmbuild in the
|
||||||
|
# rpm image declared in packaging/build-floor.env and checks the glibc
|
||||||
|
# requirement of the package it produced; it is the same script a local
|
||||||
|
# `make rpm` calls, which is what keeps the two identical.
|
||||||
|
- name: Build RPM package
|
||||||
|
id: rpm
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
||||||
|
|
||||||
|
packaging/rpm/build-rpm-container.sh \
|
||||||
|
--no-build \
|
||||||
|
--version "${{ needs.determine-versioning.outputs.linux_package_version }}" \
|
||||||
|
--output-dir deploy \
|
||||||
|
| tee /tmp/build-rpm.log
|
||||||
|
|
||||||
|
# The script prints the package path as its last line of stdout; its
|
||||||
|
# diagnostics go to stderr, as with build-deb-container.sh.
|
||||||
|
RPM_FILE=$(tail -n 1 /tmp/build-rpm.log)
|
||||||
|
if [[ ! -f "$RPM_FILE" ]]; then
|
||||||
|
echo "build-rpm-container.sh did not name a package: '$RPM_FILE'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
case "$RPM_FILE" in
|
||||||
|
*.${{ matrix.artifact_arch }}.rpm) ;;
|
||||||
|
*)
|
||||||
|
echo "Package $RPM_FILE is not ${{ matrix.artifact_arch }}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Recorded relative to the checkout, like the .deb: upload-artifact
|
||||||
|
# derives its layout from the common ancestor of its paths.
|
||||||
|
echo "rpm=${RPM_FILE#"$PWD"/}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Build systemd tarball
|
- name: Build systemd tarball
|
||||||
env:
|
env:
|
||||||
STRIP: llvm-strip
|
STRIP: llvm-strip
|
||||||
@@ -240,13 +278,15 @@ jobs:
|
|||||||
|
|
||||||
echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT"
|
echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT"
|
||||||
echo "deb=${{ steps.deb.outputs.deb }}" >> "$GITHUB_OUTPUT"
|
echo "deb=${{ steps.deb.outputs.deb }}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "rpm=${{ steps.rpm.outputs.rpm }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: SHA-256 hashes
|
- name: SHA-256 hashes
|
||||||
run: |
|
run: |
|
||||||
echo "==> Linux release assets:"
|
echo "==> Linux release assets:"
|
||||||
sha256sum \
|
sha256sum \
|
||||||
"${{ steps.linux-assets.outputs.tarball }}" \
|
"${{ steps.linux-assets.outputs.tarball }}" \
|
||||||
"${{ steps.linux-assets.outputs.deb }}"
|
"${{ steps.linux-assets.outputs.deb }}" \
|
||||||
|
"${{ steps.linux-assets.outputs.rpm }}"
|
||||||
|
|
||||||
- name: Upload artifact (GitHub only)
|
- name: Upload artifact (GitHub only)
|
||||||
if: ${{ env.ACT != 'true' }}
|
if: ${{ env.ACT != 'true' }}
|
||||||
@@ -256,6 +296,7 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
${{ steps.linux-assets.outputs.tarball }}
|
${{ steps.linux-assets.outputs.tarball }}
|
||||||
${{ steps.linux-assets.outputs.deb }}
|
${{ steps.linux-assets.outputs.deb }}
|
||||||
|
${{ steps.linux-assets.outputs.rpm }}
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- name: Build Summary
|
- name: Build Summary
|
||||||
@@ -263,6 +304,7 @@ jobs:
|
|||||||
echo "Build Summary for linux/${{ matrix.artifact_arch }}:"
|
echo "Build Summary for linux/${{ matrix.artifact_arch }}:"
|
||||||
echo " Tarball: ${{ steps.linux-assets.outputs.tarball }}"
|
echo " Tarball: ${{ steps.linux-assets.outputs.tarball }}"
|
||||||
echo " Debian: ${{ steps.linux-assets.outputs.deb }}"
|
echo " Debian: ${{ steps.linux-assets.outputs.deb }}"
|
||||||
|
echo " RPM: ${{ steps.linux-assets.outputs.rpm }}"
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Publish Linux assets to GitHub Release
|
name: Publish Linux assets to GitHub Release
|
||||||
@@ -282,7 +324,7 @@ jobs:
|
|||||||
- name: Generate Linux release checksums
|
- name: Generate Linux release checksums
|
||||||
run: |
|
run: |
|
||||||
cd dist
|
cd dist
|
||||||
find . -maxdepth 1 -type f \( -name '*.deb' -o -name '*.tar.gz' \) -printf '%P\n' \
|
find . -maxdepth 1 -type f \( -name '*.deb' -o -name '*.rpm' -o -name '*.tar.gz' \) -printf '%P\n' \
|
||||||
| LC_ALL=C sort \
|
| LC_ALL=C sort \
|
||||||
| xargs sha256sum \
|
| xargs sha256sum \
|
||||||
> checksums-linux.txt
|
> checksums-linux.txt
|
||||||
@@ -308,6 +350,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
gh release upload "${GITHUB_REF_NAME}" \
|
gh release upload "${GITHUB_REF_NAME}" \
|
||||||
dist/*.deb \
|
dist/*.deb \
|
||||||
|
dist/*.rpm \
|
||||||
dist/*.tar.gz \
|
dist/*.tar.gz \
|
||||||
dist/checksums-linux.txt \
|
dist/checksums-linux.txt \
|
||||||
--clobber \
|
--clobber \
|
||||||
|
|||||||
@@ -176,6 +176,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
#### Packaging
|
#### Packaging
|
||||||
|
|
||||||
|
- An RPM package for Fedora and RHEL (`packaging/rpm/`,
|
||||||
|
`make -C packaging rpm`), built from the same binaries the `.deb` and the
|
||||||
|
systemd tarball carry and attached to each release beside them. The package
|
||||||
|
is named `fips-mesh`, not `fips`: Fedora's namespace already has a `fips` —
|
||||||
|
an unrelated OpenGL FITS image viewer at 3.4.0 — which owns `/usr/bin/fips`,
|
||||||
|
so a `fips` at 0.6.0 is an older release of that one to every RPM tool, and
|
||||||
|
an ordinary `dnf upgrade` replaces a running mesh node with an image viewer.
|
||||||
|
The spec declares `Conflicts: fips`, since both ship `/usr/bin/fips`.
|
||||||
|
`make rpm` compiles nothing on the host: it builds in the pinned image by way
|
||||||
|
of `build-deb-container.sh`, which has already run the glibc floor and
|
||||||
|
Depends checks, and packages what that produced; `rpmbuild` itself runs in
|
||||||
|
`FIPS_RPM_BUILD_IMAGE` (AlmaLinux 9, pinned by digest), which supplies the
|
||||||
|
`systemd-rpm-macros` a build host may lack and writes packages every newer
|
||||||
|
rpm can read. `make rpm-host` remains for iteration, as `deb-host` does.
|
||||||
|
`testing/check-rpm-floor.sh` reads the glibc requirement rpm derived out of
|
||||||
|
the finished package — the table `dnf` enforces at install time — and fails a
|
||||||
|
build above the declared floor. Erase keeps `/etc/fips`: rpm has no purge, so
|
||||||
|
the code that removes a node's identity keys on a dpkg purge would run on an
|
||||||
|
ordinary erase, including the one a distribution upgrade performs.
|
||||||
|
|
||||||
- A pfSense package (`packaging/pfsense/`, `gmake pfsense`). pfSense is
|
- A pfSense package (`packaging/pfsense/`, `gmake pfsense`). pfSense is
|
||||||
FreeBSD underneath, but the FreeBSD package fails there in three
|
FreeBSD underneath, but the FreeBSD package fails there in three
|
||||||
silent ways: pfSense runs only `/usr/local/etc/rc.d/*.sh` at boot and
|
silent ways: pfSense runs only `/usr/local/etc/rc.d/*.sh` at boot and
|
||||||
@@ -328,6 +348,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
binder tearing down and rebinding every second while teardown silently
|
binder tearing down and rebinding every second while teardown silently
|
||||||
declined to abort anything.
|
declined to abort anything.
|
||||||
|
|
||||||
|
#### Packaging
|
||||||
|
|
||||||
|
- The glibc floor is 2.34, one step below the 2.35 Ubuntu 22.04 sets. Both
|
||||||
|
families ship the same binaries, so the project-wide floor is the lowest
|
||||||
|
supported member of either, and that is RHEL 9 and its rebuilds.
|
||||||
|
`packaging/build-floor.env` now records the RPM family alongside the Debian
|
||||||
|
one, and records RHEL 8 as a deliberate exclusion: its glibc is 2.28, and
|
||||||
|
reaching it means a second build image and a second floor. `FIPS_BUILD_IMAGE`
|
||||||
|
is still the oldest Debian-family distribution, which is no longer the oldest
|
||||||
|
distribution outright.
|
||||||
|
|
||||||
#### Packaging (Debian)
|
#### Packaging (Debian)
|
||||||
|
|
||||||
- An upgrade of the `.deb` now reapplies the firewall ruleset in place. Until
|
- An upgrade of the `.deb` now reapplies the firewall ruleset in place. Until
|
||||||
|
|||||||
@@ -132,6 +132,24 @@ default `/etc/fips/fips.yaml` you can edit before starting. The package
|
|||||||
enables `fips` and `fips-dns` but starts neither, which is why the
|
enables `fips` and `fips-dns` but starts neither, which is why the
|
||||||
second command is there.
|
second command is there.
|
||||||
|
|
||||||
|
On Fedora or RHEL, download `fips-mesh-<version>-<release>.x86_64.rpm` (or
|
||||||
|
`.aarch64.rpm`) and install it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install ./fips-mesh-<version>-<release>.x86_64.rpm
|
||||||
|
sudo systemctl start fips fips-dns
|
||||||
|
```
|
||||||
|
|
||||||
|
The package is `fips-mesh` because Fedora's `fips` is an unrelated FITS image
|
||||||
|
viewer that owns `/usr/bin/fips`; the two conflict and dnf will say so.
|
||||||
|
|
||||||
|
It carries the same binaries as the `.deb` — built in the same pinned
|
||||||
|
container, checked against the same glibc floor — and leaves the same
|
||||||
|
post-install state. No install-test suite covers it, and it does not
|
||||||
|
delete `/etc/fips` when removed, because rpm has no purge;
|
||||||
|
[packaging/README.md](packaging/README.md) has the full list of what it
|
||||||
|
does and does not share with the `.deb`.
|
||||||
|
|
||||||
For macOS, Windows, FreeBSD (including a pfSense build under
|
For macOS, Windows, FreeBSD (including a pfSense build under
|
||||||
`packaging/pfsense/`), OpenWrt, the systemd tarball or a Nix
|
`packaging/pfsense/`), OpenWrt, the systemd tarball or a Nix
|
||||||
flake, see [docs/getting-started.md](docs/getting-started.md)
|
flake, see [docs/getting-started.md](docs/getting-started.md)
|
||||||
@@ -200,7 +218,10 @@ and Android. Linux is not one target. Debian, Ubuntu, Arch and NixOS
|
|||||||
are the same glibc build, and what
|
are the same glibc build, and what
|
||||||
differs is the packaging: Debian and Ubuntu take the same `.deb`, Arch
|
differs is the packaging: Debian and Ubuntu take the same `.deb`, Arch
|
||||||
takes `fips` from the AUR, and NixOS uses the Nix flake described
|
takes `fips` from the AUR, and NixOS uses the Nix flake described
|
||||||
below. **Only the `.deb` is exercised by an install test**, by the
|
below, and Fedora and RHEL take the `.rpm` built from the same binaries by
|
||||||
|
`packaging/rpm/`. RPM-based distributions have no column of
|
||||||
|
their own for the same reason pfSense does not: the build is the glibc
|
||||||
|
one and only the packaging differs. **Only the `.deb` is exercised by an install test**, by the
|
||||||
`deb-install` suite across debian12, debian13, ubuntu22, ubuntu24 and
|
`deb-install` suite across debian12, debian13, ubuntu22, ubuntu24 and
|
||||||
ubuntu26; neither the AUR package nor the flake is. That suite runs on
|
ubuntu26; neither the AUR package nor the flake is. That suite runs on
|
||||||
every push and pull request, on x86_64, against a `.deb` built by the same
|
every push and pull request, on x86_64, against a `.deb` built by the same
|
||||||
|
|||||||
+12
-7
@@ -59,6 +59,8 @@ The most direct path. The release distribution carries a
|
|||||||
per-platform installer:
|
per-platform installer:
|
||||||
|
|
||||||
- Debian/Ubuntu: `.deb` package
|
- Debian/Ubuntu: `.deb` package
|
||||||
|
- Fedora/RHEL: `.rpm` package, named `fips-mesh` (Fedora's `fips` is an
|
||||||
|
unrelated FITS image viewer)
|
||||||
- Arch Linux: `fips` AUR package
|
- Arch Linux: `fips` AUR package
|
||||||
- OpenWrt: `.ipk` and `.apk` packages
|
- OpenWrt: `.ipk` and `.apk` packages
|
||||||
- macOS: `.pkg` installer
|
- macOS: `.pkg` installer
|
||||||
@@ -66,13 +68,16 @@ per-platform installer:
|
|||||||
- Windows: `.zip` with service-install scripts
|
- Windows: `.zip` with service-install scripts
|
||||||
- Generic systemd Linux: `.tar.gz` with an `install.sh` script
|
- Generic systemd Linux: `.tar.gz` with an `install.sh` script
|
||||||
|
|
||||||
The `.deb` and the systemd tarball support every version of a glibc
|
The `.deb`, the `.rpm` and the systemd tarball carry the same binaries and
|
||||||
distribution that its vendor still supports for free: currently Ubuntu
|
support every version of a glibc distribution that its vendor still supports
|
||||||
22.04, Debian 12, Ubuntu 24.04, Debian 13 and Ubuntu 26.04. Those binaries
|
for free: currently Ubuntu 22.04, Debian 12, Ubuntu 24.04, Debian 13 and
|
||||||
are built in a container pinned to the oldest of them, so they run on all
|
Ubuntu 26.04 on the Debian side, and RHEL 9 and later on the RPM side. Those
|
||||||
five, and the glibc floor that follows is declared in
|
binaries are built in a container pinned to the oldest Debian-family member,
|
||||||
`packaging/build-floor.env` and checked by `testing/check-glibc-floor.sh` on
|
and the floor they are held to is the lowest of either family — RHEL 9's glibc
|
||||||
what the release workflow produces. Arch and NixOS build from source on your
|
2.34 — declared in `packaging/build-floor.env` and checked by
|
||||||
|
`testing/check-glibc-floor.sh` on what the release workflow produces. The
|
||||||
|
`.rpm` also records that floor as an ordinary dependency, so a package built
|
||||||
|
above it is refused rather than installed. Arch and NixOS build from source on your
|
||||||
own machine, and OpenWrt is a musl target rather than glibc, so none of them
|
own machine, and OpenWrt is a musl target rather than glibc, so none of them
|
||||||
depends on that floor.
|
depends on that floor.
|
||||||
|
|
||||||
|
|||||||
+22
-1
@@ -6,6 +6,8 @@
|
|||||||
# Usage:
|
# Usage:
|
||||||
# make deb Build a Debian/Ubuntu .deb package in the pinned container
|
# make deb Build a Debian/Ubuntu .deb package in the pinned container
|
||||||
# make deb-host Build a .deb with the host toolchain (see below)
|
# make deb-host Build a .deb with the host toolchain (see below)
|
||||||
|
# make rpm Build an .rpm in the pinned container
|
||||||
|
# make rpm-host Build an .rpm with the host toolchain (see below)
|
||||||
# make tarball Build a systemd install tarball
|
# make tarball Build a systemd install tarball
|
||||||
# make ipk Build an OpenWrt .ipk package (opkg, OpenWrt 24.x and earlier)
|
# make ipk Build an OpenWrt .ipk package (opkg, OpenWrt 24.x and earlier)
|
||||||
# make apk Build an OpenWrt .apk package (apk-tools, mandatory on OpenWrt 25+)
|
# make apk Build an OpenWrt .apk package (apk-tools, mandatory on OpenWrt 25+)
|
||||||
@@ -21,7 +23,7 @@ SHELL := /bin/bash
|
|||||||
PACKAGING_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
PACKAGING_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||||
PROJECT_ROOT := $(abspath $(PACKAGING_DIR)/..)
|
PROJECT_ROOT := $(abspath $(PACKAGING_DIR)/..)
|
||||||
|
|
||||||
.PHONY: all deb deb-host tarball ipk apk aur pkg freebsd pfsense zip clean
|
.PHONY: all deb deb-host rpm rpm-host tarball ipk apk aur pkg freebsd pfsense zip clean
|
||||||
|
|
||||||
all: deb tarball
|
all: deb tarball
|
||||||
|
|
||||||
@@ -40,6 +42,25 @@ deb:
|
|||||||
deb-host:
|
deb-host:
|
||||||
@bash $(PACKAGING_DIR)/debian/build-deb.sh
|
@bash $(PACKAGING_DIR)/debian/build-deb.sh
|
||||||
|
|
||||||
|
# `rpm` compiles nothing on the host either: it builds the binaries in the same
|
||||||
|
# pinned container the .deb uses, packages those, and then checks the glibc
|
||||||
|
# requirement rpm derived for the finished package against the declared floor.
|
||||||
|
# So the RPM carries the same objects as the .deb and the tarball, and a
|
||||||
|
# package built above the floor fails here rather than at a user's `dnf
|
||||||
|
# install` -- which is what `deb` gets from its container and its Depends
|
||||||
|
# check.
|
||||||
|
rpm:
|
||||||
|
@bash $(PACKAGING_DIR)/rpm/build-rpm-container.sh
|
||||||
|
|
||||||
|
# `rpm-host` packages whatever the host toolchain built, and like `deb-host` it
|
||||||
|
# is for local iteration and NOT for anything anyone else installs. Nothing
|
||||||
|
# checks its floor, deliberately, so the check stays attached to the artifact
|
||||||
|
# that ships. Its failure is at least loud: rpm derives the requirement from
|
||||||
|
# the binaries, so a package built on a host above the floor is refused by dnf
|
||||||
|
# on an older system rather than installed and unable to start.
|
||||||
|
rpm-host:
|
||||||
|
@bash $(PACKAGING_DIR)/rpm/build-rpm.sh
|
||||||
|
|
||||||
tarball:
|
tarball:
|
||||||
@bash $(PACKAGING_DIR)/systemd/build-tarball.sh
|
@bash $(PACKAGING_DIR)/systemd/build-tarball.sh
|
||||||
|
|
||||||
|
|||||||
+125
-2
@@ -8,6 +8,7 @@ and `make apk` write to `dist/` instead.
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
make deb # Debian/Ubuntu .deb (built in the pinned container)
|
make deb # Debian/Ubuntu .deb (built in the pinned container)
|
||||||
|
make rpm # Fedora/RHEL .rpm, named fips-mesh (built in the pinned container)
|
||||||
make tarball # systemd install tarball
|
make tarball # systemd install tarball
|
||||||
make ipk # OpenWrt .ipk (opkg, OpenWrt 24.x and earlier)
|
make ipk # OpenWrt .ipk (opkg, OpenWrt 24.x and earlier)
|
||||||
make apk # OpenWrt .apk (apk-tools, mandatory on OpenWrt 25+)
|
make apk # OpenWrt .apk (apk-tools, mandatory on OpenWrt 25+)
|
||||||
@@ -22,9 +23,11 @@ make all # deb + tarball (default)
|
|||||||
## The two Debian build paths
|
## The two Debian build paths
|
||||||
|
|
||||||
`make deb` builds in a container pinned to the oldest supported
|
`make deb` builds in a container pinned to the oldest supported
|
||||||
distribution, named with the glibc floor in
|
Debian-family distribution, named with the glibc floor in
|
||||||
[build-floor.env](build-floor.env), and checks the package it produced
|
[build-floor.env](build-floor.env), and checks the package it produced
|
||||||
against that floor before handing it back. Its only host prerequisite is
|
against that floor before handing it back. The floor itself is lower than that
|
||||||
|
image's glibc: RHEL 9 is the lowest supported distribution project-wide, and
|
||||||
|
both families ship these same binaries. Its only host prerequisite is
|
||||||
docker: the toolchain and the build dependencies live in the image. This
|
docker: the toolchain and the build dependencies live in the image. This
|
||||||
is the path the release workflow, the integration suite and the internal
|
is the path the release workflow, the integration suite and the internal
|
||||||
builder all take, so a package that passes locally is built the way the
|
builder all take, so a package that passes locally is built the way the
|
||||||
@@ -74,6 +77,8 @@ packaging/
|
|||||||
common/ Shared assets (default config, hosts file) and pkg-lib.sh,
|
common/ Shared assets (default config, hosts file) and pkg-lib.sh,
|
||||||
the helpers the FreeBSD and pfSense builders share
|
the helpers the FreeBSD and pfSense builders share
|
||||||
debian/ Debian/Ubuntu .deb packaging via cargo-deb
|
debian/ Debian/Ubuntu .deb packaging via cargo-deb
|
||||||
|
rpm/ Fedora/RHEL .rpm packaging via rpmbuild, over the binaries
|
||||||
|
the Debian container build produces
|
||||||
freebsd/ FreeBSD .pkg packaging via pkg-create(8)
|
freebsd/ FreeBSD .pkg packaging via pkg-create(8)
|
||||||
pfsense/ pfSense .pkg packaging (FreeBSD-based, but not the same)
|
pfsense/ pfSense .pkg packaging (FreeBSD-based, but not the same)
|
||||||
macos/ macOS .pkg installer via pkgbuild
|
macos/ macOS .pkg installer via pkgbuild
|
||||||
@@ -118,6 +123,124 @@ sudo dpkg -r fips
|
|||||||
sudo dpkg -P fips
|
sudo dpkg -P fips
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### RPM (`.rpm`)
|
||||||
|
|
||||||
|
Built with `rpmbuild` from [rpm/fips.spec](rpm/fips.spec). The same files land
|
||||||
|
in the same places as the `.deb`, the same `fips` system group is created, the
|
||||||
|
same `/etc/fips/fips.yaml` seeding happens, and the same two units are enabled;
|
||||||
|
`fips-firewall` and `fips-gateway` stay opt-in.
|
||||||
|
|
||||||
|
**The package is named `fips-mesh`, not `fips`.** Fedora's namespace already
|
||||||
|
has a `fips` — an unrelated OpenGL FITS image viewer, currently 3.4.0 — which
|
||||||
|
owns `/usr/bin/fips`. Ours at 0.6.0 would be an *older* `fips` to every RPM
|
||||||
|
tool, so a routine `dnf upgrade` replaces a running mesh node with an image
|
||||||
|
viewer and takes the units with it; that is not hypothetical, it happened
|
||||||
|
within the hour on a test machine. The two cannot coexist either, since both
|
||||||
|
ship `/usr/bin/fips`, so the spec declares `Conflicts: fips` and dnf refuses
|
||||||
|
with both names on screen instead of a bare path.
|
||||||
|
|
||||||
|
Like the Debian package, it has two build paths, and for the same reason.
|
||||||
|
|
||||||
|
`make rpm` compiles nothing on the host: it builds the binaries in the image
|
||||||
|
declared in [build-floor.env](build-floor.env), packages those, and checks the
|
||||||
|
glibc requirement of the finished package against the declared floor. So the
|
||||||
|
RPM carries the same objects as the `.deb` and the tarball, and a package built
|
||||||
|
above the floor fails there rather than at a user's `dnf install`. rpmbuild
|
||||||
|
runs in `FIPS_RPM_BUILD_IMAGE` (AlmaLinux 9, pinned by digest), which supplies
|
||||||
|
two things a build host may lack: rpmbuild itself, and systemd-rpm-macros,
|
||||||
|
without which the spec's `%systemd_post` would not expand and the package would
|
||||||
|
ship scriptlets that quietly do nothing. Docker is the only host prerequisite.
|
||||||
|
|
||||||
|
`make rpm-host` packages whatever the host toolchain built. Like `deb-host` it
|
||||||
|
is for local iteration and not for anything anyone else installs; nothing
|
||||||
|
checks its floor.
|
||||||
|
|
||||||
|
The release workflow calls the same container script both matrix legs, with
|
||||||
|
`--no-build`, over the binaries it has already recovered from the `.deb` — one
|
||||||
|
build, three artifacts — and attaches the result to the GitHub Release next to
|
||||||
|
the `.deb` and the tarball.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Build (requires docker)
|
||||||
|
make rpm
|
||||||
|
|
||||||
|
# Install
|
||||||
|
sudo dnf install ./deploy/fips-mesh-<version>-<release>.<arch>.rpm
|
||||||
|
|
||||||
|
# Remove (keeps /etc/fips, including identity keys)
|
||||||
|
sudo dnf remove fips-mesh
|
||||||
|
```
|
||||||
|
|
||||||
|
Two firewalls, on the distributions where firewalld owns nftables. They do not
|
||||||
|
conflict — firewalld manages its own tables and `fips-firewall.service` adds
|
||||||
|
`table inet fips`, which returns immediately for anything not arriving on
|
||||||
|
`fips0` — but firewalld is filtering the node whether or not that unit ever
|
||||||
|
runs, and in two places worth knowing:
|
||||||
|
|
||||||
|
- **Inbound peers arrive on your ordinary interface**, on the transport ports
|
||||||
|
(`2121/udp` and `8443/tcp` in the shipped config), and those are in whatever
|
||||||
|
zone that interface belongs to. Fedora Workstation's default zone opens
|
||||||
|
`1025-65535` for both protocols, so it works there untouched; RHEL, CentOS
|
||||||
|
Stream and Fedora Server default to `public`, which allows `ssh`,
|
||||||
|
`dhcpv6-client` and `mdns` and nothing else, so a node there accepts no
|
||||||
|
inbound peers until the ports are opened:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo firewall-cmd --permanent --add-port=2121/udp --add-port=8443/tcp
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`fips0` itself lands in the default zone**, since nothing assigns it one —
|
||||||
|
`firewall-cmd --get-zone-of-interface=fips0` says `no zone`, which means the
|
||||||
|
default. Mesh traffic to local services is then subject to that zone as well
|
||||||
|
as to the fips baseline. Giving the interface its own zone keeps the two
|
||||||
|
decisions apart, and `trusted` leaves the filtering to `/etc/fips/fips.nft`
|
||||||
|
and its drop-ins, which is where it is meant to be:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo firewall-cmd --permanent --zone=trusted --change-interface=fips0
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
Either way the fips table stays invisible to firewalld: `firewall-cmd
|
||||||
|
--list-all` will not show it, and opening a port with `firewall-cmd` does not
|
||||||
|
open it in the fips table. That is what `/etc/fips/fips.d/` is for.
|
||||||
|
|
||||||
|
Note also that a default RHEL, CentOS Stream or AlmaLinux install has no
|
||||||
|
resolver backend `fips-dns-setup` can use: systemd is older than the
|
||||||
|
`dns-delegate` drop-in, systemd-resolved is installed but not enabled, and
|
||||||
|
dnsmasq is not installed. The script falls through to its last branch and
|
||||||
|
prints manual instructions, so `.fips` names do not resolve until a backend is
|
||||||
|
in place. Fedora, which enables systemd-resolved, is configured automatically.
|
||||||
|
|
||||||
|
Three things differ from the Debian package, because the package managers do:
|
||||||
|
|
||||||
|
- **The floor is checked on the package, not against it.** `cargo-deb` writes a
|
||||||
|
dependency floor that can disagree with the binaries, so
|
||||||
|
`testing/check-deb-depends.sh` compares the two. rpm derives the requirement
|
||||||
|
from the ELF files and cannot disagree with them, which moves the risk one
|
||||||
|
step back — to binaries built above the floor in the first place.
|
||||||
|
`testing/check-rpm-floor.sh` reads `libc.so.6(GLIBC_x.y)` out of the finished
|
||||||
|
package, the same table `dnf` enforces at install time, and fails the build
|
||||||
|
above the floor.
|
||||||
|
- **No purge.** dpkg distinguishes remove from purge, and `postrm purge`
|
||||||
|
deletes `/etc/fips` and the `fips` group. rpm has no such distinction, so the
|
||||||
|
equivalent would run on an ordinary erase — and during a distribution upgrade
|
||||||
|
that erases and reinstalls — taking the node's identity keys with it.
|
||||||
|
Configuration and keys therefore survive `dnf remove`; delete `/etc/fips`
|
||||||
|
yourself if you mean it.
|
||||||
|
- **Version vs Release.** A dev build is `0.6.0-0.dev.git<date>.<sha>` rather
|
||||||
|
than the `.deb`'s `0.6.0~dev+git<date>.<sha>-1`. rpm has understood `~` since
|
||||||
|
4.10, so this is a choice rather than a limitation: a Release beginning with
|
||||||
|
`0.` is the convention for pre-release packages in this ecosystem, and it
|
||||||
|
sorts below the `1` a tagged release carries. The Release carries no `%{dist}` tag
|
||||||
|
either: there is one build, the glibc one, and a dist tag would name whichever
|
||||||
|
image happened to run rpmbuild in an artifact that installs on all of them.
|
||||||
|
|
||||||
|
No install-test suite covers the RPM. The `deb-install` suite exercises the
|
||||||
|
`.deb` across five distributions on every push; the RPM is built on every push
|
||||||
|
and installed by nobody but you.
|
||||||
|
|
||||||
### systemd Tarball
|
### systemd Tarball
|
||||||
|
|
||||||
A self-contained tarball with binaries and an `install.sh` script for
|
A self-contained tarball with binaries and an `install.sh` script for
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
# The glibc floor for the Linux release artifacts, and the image that produces it.
|
# The glibc floor for the Linux release artifacts, and the image that produces it.
|
||||||
#
|
#
|
||||||
# Sourced by packaging/debian/build-deb-container.sh and by
|
# Sourced by packaging/debian/build-deb-container.sh,
|
||||||
# testing/check-glibc-floor.sh. It exists so the floor is a decision written
|
# packaging/rpm/build-rpm-container.sh, testing/check-glibc-floor.sh and
|
||||||
# down in one place rather than a side effect of whichever build host ran last.
|
# testing/check-rpm-floor.sh. It exists so the floor is a decision written
|
||||||
|
# down in one place rather than a side effect of whichever build host ran
|
||||||
|
# last.
|
||||||
#
|
#
|
||||||
# The rule it encodes: FIPS installs on every version of a supported operating
|
# The rule it encodes: FIPS installs on every version of a supported operating
|
||||||
# system that its distributor still supports for free. As of 2026-09-05 that is
|
# system that its distributor still supports for free. As of 2026-09-05 that is
|
||||||
@@ -13,8 +15,31 @@
|
|||||||
# Debian 13 glibc 2.41 LTS ends 2030-06-30
|
# Debian 13 glibc 2.41 LTS ends 2030-06-30
|
||||||
# Ubuntu 26.04 glibc 2.43
|
# Ubuntu 26.04 glibc 2.43
|
||||||
#
|
#
|
||||||
# so the lowest is Ubuntu 22.04 and the floor is its 2.35. Debian 11 left the
|
# and on the RPM side, where the same binaries ship as fips-mesh:
|
||||||
# set on 2026-08-31 and is deliberately not counted.
|
#
|
||||||
|
# RHEL 9 and rebuilds glibc 2.34 AlmaLinux/Rocky 9 supported to 2032-05
|
||||||
|
# Fedora (current two) glibc 2.42+ each release supported ~13 months
|
||||||
|
#
|
||||||
|
# so the lowest of both families is RHEL 9 and the floor is its 2.34. Debian 11
|
||||||
|
# left the set on 2026-08-31 and is deliberately not counted. openSUSE is not
|
||||||
|
# in the set yet: nobody has run the package on Leap, and it joins when an
|
||||||
|
# install leg does.
|
||||||
|
#
|
||||||
|
# RHEL 8 and its rebuilds are deliberately NOT in the set. Their glibc is 2.28
|
||||||
|
# and AlmaLinux 8 and Rocky 8 are in free support until 2029-05, so this is a
|
||||||
|
# real exclusion rather than an oversight: reaching 2.28 means building on an
|
||||||
|
# EL8-era toolchain, which is a second build image and a second floor, and no
|
||||||
|
# one has asked for it. If someone does, that is the decision to reopen -- not
|
||||||
|
# this number.
|
||||||
|
#
|
||||||
|
# The RPM family is why the floor is 2.34 rather than Ubuntu 22.04's 2.35: both
|
||||||
|
# families ship the same binaries, so the project-wide floor is the lowest
|
||||||
|
# member of either, and that is RHEL 9. The binaries built in FIPS_BUILD_IMAGE
|
||||||
|
# happen to reference nothing above 2.34 today, which is what lets a package
|
||||||
|
# built there install on RHEL 9 at all; without this line that is luck, and the
|
||||||
|
# first commit to pull in a 2.35 symbol would pass the check and silently drop
|
||||||
|
# every EL9 host. One step tighter costs the Debian side nothing and makes the
|
||||||
|
# EL9 claim enforced.
|
||||||
#
|
#
|
||||||
# Deliberately NOT a GitHub runner label. Runner availability follows GitHub's
|
# Deliberately NOT a GitHub runner label. Runner availability follows GitHub's
|
||||||
# rule of supporting the newest two images; the floor follows distributors'
|
# rule of supporting the newest two images; the floor follows distributors'
|
||||||
@@ -26,10 +51,36 @@
|
|||||||
# Changing FIPS_GLIBC_FLOOR drops support for every distribution below it. Check
|
# Changing FIPS_GLIBC_FLOOR drops support for every distribution below it. Check
|
||||||
# the table above first, and expect check-glibc-floor.sh to hold you to it.
|
# the table above first, and expect check-glibc-floor.sh to hold you to it.
|
||||||
|
|
||||||
# Base image for the build. Pinned to the oldest supported distribution.
|
# Base image for the build. The oldest supported *Debian-family* distribution,
|
||||||
|
# which is no longer the oldest supported distribution outright: RHEL 9 sits a
|
||||||
|
# step below it at 2.34, and FIPS_GLIBC_FLOOR rather than this image is what
|
||||||
|
# the binaries are held to.
|
||||||
FIPS_BUILD_IMAGE="ubuntu:22.04"
|
FIPS_BUILD_IMAGE="ubuntu:22.04"
|
||||||
|
|
||||||
|
# Image that runs rpmbuild. It compiles nothing -- the binaries it packages are
|
||||||
|
# built in FIPS_BUILD_IMAGE -- and supplies two things the build host may not
|
||||||
|
# have: rpmbuild itself, and systemd-rpm-macros, without which the spec's
|
||||||
|
# %systemd_post would not expand and the package would ship scriptlets that
|
||||||
|
# quietly do nothing. The oldest rpm in free support (RHEL 9, rpm 4.16), so a
|
||||||
|
# package it writes is readable by every newer rpm, which is this file's glibc
|
||||||
|
# rule applied to the packaging format.
|
||||||
|
#
|
||||||
|
# Pinned by digest, not by tag. `almalinux:9` floats: it moves with every
|
||||||
|
# rebuild, and the systemd-rpm-macros it carries is what expands %systemd_post
|
||||||
|
# into the scriptlets a release artifact ships. A floating input to a release
|
||||||
|
# artifact is the thing this file exists to prevent, and the workflow that
|
||||||
|
# consumes it runs on three branches, every pull request and every tag. The
|
||||||
|
# digest is a multi-arch index, so both matrix legs resolve their own
|
||||||
|
# architecture from it.
|
||||||
|
#
|
||||||
|
# To move it: `docker buildx imagetools inspect almalinux:9 --format
|
||||||
|
# '{{.Manifest.Digest}}'`, and check that the rpm inside is still old enough
|
||||||
|
# for the distributions in the table above.
|
||||||
|
FIPS_RPM_BUILD_IMAGE="almalinux@sha256:3a3fa7f043b142bc8008c8b308d39b47d2c84008addcd52f9f9a7a82d2a90474"
|
||||||
|
|
||||||
# Highest glibc symbol version any shipped binary may require. Building on
|
# Highest glibc symbol version any shipped binary may require. Building on
|
||||||
# FIPS_BUILD_IMAGE currently yields 2.34, one step below this, so there is a
|
# FIPS_BUILD_IMAGE currently yields exactly this, so there is no headroom left:
|
||||||
# little headroom: the check is an upper bound, not an equality.
|
# the check is an upper bound, and the build sits on it. A change that raises
|
||||||
FIPS_GLIBC_FLOOR="2.35"
|
# what the binaries need will fail check-glibc-floor.sh rather than ship a
|
||||||
|
# package RHEL 9 refuses.
|
||||||
|
FIPS_GLIBC_FLOOR="2.34"
|
||||||
|
|||||||
Executable
+265
@@ -0,0 +1,265 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Build the RPM from binaries compiled in the pinned container, then check the
|
||||||
|
# floor of the package it produced.
|
||||||
|
#
|
||||||
|
# This is the supported path, and the counterpart of
|
||||||
|
# packaging/debian/build-deb-container.sh. Both exist for the same reason: a
|
||||||
|
# package built against the host's C library carries that library's version
|
||||||
|
# floor, and the host is almost never the oldest system the package has to
|
||||||
|
# install on. The Debian package answered that with a pinned build image; this
|
||||||
|
# reuses that image rather than pinning a second one, so the RPM ships the same
|
||||||
|
# objects the .deb and the tarball do.
|
||||||
|
#
|
||||||
|
# rpmbuild itself runs in FIPS_RPM_BUILD_IMAGE, which compiles nothing. It is
|
||||||
|
# there because the build host may have no rpmbuild at all, and -- the part
|
||||||
|
# that would fail quietly -- may have no systemd-rpm-macros, without which the
|
||||||
|
# spec's %systemd_post does not expand and the package ships scriptlets that do
|
||||||
|
# nothing.
|
||||||
|
#
|
||||||
|
# Usage: build-rpm-container.sh [--output-dir DIR] [--version V] [--features L]
|
||||||
|
# [--no-build] [--bin-dir DIR]
|
||||||
|
#
|
||||||
|
# --no-build packages the binaries already under target/release instead of
|
||||||
|
# building any, for a caller that has them: the release workflow recovers them
|
||||||
|
# from the .deb it just built, and building them twice would only be slower.
|
||||||
|
# --bin-dir says where those binaries are, if not target/release.
|
||||||
|
#
|
||||||
|
# --features reaches cargo through the Debian container build and then marks
|
||||||
|
# the Release, so a feature build of a commit is a different package from the
|
||||||
|
# default build of the same commit.
|
||||||
|
#
|
||||||
|
# Requires docker. Nothing else: no rust toolchain, no rpmbuild, no dpkg.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
# shellcheck source=../build-floor.env
|
||||||
|
. "$REPO_ROOT/packaging/build-floor.env"
|
||||||
|
# shellcheck source=SCRIPTDIR/../../testing/lib/image-build.sh
|
||||||
|
. "$REPO_ROOT/testing/lib/image-build.sh"
|
||||||
|
|
||||||
|
DEST_DIR="$REPO_ROOT/deploy"
|
||||||
|
VERSION=""
|
||||||
|
FEATURES=""
|
||||||
|
NO_BUILD=0
|
||||||
|
BIN_DIR=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--output-dir) DEST_DIR="${2:?missing value for --output-dir}"; shift 2 ;;
|
||||||
|
--version) VERSION="${2:?missing value for --version}"; shift 2 ;;
|
||||||
|
--features) FEATURES="${2:?missing value for --features}"; shift 2 ;;
|
||||||
|
--no-build) NO_BUILD=1; shift ;;
|
||||||
|
--bin-dir) BIN_DIR="${2:?missing value for --bin-dir}"; NO_BUILD=1; shift 2 ;;
|
||||||
|
-h | --help) sed -n '2,28p' "$0"; exit 0 ;;
|
||||||
|
*) echo "Unknown option: $1" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
command -v docker >/dev/null 2>&1 || {
|
||||||
|
echo "build-rpm-container: docker is required and was not found." >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
# The same refusal build-rpm.sh makes, and for the same reason: with no build
|
||||||
|
# of our own the features cannot reach cargo, so the Release marking below
|
||||||
|
# would claim binaries that were compiled by somebody else, with who knows
|
||||||
|
# what. Refused here rather than after the container build that --no-build was
|
||||||
|
# asked to skip.
|
||||||
|
if [ -n "$FEATURES" ] && [ "$NO_BUILD" -eq 1 ]; then
|
||||||
|
echo "build-rpm-container: --features cannot be combined with --no-build or" >&2
|
||||||
|
echo "--bin-dir: the features would not reach the binaries, but the Release" >&2
|
||||||
|
echo "would claim they had." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$DEST_DIR"
|
||||||
|
DEST_ABS="$(cd "$DEST_DIR" && pwd)"
|
||||||
|
|
||||||
|
# Both scratch directories live inside the output directory rather than under
|
||||||
|
# /tmp, which is the shape build-deb-container.sh takes and for the same
|
||||||
|
# reason: a bind-mount source is resolved by the Docker daemon in the host's
|
||||||
|
# mount namespace, so under a private /tmp -- systemd's PrivateTmp=, which the
|
||||||
|
# CI worker sets -- a path from a bare `mktemp -d` exists only in this
|
||||||
|
# process's namespace. The daemon would create its own directory at that path
|
||||||
|
# in the host's /tmp, the container would write there, and this script would
|
||||||
|
# read an empty one. The output directory is already bind-mounted as /out and
|
||||||
|
# so resolves the same way in both namespaces.
|
||||||
|
#
|
||||||
|
# The traps clear them on any ordinary exit but not on a SIGKILL, and the
|
||||||
|
# builder's watch loop group-kills a run that overruns or is superseded, so
|
||||||
|
# sweep siblings old enough that no live run can own them.
|
||||||
|
find "$DEST_ABS" -maxdepth 1 -type d \( -name '.name.*' -o -name '.stage.*' \) \
|
||||||
|
-mmin +120 -exec rm -rf {} + 2>/dev/null || :
|
||||||
|
|
||||||
|
STAGE=""
|
||||||
|
# The body is last, not the test: written as `[ -n "$STAGE" ] && rm -rf ...`,
|
||||||
|
# an unset STAGE makes the test the handler's final command, the handler
|
||||||
|
# returns 1, and from an EXIT trap under `set -e` that becomes the script's
|
||||||
|
# exit status.
|
||||||
|
cleanup() {
|
||||||
|
if [ -n "$STAGE" ]; then
|
||||||
|
rm -rf "$STAGE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [ "$NO_BUILD" -eq 0 ]; then
|
||||||
|
# Build the .deb in the pinned container and package the binaries out of
|
||||||
|
# it. That is one build rather than two, and it is the build that
|
||||||
|
# build-deb-container.sh has already run the glibc floor and Depends checks
|
||||||
|
# on, so the RPM cannot carry objects those checks never saw.
|
||||||
|
STAGE=$(mktemp -d "$DEST_ABS/.stage.XXXXXX") || {
|
||||||
|
echo "build-rpm-container: could not create a staging directory in $DEST_ABS" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
DEB_DIR="$STAGE/deb"
|
||||||
|
BIN_DIR="$STAGE/bin"
|
||||||
|
mkdir -p "$DEB_DIR" "$BIN_DIR"
|
||||||
|
|
||||||
|
deb_args=(--output-dir "$DEB_DIR")
|
||||||
|
[ -n "$VERSION" ] && deb_args+=(--version "$VERSION")
|
||||||
|
[ -n "$FEATURES" ] && deb_args+=(--features "$FEATURES")
|
||||||
|
|
||||||
|
echo "=== Building the binaries in the pinned container ===" >&2
|
||||||
|
DEB=$("$REPO_ROOT/packaging/debian/build-deb-container.sh" "${deb_args[@]}" | tail -n 1)
|
||||||
|
[ -f "$DEB" ] || {
|
||||||
|
echo "build-rpm-container: the Debian build did not produce a package" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# dpkg-deb lives in the build image, not necessarily on a host that wants
|
||||||
|
# an RPM -- a Fedora workstation has no dpkg at all.
|
||||||
|
BUILD_IMAGE=$("$REPO_ROOT/packaging/debian/build-deb-container.sh" --print-image-tag)
|
||||||
|
docker run --rm \
|
||||||
|
-v "$DEB_DIR":/deb:ro \
|
||||||
|
-v "$BIN_DIR":/bin-out \
|
||||||
|
-e "HOST_UID=$(id -u)" -e "HOST_GID=$(id -g)" \
|
||||||
|
"$BUILD_IMAGE" \
|
||||||
|
bash -euo pipefail -c '
|
||||||
|
unpack=$(mktemp -d)
|
||||||
|
dpkg-deb -x /deb/*.deb "$unpack"
|
||||||
|
for binary in fips fipsctl fipstop fips-gateway; do
|
||||||
|
install -m 0755 "$unpack/usr/bin/$binary" "/bin-out/$binary"
|
||||||
|
done
|
||||||
|
chown "$HOST_UID:$HOST_GID" /bin-out/*
|
||||||
|
' >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
: "${BIN_DIR:=$REPO_ROOT/target/release}"
|
||||||
|
BIN_DIR="$(cd "$BIN_DIR" && pwd)"
|
||||||
|
|
||||||
|
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git -C "$REPO_ROOT" log -1 --format=%ct)}"
|
||||||
|
|
||||||
|
# The version is derived on the host and passed in, because a worktree's .git
|
||||||
|
# is a file pointing outside the mount and git in the container cannot read it.
|
||||||
|
# Same reasoning as the Debian container build.
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
CRATE_VERSION=$(awk -F'"' '/^version = /{print $2; exit}' "$REPO_ROOT/Cargo.toml")
|
||||||
|
if [[ "$CRATE_VERSION" == *-dev ]]; then
|
||||||
|
GIT_DATE=$(git -C "$REPO_ROOT" log -1 --format=%cs | tr -d '-')
|
||||||
|
GIT_SHA=$(git -C "$REPO_ROOT" rev-parse --short HEAD)
|
||||||
|
DIRTY=""
|
||||||
|
[ -n "$(git -C "$REPO_ROOT" status --porcelain 2>/dev/null)" ] && DIRTY=".dirty"
|
||||||
|
VERSION="${CRATE_VERSION%-dev}-0.dev.git${GIT_DATE}.${GIT_SHA}${DIRTY}"
|
||||||
|
else
|
||||||
|
VERSION="$CRATE_VERSION"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# `docker run` pulls the image implicitly on a miss, and that pull is not
|
||||||
|
# retried by anything. It reaches a registry on every runner that has not seen
|
||||||
|
# the digest before, which is every fresh one. retry_build is what the Debian
|
||||||
|
# builder image uses, so a pull that fails and then succeeds leaves a warning
|
||||||
|
# on the run rather than passing silently.
|
||||||
|
if ! docker image inspect "$FIPS_RPM_BUILD_IMAGE" >/dev/null 2>&1; then
|
||||||
|
retry_build "docker pull $FIPS_RPM_BUILD_IMAGE" \
|
||||||
|
docker pull --quiet "$FIPS_RPM_BUILD_IMAGE" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== Packaging fips $VERSION in $FIPS_RPM_BUILD_IMAGE ===" >&2
|
||||||
|
|
||||||
|
NAME_DIR=$(mktemp -d "$DEST_ABS/.name.XXXXXX") || {
|
||||||
|
echo "build-rpm-container: could not create a name directory in $DEST_ABS" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
trap 'cleanup; rm -rf "$NAME_DIR"' EXIT
|
||||||
|
|
||||||
|
# The features reached cargo in the build above, so the binaries already have
|
||||||
|
# them; what is left is to say so in the Release. build-rpm.sh refuses
|
||||||
|
# --features with --no-build for exactly that reason -- the flag would promise
|
||||||
|
# a build it is not doing -- so the marker is folded into the version here
|
||||||
|
# instead of passed inward.
|
||||||
|
if [ -n "$FEATURES" ]; then
|
||||||
|
MARKER="features.$(printf '%s' "$FEATURES" | tr -c 'a-zA-Z0-9.' '.')"
|
||||||
|
case "$VERSION" in
|
||||||
|
*-*) VERSION="${VERSION}.${MARKER}" ;;
|
||||||
|
*) VERSION="${VERSION}-1.${MARKER}" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
rpm_args=(--no-build --bin-dir /bins --version "$VERSION" --output-dir /out --name-file /name/rpm)
|
||||||
|
|
||||||
|
docker run --rm \
|
||||||
|
-v "$REPO_ROOT":/src:ro \
|
||||||
|
-v "$BIN_DIR":/bins:ro \
|
||||||
|
-v "$DEST_ABS":/out \
|
||||||
|
-v "$NAME_DIR":/name \
|
||||||
|
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||||
|
-e "HOST_UID=$(id -u)" -e "HOST_GID=$(id -g)" \
|
||||||
|
-w /src \
|
||||||
|
"$FIPS_RPM_BUILD_IMAGE" \
|
||||||
|
bash -euo pipefail -c "
|
||||||
|
# Retried: this reaches a mirror, and a transient failure here would
|
||||||
|
# fail a release build that has nothing wrong with it. The Debian
|
||||||
|
# builder image gets the same treatment one layer up, in
|
||||||
|
# testing/lib/image-build.sh.
|
||||||
|
for attempt in 1 2 3; do
|
||||||
|
if dnf install -y --setopt=install_weak_deps=False rpm-build systemd-rpm-macros >/dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ \"\$attempt\" -eq 3 ]; then
|
||||||
|
echo 'could not install rpm-build and systemd-rpm-macros' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep \$((attempt * 5))
|
||||||
|
done
|
||||||
|
packaging/rpm/build-rpm.sh ${rpm_args[*]}
|
||||||
|
chown \"\$HOST_UID:\$HOST_GID\" /name/rpm
|
||||||
|
" >&2
|
||||||
|
|
||||||
|
RPM_NAME=""
|
||||||
|
[ -f "$NAME_DIR/rpm" ] && RPM_NAME=$(head -n 1 "$NAME_DIR/rpm")
|
||||||
|
[ -n "$RPM_NAME" ] || {
|
||||||
|
echo "build-rpm-container: the build did not name its package" >&2
|
||||||
|
echo "build-rpm-container: the name travels through $NAME_DIR, bind-mounted as /name." >&2
|
||||||
|
echo "build-rpm-container: if that path is not visible to the Docker daemon -- a private" >&2
|
||||||
|
echo "build-rpm-container: /tmp is the usual cause -- the container wrote the name elsewhere." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if [[ "$RPM_NAME" == */* || "$RPM_NAME" != fips-mesh-*.rpm ]]; then
|
||||||
|
echo "build-rpm-container: the build named '$RPM_NAME', which is not a package file name" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RPM="$DEST_ABS/$RPM_NAME"
|
||||||
|
[ -f "$RPM" ] || {
|
||||||
|
echo "build-rpm-container: the build named $RPM_NAME but $RPM does not exist" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check the artifact, not its inputs. rpm records the requirement it derived
|
||||||
|
# from the binaries, which is the table dnf enforces at install time, so this
|
||||||
|
# reads what a user's package manager will read. It runs in the rpm image
|
||||||
|
# because the host may have no rpm.
|
||||||
|
docker run --rm \
|
||||||
|
-v "$REPO_ROOT":/src:ro \
|
||||||
|
-v "$DEST_ABS":/out:ro \
|
||||||
|
-w /src \
|
||||||
|
"$FIPS_RPM_BUILD_IMAGE" \
|
||||||
|
testing/check-rpm-floor.sh "/out/$RPM_NAME" >&2
|
||||||
|
|
||||||
|
echo "=== Built $RPM ===" >&2
|
||||||
|
printf '%s\n' "$RPM"
|
||||||
Executable
+264
@@ -0,0 +1,264 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build an .rpm package for FIPS.
|
||||||
|
#
|
||||||
|
# The counterpart of packaging/debian/build-deb.sh, and deliberately the same
|
||||||
|
# shape: the same options, the same dev-version derivation, the same output in
|
||||||
|
# deploy/. cargo-deb builds the binaries itself; here cargo builds them and
|
||||||
|
# rpmbuild packages what it produced, so one cargo invocation stays the only
|
||||||
|
# thing that compiles FIPS.
|
||||||
|
#
|
||||||
|
# Usage: ./build-rpm.sh [--target <triple>] [--version <version>] [--no-build]
|
||||||
|
# [--features <list>] [--output-dir <dir>]
|
||||||
|
# [--name-file <path>] [--bin-dir <dir>]
|
||||||
|
#
|
||||||
|
# Prerequisites: rpm-build and systemd-rpm-macros.
|
||||||
|
# Output: deploy/fips-mesh-<version>-<release>.<arch>.rpm
|
||||||
|
#
|
||||||
|
# "fips-mesh", not "fips": Fedora's namespace has a `fips` package already (an
|
||||||
|
# unrelated FITS image viewer), and ours would look like an old version of it.
|
||||||
|
# See the header of fips.spec.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||||
|
SPEC="${SCRIPT_DIR}/fips.spec"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: packaging/rpm/build-rpm.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--target <triple> Rust target triple to build/package
|
||||||
|
--version <version> Override the RPM Version-Release. Accepts either
|
||||||
|
"<version>" or "<version>-<release>".
|
||||||
|
--no-build Package existing binaries without running cargo build
|
||||||
|
--features <list> Cargo features to build with (comma-separated). Marks the
|
||||||
|
auto-derived Release so the package is distinguishable
|
||||||
|
from a default build of the same commit.
|
||||||
|
--output-dir <dir> Where to put the finished .rpm. Defaults to deploy/ under
|
||||||
|
the project root.
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
HOST_UID, HOST_GID Give the finished package to this owner. Set by a
|
||||||
|
container build, whose root would otherwise leave a file
|
||||||
|
on the mounted tree that its owner cannot remove.
|
||||||
|
--name-file <path> Also write the finished package's file name (basename
|
||||||
|
only) to <path>.
|
||||||
|
--bin-dir <dir> Package the binaries in <dir> rather than the ones under
|
||||||
|
target/. Implies --no-build. This is how the container
|
||||||
|
build packages binaries compiled somewhere else.
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
TARGET_TRIPLE=""
|
||||||
|
VERSION_OVERRIDE=""
|
||||||
|
BIN_DIR_OVERRIDE=""
|
||||||
|
NO_BUILD=0
|
||||||
|
FEATURES=""
|
||||||
|
DEST_DIR=""
|
||||||
|
NAME_FILE=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--target)
|
||||||
|
TARGET_TRIPLE="${2:?missing value for --target}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--version)
|
||||||
|
VERSION_OVERRIDE="${2:?missing value for --version}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--no-build)
|
||||||
|
NO_BUILD=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--features)
|
||||||
|
FEATURES="${2:?missing value for --features}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--output-dir)
|
||||||
|
DEST_DIR="${2:?missing value for --output-dir}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--name-file)
|
||||||
|
NAME_FILE="${2:?missing value for --name-file}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--bin-dir)
|
||||||
|
BIN_DIR_OVERRIDE="${2:?missing value for --bin-dir}"
|
||||||
|
NO_BUILD=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Same refusal as the Debian build, for the same reason: a feature build that
|
||||||
|
# skips the build step would stamp a feature-marked Release onto whatever
|
||||||
|
# binaries already sit in target/, which is the one outcome the marking exists
|
||||||
|
# to prevent.
|
||||||
|
if [[ -n "${FEATURES}" && "${NO_BUILD}" -eq 1 ]]; then
|
||||||
|
echo "--features cannot be combined with --no-build: the features would not" >&2
|
||||||
|
echo "reach the binaries, but the Release would claim they had." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "${PROJECT_ROOT}"
|
||||||
|
|
||||||
|
if ! command -v rpmbuild &>/dev/null; then
|
||||||
|
echo "rpmbuild not found. Install the rpm-build package." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reproducible builds, as on the Debian side.
|
||||||
|
if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then
|
||||||
|
SOURCE_DATE_EPOCH="$(git log -1 --format=%ct)"
|
||||||
|
export SOURCE_DATE_EPOCH
|
||||||
|
fi
|
||||||
|
|
||||||
|
CRATE_VERSION=$(awk -F'"' '/^version = /{print $2; exit}' Cargo.toml)
|
||||||
|
|
||||||
|
if [[ -n "${VERSION_OVERRIDE}" ]]; then
|
||||||
|
# Accept "<version>" or "<version>-<release>".
|
||||||
|
RPM_VERSION="${VERSION_OVERRIDE%%-*}"
|
||||||
|
if [[ "${VERSION_OVERRIDE}" == *-* ]]; then
|
||||||
|
RPM_RELEASE="${VERSION_OVERRIDE#*-}"
|
||||||
|
else
|
||||||
|
RPM_RELEASE="1"
|
||||||
|
fi
|
||||||
|
elif [[ "${CRATE_VERSION}" == *-dev ]]; then
|
||||||
|
# A dev build gets a Release that sorts BELOW the eventual tagged release
|
||||||
|
# and differs between commits, so `dnf upgrade` on one dev package
|
||||||
|
# installed over another is not a silent no-op. rpm has understood "~"
|
||||||
|
# since 4.10 and the Debian version uses it; a Release beginning with 0. is
|
||||||
|
# the convention for pre-release packages here and is what this picks.
|
||||||
|
RPM_VERSION="${CRATE_VERSION%-dev}"
|
||||||
|
GIT_DATE=$(git log -1 --format=%cs | tr -d '-')
|
||||||
|
GIT_SHA=$(git rev-parse --short HEAD)
|
||||||
|
RPM_RELEASE="0.dev.git${GIT_DATE}.${GIT_SHA}"
|
||||||
|
if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then
|
||||||
|
RPM_RELEASE="${RPM_RELEASE}.dirty"
|
||||||
|
fi
|
||||||
|
# A feature build of a commit is a different package from the default
|
||||||
|
# build of the same commit, and nothing else in the version says so. The
|
||||||
|
# suffix sorts above the unsuffixed build, so installing a feature build
|
||||||
|
# is an upgrade and going back is a downgrade — which dnf refuses unless
|
||||||
|
# told; use `dnf downgrade` or `rpm -U --oldpackage`.
|
||||||
|
if [[ -n "${FEATURES}" ]]; then
|
||||||
|
RPM_RELEASE="${RPM_RELEASE}.features.$(printf '%s' "${FEATURES}" | tr -c 'a-zA-Z0-9.' '.')"
|
||||||
|
fi
|
||||||
|
echo "Auto-derived dev Version-Release: ${RPM_VERSION}-${RPM_RELEASE}"
|
||||||
|
else
|
||||||
|
RPM_VERSION="${CRATE_VERSION}"
|
||||||
|
RPM_RELEASE="1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the binaries, unless we were told they are already there.
|
||||||
|
if [[ "${NO_BUILD}" -eq 0 ]]; then
|
||||||
|
cargo_args=(build --release)
|
||||||
|
[[ -n "${TARGET_TRIPLE}" ]] && cargo_args+=(--target "${TARGET_TRIPLE}")
|
||||||
|
[[ -n "${FEATURES}" ]] && cargo_args+=(--features "${FEATURES}")
|
||||||
|
echo "Building binaries..."
|
||||||
|
cargo "${cargo_args[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${BIN_DIR_OVERRIDE}" ]]; then
|
||||||
|
BIN_DIR="$(cd "${BIN_DIR_OVERRIDE}" && pwd)"
|
||||||
|
RPM_ARCH="$(rpm --eval '%{_target_cpu}')"
|
||||||
|
elif [[ -n "${TARGET_TRIPLE}" ]]; then
|
||||||
|
BIN_DIR="${PROJECT_ROOT}/target/${TARGET_TRIPLE}/release"
|
||||||
|
# rpm names architectures its own way; map the ones we cross-build for.
|
||||||
|
case "${TARGET_TRIPLE}" in
|
||||||
|
x86_64-*) RPM_ARCH="x86_64" ;;
|
||||||
|
aarch64-*) RPM_ARCH="aarch64" ;;
|
||||||
|
armv7-*) RPM_ARCH="armv7hl" ;;
|
||||||
|
riscv64-*) RPM_ARCH="riscv64" ;;
|
||||||
|
*)
|
||||||
|
echo "Unknown target triple for rpm: ${TARGET_TRIPLE}" >&2
|
||||||
|
echo "Add it to the case in $(basename "$0")." >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
BIN_DIR="${PROJECT_ROOT}/target/release"
|
||||||
|
RPM_ARCH="$(rpm --eval '%{_target_cpu}')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for binary in fips fipsctl fipstop fips-gateway; do
|
||||||
|
if [[ ! -x "${BIN_DIR}/${binary}" ]]; then
|
||||||
|
echo "Missing ${BIN_DIR}/${binary}." >&2
|
||||||
|
[[ "${NO_BUILD}" -eq 1 ]] && echo "Drop --no-build, or build first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
TOP_DIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "${TOP_DIR}"' EXIT
|
||||||
|
mkdir -p "${TOP_DIR}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
|
|
||||||
|
echo "Building .rpm package..."
|
||||||
|
# The Release carries no %{dist} tag. A dist tag says which distribution's
|
||||||
|
# build of a package this is, and there is one build: the same glibc binaries
|
||||||
|
# the .deb and the tarball ship, packaged. Leaving it in would name whichever
|
||||||
|
# image or host happened to run rpmbuild (.el9 from the release build, .fc44
|
||||||
|
# from a developer's) in an artifact that installs on all of them.
|
||||||
|
rpmbuild -bb "${SPEC}" \
|
||||||
|
--target "${RPM_ARCH}" \
|
||||||
|
--define "dist %{nil}" \
|
||||||
|
--define "_topdir ${TOP_DIR}" \
|
||||||
|
--define "_sourcedir ${PROJECT_ROOT}" \
|
||||||
|
--define "fips_srcdir ${PROJECT_ROOT}" \
|
||||||
|
--define "fips_bindir ${BIN_DIR}" \
|
||||||
|
--define "fips_version ${RPM_VERSION}" \
|
||||||
|
--define "fips_release ${RPM_RELEASE}" \
|
||||||
|
--quiet
|
||||||
|
|
||||||
|
: "${DEST_DIR:=deploy}"
|
||||||
|
mkdir -p "${DEST_DIR}"
|
||||||
|
RPM_FILE=$(find "${TOP_DIR}/RPMS" -name '*.rpm' -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)
|
||||||
|
|
||||||
|
if [ -z "${RPM_FILE}" ]; then
|
||||||
|
echo "Error: No .rpm file found under ${TOP_DIR}/RPMS" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "${RPM_FILE}" "${DEST_DIR}/"
|
||||||
|
BASENAME=$(basename "${RPM_FILE}")
|
||||||
|
|
||||||
|
# A container build runs as root on a mounted source tree, which would leave a
|
||||||
|
# package its owner cannot delete without sudo. HOST_UID/HOST_GID say who asked
|
||||||
|
# for it; unset (the ordinary case, building as yourself) changes nothing.
|
||||||
|
if [[ -n "${HOST_UID:-}" && -n "${HOST_GID:-}" ]]; then
|
||||||
|
chown "${HOST_UID}:${HOST_GID}" "${DEST_DIR}/${BASENAME}"
|
||||||
|
fi
|
||||||
|
if [[ -n "${NAME_FILE}" ]]; then
|
||||||
|
printf '%s\n' "${BASENAME}" > "${NAME_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# dnf needs a path it can tell from a package name, so a relative one gets a
|
||||||
|
# "./" and an absolute one is already unambiguous.
|
||||||
|
OUT_PATH="${DEST_DIR}/${BASENAME}"
|
||||||
|
case "${OUT_PATH}" in
|
||||||
|
/*) INSTALL_PATH="${OUT_PATH}" ;;
|
||||||
|
*) INSTALL_PATH="./${OUT_PATH}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "Package built: ${OUT_PATH}"
|
||||||
|
echo ""
|
||||||
|
echo "Install with: sudo dnf install ${INSTALL_PATH}"
|
||||||
|
echo "Remove with: sudo dnf remove fips-mesh (keeps /etc/fips and its identity keys)"
|
||||||
|
|
||||||
|
# The last line of stdout is the package path, and nothing may follow it: the
|
||||||
|
# release workflow reads it to learn which package this run produced, exactly
|
||||||
|
# as it does with build-deb-container.sh.
|
||||||
|
echo "${OUT_PATH}"
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
# FIPS RPM packaging.
|
||||||
|
#
|
||||||
|
# The counterpart of packaging/debian: the same files land in the same places,
|
||||||
|
# the same group is created, the same config is seeded, and the same two units
|
||||||
|
# are enabled. Where the two package managers differ, the differences are
|
||||||
|
# marked below rather than smoothed over.
|
||||||
|
#
|
||||||
|
# The binaries are built before rpmbuild runs, by packaging/rpm/build-rpm.sh,
|
||||||
|
# and this spec packages them. That is how cargo-deb works on the Debian side,
|
||||||
|
# and it keeps one cargo invocation — with its target directory, features and
|
||||||
|
# cross-compilation flags — as the only thing that compiles FIPS. So there is
|
||||||
|
# no %%prep and no %%build here, and `fips_bindir` says where the binaries are.
|
||||||
|
#
|
||||||
|
# Dependencies are not listed: rpmbuild derives them from the ELF files, down
|
||||||
|
# to the glibc and libdbus symbol versions, which is what the Debian side gets
|
||||||
|
# from "$auto" plus testing/check-deb-depends.sh. An RPM built on a host newer
|
||||||
|
# than the target therefore *refuses to install* there rather than installing
|
||||||
|
# and failing to start. rpm derives the glibc requirement from the binaries, so
|
||||||
|
# what needs checking is the binaries themselves: testing/check-rpm-floor.sh
|
||||||
|
# reads that requirement out of the finished package and compares it with
|
||||||
|
# FIPS_GLIBC_FLOOR — see packaging/README.md.
|
||||||
|
|
||||||
|
%global fips_group fips
|
||||||
|
%global fips_libdir %{_prefix}/lib/fips
|
||||||
|
|
||||||
|
# Where build-rpm.sh leaves the binaries and where the source tree is. Both are
|
||||||
|
# passed with --define; the defaults only exist so `rpmspec -q` can parse this
|
||||||
|
# file without them.
|
||||||
|
%{!?fips_bindir: %global fips_bindir %{_sourcedir}/target/release}
|
||||||
|
%{!?fips_srcdir: %global fips_srcdir %{_sourcedir}}
|
||||||
|
|
||||||
|
# NOT "fips". Fedora's namespace already has a package by that name -- an
|
||||||
|
# OpenGL FITS image viewer (github.com/matwey/fips3), currently 3.4.0 -- and it
|
||||||
|
# owns /usr/bin/fips. A package named `fips` at 0.6.0 is therefore an *older*
|
||||||
|
# `fips` to every RPM tool there is, so a routine `dnf upgrade` replaces a
|
||||||
|
# running mesh node with an image viewer and takes the units with it. That is
|
||||||
|
# not hypothetical: it happened on a test machine within the hour, silently.
|
||||||
|
#
|
||||||
|
# The Debian side has no such collision, which is why only this name differs.
|
||||||
|
Name: fips-mesh
|
||||||
|
Version: %{?fips_version}%{!?fips_version:0.6.0}
|
||||||
|
Release: %{?fips_release}%{!?fips_release:1}%{?dist}
|
||||||
|
Summary: Free Internetworking Peering System mesh network daemon
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
URL: https://github.com/jmcorgan/fips
|
||||||
|
# The source is the working tree, not a tarball: see the header.
|
||||||
|
Source0: %{name}-%{version}.tar.gz
|
||||||
|
|
||||||
|
# Shipped by systemd, needed by the scriptlets below.
|
||||||
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
|
Requires: systemd
|
||||||
|
# The FITS viewer owns /usr/bin/fips, so the two cannot both be installed. rpm
|
||||||
|
# would refuse on the file conflict anyway; saying so here makes the refusal
|
||||||
|
# name the problem instead of naming a path.
|
||||||
|
Conflicts: fips
|
||||||
|
# groupadd, used by %%post. openSUSE calls the package `shadow`; the rich
|
||||||
|
# dependency satisfies both without naming a distribution.
|
||||||
|
Requires(post): (shadow-utils or shadow)
|
||||||
|
# Bluetooth (BLE) transport at runtime; the daemon runs without it.
|
||||||
|
Recommends: bluez
|
||||||
|
# fips-firewall.service runs nft(8). Not required: the unit is opt-in and the
|
||||||
|
# daemon does not need it.
|
||||||
|
Recommends: nftables
|
||||||
|
|
||||||
|
%description
|
||||||
|
FIPS is a distributed, decentralized network routing protocol for mesh nodes
|
||||||
|
connecting over arbitrary transports including UDP, TCP, Ethernet, Tor, and
|
||||||
|
Bluetooth (BLE). It provides encrypted peer-to-peer connectivity with automatic
|
||||||
|
key management, TUN-based virtual networking, and .fips DNS resolution.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
# Nothing to unpack: the binaries and the data files come from the working tree.
|
||||||
|
|
||||||
|
%build
|
||||||
|
# Nothing to build: see the header.
|
||||||
|
|
||||||
|
%install
|
||||||
|
install -D -m 0755 %{fips_bindir}/fips %{buildroot}%{_bindir}/fips
|
||||||
|
install -D -m 0755 %{fips_bindir}/fipsctl %{buildroot}%{_bindir}/fipsctl
|
||||||
|
install -D -m 0755 %{fips_bindir}/fipstop %{buildroot}%{_bindir}/fipstop
|
||||||
|
install -D -m 0755 %{fips_bindir}/fips-gateway %{buildroot}%{_bindir}/fips-gateway
|
||||||
|
|
||||||
|
install -D -m 0755 %{fips_srcdir}/packaging/common/fips-dns-setup \
|
||||||
|
%{buildroot}%{fips_libdir}/fips-dns-setup
|
||||||
|
install -D -m 0755 %{fips_srcdir}/packaging/common/fips-dns-teardown \
|
||||||
|
%{buildroot}%{fips_libdir}/fips-dns-teardown
|
||||||
|
|
||||||
|
# The units are the Debian package's, unmodified. Both packages install the
|
||||||
|
# binaries to %%{_bindir} and target the same systemd, so a second copy of four
|
||||||
|
# unit files would only be a second thing to keep in step.
|
||||||
|
install -D -m 0644 %{fips_srcdir}/packaging/debian/fips.service \
|
||||||
|
%{buildroot}%{_unitdir}/fips.service
|
||||||
|
install -D -m 0644 %{fips_srcdir}/packaging/debian/fips-dns.service \
|
||||||
|
%{buildroot}%{_unitdir}/fips-dns.service
|
||||||
|
install -D -m 0644 %{fips_srcdir}/packaging/debian/fips-firewall.service \
|
||||||
|
%{buildroot}%{_unitdir}/fips-firewall.service
|
||||||
|
install -D -m 0644 %{fips_srcdir}/packaging/debian/fips-gateway.service \
|
||||||
|
%{buildroot}%{_unitdir}/fips-gateway.service
|
||||||
|
|
||||||
|
install -D -m 0644 %{fips_srcdir}/packaging/debian/fips.tmpfiles \
|
||||||
|
%{buildroot}%{_tmpfilesdir}/fips.conf
|
||||||
|
|
||||||
|
# The example config is read by %%post, so it is not under %%{_docdir}: minimal
|
||||||
|
# and container installs path-exclude that directory. Same reasoning as the
|
||||||
|
# Debian package.
|
||||||
|
install -D -m 0644 %{fips_srcdir}/packaging/common/fips.yaml \
|
||||||
|
%{buildroot}%{_datadir}/fips/fips.yaml.example
|
||||||
|
|
||||||
|
install -D -m 0644 %{fips_srcdir}/packaging/common/hosts \
|
||||||
|
%{buildroot}%{_sysconfdir}/fips/hosts
|
||||||
|
install -D -m 0644 %{fips_srcdir}/packaging/common/fips.nft \
|
||||||
|
%{buildroot}%{_sysconfdir}/fips/fips.nft
|
||||||
|
|
||||||
|
# Drop-in directory for operator nftables rules included by /etc/fips/fips.nft.
|
||||||
|
# Empty by default; the include glob matches nothing cleanly out of the box.
|
||||||
|
install -d -m 0755 %{buildroot}%{_sysconfdir}/fips/fips.d
|
||||||
|
|
||||||
|
install -D -m 0644 %{fips_srcdir}/docs/design/fips-security.md \
|
||||||
|
%{buildroot}%{_docdir}/fips/fips-security.md
|
||||||
|
install -D -m 0644 %{fips_srcdir}/LICENSE %{buildroot}%{_licensedir}/fips/LICENSE
|
||||||
|
|
||||||
|
%post
|
||||||
|
# Control-socket access is by group membership, so the group exists before the
|
||||||
|
# daemon can create the socket.
|
||||||
|
getent group %{fips_group} >/dev/null || groupadd --system %{fips_group}
|
||||||
|
|
||||||
|
# Seed /etc/fips/fips.yaml from the shipped example only if it does not already
|
||||||
|
# exist. The live config is deliberately not a packaged config file: this
|
||||||
|
# copy-if-absent yields to any operator- or configuration-management-rendered
|
||||||
|
# file and never clobbers it, and never leaves a .rpmnew beside it either.
|
||||||
|
if [ ! -e %{_sysconfdir}/fips/fips.yaml ]; then
|
||||||
|
install -m 0600 -o root -g root \
|
||||||
|
%{_datadir}/fips/fips.yaml.example \
|
||||||
|
%{_sysconfdir}/fips/fips.yaml
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d /run/systemd/system ]; then
|
||||||
|
systemd-tmpfiles --create %{_tmpfilesdir}/fips.conf >/dev/null 2>&1 || :
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Presets first: a distribution preset that disables these units is applied
|
||||||
|
# here, though the explicit enable below then overrides it, so the presets have
|
||||||
|
# the last word only on units this package does not name.
|
||||||
|
%systemd_post fips.service fips-dns.service
|
||||||
|
|
||||||
|
# Then enable the two units the package considers its own: installing FIPS is
|
||||||
|
# how an operator asks for a mesh node, and a node that is installed but not
|
||||||
|
# enabled is not one. fips-firewall.service and fips-gateway.service are
|
||||||
|
# deliberately left alone — both are opt-in, see
|
||||||
|
# %%{_docdir}/fips/fips-security.md.
|
||||||
|
#
|
||||||
|
# On first install only, which is narrower than the Debian postinst: that one
|
||||||
|
# enables on every configure, so it re-enables a unit an operator has disabled.
|
||||||
|
# Here a later `systemctl disable fips` survives an upgrade, which is the
|
||||||
|
# behaviour an operator who disabled it would expect.
|
||||||
|
#
|
||||||
|
# Enabled, not started. The Debian postinst starts units only under
|
||||||
|
# `[ -n "$2" ]`, which holds on an upgrade and never on a fresh install, so a
|
||||||
|
# first install there leaves the node to the next boot or to the operator.
|
||||||
|
# Starting here would also mean fips-dns.service — Type=oneshot running
|
||||||
|
# fips-dns-setup — rewriting the host resolver inside the install transaction,
|
||||||
|
# against a fips.yaml seeded from the example seconds earlier. An upgrade
|
||||||
|
# queues a restart of whatever was running, in the try-restart in %%postun
|
||||||
|
# below.
|
||||||
|
#
|
||||||
|
# (A package submitted to Fedora proper would drop the enables too and let the
|
||||||
|
# distribution's presets decide, which is the policy there. This package is
|
||||||
|
# built upstream and installed deliberately, so it matches the .deb instead.)
|
||||||
|
if [ $1 -eq 1 ] && [ -d /run/systemd/system ]; then
|
||||||
|
systemctl enable fips.service >/dev/null 2>&1 || :
|
||||||
|
systemctl enable fips-dns.service >/dev/null 2>&1 || :
|
||||||
|
fi
|
||||||
|
|
||||||
|
# On upgrade, reapply the firewall ruleset in place, before the daemon is
|
||||||
|
# restarted by %%systemd_postun_with_restart below -- %%post of the new package
|
||||||
|
# runs ahead of %%postun of the old one, which is the ordering the Debian
|
||||||
|
# postinst has. "try" leaves an inactive unit alone, so this never opts a host
|
||||||
|
# in, and it must be a reload rather than a restart: that unit's ExecStop
|
||||||
|
# deletes the table, and a restart would leave the mesh interface unfiltered
|
||||||
|
# in between. A reload that fails leaves the previous ruleset in force, so it
|
||||||
|
# is reported and the upgrade goes on.
|
||||||
|
if [ $1 -ge 2 ] && [ -d /run/systemd/system ]; then
|
||||||
|
# The unit files this upgrade installed are not loaded yet -- the reload
|
||||||
|
# systemd runs from a file trigger comes at the end of the transaction --
|
||||||
|
# so without this the reload below would act on the pre-upgrade unit.
|
||||||
|
systemctl daemon-reload >/dev/null 2>&1 || :
|
||||||
|
if ! systemctl try-reload-or-restart fips-firewall.service >/dev/null 2>&1; then
|
||||||
|
echo "fips: reloading fips-firewall.service failed; the ruleset loaded before the upgrade stays in force" >&2
|
||||||
|
echo "fips: check /etc/fips/fips.nft and the rules in /etc/fips/fips.d/" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
%preun
|
||||||
|
# Stops and disables only on the last erase, not on an upgrade.
|
||||||
|
%systemd_preun fips.service fips-dns.service fips-gateway.service fips-firewall.service
|
||||||
|
|
||||||
|
%postun
|
||||||
|
# Restarts what was running, on upgrade only. fips-gateway.service is in the
|
||||||
|
# list because a host that opted it in would otherwise keep running the old
|
||||||
|
# binary; try-restart leaves an inactive unit alone, so listing it opts nobody
|
||||||
|
# in. fips-firewall.service is not: it is reloaded in %%post above, because
|
||||||
|
# restarting it would run its ExecStop and delete the table.
|
||||||
|
#
|
||||||
|
# Spelled out rather than left to %%systemd_postun_with_restart. That macro is
|
||||||
|
# expanded at build time, in the image this package is built in, and the EL9
|
||||||
|
# expansion only *marks* the units -- `systemd-update-helper
|
||||||
|
# mark-restart-system-units` -- for a file trigger in that distribution's
|
||||||
|
# systemd package to act on. On a distribution without that trigger the mark is
|
||||||
|
# written and nothing ever reads it, so an upgrade silently leaves the old
|
||||||
|
# binary running. try-restart is portable, and is what the macro would have
|
||||||
|
# reached in the end anyway.
|
||||||
|
#
|
||||||
|
# Queued, not waited on. `systemctl try-restart` without --no-block returns
|
||||||
|
# when the jobs finish, and this scriptlet runs inside the rpm transaction,
|
||||||
|
# holding dnf's lock: fips-dns.service is Type=oneshot and fips-dns-setup waits
|
||||||
|
# up to 30 s for fips0, so a daemon that comes back slowly -- or not at all --
|
||||||
|
# would hold the whole upgrade there. The restart is a request; whether it
|
||||||
|
# succeeds is the daemon's business and the journal's, not the package
|
||||||
|
# manager's.
|
||||||
|
#
|
||||||
|
# This is also where the RPM deliberately parts from the Debian postinst,
|
||||||
|
# which waits for each unit it starts with a bounded poll. That bound exists
|
||||||
|
# because a blocking `systemctl start` under dpkg held apt, and every package
|
||||||
|
# operation queued behind it, for ever. Queuing the restart avoids the problem
|
||||||
|
# the bound was written to contain, rather than reimplementing the bound.
|
||||||
|
if [ $1 -ge 1 ] && [ -d /run/systemd/system ]; then
|
||||||
|
systemctl --no-block try-restart fips.service fips-dns.service fips-gateway.service >/dev/null 2>&1 || :
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $1 -eq 0 ]; then
|
||||||
|
# The runtime directory is not packaged, so nothing else removes it.
|
||||||
|
rm -rf /run/fips
|
||||||
|
|
||||||
|
# DNS configuration fips-dns-setup may have written outside the package,
|
||||||
|
# one file per backend it picks between. fips-dns-teardown runs on
|
||||||
|
# ExecStop and removes the file of the backend recorded in its state file,
|
||||||
|
# or all four when the state file is missing; %%systemd_preun stops the unit
|
||||||
|
# before rpm gets here, so this is what catches a host where the unit was
|
||||||
|
# not running. Each resolver whose file is removed is told to drop it, as
|
||||||
|
# the Debian postrm does: otherwise a host erased while fips-dns was stopped
|
||||||
|
# keeps sending .fips queries to the daemon's resolver port until that
|
||||||
|
# resolver next restarts. rpm has no purge, so this erase branch is the only
|
||||||
|
# cleanup that will ever run.
|
||||||
|
restart_resolved=0
|
||||||
|
if [ -f %{_sysconfdir}/systemd/dns-delegate.d/fips.dns-delegate ]; then
|
||||||
|
rm -f %{_sysconfdir}/systemd/dns-delegate.d/fips.dns-delegate
|
||||||
|
restart_resolved=1
|
||||||
|
fi
|
||||||
|
if [ -f %{_sysconfdir}/systemd/resolved.conf.d/fips.conf ]; then
|
||||||
|
rm -f %{_sysconfdir}/systemd/resolved.conf.d/fips.conf
|
||||||
|
restart_resolved=1
|
||||||
|
fi
|
||||||
|
if [ "$restart_resolved" = 1 ] && [ -d /run/systemd/system ] \
|
||||||
|
&& systemctl is-active --quiet systemd-resolved.service; then
|
||||||
|
systemctl restart systemd-resolved \
|
||||||
|
|| echo "fips: warning: could not restart systemd-resolved; restart it to drop the .fips route"
|
||||||
|
fi
|
||||||
|
if [ -f %{_sysconfdir}/dnsmasq.d/fips.conf ]; then
|
||||||
|
rm -f %{_sysconfdir}/dnsmasq.d/fips.conf
|
||||||
|
if [ -d /run/systemd/system ] \
|
||||||
|
&& systemctl is-active --quiet dnsmasq.service; then
|
||||||
|
systemctl reload dnsmasq \
|
||||||
|
|| echo "fips: warning: could not reload dnsmasq; reload it to drop the .fips route"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -f %{_sysconfdir}/NetworkManager/dnsmasq.d/fips.conf ]; then
|
||||||
|
rm -f %{_sysconfdir}/NetworkManager/dnsmasq.d/fips.conf
|
||||||
|
if [ -d /run/systemd/system ] \
|
||||||
|
&& systemctl is-active --quiet NetworkManager.service \
|
||||||
|
&& command -v nmcli >/dev/null 2>&1; then
|
||||||
|
nmcli general reload \
|
||||||
|
|| echo "fips: warning: could not reload NetworkManager; reload it to drop the .fips route"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Note what is *not* here. The Debian postrm removes /etc/fips and the fips
|
||||||
|
# group on purge — an explicit, separate operator action. rpm has no purge, so
|
||||||
|
# the equivalent code would run on an ordinary erase and take the node's
|
||||||
|
# identity keys with it, including during a distribution upgrade that erases
|
||||||
|
# and reinstalls. Config and keys therefore survive an erase; remove
|
||||||
|
# /etc/fips yourself if you mean it.
|
||||||
|
|
||||||
|
%files
|
||||||
|
%dir %{_licensedir}/fips
|
||||||
|
%license %{_licensedir}/fips/LICENSE
|
||||||
|
%dir %{_docdir}/fips
|
||||||
|
%doc %{_docdir}/fips/fips-security.md
|
||||||
|
%{_bindir}/fips
|
||||||
|
%{_bindir}/fipsctl
|
||||||
|
%{_bindir}/fipstop
|
||||||
|
%{_bindir}/fips-gateway
|
||||||
|
%dir %{fips_libdir}
|
||||||
|
%{fips_libdir}/fips-dns-setup
|
||||||
|
%{fips_libdir}/fips-dns-teardown
|
||||||
|
%{_unitdir}/fips.service
|
||||||
|
%{_unitdir}/fips-dns.service
|
||||||
|
%{_unitdir}/fips-firewall.service
|
||||||
|
%{_unitdir}/fips-gateway.service
|
||||||
|
%{_tmpfilesdir}/fips.conf
|
||||||
|
%dir %{_datadir}/fips
|
||||||
|
%{_datadir}/fips/fips.yaml.example
|
||||||
|
%dir %{_sysconfdir}/fips
|
||||||
|
%dir %{_sysconfdir}/fips/fips.d
|
||||||
|
%config(noreplace) %{_sysconfdir}/fips/hosts
|
||||||
|
%config(noreplace) %{_sysconfdir}/fips/fips.nft
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Sat Sep 19 2026 Johnathan Corgan <johnathan@corganlabs.com>
|
||||||
|
- Packaging for RPM-based distributions, translated from the Debian recipe.
|
||||||
Executable
+93
@@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Fail when an RPM records a glibc requirement above the declared floor.
|
||||||
|
#
|
||||||
|
# The counterpart of check-deb-depends.sh, for the other package format and for
|
||||||
|
# a different failure. On the Debian side the package's Depends are written by
|
||||||
|
# hand and can disagree with what the binaries need, so that check compares the
|
||||||
|
# two. rpm derives the requirement from the ELF files and cannot disagree with
|
||||||
|
# them -- which moves the risk one step back: the binaries themselves may have
|
||||||
|
# been built somewhere above the floor, and the package that results installs
|
||||||
|
# nowhere older, silently, until someone tries.
|
||||||
|
#
|
||||||
|
# This reads the requirement out of the finished package, which is the artifact
|
||||||
|
# that ships and the same table dnf enforces at install time.
|
||||||
|
#
|
||||||
|
# Usage: check-rpm-floor.sh <package.rpm>...
|
||||||
|
#
|
||||||
|
# Reads the floor from packaging/build-floor.env unless FIPS_GLIBC_FLOOR is set.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
if [ -z "${FIPS_GLIBC_FLOOR:-}" ]; then
|
||||||
|
# shellcheck source=../packaging/build-floor.env
|
||||||
|
. "$REPO_ROOT/packaging/build-floor.env"
|
||||||
|
fi
|
||||||
|
FLOOR="${FIPS_GLIBC_FLOOR:?no floor declared}"
|
||||||
|
|
||||||
|
command -v rpm >/dev/null 2>&1 || {
|
||||||
|
echo "check-rpm-floor: rpm is not installed; cannot check anything." >&2
|
||||||
|
echo " Refusing to report a pass I did not establish." >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
[ $# -gt 0 ] || {
|
||||||
|
echo "usage: check-rpm-floor.sh <package.rpm>..." >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sorts versions the way rpm does, so 2.10 is above 2.9 rather than below it.
|
||||||
|
version_gt() {
|
||||||
|
[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -1)" = "$1" ] && [ "$1" != "$2" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
FAILED=0
|
||||||
|
CHECKED=0
|
||||||
|
|
||||||
|
for pkg in "$@"; do
|
||||||
|
if [ ! -f "$pkg" ]; then
|
||||||
|
echo " ERROR $pkg does not exist" >&2
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Every libc.so.6(GLIBC_x.y) entry rpm derived from the packaged binaries.
|
||||||
|
# The highest one is the floor the package will be held to.
|
||||||
|
need=$(rpm -qp --requires "$pkg" 2>/dev/null \
|
||||||
|
| grep -oE 'GLIBC_[0-9.]+' \
|
||||||
|
| sed 's/GLIBC_//' \
|
||||||
|
| sort -V \
|
||||||
|
| tail -1) || true
|
||||||
|
|
||||||
|
if [ -z "$need" ]; then
|
||||||
|
# No requirement at all means the package holds no dynamically linked
|
||||||
|
# binary, which for this package means the file list moved. Not a pass.
|
||||||
|
echo " ERROR $(basename "$pkg") records no glibc requirement" >&2
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
CHECKED=$((CHECKED + 1))
|
||||||
|
if version_gt "$need" "$FLOOR"; then
|
||||||
|
echo " FAIL $(basename "$pkg") requires glibc $need, above the declared floor $FLOOR" >&2
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
else
|
||||||
|
echo " ok $(basename "$pkg") requires glibc $need"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$FAILED" -ne 0 ]; then
|
||||||
|
echo "check-rpm-floor: $FAILED check(s) failed against floor $FLOOR." >&2
|
||||||
|
echo " The package was built from binaries compiled above the floor. Build" >&2
|
||||||
|
echo " them in the pinned container: packaging/rpm/build-rpm-container.sh." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CHECKED" -eq 0 ]; then
|
||||||
|
echo "check-rpm-floor: nothing was checked; refusing to report a pass." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== RPM glibc floor check passed ($CHECKED package(s)) ==="
|
||||||
Reference in New Issue
Block a user