From 24ab653da475871af02dba9c70fbf524cba636a8 Mon Sep 17 00:00:00 2001 From: Giacomo Sanchietti Date: Tue, 4 Aug 2026 16:46:48 +0200 Subject: [PATCH 1/3] build(nginx): update to 1.30.4 The packages feed pinned by OpenWrt v25.12.5 still ships nginx 1.26.3, which is missing four security releases (1.30.1 to 1.30.4). Upstream has only packaged 1.30.3 so far, so patch the feed Makefile directly instead of waiting for the pin to move. All ten existing feed patches still apply to the 1.30.4 sources, so only version and hash change. Assisted-by: Claude Code:claude-opus-5 --- .../packages/101-nginx-update-1.30.4.patch | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 patches/feeds/packages/101-nginx-update-1.30.4.patch diff --git a/patches/feeds/packages/101-nginx-update-1.30.4.patch b/patches/feeds/packages/101-nginx-update-1.30.4.patch new file mode 100644 index 000000000..3ef6271a8 --- /dev/null +++ b/patches/feeds/packages/101-nginx-update-1.30.4.patch @@ -0,0 +1,19 @@ +diff --git a/net/nginx/Makefile b/net/nginx/Makefile +--- a/net/nginx/Makefile ++++ b/net/nginx/Makefile +@@ -8,12 +8,12 @@ + include $(TOPDIR)/rules.mk + + PKG_NAME:=nginx +-PKG_VERSION:=1.26.3 +-PKG_RELEASE:=4 ++PKG_VERSION:=1.30.4 ++PKG_RELEASE:=1 + + PKG_SOURCE:=nginx-$(PKG_VERSION).tar.gz + PKG_SOURCE_URL:=https://nginx.org/download/ +-PKG_HASH:=69ee2b237744036e61d24b836668aad3040dda461fe6f570f1787eab570c75aa ++PKG_HASH:=4261dc90e9e47c1c4041276e9aaa3d48ebe2e664f728e14fa95ae6c67d57a08b + + PKG_MAINTAINER:=Thomas Heil \ + Christian Marangi From e57ed875cd126ba8130831b3cd9cf88af662179b Mon Sep 17 00:00:00 2001 From: Giacomo Sanchietti Date: Tue, 4 Aug 2026 16:57:43 +0200 Subject: [PATCH 2/3] build(nginx): let Renovate track the version patch The version now lives in a file Renovate can edit, so future nginx releases show up as pull requests instead of being noticed by accident. Versions are read from nginx upstream, not from the OpenWrt feed, which is what we are compensating for. Renovate cannot compute PKG_HASH, so its pull request fails the build until the hash is refreshed by hand. That failure is the notification. Assisted-by: Claude Code:claude-opus-5 --- .../packages/101-nginx-track-stable.patch | 36 +++++++++++++++++++ .../packages/101-nginx-update-1.30.4.patch | 19 ---------- renovate.json | 20 +++++++++++ 3 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 patches/feeds/packages/101-nginx-track-stable.patch delete mode 100644 patches/feeds/packages/101-nginx-update-1.30.4.patch diff --git a/patches/feeds/packages/101-nginx-track-stable.patch b/patches/feeds/packages/101-nginx-track-stable.patch new file mode 100644 index 000000000..2be919d4a --- /dev/null +++ b/patches/feeds/packages/101-nginx-track-stable.patch @@ -0,0 +1,36 @@ +# Carry the current nginx stable release ahead of the OpenWrt packages feed. +# See the "Upstream security tracking" section of docs/build/. +# +# The feed revision pinned by OWRT_VERSION lags nginx upstream by months and +# the OpenWrt release branches receive no nginx security backports, so we +# track the stable branch ourselves. +# +# Renovate updates the PKG_VERSION line below. PKG_HASH is not derivable by +# Renovate and must be refreshed by hand in the same pull request: +# +# curl -sL https://nginx.org/download/nginx-.tar.gz | sha256sum +# +# Do NOT delete this patch to unbreak the build after an OWRT_VERSION bump: +# that silently downgrades nginx. Re-target the hunk against the new feed +# version instead, and remove the patch only once the feed itself ships a +# version greater than or equal to the one below. + +diff --git a/net/nginx/Makefile b/net/nginx/Makefile +--- a/net/nginx/Makefile ++++ b/net/nginx/Makefile +@@ -8,12 +8,12 @@ + include $(TOPDIR)/rules.mk + + PKG_NAME:=nginx +-PKG_VERSION:=1.26.3 +-PKG_RELEASE:=4 ++PKG_VERSION:=1.30.4 ++PKG_RELEASE:=1 + + PKG_SOURCE:=nginx-$(PKG_VERSION).tar.gz + PKG_SOURCE_URL:=https://nginx.org/download/ +-PKG_HASH:=69ee2b237744036e61d24b836668aad3040dda461fe6f570f1787eab570c75aa ++PKG_HASH:=4261dc90e9e47c1c4041276e9aaa3d48ebe2e664f728e14fa95ae6c67d57a08b + + PKG_MAINTAINER:=Thomas Heil \ + Christian Marangi diff --git a/patches/feeds/packages/101-nginx-update-1.30.4.patch b/patches/feeds/packages/101-nginx-update-1.30.4.patch deleted file mode 100644 index 3ef6271a8..000000000 --- a/patches/feeds/packages/101-nginx-update-1.30.4.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/net/nginx/Makefile b/net/nginx/Makefile ---- a/net/nginx/Makefile -+++ b/net/nginx/Makefile -@@ -8,12 +8,12 @@ - include $(TOPDIR)/rules.mk - - PKG_NAME:=nginx --PKG_VERSION:=1.26.3 --PKG_RELEASE:=4 -+PKG_VERSION:=1.30.4 -+PKG_RELEASE:=1 - - PKG_SOURCE:=nginx-$(PKG_VERSION).tar.gz - PKG_SOURCE_URL:=https://nginx.org/download/ --PKG_HASH:=69ee2b237744036e61d24b836668aad3040dda461fe6f570f1787eab570c75aa -+PKG_HASH:=4261dc90e9e47c1c4041276e9aaa3d48ebe2e664f728e14fa95ae6c67d57a08b - - PKG_MAINTAINER:=Thomas Heil \ - Christian Marangi diff --git a/renovate.json b/renovate.json index 7061af0b6..13bdcdcbe 100644 --- a/renovate.json +++ b/renovate.json @@ -14,7 +14,27 @@ "go": "1.26" }, "constraintsFiltering": "strict", + "customManagers": [ + { + "customType": "regex", + "description": "Track nginx releases from nginx upstream (github.com/nginx/nginx, release-* tags), NOT from the OpenWrt packages feed, which lags upstream by months. Feeds patches/feeds/packages/101-nginx-track-stable.patch. Renovate bumps PKG_VERSION only; PKG_HASH must be refreshed by hand in the same PR.", + "managerFilePatterns": [ + "/^patches/feeds/packages/101-nginx-track-stable\\.patch$/" + ], + "matchStrings": [ + "\\n\\+PKG_VERSION:=(?[0-9.]+)\\n" + ], + "depNameTemplate": "nginx/nginx", + "datasourceTemplate": "github-tags", + "extractVersionTemplate": "^release-(?.*)$" + } + ], "packageRules": [ + { + "description": "Track the nginx stable branch only: mainline releases (odd minor) are not supported for production use. Raise this bound when adopting a new stable branch, and review upstream's 'Change:' entries before doing so.", + "matchDepNames": ["nginx/nginx"], + "allowedVersions": "<1.31" + }, { "matchPaths": [ "tools/**", From 3fa744dc964b7e17445806e52ba9bba68623b5d9 Mon Sep 17 00:00:00 2001 From: Giacomo Sanchietti Date: Tue, 4 Aug 2026 16:57:44 +0200 Subject: [PATCH 3/3] docs(build): add upstream security tracking policy nginx was frozen at 1.25.2 for 28 months as a fork, then frozen again after reverting to the OpenWrt feed. Neither mechanism was the problem: nobody was watching nginx releases, and a stale version never fails a build, so it stays invisible. Write down which packages we track ahead of OpenWrt, that they must follow the upstream stable branch, that Renovate must be able to see them, and that deleting a version patch to unbreak a build is a silent downgrade. Assisted-by: Claude Code:claude-opus-5 --- AGENTS.md | 1 + docs/build/index.md | 110 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a0e4eb0e7..d931d9540 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,6 +82,7 @@ recent host Ruby (4.x), so the site is built in a `ruby:3.3` container and the - **Do not set `PKG_SOURCE_URL`** when package code lives in this repo. Set it only when fetching from external GitHub releases. - To add a package to the image, create a corresponding `config/.conf` fragment that enables it at build time. - Renovate manages external package versions via magic comments in Makefiles: `# renovate: datasource=github-tags depName=Org/Repo` +- A few upstream feed packages are deliberately kept ahead of the pinned OpenWrt feed for security reasons (currently nginx). Before touching one, read the **Upstream security tracking** section in [`docs/build/index.md`](docs/build/index.md): tracked packages follow the upstream *stable* branch only, diverge via a version-only patch under `patches/feeds/packages/` rather than a fork, and must stay visible to Renovate. Never delete such a patch to unbreak a build — that silently downgrades the package. --- diff --git a/docs/build/index.md b/docs/build/index.md index 22baab77a..58b2d6edf 100644 --- a/docs/build/index.md +++ b/docs/build/index.md @@ -140,28 +140,128 @@ For other channels: To change the OpenWrt version used by NethSecurity, update the `OWRT_VERSION` variable inside the `build.conf.defaults` file (versioned, always tracked by Git). This ensures all developers and CI get the same default version. +## Upstream security tracking + +The `packages` feed is pinned by the OpenWrt release we build against, so a package's version normally +moves only when `OWRT_VERSION` moves. That is the right default for most of the image, but it is not +safe for everything, because the OpenWrt release branches receive almost no security backports. + +nginx is the worked example. At the time of writing the `openwrt-25.12` feed branch still ships nginx +1.26.3 and `openwrt-24.10` ships 1.26.1, which never received the 1.26.3 security fix. The `master` +feed itself stayed on 1.26.3 for fifteen months and skipped the whole 1.28 stable branch. + +NethSecurity has already tried both extremes for nginx and both failed the same way: + +- a fork was added under `packages/nginx` in November 2023 at nginx 1.25.2 and removed in March 2026 + still at 1.25.2, twenty-eight months without a single bump +- reverting to the feed then left us on whatever the pinned feed happened to ship + +Neither mechanism was at fault. In both regimes nobody was watching nginx releases, and a stale +version is silent: a frozen fork keeps building forever and nothing ever fails. Both mechanism +switches also dropped the version on the floor, because the switch was the only moment anyone looked. + +The policy below therefore fixes ownership, not mechanism. + +### Default: follow OpenWrt + +Do not diverge from the pinned feed. Versions move when `OWRT_VERSION` moves. This applies to +everything not named on the tracked list. + +### Tracked packages + +A package joins the tracked list when both of these hold: + +- it processes untrusted network input directly, so it is attack surface +- its upstream maintains a stable branch with security-only releases + +Currently tracked: **nginx**, stable branch 1.30.x. + +Rules for a tracked package: + +- **Watched by Renovate, always.** A tracked package that Renovate cannot see will go stale. This is + not a guideline, it is the one control that distinguishes this policy from the two attempts that + failed. See [Renovate wiring](#renovate-wiring) below. +- **Compare against the upstream project, never against OpenWrt.** The whole point of tracking a + package is that OpenWrt's version is stale, so OpenWrt cannot be the reference. Renovate must read + the upstream project's own releases: for nginx that is `github.com/nginx/nginx` and its `release-*` + tags, which is nginx's own repository, not a distribution package index. +- **Stable branch only.** Never ship an upstream mainline release. For nginx that means 1.30.x, never + 1.31.x. Inside a stable branch releases are security-only, so bumps stay cheap: patches keep + applying and runtime behaviour does not drift. +- **Adopt a new stable branch within one release cycle of it opening.** The expensive part of an + upgrade is the branch jump, not the point release; deferring it is what turned a routine bump into + a fifteen-month gap. Adopting a branch requires reading upstream's `*) Change:` entries between the + old and new version and testing the affected paths on a device. +- **Open the bump within a week of an upstream security release.** Security updates skip the one-week + repository hold, see [development process](../development_process/#publish-packages). + +### Mechanism: patch, not fork + +Use a [package patch](#package-patches) that changes only `PKG_VERSION`, `PKG_RELEASE` and `PKG_HASH`. +Keep the version out of the patch filename so the file is stable and Renovate can edit it in place: +`patches/feeds/packages/101-nginx-track-stable.patch`. + +A patch is preferred over a fork here because the divergence is version-only. We carry three lines and +inherit every other fix the feed makes to that package, including its build patches and its +third-party module pins. Fork under `packages/` only when we also need to change the package's own +code, as with `adblock`, `banip` and `mwan3`. + +Two obligations come with the patch mechanism: + +- The patch must carry a header comment stating the base version it patches, why we are ahead of the + feed, and how to refresh `PKG_HASH`. `patch` ignores leading comment lines. +- When `OWRT_VERSION` moves, the patch stops applying and aborts the container build. **Re-target the + hunk against the new base version. Do not delete the patch to unbreak the build** — that silently + downgrades the package. Delete it only once the feed ships a version greater than or equal to ours. + +### Renovate wiring + +Renovate cannot use the `customManagers:makefileVersions` preset on a patch file, so a tracked package +needs a `customManagers` entry in `renovate.json` matching the patch and capturing the added +`PKG_VERSION` line. Renovate rewrites only the captured occurrence, so the `-PKG_VERSION` context +line is left intact and the patch keeps applying. Pair it with a `packageRules` entry bounding the +version to the stable branch. + +Renovate cannot compute `PKG_HASH`, so its pull request will fail the build until the hash is +refreshed by hand in the same PR: + +``` +curl -sL https://nginx.org/download/nginx-.tar.gz | sha256sum +``` + +Treat that failing Renovate PR as the notification it is. It is the signal that was missing for +twenty-eight months. + +### Any change of mechanism + +Adding or removing a fork or a patch for a tracked package must state the effective package version +before and after the change in the pull request description, and must not lower it. Both previous +nginx mechanism switches regressed the version without anyone noticing. + ## Release new image checklist When releasing a new image, follow these steps: 1. **Update the versioned build defaults**: Bump `NETHSECURITY_VERSION` in [build.conf.defaults](https://github.com/NethServer/nethsecurity/tree/main/build.conf.defaults) if this release needs a new base version. -2. **Merge the release branch**: Push the tested change to `release` so CI publishes the final image and packages automatically. +2. **Check the tracked packages**: Confirm each package on the [tracked list](#tracked-packages) is on the current upstream stable release, and that no pending Renovate pull request for one is still open. See [Upstream security tracking](#upstream-security-tracking). + +3. **Merge the release branch**: Push the tested change to `release` so CI publishes the final image and packages automatically. -3. **Create the Git tag:** +4. **Create the Git tag:** - Create the tag on the stable release commit in the NethSecurity repository. - The tag is the human-facing release marker and is visible in the repository tags list on GitHub. -4. **Draft the GitHub release:** +5. **Draft the GitHub release:** - Use the tag as the release name. - Attach the manifest file and the SBOM file. - Do not attach image artifacts. -5. **Finalize the release notes:** +6. **Finalize the release notes:** - Update the changelog with the date of release and relevant changes inside the [administrator manual](https://github.com/NethServer/nethsecurity-docs). - Merge any pending documentation PRs and rebuild the docs if needed. -6. **Complete the rest of the release tasks:** +7. **Complete the rest of the release tasks:** - Close related issues and milestones. - Archive completed project-board items. - Release NethSecurity Controller if applicable.