def WriteRootCaCerts(client): """Write the internal root CA certs to a file.""" logging.debug('WriteRootCaCerts') managed_installs_dir = munkicommon.pref('ManagedInstallDir') certs_dir = os.path.join(managed_installs_dir, 'certs') cert_file_path = os.path.join(certs_dir, 'ca.pem') if not os.path.isdir(certs_dir): os.makedirs(certs_dir) tmpfile = tempfile.NamedTemporaryFile( dir=os.path.dirname(os.path.realpath(cert_file_path))) tmpfile.write(client.GetSystemRootCACertChain()) logging.debug('WriteRootCaCerts: writing to tmp %s', tmpfile.name) try: os.unlink(cert_file_path) except OSError: pass try: os.link(tmpfile.name, cert_file_path) except OSError, e: tmpfile.close() raise client.Error('Error writing root CA certs: %s' % str(e))
def get_managed_install_report(): """Return Munki ManagedInstallsReport.plist as a plist dict. Returns: ManagedInstalls report for last Munki run as a plist dict, or an empty dict. """ # Checks munki preferences to see where the install directory is set to. managed_install_dir = munkicommon.pref('ManagedInstallDir') # set the paths based on munki's configuration. managed_install_report = os.path.join( managed_install_dir, 'ManagedInstallReport.plist') munkicommon.display_debug2( "Looking for munki's ManagedInstallReport.plist at {} ...".format( managed_install_report)) try: munki_report = FoundationPlist.readPlist(managed_install_report) except FoundationPlist.FoundationPlistException: munki_report = {} if 'MachineInfo' not in munki_report: munki_report['MachineInfo'] = {} munkicommon.display_debug2('ManagedInstallReport.plist:') munkicommon.display_debug2(format_plist(munki_report)) return munki_report
def UploadClientLogFiles(client): """Uploads the Munki client log files to the server. Args: client: A SimianAuthClient object. """ managed_installs_dir = munkicommon.pref('ManagedInstallDir') log_file_paths = [ os.path.join(managed_installs_dir, 'InstallInfo.plist'), os.path.join(managed_installs_dir, 'ManagedInstallReport.plist'), os.path.join(managed_installs_dir, 'Logs', 'ManagedSoftwareUpdate.log'), '/Users/Shared/.SelfServeManifest', '/var/log/system.log', '/var/log/debug.log', '/var/log/install.log', ] for log_file_path in log_file_paths: if os.path.exists(log_file_path): client.UploadFile(log_file_path, 'log') # Upload output of 'ps -ef'. return_code, stdout, _ = Exec(['/bin/ps', '-ef']) if not return_code: path = os.path.join( tempfile.mkdtemp(prefix='munki_ps_ef_output_', dir='/tmp'), 'ps_ef_output') f = open(path, 'w') f.write(stdout) f.close() client.UploadFile(path, 'log')
def formated_prefs(): """Formated dictionary object for output to plist""" my_dict = {} for pref in munki_prefs(): pref_value = pref_to_str(munkicommon.pref(pref)) my_dict.update({pref: pref_value}) return my_dict
def WriteRootCaCerts(client): """Write the internal root CA certs to a file.""" logging.debug('WriteRootCaCerts') managed_installs_dir = munkicommon.pref('ManagedInstallDir') certs_dir = os.path.join(managed_installs_dir, 'certs') cert_file_path = os.path.join(certs_dir, 'ca.pem') if not os.path.isdir(certs_dir): os.makedirs(certs_dir) tmpfile = tempfile.NamedTemporaryFile( dir=os.path.dirname(os.path.realpath(cert_file_path))) tmpfile.write(client.GetSystemRootCACertChain()) logging.debug('WriteRootCaCerts: writing to tmp %s', tmpfile.name) try: os.unlink(cert_file_path) except OSError: pass try: os.link(tmpfile.name, cert_file_path) except OSError as e: tmpfile.close() raise client.Error('Error writing root CA certs: %s' % str(e)) tmpfile.close() logging.debug('WriteRootCaCerts: success')
def CreateEmptyDirectory(attempt=0): """Create and/or maintain an empty directory. Args: attempt: int, default 0, the attempt number. Returns: str, path to empty directory Exits: with status STATUS_FAIL_CONFIG_SETUP[0] if MAX_ATTEMPTS have been made. """ if attempt == MAX_ATTEMPTS: logging.error('preflight failure setting up empty dir') sys.exit(STATUS_FAIL_CONFIG_SETUP[0]) time.sleep(attempt) managed_installs_dir = munkicommon.pref('ManagedInstallDir') path = os.path.join(managed_installs_dir, '.purposefully_empty_dir') remove = False create = False if os.path.exists(path): if os.path.isdir(path): if os.listdir(path): remove = 'd' else: remove = 'f' else: create = True if remove: try: if remove == 'd': shutil.rmtree(path) elif remove == 'f': os.unlink(path) except OSError as e: if e.args[0] == errno.ENOENT: # it went missing after we just found it. try to regain control. logging.critical('%s went missing after it existed', path) return CreateEmptyDirectory(attempt + 1) else: # some other error. return CreateEmptyDirectory(attempt + 1) if remove or create: try: os.mkdir(path) except OSError as e: if e.args[0] == errno.EEXIST: # it already exists. try to regain control of it. return CreateEmptyDirectory(attempt + 1) else: # some other error. try again. logging.critical('mkdir(%s) error: %s', path, str(e)) return CreateEmptyDirectory(attempt + 1) return path
def get_munkiprotocol(): """The protocol munki is using""" software_repo_url = pref_to_str(munkicommon.pref('SoftwareRepoURL')) try: url_parse = urlparse.urlparse(software_repo_url) return url_parse.scheme except AttributeError: return 'Could not obtain protocol'
def main(): # Skip a manual check if len(sys.argv) > 1: if sys.argv[1] == 'manualcheck': munkicommon.display_debug2("Manual check: skipping MunkiInfo Plugin") exit(0) data = {pref: str(munkicommon.pref(pref)) for pref in PREFS_TO_GET} add_plugin_results('MunkiInfo', data)
def main(): # Skip a manual check if len(sys.argv) > 1: if sys.argv[1] == 'manualcheck': # Manual check: skipping MunkiInfo Plugin exit(0) data = {pref: str(munkicommon.pref(pref)) for pref in PREFS_TO_GET} utils.add_plugin_results('MunkiInfo', data)
def GetManagedInstallReport(install_report_path=None): """Returns the ManagedInstallReport.plist plist object.""" if not install_report_path: managed_installs_dir = munkicommon.pref('ManagedInstallDir') install_report_path = os.path.join(managed_installs_dir, 'ManagedInstallReport.plist') try: install_report = fpl.readPlist(install_report_path) except fpl.NSPropertyListSerializationException, e: logging.debug('Error reading %s : %s', install_report_path, str(e)) return {}, install_report_path
def GetManagedInstallReport(install_report_path=None): """Returns the ManagedInstallReport.plist plist object.""" if not install_report_path: managed_installs_dir = munkicommon.pref('ManagedInstallDir') install_report_path = os.path.join( managed_installs_dir, 'ManagedInstallReport.plist') try: install_report = fpl.readPlist(install_report_path) except fpl.NSPropertyListSerializationException, e: logging.debug('Error reading %s : %s', install_report_path, str(e)) return {}, install_report_path
def _SetCustomCatalogURL(catalog_url): """Sets Software Update's CatalogURL to custom value.""" key = 'SimianCatalogURL' if munkicommon.pref(key) == catalog_url: return munkicommon.set_pref(key, catalog_url) os_version_tuple = munkicommon.getOsVersion(as_tuple=True) # set custom catalog url on each preflight for apple updates if os_version_tuple >= (10, 11): Exec(['/usr/sbin/softwareupdate', '--set-catalog', catalog_url]) Exec([ '/usr/bin/defaults', 'write', APPLE_SUS_PLIST, 'CatalogURL', catalog_url])
def UploadAllManagedInstallReports(on_corp, logout=False): """Uploads any installs, updates, uninstalls back to Simian server. If no reports of installs/updates/uninstalls exist to report, then this function only contacts the server if logout is True. Args: on_corp: str, on_corp status from GetClientIdentifier. logout: bool, default False, whether to logout or not when finished. """ # Report installs from the ManagedInstallsReport archives. archives_dir = os.path.join(munkicommon.pref('ManagedInstallDir'), 'Archives') if os.path.isdir(archives_dir): for fname in os.listdir(archives_dir): if not fname.startswith('ManagedInstallReport-'): continue install_report_path = os.path.join(archives_dir, fname) if not os.path.isfile(install_report_path): continue install_report, _ = GetManagedInstallReport( install_report_path=install_report_path) try: _UploadManagedInstallReport(on_corp, install_report) try: os.unlink(install_report_path) except (IOError, OSError): logging.warning( 'Failed to delete ManagedInstallsReport.plist: %s', install_report_path) except ServerRequestError: logging.exception( 'Error uploading ManagedInstallReport installs.') # Report installs from the current ManagedInstallsReport.plist. install_report, install_report_path = GetManagedInstallReport() try: _UploadManagedInstallReport(on_corp, install_report, logout=logout) # Clear reportable information now that is has been published. install_report['InstallResults'] = [] install_report['RemovalResults'] = [] install_report['ProblemInstalls'] = [] fpl.writePlist(install_report, install_report_path) except ServerRequestError: logging.exception('Error uploading ManagedInstallReport installs.')
def get_optional_manifest(): """Return Munki SelfServeManifest as a plist dict. Returns: SelfServeManifest for last Munki run as a plist dict, or an empty dict. """ # Checks munki preferences to see where the install directory is set to. managed_install_dir = munkicommon.pref('ManagedInstallDir') # set the paths based on munki's configuration. optional_manifest_path = pathlib.Path(managed_install_dir) / 'manifests/SelfServeManifest' try: optional_manifest = plistlib.loads(optional_manifest_path.read_bytes()) except (IOError, plistlib.InvalidFileException): optional_manifest = {} return optional_manifest
def UploadAllManagedInstallReports(on_corp, logout=False): """Uploads any installs, updates, uninstalls back to Simian server. If no reports of installs/updates/uninstalls exist to report, then this function only contacts the server if logout is True. Args: on_corp: str, on_corp status from GetClientIdentifier. logout: bool, default False, whether to logout or not when finished. """ # Report installs from the ManagedInstallsReport archives. archives_dir = os.path.join(munkicommon.pref('ManagedInstallDir'), 'Archives') if os.path.isdir(archives_dir): for fname in os.listdir(archives_dir): if not fname.startswith('ManagedInstallReport-'): continue install_report_path = os.path.join(archives_dir, fname) if not os.path.isfile(install_report_path): continue install_report, _ = GetManagedInstallReport( install_report_path=install_report_path) try: _UploadManagedInstallReport(on_corp, install_report) try: os.unlink(install_report_path) except (IOError, OSError): logging.warning( 'Failed to delete ManagedInstallsReport.plist: %s', install_report_path) except ServerRequestError: logging.exception('Error uploading ManagedInstallReport installs.') # Report installs from the current ManagedInstallsReport.plist. install_report, install_report_path = GetManagedInstallReport() try: _UploadManagedInstallReport(on_corp, install_report, logout=logout) # Clear reportable information now that is has been published. install_report['InstallResults'] = [] install_report['RemovalResults'] = [] install_report['ProblemInstalls'] = [] fpl.writePlist(install_report, install_report_path) except ServerRequestError: logging.exception('Error uploading ManagedInstallReport installs.')
def get_optional_manifest(): """Return Munki SelfServeManifest as a plist dict. Returns: SelfServeManifest for last Munki run as a plist dict, or an empty dict. """ # Checks munki preferences to see where the install directory is set to. managed_install_dir = munkicommon.pref('ManagedInstallDir') # set the paths based on munki's configuration. optional_manifest_path = os.path.join(managed_install_dir, 'manifests/SelfServeManifest') try: optional_manifest = FoundationPlist.readPlist(optional_manifest_path) except (IOError, FoundationPlist.NSPropertyListSerializationException): optional_manifest = {} return optional_manifest
def UploadAllManagedInstallReports(client, on_corp): """Uploads any installs, updates, uninstalls back to Simian server. Args: client: A SimianAuthClient. on_corp: str, on_corp status from GetClientIdentifier. """ # Report installs from the ManagedInstallsReport archives. archives_dir = os.path.join(munkicommon.pref('ManagedInstallDir'), 'Archives') if os.path.isdir(archives_dir): for fname in os.listdir(archives_dir): if not fname.startswith('ManagedInstallReport-'): continue install_report_path = os.path.join(archives_dir, fname) if not os.path.isfile(install_report_path): continue install_report, _ = GetManagedInstallReport( install_report_path=install_report_path) try: _UploadManagedInstallReport(client, on_corp, install_report) try: os.unlink(install_report_path) except (IOError, OSError): logging.warning( 'Failed to delete ManagedInstallsReport.plist: %s', install_report_path) except ServerRequestError: logging.exception( 'Error uploading ManagedInstallReport installs.') # Report installs from the current ManagedInstallsReport.plist. install_report, install_report_path = GetManagedInstallReport() try: _UploadManagedInstallReport(client, on_corp, install_report) # Clear reportable information now that is has been published. install_report['InstallResults'] = [] install_report['RemovalResults'] = [] install_report['ProblemInstalls'] = [] fpl.writePlist(install_report, install_report_path) except ServerRequestError: logging.exception('Error uploading ManagedInstallReport installs.')
def main(): # Skip a manual check if len(sys.argv) > 1: if sys.argv[1] == 'manualcheck': munkicommon.display_debug2( "Manual check: skipping MunkiInfo Plugin") exit(0) prefs_to_get = [ 'ManagedInstallDir', 'SoftwareRepoURL', 'ClientIdentifier', 'LogFile', 'LoggingLevel', 'LogToSyslog', 'InstallAppleSoftwareUpdates', 'AppleSoftwareUpdatesOnly', 'SoftwareUpdateServerURL', 'DaysBetweenNotifications', 'LastNotifiedDate', 'UseClientCertificate', 'SuppressUserNotification', 'SuppressAutoInstall', 'SuppressStopButtonOnInstall', 'PackageVerificationMode', 'FollowHTTPRedirects', 'UnattendedAppleUpdates', 'ClientCertificatePath', 'ClientKeyPath', 'LastAppleSoftwareUpdateCheck', 'LastCheckDate', 'LastCheckResult', 'LogFile', 'SoftwareRepoCACertificate', 'SoftwareRepoCAPath', 'PackageURL', 'CatalogURL', 'ManifestURL', 'IconURL', 'ClientResourceURL', 'ClientResourcesFilename', 'HelpURL', 'UseClientCertificateCNAsClientIdentifier', 'AdditionalHttpHeaders', 'SuppressLoginwindowInstall', 'InstallRequiresLogout', 'ShowRemovalDetail', 'MSULogEnabled', 'MSUDebugLogEnabled', 'LocalOnlyManifest', 'UnattendedAppleUpdates' ] plist_path = '/usr/local/sal/plugin_results.plist' if os.path.exists(plist_path): plist = FoundationPlist.readPlist(plist_path) else: plist = [] result = {} result['plugin'] = 'MunkiInfo' result['historical'] = False data = {} for the_pref in prefs_to_get: data[the_pref] = str(munkicommon.pref(the_pref)) result['data'] = data plist.append(result) FoundationPlist.writePlist(plist, plist_path)
def UploadAllManagedInstallReports(client, on_corp): """Uploads any installs, updates, uninstalls back to Simian server. Args: client: A SimianAuthClient. on_corp: str, on_corp status from GetClientIdentifier. """ # Report installs from the ManagedInstallsReport archives. archives_dir = os.path.join(munkicommon.pref('ManagedInstallDir'), 'Archives') if os.path.isdir(archives_dir): for fname in os.listdir(archives_dir): if not fname.startswith('ManagedInstallReport-'): continue install_report_path = os.path.join(archives_dir, fname) if not os.path.isfile(install_report_path): continue install_report, _ = GetManagedInstallReport( install_report_path=install_report_path) try: _UploadManagedInstallReport(client, on_corp, install_report) try: os.unlink(install_report_path) except (IOError, OSError): logging.warning( 'Failed to delete ManagedInstallsReport.plist: %s', install_report_path) except ServerRequestError: logging.exception('Error uploading ManagedInstallReport installs.') # Report installs from the current ManagedInstallsReport.plist. install_report, install_report_path = GetManagedInstallReport() try: _UploadManagedInstallReport(client, on_corp, install_report) # Clear reportable information now that is has been published. install_report['InstallResults'] = [] install_report['RemovalResults'] = [] install_report['ProblemInstalls'] = [] fpl.writePlist(install_report, install_report_path) except ServerRequestError: logging.exception('Error uploading ManagedInstallReport installs.')
def get_managed_install_report(): """Return Munki ManagedInstallsReport.plist as a plist dict. Returns: ManagedInstalls report for last Munki run as a plist dict, or an empty dict. """ # Checks munki preferences to see where the install directory is set to. managed_install_dir = munkicommon.pref('ManagedInstallDir') # set the paths based on munki's configuration. managed_install_report = pathlib.Path(managed_install_dir) / 'ManagedInstallReport.plist' try: munki_report = plistlib.loads(managed_install_report.read_bytes()) except (IOError, plistlib.InvalidFileException): munki_report = {} if 'MachineInfo' not in munki_report: munki_report['MachineInfo'] = {} return sal.unobjctify(munki_report)
def get_managed_install_report(): """Return Munki ManagedInstallsReport.plist as a plist dict. Returns: ManagedInstalls report for last Munki run as a plist dict, or an empty dict. """ # Checks munki preferences to see where the install directory is set to. managed_install_dir = munkicommon.pref('ManagedInstallDir') # set the paths based on munki's configuration. managed_install_report = os.path.join(managed_install_dir, 'ManagedInstallReport.plist') try: munki_report = FoundationPlist.readPlist(managed_install_report) except (IOError, FoundationPlist.NSPropertyListSerializationException): munki_report = {} if 'MachineInfo' not in munki_report: munki_report['MachineInfo'] = {} return utils.unobjctify(munki_report)
def UploadClientLogFiles(): """Uploads the Munki client log files to the server.""" params = [] # Upload select Munki logs. managed_installs_dir = munkicommon.pref('ManagedInstallDir') log_file_names = ['ManagedSoftwareUpdate.log'] for log_file_name in log_file_names: log_file_path = os.path.join(managed_installs_dir, 'Logs', log_file_name) if log_file_path: params.append('--uploadfile') params.append(log_file_path) # Upload system.log. params.append('--uploadfile') params.append('/var/log/system.log') # Upload install.log. params.append('--uploadfile') params.append('/var/log/install.log') # Upload output of 'ps -ef'. return_code, stdout, _ = Exec(['ps', '-ef']) if not return_code: path = os.path.join(tempfile.mkdtemp(dir='/tmp'), 'ps_ef_output') f = open(path, 'w') f.write(stdout) f.close() params.append('--uploadfile') params.append(path) # Inform simianauth which type of file(s) we're uploading. params.append('--uploadfiletype') params.append('log') PerformServerRequest(params)
def process(serial, items): """Process receives a list of items, checks if they need updating and updates them if necessary""" # Sanitize serial serial = ''.join([c for c in serial if c.isalnum()]) # Get prefs baseurl = pref('BaseUrl') or \ munkicommon.pref('SoftwareRepoURL') + '/report/' hashurl = baseurl + "index.php?/report/hash_check" checkurl = baseurl + "index.php?/report/check_in" # Get passphrase passphrase = pref('Passphrase') # Get hashes for all scripts for key, i in items.items(): if i.get('path'): i['hash'] = munkicommon.getmd5hash(i.get('path')) # Check dict check = {} for key, i in items.items(): if i.get('hash'): check[key] = {'hash': i.get('hash')} # Send hashes to server values = {'serial': serial,\ 'items': serialize(check),\ 'passphrase' : passphrase} response = curl(hashurl, values) server_data = response.read() # Decode response try: result = unserialize(server_data) except: display_error('Illegal response from the server: %s' % server_data) return -1 if result.get('error') != '': display_error('Server error: %s' % result['error']) return -1 # Retrieve hashes that need updating for i in items.keys(): if i in result: display_detail('Need to update %s' % i) if items[i].get('path'): try: f = open(items[i]['path'], "r") items[i]['data'] = f.read() except: display_warning("Can't open %s" % items[i]['path']) else: # delete items that don't have to be uploaded del items[i] # Send new files with hashes if len(items): display_detail('Sending items') response = curl(checkurl, {'serial': serial,\ 'items': serialize(items),\ 'passphrase': passphrase}) display_detail(response.read()) else: display_detail('No changes')
def main(): # Skip a manual check if len(sys.argv) > 1: if sys.argv[1] == 'manualcheck': munkicommon.display_debug2("Manual check: skipping MunkiInfo Plugin") exit(0) prefs_to_get = [ 'ManagedInstallDir', 'SoftwareRepoURL', 'ClientIdentifier', 'LogFile', 'LoggingLevel', 'LogToSyslog', 'InstallAppleSoftwareUpdates', 'AppleSoftwareUpdatesOnly', 'SoftwareUpdateServerURL', 'DaysBetweenNotifications', 'LastNotifiedDate', 'UseClientCertificate', 'SuppressUserNotification', 'SuppressAutoInstall', 'SuppressStopButtonOnInstall', 'PackageVerificationMode', 'FollowHTTPRedirects', 'UnattendedAppleUpdates', 'ClientCertificatePath', 'ClientKeyPath', 'LastAppleSoftwareUpdateCheck', 'LastCheckDate', 'LastCheckResult', 'LogFile', 'SoftwareRepoCACertificate', 'SoftwareRepoCAPath', 'PackageURL', 'CatalogURL', 'ManifestURL', 'IconURL', 'ClientResourceURL', 'ClientResourcesFilename', 'HelpURL', 'UseClientCertificateCNAsClientIdentifier', 'AdditionalHttpHeaders', 'SuppressLoginwindowInstall', 'InstallRequiresLogout', 'ShowRemovalDetail', 'MSULogEnabled', 'MSUDebugLogEnabled', 'LocalOnlyManifest', 'UnattendedAppleUpdates' ] plist_path = '/usr/local/sal/plugin_results.plist' if os.path.exists(plist_path): plist = FoundationPlist.readPlist(plist_path) else: plist = [] result = {} result['plugin'] = 'MunkiInfo' result['historical'] = False data = {} for the_pref in prefs_to_get: data[the_pref] = str(munkicommon.pref(the_pref)) result['data'] = data plist.append(result) FoundationPlist.writePlist(plist, plist_path)