linux_hardened: only provide latest LTS and lattest stable version

As proposed in #346018 (not closing the ticket, this affects other
variants as well).

The packaging for hardened is in a pretty sad state: it was lagging
several patch-releases behind and nobody seems to care. The update
script aged poorly: the automatic removal was flat-out broken, several
type annotations are plain wrong (`list[int] != packaging.Version`).

This patch is an attempt to reduce the scope for the maintainer team
drastically to provide _some_ maintenance again by only packaging latest
LTS and latest stable.

Also, remove the top-level attributes for this. I still don't see any
compelling reason to give hardly used flavours that special treatment.
This commit is contained in:
Maximilian Bosch
2025-08-23 17:42:48 +02:00
parent 22b75542c0
commit 91f7851fb2
5 changed files with 86 additions and 94 deletions
+1 -7
View File
@@ -35,14 +35,8 @@ let
) args);
kernels = pkgs.linuxKernel.vanillaPackages // {
inherit (pkgs.linuxKernel.packages)
linux_5_4_hardened
linux_5_10_hardened
linux_5_15_hardened
linux_6_1_hardened
linux_6_6_hardened
linux_6_12_hardened
linux_6_13_hardened
linux_6_14_hardened
linux_6_15_hardened
linux_rt_5_4
linux_rt_5_10
linux_rt_5_15
@@ -156,10 +156,11 @@ def fetch_patch(*, name: str, release_info: ReleaseInfo) -> Optional[Patch]:
)
def parse_version(version_str: str) -> Version:
def normalize_kernel_version(version_str: str) -> list[str|int]:
# There have been two variants v6.10[..] and 6.10[..], drop the v
version_str_without_v = version_str[1:] if not version_str[0].isdigit() else version_str
version: Version = []
version: list[str|int] = []
for component in re.split(r'\.|\-', version_str_without_v):
try:
@@ -173,14 +174,14 @@ def version_string(version: Version) -> str:
return ".".join(str(component) for component in version)
def major_kernel_version_key(kernel_version: Version) -> str:
def major_kernel_version_key(kernel_version: list[int|str]) -> str:
return version_string(kernel_version[:-1])
def commit_patches(*, kernel_key: str, message: str) -> None:
def commit_patches(*, kernel_key: Version, message: str) -> None:
new_patches_path = HARDENED_PATCHES_PATH.with_suffix(".new")
with open(new_patches_path, "w") as new_patches_file:
json.dump(patches, new_patches_file, indent=4, sort_keys=True)
json.dump(patch_json, new_patches_file, indent=4, sort_keys=True)
new_patches_file.write("\n")
os.rename(new_patches_path, HARDENED_PATCHES_PATH)
message = f"linux/hardened/patches/{kernel_key}: {message}"
@@ -197,43 +198,32 @@ def commit_patches(*, kernel_key: str, message: str) -> None:
# Load the existing patches.
patches: Dict[str, Patch]
with open(HARDENED_PATCHES_PATH) as patches_file:
patches = json.load(patches_file)
patch_json = json.load(patches_file)
patch_versions = set([parse_version(k) for k in patch_json.keys()])
# Get the set of currently packaged kernel versions.
kernel_versions = {}
with open(NIXPKGS_KERNEL_PATH / "kernels-org.json") as kernel_versions_json:
kernel_versions = json.load(kernel_versions_json)
for kernel_branch_str in kernel_versions:
if kernel_branch_str == "testing": continue
kernel_branch = [int(i) for i in kernel_branch_str.split(".")]
if kernel_branch < MIN_KERNEL_VERSION: continue
kernel_version = [int(i) for i in kernel_versions[kernel_branch_str]["version"].split(".")]
kernel_versions[kernel_branch_str] = kernel_version
# Remove patches for unpackaged kernel versions.
for kernel_key in sorted(patches.keys() - kernel_versions.keys()):
del patches[kernel_key]
commit_patches(kernel_key=kernel_key, message="remove")
kernels = {
parse_version(version): meta
for version, meta in kernel_versions.items()
if version != "testing"
}
latest_lts = sorted(ver for ver, meta in kernels.items() if meta.get("lts", False))[-1]
keys = sorted(kernels.keys())
latest_release = keys[-1]
fallback = keys[-2]
g = Github(os.environ.get("GITHUB_TOKEN"))
repo = g.get_repo(HARDENED_GITHUB_REPO)
failures = False
# Match each kernel version with the best patch version.
releases = {}
i = 0
for release in repo.get_releases():
# Dirty workaround to make sure that we don't run into issues because
# GitHub's API only allows fetching the last 1000 releases.
# It's not reliable to exit earlier because not every kernel minor may
# have hardened patches, hence the naive search below.
i += 1
if i > 100:
break
version = parse_version(release.tag_name)
all_candidates = set([latest_lts, latest_release, fallback])
kernels_to_package = {}
for release in repo.get_releases()[:30]:
version = normalize_kernel_version(release.tag_name)
# needs to look like e.g. 5.6.3-hardened1
if len(version) < 4:
continue
@@ -242,44 +232,51 @@ for release in repo.get_releases():
continue
kernel_version = version[:-1]
kernel_key = parse_version(major_kernel_version_key(kernel_version))
kernel_key = major_kernel_version_key(kernel_version)
try:
packaged_kernel_version = kernel_versions[kernel_key]
except KeyError:
if kernel_key not in all_candidates:
continue
release_info = ReleaseInfo(version=version, release=release)
if kernel_version == packaged_kernel_version:
releases[kernel_key] = release_info
else:
# Fall back to the latest patch for this major kernel version,
# skipping patches for kernels newer than the packaged one.
if '.'.join(str(x) for x in kernel_version) > '.'.join(str(x) for x in packaged_kernel_version):
try:
found = kernels_to_package[kernel_key]
if found.version > version:
continue
elif (
kernel_key not in releases or releases[kernel_key].version < version
):
releases[kernel_key] = release_info
except KeyError:
pass
kernels_to_package[kernel_key] = ReleaseInfo(version=version, release=release)
if latest_release in kernels_to_package:
if fallback != latest_lts:
del kernels_to_package[fallback]
kernel_versions = set([latest_lts, latest_release])
else:
kernel_versions = set([latest_lts, fallback])
# Remove patches for unpackaged kernel versions.
removals = False
for kernel_key in sorted(patch_versions - kernels_to_package.keys()):
del patch_json[str(kernel_key)]
removals = True
commit_patches(kernel_key=kernel_key, message="remove")
# Update hardened-patches.json for each release.
for kernel_key in sorted(releases.keys()):
release_info = releases[kernel_key]
for kernel_key in sorted(kernels_to_package.keys()):
release_info = kernels_to_package[kernel_key]
release = release_info.release
version = release_info.version
version_str = release.tag_name
name = f"linux-hardened-{version_str}"
old_version: Optional[Version] = None
old_version: Optional[list[int|str]] = None
old_version_str: Optional[str] = None
update: bool
try:
old_filename = patches[kernel_key]["patch"]["name"]
old_filename = patch_json[str(kernel_key)]["patch"]["name"]
old_version_str = old_filename.replace("linux-hardened-", "").replace(
".patch", ""
)
old_version = parse_version(old_version_str)
old_version = normalize_kernel_version(old_version_str)
update = old_version < version
except KeyError:
update = True
@@ -289,21 +286,16 @@ for kernel_key in sorted(releases.keys()):
if patch is None:
failures = True
else:
patches[kernel_key] = patch
if old_version:
if str(kernel_key) in patch_json:
message = f"{old_version_str} -> {version_str}"
else:
message = f"init at {version_str}"
patch_json[str(kernel_key)] = patch
commit_patches(kernel_key=kernel_key, message=message)
missing_kernel_versions = kernel_versions.keys() - patches.keys()
if missing_kernel_versions:
print(
f"warning: no patches for kernel versions "
+ ", ".join(missing_kernel_versions),
file=sys.stderr,
)
if removals:
print("Hardened kernels were removed. Don't forget to remove their attributes!")
if failures:
sys.exit(1)
+16
View File
@@ -1413,6 +1413,22 @@ mapAliases {
'';
linux_latest_hardened = linuxPackages_latest_hardened;
# Added 2025-08-10
linuxPackages_hardened = linuxKernel.packages.linux_hardened;
linux_hardened = linuxPackages_hardened.kernel;
linuxPackages_5_4_hardened = linuxKernel.packages.linux_5_4_hardened;
linux_5_4_hardened = linuxKernel.kernels.linux_5_4_hardened;
linuxPackages_5_10_hardened = linuxKernel.packages.linux_5_10_hardened;
linux_5_10_hardened = linuxKernel.kernels.linux_5_10_hardened;
linuxPackages_5_15_hardened = linuxKernel.packages.linux_5_15_hardened;
linux_5_15_hardened = linuxKernel.kernels.linux_5_15_hardened;
linuxPackages_6_1_hardened = linuxKernel.packages.linux_6_1_hardened;
linux_6_1_hardened = linuxKernel.kernels.linux_6_1_hardened;
linuxPackages_6_6_hardened = linuxKernel.packages.linux_6_6_hardened;
linux_6_6_hardened = linuxKernel.kernels.linux_6_6_hardened;
linuxPackages_6_12_hardened = linuxKernel.packages.linux_6_12_hardened;
linux_6_12_hardened = linuxKernel.kernels.linux_6_12_hardened;
# Added 2023-11-18, modified 2024-01-09
linuxPackages_testing_bcachefs = throw "'linuxPackages_testing_bcachefs' has been removed, please use 'linuxPackages_latest', any kernel version at least 6.7, or any other linux kernel with bcachefs support";
linux_testing_bcachefs = throw "'linux_testing_bcachefs' has been removed, please use 'linux_latest', any kernel version at least 6.7, or any other linux kernel with bcachefs support";
-14
View File
@@ -10602,20 +10602,6 @@ with pkgs;
linux-rt = linuxPackages-rt.kernel;
linux-rt_latest = linuxPackages-rt_latest.kernel;
# hardened kernels
linuxPackages_hardened = linuxKernel.packages.linux_hardened;
linux_hardened = linuxPackages_hardened.kernel;
linuxPackages_5_10_hardened = linuxKernel.packages.linux_5_10_hardened;
linux_5_10_hardened = linuxKernel.kernels.linux_5_10_hardened;
linuxPackages_5_15_hardened = linuxKernel.packages.linux_5_15_hardened;
linux_5_15_hardened = linuxKernel.kernels.linux_5_15_hardened;
linuxPackages_6_1_hardened = linuxKernel.packages.linux_6_1_hardened;
linux_6_1_hardened = linuxKernel.kernels.linux_6_1_hardened;
linuxPackages_6_6_hardened = linuxKernel.packages.linux_6_6_hardened;
linux_6_6_hardened = linuxKernel.kernels.linux_6_6_hardened;
linuxPackages_6_12_hardened = linuxKernel.packages.linux_6_12_hardened;
linux_6_12_hardened = linuxKernel.kernels.linux_6_12_hardened;
# GNU Linux-libre kernels
linuxPackages-libre = linuxKernel.packages.linux_libre;
linux-libre = linuxPackages-libre.kernel;
+14 -10
View File
@@ -294,14 +294,10 @@ in
linux_latest_libre = deblobKernel packageAliases.linux_latest.kernel;
linux_hardened = hardenedKernelFor packageAliases.linux_default.kernel { };
linux_5_10_hardened = hardenedKernelFor kernels.linux_5_10 { };
linux_5_15_hardened = hardenedKernelFor kernels.linux_5_15 { };
linux_6_1_hardened = hardenedKernelFor kernels.linux_6_1 { };
linux_6_6_hardened = hardenedKernelFor kernels.linux_6_6 { };
linux_6_12_hardened = hardenedKernelFor kernels.linux_6_12 { };
linux_6_15_hardened = hardenedKernelFor kernels.linux_6_15 { };
linux_hardened = hardenedKernelFor packageAliases.linux_default.kernel { };
}
// lib.optionalAttrs config.allowAliases {
linux_4_19 = throw "linux 4.19 was removed because it will reach its end of life within 24.11";
@@ -311,6 +307,11 @@ in
linux_6_13 = throw "linux 6.13 was removed because it has reached its end of life upstream";
linux_6_14 = throw "linux 6.14 was removed because it has reached its end of life upstream";
linux_5_10_hardened = throw "linux_hardened on nixpkgs only contains latest stable and latest LTS";
linux_5_15_hardened = throw "linux_hardened on nixpkgs only contains latest stable and latest LTS";
linux_6_1_hardened = throw "linux_hardened on nixpkgs only contains latest stable and latest LTS";
linux_6_6_hardened = throw "linux_hardened on nixpkgs only contains latest stable and latest LTS";
linux_4_19_hardened = throw "linux 4.19 was removed because it will reach its end of life within 24.11";
linux_5_4_hardened = throw "linux_5_4_hardened was removed because it was broken";
linux_6_9_hardened = throw "linux 6.9 was removed because it has reached its end of life upstream";
@@ -776,11 +777,8 @@ in
linux_hardened = recurseIntoAttrs (packagesFor kernels.linux_hardened);
linux_5_10_hardened = recurseIntoAttrs (packagesFor kernels.linux_5_10_hardened);
linux_5_15_hardened = recurseIntoAttrs (packagesFor kernels.linux_5_15_hardened);
linux_6_1_hardened = recurseIntoAttrs (packagesFor kernels.linux_6_1_hardened);
linux_6_6_hardened = recurseIntoAttrs (packagesFor kernels.linux_6_6_hardened);
linux_6_12_hardened = recurseIntoAttrs (packagesFor kernels.linux_6_12_hardened);
linux_6_15_hardened = recurseIntoAttrs (packagesFor kernels.linux_6_15_hardened);
linux_zen = recurseIntoAttrs (packagesFor kernels.linux_zen);
linux_lqx = recurseIntoAttrs (packagesFor kernels.linux_lqx);
@@ -794,6 +792,12 @@ in
__recurseIntoDerivationForReleaseJobs = true;
}
// lib.optionalAttrs config.allowAliases {
linux_5_10_hardened = throw "linux_hardened on nixpkgs only contains latest stable and latest LTS";
linux_5_15_hardened = throw "linux_hardened on nixpkgs only contains latest stable and latest LTS";
linux_6_1_hardened = throw "linux_hardened on nixpkgs only contains latest stable and latest LTS";
linux_6_6_hardened = throw "linux_hardened on nixpkgs only contains latest stable and latest LTS";
linux_4_19_hardened = throw "linux 4.19 was removed because it will reach its end of life within 24.11";
linux_5_4_hardened = throw "linux_5_4_hardened was removed because it was broken";
linux_6_9_hardened = throw "linux 6.9 was removed because it has reached its end of life upstream";