#!/usr/bin/env python # vim:ts=4 sw=4 et: # # vulnerability_report.py # # dumps a vulnerability report, but will also do it based on full errata, not just what # is available in the clone channel a system is tied to. # This does assume satellite >= 5.4.0. I have no idea if 5.3 will work yet. import sys import xmlrpclib import pprint import ConfigParser from optparse import OptionParser def getArch(arch): ''' This is a function because the API doesn't return good data. Specifically system.listPackages() returns AMD64 for the arch instead of x86_64. If you try to use the arch value directly in packages.findByNvrea() it will blow up in your face. ''' if arch == 'AMD64': return 'x86_64' else: return arch parser = OptionParser() parser.add_option("-c", "--configfile", action="store", type="string", dest="configfile", help="Alternate config file to set defaults") parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Turn on Debug mode") parser.add_option("-l", "--login", dest="login", help="Satellite Server Login Name") parser.add_option("-p", "--password", dest="password", help="Satellite Server Password") parser.add_option("-u", "--url", dest="url", help="URL of Satellite Server") parser.set_defaults(configfile=False) parser.set_defaults(debug=False) parser.set_defaults(url='http://localhost/rpc/api') (options, args) = parser.parse_args() if __name__ == '__main__': login = False password = False # Read config file for options if options.configfile: config = ConfigParser.ConfigParser() config.read(options.configfile) try: url = config.get('config','url') except ConfigParser.NoOptionError: pass except ConfigParser.NoSectionError: parser.print_help() print print >>sys.stderr, 'No config section found in your config file.' print >>sys.stderr, 'Please verify that %s exists and has the correct syntax.' % (options.configfile) sys.exit(1) try: login = config.get('config','login') except ConfigParser.NoOptionError: pass try: password = config.get('config','password') except ConfigParser.NoOptionError: pass url = options.url debug = options.debug if options.login: login = options.login if options.password: password = options.password # Various checks up front to save time if login and password: client = xmlrpclib.Server(url, verbose=0) authKey = client.auth.login(login, password) else: parser.print_help() parser.error("Login or Password have not been defined on the commandline or in a config file") # Establish which system arch types to look for rhel5_x64 = False rhel4_x64 = False rhel3_x64 = False rhel5_i386 = False rhel4_i386 = False rhel3_i386 = False # get a list of servers to iterate over if debug: print >>sys.stderr, "Starting collection of system names" active_systems = [] if args: args.sort() for system in args: try: data = client.system.getId(authKey,system)[0] cpu = client.system.getCpu(authKey,data['id']) detail = client.system.getDetails(authKey,data['id']) if cpu['arch'] == 'x86_64': if detail['release'] == '5Server': rhel5_x64 = True if detail['release'] == '4AS': rhel4_x64 = True if detail['release'] == '3AS': rhel3_x64 = True if cpu['arch'] == 'i386' or cpu['arch'] == 'i686': if detail['release'] == '5Server': rhel5_i386 = True if detail['release'] == '4AS': rhel4_i386 = True if detail['release'] == '3AS': rhel3_i386 = True data['arch'] = cpu['arch'] data['release'] = detail['release'] active_systems.append(data) except: print >>sys.stderr, "ERROR: Could not identify servername: %s" % system else: systems = sorted(client.system.listActiveSystems(authKey), key=lambda k: k['name']) for system in systems: data = system cpu = client.system.getCpu(authKey,data['id']) detail = client.system.getDetails(authKey,data['id']) data['arch'] = cpu['arch'] data['release'] = detail['release'] active_systems.append(data) rhel5_x64 = True rhel4_x64 = True rhel3_x64 = True rhel5_i386 = True rhel4_i386 = True rhel3_i386 = True # ALL RHEL3-64 Packages if rhel3_x64: if debug: print >>sys.stderr, "Starting collection of RHEL3 64bit" rhel3 = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-3') rhel3_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-3-as-x86_64') rhel3_x64_all = rhel3 + rhel3_tools # ALL RHEL3-32 Packages if rhel3_i386: if debug: print >>sys.stderr, "Starting collection of RHEL3 32bit" rhel3 = client.channel.software.listAllPackages(authKey,'rhel-i386-as-3') rhel3_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-3-as-i386') rhel3_i386_all = rhel3 + rhel3_tools # ALL RHEL4-64 Packages if rhel4_x64: if debug: print >>sys.stderr, "Starting collection of RHEL4 64bit" rhel4 = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-4') rhel4_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-4-as-x86_64') rhel4_cluster = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-4-cluster') rhel4_gfs = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-4-gfs-6.1') rhel4_proxy = client.channel.software.listAllPackages(authKey,'redhat-rhn-proxy-5.1-as-x86_64-4') rhel4_extras = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-4-extras') rhel4_x64_all = rhel4 + rhel4_tools + rhel4_cluster + rhel4_gfs + rhel4_proxy + rhel4_extras # ALL RHEL4-32 Packages if rhel4_i386: if debug: print >>sys.stderr, "Starting collection of RHEL4 32bit" rhel4 = client.channel.software.listAllPackages(authKey,'rhel-i386-as-4') rhel4_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-4-as-i386') rhel4_cluster = client.channel.software.listAllPackages(authKey,'rhel-i386-as-4-cluster') rhel4_gfs = client.channel.software.listAllPackages(authKey,'rhel-i386-as-4-gfs-6.1') rhel4_proxy = client.channel.software.listAllPackages(authKey,'redhat-rhn-proxy-5.1-as-i386-4') rhel4_extras = client.channel.software.listAllPackages(authKey,'rhel-i386-as-4-extras') rhel4_i386_all = rhel4 + rhel4_tools + rhel4_cluster + rhel4_gfs + rhel4_proxy + rhel4_extras # ALL RHEL5-64 Packages if rhel5_x64: if debug: print >>sys.stderr, "Starting collection of RHEL5 64bit" rhel5 = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-5') rhel5_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-x86_64-server-5') rhel5_cluster_storage = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-cluster-storage-5') rhel5_cluster = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-cluster-5') rhel5_prod = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-productivity-5') rhel5_supp = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-supplementary-5') rhel5_virt = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-vt-5') rhel5_x64_all = rhel5 + rhel5_tools + rhel5_cluster_storage + rhel5_cluster + rhel5_prod + rhel5_supp + rhel5_virt # ALL RHEL5-32 Packages if rhel5_i386: if debug: print >>sys.stderr, "Starting collection of RHEL5 32bit" rhel5 = client.channel.software.listAllPackages(authKey,'rhel-i386-server-5') rhel5_proxy = client.channel.software.listAllPackages(authKey,'redhat-rhn-proxy-5.3-server-i386-5') rhel5_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-i386-server-5') rhel5_cluster_storage = client.channel.software.listAllPackages(authKey,'rhel-i386-server-cluster-storage-5') rhel5_cluster = client.channel.software.listAllPackages(authKey,'rhel-i386-server-cluster-5') rhel5_prod = client.channel.software.listAllPackages(authKey,'rhel-i386-server-productivity-5') rhel5_supp = client.channel.software.listAllPackages(authKey,'rhel-i386-server-supplementary-5') rhel5_i386_all = rhel5 + rhel5_proxy + rhel5_tools + rhel5_cluster_storage + rhel5_cluster + rhel5_prod + rhel5_supp for system in active_systems: # re-auth every system to avoid timeouts authKey = client.auth.login(login, password) system_package_list = client.system.listPackages(authKey,system['id']) package_dict = {} unmatched_packages = [] try: if system['arch'] == 'x86_64': if system['release'] == '5Server': if debug: print >>sys.stderr, "RHEL5 64bit collected" base_package_list = rhel5_x64_all elif system['release'] == '4AS': if debug: print >>sys.stderr, "RHEL4 64bit collected" base_package_list = rhel4_x64_all else: if debug: print >>sys.stderr, "RHEL3 64bit collected" base_package_list = rhel3_x64_all if system['arch'] == 'i386' or system['arch'] == 'i686': if system['release'] == '5Server': if debug: print >>sys.stderr, "RHEL5 32bit collected" base_package_list = rhel5_i386_all elif system['release'] == '4AS': if debug: print >>sys.stderr, "RHEL4 32bit collected" base_package_list = rhel4_i386_all else: if debug: print >>sys.stderr, "RHEL3 32bit collected" base_package_list = rhel3_i386_all except: base_package_list = [] print 'WARNING, Unable to find packages -- System: %s, Arch: %s, Release: %s' % (system['name'], system['arch'], system['release'],) continue for package in system_package_list: arch = getArch(package['arch']) matched = False for base_package in base_package_list: pname = package['name'] == base_package['name'] parch = arch == base_package['arch_label'] release = package['release'] == base_package['release'] version = package['version'] == base_package['version'] if pname: matched = True pkey = base_package['name']+base_package['arch_label'] package_dict.setdefault(pkey,{}) package_dict[pkey][base_package['id']] = {} package_dict[pkey][base_package['id']]['data'] = base_package if release and version: package_dict[pkey][base_package['id']]['current'] = True else: package_dict[pkey][base_package['id']]['current'] = False if not matched: unmatched_packages.append(package['name']+'.'+package['arch']) bugfix_list = {} security_list = {} enhancement_list = {} package_keys = package_dict.keys() package_keys.sort() for package in package_keys: get_advisory = False current = 0 pkg = package_dict[package] pkg_keys = pkg.keys() pkg_keys.sort() for pkey in pkg_keys: if pkg[pkey]['current']: if not pkey == pkg_keys[-1]: get_advisory = True current = pkg[pkey]['data']['id'] continue if get_advisory: advisories = client.packages.listProvidingErrata(authKey,pkg[pkey]['data']['id']) for advisory in advisories: if 'RH' in advisory['advisory']: if 'Bug Fix Advisory' in advisory['type']: try: bugfix_list[advisory['advisory']].append(pkg[pkey]['data']) except: bugfix_list[advisory['advisory']] = [] bugfix_list[advisory['advisory']].append(pkg[pkey]['data']) if 'Security Advisory' in advisory['type']: try: security_list[advisory['advisory']].append(pkg[pkey]['data']) except: security_list[advisory['advisory']] = [] security_list[advisory['advisory']].append(pkg[pkey]['data']) if 'Product Enhancement Advisory' in advisory['type']: try: enhancement_list[advisory['advisory']].append(pkg[pkey]['data']) except: enhancement_list[advisory['advisory']] = [] enhancement_list[advisory['advisory']].append(pkg[pkey]['data']) #print 'ADVISORY ALERT %s:%s -- Package: %s %s %s, Base Package: %s %s %s' % (advisory['advisory'], # advisory['type'], # pkg[current]['data']['name'], # pkg[current]['data']['version'], # pkg[current]['data']['release'], # pkg[pkey]['data']['name'], # pkg[pkey]['data']['version'], # pkg[pkey]['data']['release']) print 'System: %s, Arch: %s, Release: %s, Bugfixes: %d, Security Fixes: %d, Enhancements: %d' % (system['name'], system['arch'], system['release'], len(bugfix_list), len(security_list), len(enhancement_list)) if debug: print print 'UNMATCHED PACKAGES' pprint.pprint(unmatched_packages) print print 'BUGFIX LIST' pprint.pprint(bugfix_list) print print 'SECURITY LIST' pprint.pprint(security_list) print print 'ENHANCEMENT LIST' pprint.pprint(enhancement_list) sys.stdout.flush() client.auth.logout(authKey)