diff --git a/DOWNLOAD_AND_VERIFY_ISO.md b/DOWNLOAD_AND_VERIFY_ISO.md index c72865113..0e726eaeb 100644 --- a/DOWNLOAD_AND_VERIFY_ISO.md +++ b/DOWNLOAD_AND_VERIFY_ISO.md @@ -1,17 +1,17 @@ -### 2.4.150-20250512 ISO image released on 2025/05/12 +### 2.4.150-20250522 ISO image released on 2025/05/22 ### Download and Verify -2.4.150-20250512 ISO image: -https://download.securityonion.net/file/securityonion/securityonion-2.4.150-20250512.iso +2.4.150-20250522 ISO image: +https://download.securityonion.net/file/securityonion/securityonion-2.4.150-20250522.iso -MD5: 7A7469A7A38EA9A2DB770C36AE36A0CA -SHA1: 7E768D515353F339DC536DED6207B786DAFF7D27 -SHA256: F8B2EB6B332F2367F0C097D211577565C8FB5CC7809E97D63687C634035B3699 +MD5: 239E69B83072BBF2602D4043FE53A160 +SHA1: C62893D3C7F5592665BFDCBC9A45BB20A926F9A8 +SHA256: 2ADE037C7FD34591030B1FAC10392C4E6613F152DD24BFBD897E57EE300895B9 Signature for ISO image: -https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.150-20250512.iso.sig +https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.150-20250522.iso.sig Signing key: https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.4/main/KEYS @@ -25,22 +25,22 @@ wget https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2. Download the signature file for the ISO: ``` -wget https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.150-20250512.iso.sig +wget https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.150-20250522.iso.sig ``` Download the ISO image: ``` -wget https://download.securityonion.net/file/securityonion/securityonion-2.4.150-20250512.iso +wget https://download.securityonion.net/file/securityonion/securityonion-2.4.150-20250522.iso ``` Verify the downloaded ISO image using the signature file: ``` -gpg --verify securityonion-2.4.150-20250512.iso.sig securityonion-2.4.150-20250512.iso +gpg --verify securityonion-2.4.150-20250522.iso.sig securityonion-2.4.150-20250522.iso ``` The output should show "Good signature" and the Primary key fingerprint should match what's shown below: ``` -gpg: Signature made Fri 09 May 2025 06:27:29 PM EDT using RSA key ID FE507013 +gpg: Signature made Thu 22 May 2025 11:15:06 AM EDT using RSA key ID FE507013 gpg: Good signature from "Security Onion Solutions, LLC " gpg: WARNING: This key is not certified with a trusted signature! gpg: There is no indication that the signature belongs to the owner. diff --git a/HOTFIX b/HOTFIX index e69de29bb..bfbfc7c42 100644 --- a/HOTFIX +++ b/HOTFIX @@ -0,0 +1 @@ +20250522 diff --git a/salt/common/tools/sbin/so-docker-prune b/salt/common/tools/sbin/so-docker-prune index 224cbd222..156c3b002 100755 --- a/salt/common/tools/sbin/so-docker-prune +++ b/salt/common/tools/sbin/so-docker-prune @@ -4,22 +4,16 @@ # or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at # https://securityonion.net/license; you may not use this file except in compliance with the # Elastic License 2.0. - - - -import sys, argparse, re, docker +import sys, argparse, re, subprocess, json from packaging.version import Version, InvalidVersion from itertools import groupby, chain - def get_image_name(string) -> str: return ':'.join(string.split(':')[:-1]) - def get_so_image_basename(string) -> str: return get_image_name(string).split('/so-')[-1] - def get_image_version(string) -> str: ver = string.split(':')[-1] if ver == 'latest': @@ -35,56 +29,75 @@ def get_image_version(string) -> str: return '999999.9.9' return ver +def run_command(command): + process = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if process.returncode != 0: + print(f"Error executing command: {command}", file=sys.stderr) + print(f"Error message: {process.stderr}", file=sys.stderr) + exit(1) + return process.stdout def main(quiet): - client = docker.from_env() - - # Prune old/stopped containers - if not quiet: print('Pruning old containers') - client.containers.prune() - - image_list = client.images.list(filters={ 'dangling': False }) - - # Map list of image objects to flattened list of tags (format: "name:version") - tag_list = list(chain.from_iterable(list(map(lambda x: x.attrs.get('RepoTags'), image_list)))) - - # Filter to only SO images (base name begins with "so-") - tag_list = list(filter(lambda x: re.match(r'^.*\/so-[^\/]*$', get_image_name(x)), tag_list)) - - # Group tags into lists by base name (sort by same projection first) - tag_list.sort(key=lambda x: get_so_image_basename(x)) - grouped_tag_lists = [ list(it) for _, it in groupby(tag_list, lambda x: get_so_image_basename(x)) ] - - no_prunable = True - for t_list in grouped_tag_lists: try: - # Group tags by version, in case multiple images exist with the same version string - t_list.sort(key=lambda x: Version(get_image_version(x)), reverse=True) - grouped_t_list = [ list(it) for _,it in groupby(t_list, lambda x: get_image_version(x)) ] - - # Keep the 2 most current version groups - if len(grouped_t_list) <= 2: - continue - else: - no_prunable = False - for group in grouped_t_list[2:]: - for tag in group: - if not quiet: print(f'Removing image {tag}') + # Prune old/stopped containers using docker CLI + if not quiet: print('Pruning old containers') + run_command('docker container prune -f') + + # Get list of images using docker CLI + images_json = run_command('docker images --format "{{json .}}"') + + # Parse the JSON output + image_list = [] + for line in images_json.strip().split('\n'): + if line: # Skip empty lines + image_list.append(json.loads(line)) + + # Extract tags in the format "name:version" + tag_list = [] + for img in image_list: + # Skip dangling images + if img.get('Repository') != "" and img.get('Tag') != "": + tag = f"{img.get('Repository')}:{img.get('Tag')}" + # Filter to only SO images (base name begins with "so-") + if re.match(r'^.*\/so-[^\/]*$', get_image_name(tag)): + tag_list.append(tag) + + # Group tags into lists by base name (sort by same projection first) + tag_list.sort(key=lambda x: get_so_image_basename(x)) + grouped_tag_lists = [list(it) for k, it in groupby(tag_list, lambda x: get_so_image_basename(x))] + + no_prunable = True + for t_list in grouped_tag_lists: try: - client.images.remove(tag, force=True) - except docker.errors.ClientError as e: - print(f'Could not remove image {tag}, continuing...') - except (docker.errors.APIError, InvalidVersion) as e: - print(f'so-{get_so_image_basename(t_list[0])}: {e}', file=sys.stderr) - exit(1) + # Group tags by version, in case multiple images exist with the same version string + t_list.sort(key=lambda x: Version(get_image_version(x)), reverse=True) + grouped_t_list = [list(it) for k, it in groupby(t_list, lambda x: get_image_version(x))] + # Keep the 2 most current version groups + if len(grouped_t_list) <= 2: + continue + else: + no_prunable = False + for group in grouped_t_list[2:]: + for tag in group: + if not quiet: print(f'Removing image {tag}') + try: + run_command(f'docker rmi -f {tag}') + except Exception as e: + print(f'Could not remove image {tag}, continuing...') + except (InvalidVersion) as e: + print(f'so-{get_so_image_basename(t_list[0])}: {e}', file=sys.stderr) + exit(1) + except Exception as e: + print('Unhandled exception occurred:') + print(f'so-{get_so_image_basename(t_list[0])}: {e}', file=sys.stderr) + exit(1) + + if no_prunable and not quiet: + print('No Security Onion images to prune') + except Exception as e: - print('Unhandled exception occurred:') - print(f'so-{get_so_image_basename(t_list[0])}: {e}', file=sys.stderr) - exit(1) - - if no_prunable and not quiet: - print('No Security Onion images to prune') - + print(f"Error: {e}", file=sys.stderr) + exit(1) if __name__ == "__main__": main_parser = argparse.ArgumentParser(add_help=False) diff --git a/sigs/securityonion-2.4.150-20250522.iso.sig b/sigs/securityonion-2.4.150-20250522.iso.sig new file mode 100644 index 000000000..aecfe4767 Binary files /dev/null and b/sigs/securityonion-2.4.150-20250522.iso.sig differ