Merge pull request #85880 from emilazy/linux-hardened-update-resilience

This commit is contained in:
Jörg Thalheim 2020-04-24 12:24:23 +01:00 committed by GitHub
commit 16e4b9ca69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 109 additions and 87 deletions

View File

@ -1,27 +1,27 @@
{ {
"4.14.176": { "4.14": {
"name": "linux-hardened-4.14.176.a.patch",
"sha256": "0pr3m2j63mc746fcbzg1hlwv85im9f87qkl6r4033gwnpa9brcgk", "sha256": "0pr3m2j63mc746fcbzg1hlwv85im9f87qkl6r4033gwnpa9brcgk",
"url": "https://github.com/anthraxx/linux-hardened/releases/download/4.14.176.a/linux-hardened-4.14.176.a.patch", "url": "https://github.com/anthraxx/linux-hardened/releases/download/4.14.176.a/linux-hardened-4.14.176.a.patch"
"version_suffix": "a"
}, },
"4.19.117": { "4.19": {
"name": "linux-hardened-4.19.117.a.patch",
"sha256": "0c8dvh49nzypxwvsls10i896smvpdrk40x8ybljb3qk3r8j7niaw", "sha256": "0c8dvh49nzypxwvsls10i896smvpdrk40x8ybljb3qk3r8j7niaw",
"url": "https://github.com/anthraxx/linux-hardened/releases/download/4.19.117.a/linux-hardened-4.19.117.a.patch", "url": "https://github.com/anthraxx/linux-hardened/releases/download/4.19.117.a/linux-hardened-4.19.117.a.patch"
"version_suffix": "a"
}, },
"5.4.35": { "5.4": {
"name": "linux-hardened-5.4.34.a.patch",
"sha256": "1xwpqr9nzpjg837b3wnzb8fmrl2g9rz8gz5yb55vnnllbzbz36v6", "sha256": "1xwpqr9nzpjg837b3wnzb8fmrl2g9rz8gz5yb55vnnllbzbz36v6",
"url": "https://github.com/anthraxx/linux-hardened/releases/download/5.4.34.a/linux-hardened-5.4.34.a.patch", "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.4.34.a/linux-hardened-5.4.34.a.patch"
"version_suffix": "a"
}, },
"5.5.19": { "5.5": {
"name": "linux-hardened-5.5.19.a.patch",
"sha256": "1ya5nsfhr3nwz6qiz4pdhvm6k9mx1kr0prhdvhx3p40f1vk281sc", "sha256": "1ya5nsfhr3nwz6qiz4pdhvm6k9mx1kr0prhdvhx3p40f1vk281sc",
"url": "https://github.com/anthraxx/linux-hardened/releases/download/5.5.19.a/linux-hardened-5.5.19.a.patch", "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.5.19.a/linux-hardened-5.5.19.a.patch"
"version_suffix": "a"
}, },
"5.6.7": { "5.6": {
"name": "linux-hardened-5.6.6.a.patch",
"sha256": "0jiqh0frxirjbccgfdk007fca6r6n36n0pkqq4jszkckn59ayl7r", "sha256": "0jiqh0frxirjbccgfdk007fca6r6n36n0pkqq4jszkckn59ayl7r",
"url": "https://github.com/anthraxx/linux-hardened/releases/download/5.6.6.a/linux-hardened-5.6.6.a.patch", "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.6.6.a/linux-hardened-5.6.6.a.patch"
"version_suffix": "a"
} }
} }

View File

@ -39,16 +39,9 @@
}; };
hardened = let hardened = let
mkPatch = kernelVersion: patch: let mkPatch = kernelVersion: src: {
fullVersion = "${kernelVersion}.${patch.version_suffix}"; name = lib.removeSuffix ".patch" src.name;
name = "linux-hardened-${fullVersion}"; patch = fetchurl src;
in {
inherit name;
patch = fetchurl {
name = "${name}.patch";
inherit (patch) url sha256;
meta.maintainers = with lib.maintainers; [ emily ];
};
}; };
patches = builtins.fromJSON (builtins.readFile ./hardened-patches.json); patches = builtins.fromJSON (builtins.readFile ./hardened-patches.json);
in lib.mapAttrs mkPatch patches; in lib.mapAttrs mkPatch patches;

View File

@ -17,17 +17,7 @@ HERE = os.path.dirname(os.path.realpath(__file__))
HARDENED_GITHUB_REPO = 'anthraxx/linux-hardened' HARDENED_GITHUB_REPO = 'anthraxx/linux-hardened'
HARDENED_TRUSTED_KEY = os.path.join(HERE, 'anthraxx.asc') HARDENED_TRUSTED_KEY = os.path.join(HERE, 'anthraxx.asc')
HARDENED_PATCHES_PATH = os.path.join(HERE, 'hardened-patches.json') HARDENED_PATCHES_PATH = os.path.join(HERE, 'hardened-patches.json')
MIN_KERNEL = (4, 14) MIN_KERNEL_VERSION = [4, 14]
HARDENED_VERSION_RE = re.compile(r'''
(?P<kernel_version> [\d.]+) \.
(?P<version_suffix> [a-z]+)
''', re.VERBOSE)
def parse_version(version):
match = HARDENED_VERSION_RE.fullmatch(version)
if match:
return match.groups()
def run(*args, **kwargs): def run(*args, **kwargs):
try: try:
@ -78,11 +68,12 @@ def fetch_patch(*, name, release):
except StopIteration: except StopIteration:
raise KeyError(filename) raise KeyError(filename)
patch_filename = f'{name}.patch'
try: try:
patch_url = find_asset(f'{name}.patch') patch_url = find_asset(patch_filename)
sig_url = find_asset(f'{name}.patch.sig') sig_url = find_asset(patch_filename + '.sig')
except KeyError: except KeyError:
print(f'error: {name}.patch{{,sig}} not present', file=sys.stderr) print(f'error: {patch_filename}{{,.sig}} not present', file=sys.stderr)
return None return None
sha256, patch_path = nix_prefetch_url(patch_url) sha256, patch_path = nix_prefetch_url(patch_url)
@ -97,16 +88,32 @@ def fetch_patch(*, name, release):
return None return None
return { return {
'name': patch_filename,
'url': patch_url, 'url': patch_url,
'sha256': sha256, 'sha256': sha256,
} }
def commit_patches(*, kernel_version, message): def parse_version(version_str):
version = []
for component in version_str.split('.'):
try:
version.append(int(component))
except ValueError:
version.append(component)
return version
def version_string(version):
return '.'.join(str(component) for component in version)
def major_kernel_version_key(kernel_version):
return version_string(kernel_version[:-1])
def commit_patches(*, kernel_key, message):
with open(HARDENED_PATCHES_PATH + '.new', 'w') as new_patches_file: with open(HARDENED_PATCHES_PATH + '.new', 'w') as new_patches_file:
json.dump(patches, new_patches_file, indent=4, sort_keys=True) json.dump(patches, new_patches_file, indent=4, sort_keys=True)
new_patches_file.write('\n') new_patches_file.write('\n')
os.rename(HARDENED_PATCHES_PATH + '.new', HARDENED_PATCHES_PATH) os.rename(HARDENED_PATCHES_PATH + '.new', HARDENED_PATCHES_PATH)
message = f'linux/hardened-patches/{kernel_version}: {message}' message = f'linux/hardened-patches/{kernel_key}: {message}'
print(message) print(message)
if os.environ.get('COMMIT'): if os.environ.get('COMMIT'):
run( run(
@ -125,49 +132,74 @@ NIX_VERSION_RE = re.compile(r'''
''', re.VERBOSE) ''', re.VERBOSE)
# Get the set of currently packaged kernel versions. # Get the set of currently packaged kernel versions.
kernel_versions = set() kernel_versions = {}
for filename in os.listdir(HERE): for filename in os.listdir(HERE):
filename_match = re.fullmatch(r'linux-(\d+)\.(\d+)\.nix', filename) filename_match = re.fullmatch(r'linux-(\d+)\.(\d+)\.nix', filename)
if filename_match: if filename_match:
if tuple(int(v) for v in filename_match.groups()) < MIN_KERNEL:
continue
with open(os.path.join(HERE, filename)) as nix_file: with open(os.path.join(HERE, filename)) as nix_file:
for nix_line in nix_file: for nix_line in nix_file:
match = NIX_VERSION_RE.fullmatch(nix_line) match = NIX_VERSION_RE.fullmatch(nix_line)
if match: if match:
kernel_versions.add(match.group('version')) kernel_version = parse_version(match.group('version'))
if kernel_version < MIN_KERNEL_VERSION:
continue
kernel_key = major_kernel_version_key(kernel_version)
kernel_versions[kernel_key] = kernel_version
# Remove patches for old kernel versions. # Remove patches for unpackaged kernel versions.
for kernel_version in patches.keys() - kernel_versions: for kernel_key in sorted(patches.keys() - kernel_versions.keys()):
del patches[kernel_version] commit_patches(kernel_key=kernel_key, message='remove')
commit_patches(kernel_version=kernel_version, message='remove')
g = Github(os.environ.get('GITHUB_TOKEN')) g = Github(os.environ.get('GITHUB_TOKEN'))
repo = g.get_repo(HARDENED_GITHUB_REPO) repo = g.get_repo(HARDENED_GITHUB_REPO)
releases = repo.get_releases()
found_kernel_versions = set()
failures = False failures = False
for release in releases: # Match each kernel version with the best patch version.
remaining_kernel_versions = kernel_versions - found_kernel_versions releases = {}
for release in repo.get_releases():
if not remaining_kernel_versions: version = parse_version(release.tag_name)
break # needs to look like e.g. 5.6.3.a
if len(version) < 4:
version = release.tag_name
name = f'linux-hardened-{version}'
version_info = parse_version(version)
if not version_info:
continue continue
kernel_version, version_suffix = version_info
if kernel_version in remaining_kernel_versions: kernel_version = version[:-1]
found_kernel_versions.add(kernel_version) kernel_key = major_kernel_version_key(kernel_version)
try: try:
old_version_suffix = patches[kernel_version]['version_suffix'] packaged_kernel_version = kernel_versions[kernel_key]
old_version = f'{kernel_version}.{old_version_suffix}' except KeyError:
update = old_version_suffix < version_suffix continue
release_info = {
'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 kernel_version > packaged_kernel_version:
continue
elif (kernel_key not in releases or
releases[kernel_key]['version'] < version):
releases[kernel_key] = release_info
# Update hardened-patches.json for each release.
for kernel_key, release_info in releases.items():
release = release_info['release']
version = release_info['version']
version_str = release.tag_name
name = f'linux-hardened-{version_str}'
try:
old_filename = patches[kernel_key]['name']
old_version_str = (old_filename
.replace('linux-hardened-', '')
.replace('.patch', ''))
old_version = parse_version(old_version_str)
update = old_version < version
except KeyError: except KeyError:
update = True update = True
old_version = None old_version = None
@ -177,22 +209,19 @@ for release in releases:
if patch is None: if patch is None:
failures = True failures = True
else: else:
patch['version_suffix'] = version_suffix patches[kernel_key] = patch
patches[kernel_version] = patch
if old_version: if old_version:
message = f'{old_version} -> {version}' message = f'{old_version_str} -> {version_str}'
else: else:
message = f'init at {version}' message = f'init at {version_str}'
commit_patches(kernel_version=kernel_version, message=message) commit_patches(kernel_key=kernel_key, message=message)
missing_kernel_versions = kernel_versions - patches.keys() missing_kernel_versions = kernel_versions.keys() - patches.keys()
if missing_kernel_versions: if missing_kernel_versions:
print( print(
f'warning: no patches for kernel versions ' + f'warning: no patches for kernel versions ' +
', '.join(missing_kernel_versions) + ', '.join(missing_kernel_versions),
'\nwarning: consider manually backporting older patches (bump '
'JSON key, set version_suffix to "NixOS-a")',
file=sys.stderr, file=sys.stderr,
) )

View File

@ -17051,7 +17051,7 @@ in
}; };
kernelPatches = kernel.kernelPatches ++ [ kernelPatches = kernel.kernelPatches ++ [
kernelPatches.tag_hardened kernelPatches.tag_hardened
kernelPatches.hardened.${kernel.version} kernelPatches.hardened.${kernel.meta.branch}
]; ];
modDirVersionArg = kernel.modDirVersion + "-hardened"; modDirVersionArg = kernel.modDirVersion + "-hardened";
}); });