Offline bundle: pin and hash the webapp wheels, refuse unpinned ones, check for updates

- webapp/requirements.txt pins all eight wheels with sha256, at the fixed
  releases (lxml 6.1.3 for GHSA-vfmq-68hx-4jfw / PYSEC-2026-87, werkzeug
  3.1.9 for GHSA-g6x2-hccm-hh4m).
- download-packages.sh fetches exactly that set for Python 3.12 / x86_64
  into an emptied folder; PIP_DIR is honoured inside the container (own
  mount), files are handed back to the calling user, USE_DOCKER=1 forces
  the container on a noble host, an unknown package name now prints apt's
  error instead of failing silently, and linux-modules-extra-7.0.* is gone
  (it does not exist; 7.0 ships those modules in linux-modules).
- Playbook: the wheel installer refuses any wheel whose sha256 is not
  pinned, needs exactly one wheel per pin, and removes an older installed
  version by its RECORD first. The webapp copy is a full rsync every run
  (was cp with creates: app.py, which never updated an install).
- scripts/check-updates.py: offline debs against noble security/updates
  and the pins against PyPI + osv.dev; exit 1 when something needs
  updating. Run it before building a USB.

Tested: installer in a Python 3.12 venv (clean install, upgrade over lxml
6.0.4 + werkzeug 3.1.8 leaves no old dist-info, re-run is a no-op, a stray
wheel is refused); check-updates.py flags the April bundle (94 security
updates) and passes the pins.
This commit is contained in:
cproudlock
2026-10-15 09:41:16 -04:00
parent ead3314f58
commit 18f20b00e2
4 changed files with 225 additions and 21 deletions

View File

@@ -1,2 +1,24 @@
flask
lxml
# Webapp Python dependencies, pinned with sha256 for the PXE server's
# Python 3.12 (Ubuntu 24.04, x86_64). scripts/download-packages.sh fetches
# exactly these into pip-wheels/; the playbook refuses any other wheel.
# To update: change a version here, regenerate the hashes
# (pip hash, or the PyPI JSON API), re-run download-packages.sh, and check
# for known vulnerabilities (osv.dev) before building a USB.
blinker==1.9.0 \
--hash=sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc
click==8.5.0 \
--hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360
flask==3.1.3 \
--hash=sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c
itsdangerous==2.2.0 \
--hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef
jinja2==3.1.6 \
--hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67
lxml==6.1.3 \
--hash=sha256:379f8a75cf6eb7eef0af074b55f49ab73b868388a98de14646abcdfa4564bb11 \
--hash=sha256:909f4e927bb051f7740d6367285fc60cdcfdaf0258c2dba4ff5ba7eadadc250c
markupsafe==3.0.4 \
--hash=sha256:8e124f974786f831d6043728e38296969d3579db8896fe004682f5758e613581
werkzeug==3.1.9 \
--hash=sha256:6392e50c78460ba618e5b21f08a71f59c99ce99cdc6cf6e3dd7e6ccca8754fab