Created
April 25, 2025 12:16
-
-
Save jonaslejon/fef7aae5fd6c539ce408a1dbf9a2b0ba to your computer and use it in GitHub Desktop.
OpenSSH EOL Linux dist checker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import xml.etree.ElementTree as ET | |
import re | |
from datetime import datetime | |
from colorama import init, Fore, Style | |
# Initialize colorama | |
init(autoreset=True) | |
# EOL Dates | |
ubuntu_eol = { | |
'16.04': '2021-04-30', # Xenial | |
'18.04': '2023-05-31', # Bionic | |
'20.04': '2025-04-30', # Focal | |
'22.04': '2027-04-30', # Jammy | |
} | |
debian_eol = { | |
'10': '2024-06-30', # Buster | |
'11': '2026-06-30', # Bullseye | |
'12': '2028-06-30', # Bookworm | |
} | |
def check_eol(distro, version): | |
today = datetime.today().date() | |
if distro == 'Ubuntu': | |
eol_date_str = ubuntu_eol.get(version) | |
elif distro == 'Debian': | |
eol_date_str = debian_eol.get(version) | |
else: | |
return None | |
if eol_date_str: | |
eol_date = datetime.strptime(eol_date_str, "%Y-%m-%d").date() | |
return today > eol_date | |
return None | |
def guess_ubuntu_version(package): | |
# Handle different Ubuntu package patterns | |
if package.startswith('3ubuntu0') or package.startswith('3ubuntu13'): | |
return '22.04' # Jammy | |
if package.startswith('1ubuntu7') or package.startswith('1ubuntu6'): | |
return '20.04' # Focal (sometimes older 18.04 too, safe assumption for now) | |
if package.startswith('4ubuntu0') or package.startswith('4ubuntu0.'): | |
return '18.04' # Bionic | |
if package.startswith('4ubuntu2') or '+esm' in package: | |
return '16.04' # Xenial | |
return None | |
def guess_debian_version(package): | |
# Handle different Debian package patterns | |
if 'deb12' in package: | |
return '12' # Bookworm | |
if 'deb11' in package: | |
return '11' # Bullseye | |
if 'deb10' in package: | |
return '10' # Buster | |
return None | |
def parse_nmap_ssh_banner(xml_file): | |
tree = ET.parse(xml_file) | |
root = tree.getroot() | |
for host in root.findall('host'): | |
ip_elem = host.find('address') | |
ip = ip_elem.get('addr') if ip_elem is not None else "Unknown" | |
for port in host.findall(".//port[@portid='22']"): | |
service = port.find('service') | |
if service is not None: | |
product = service.get('product', '') | |
version = service.get('version', '') | |
banner = f"{product} {version}".strip() | |
distro = None | |
release_version = None | |
esm = False | |
# Ubuntu detection | |
if 'Ubuntu' in version: | |
match = re.search(r'Ubuntu\s+([^\s]+)', version) | |
if match: | |
package = match.group(1) | |
if '+esm' in package: | |
esm = True | |
release_version = guess_ubuntu_version(package) | |
distro = 'Ubuntu' | |
# Debian detection | |
elif 'Debian' in version: | |
match = re.search(r'Debian\s+([^\s]+)', version) | |
if match: | |
package = match.group(1) | |
release_version = guess_debian_version(package) | |
distro = 'Debian' | |
# Final print | |
if distro and release_version: | |
is_eol = check_eol(distro, release_version) | |
if is_eol is True: | |
status = f"{Fore.RED}EOL" | |
elif is_eol is False: | |
status = f"{Fore.GREEN}Supported" | |
else: | |
status = f"{Fore.YELLOW}Unknown" | |
esm_note = " (ESM)" if esm else "" | |
print(f"{Fore.CYAN}{ip} - {Fore.MAGENTA}{banner} - {distro} {release_version}{esm_note} - {status}{Style.RESET_ALL}") | |
else: | |
if banner: | |
print(f"{Fore.CYAN}{ip} - {Fore.MAGENTA}{banner} - {Fore.YELLOW}Could not detect distro/version{Style.RESET_ALL}") | |
else: | |
print(f"{Fore.CYAN}{ip} - {Fore.YELLOW}No SSH banner detected{Style.RESET_ALL}") | |
def main(): | |
parser = argparse.ArgumentParser(description='Check if Linux distros (from SSH banners) are End of Life.') | |
parser.add_argument('xml_file', help='Path to Nmap XML output file') | |
args = parser.parse_args() | |
parse_nmap_ssh_banner(args.xml_file) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment