def runKadmin(self, query): """ Executes kadmin or kadmin.local and runs the requested query. Returns the resulting text from the output of kadmin(.local). TODO: Strip the authentication prompts from the beginning of stdout before returning text. """ # kadmin(.local) -r <realm> [-p <principal>] -q "request" options = '' if self.principal: options = " -p '%s' " %self.principal if self.realm: options = " -r '%s' " %self.realm # Fetch the path to the requested kadmin app kadmin_path = self.app_paths[self.kadmin_type] if os.access(self.credentials_path, os.X_OK): raise KadminDaemonOpError('%s does not exist or is not executable: %s' %(self.kadmin_type, self.app_paths[self.kadmin_type])) # Run kadmin.local via runExternalProcess since it doesn't ask for a password if self.kadmin_type == 'kadmin.local': kadmin_info = nixcommon.runExternalProcess("%s -q '%s' " %(kadmin_path, query)) if kadmin_info['return_code']: raise KadminExecError(kadmin_info['stdErr'] ) # Run kadmin normally, utilizing a keytab or credentials cache for auth else: # Determine what kind of auth we're using if self.auth_type == KRB_AUTH_CC: # Credentials Cache creds_switch = '-c' elif self.auth_type == KRB_AUTH_KT: # Keytab creds_switch = '-k -t' else: raise KadminInvalidAuthType(self.auth_type) # Make sure the credentials file exists if not os.access(self.credentials_path, os.R_OK): raise KadminInvalidCredentials(self.credentials_path) # Build auth into options list options = "%s %s %s" %(creds_switch, self.credentials_path, options) kadmin_cmd = "%s %s -q %s" %(kadmin_path, options, query) kadmin_info = nixcommon.runExternalProcess(kadmin_cmd) if kadmin_info['return_code']: raise KadminExecError(kadmin_info['stderr']) return kadmin_info
def setHostName(self): """ Sets the system's hostname. This includes modifying /etc/hosts, /etc/hostname and applying the hostname to the running server with ``hostname -F``. """ fqhn = "%s.%s" %(self.na_setup_config['hostName'], self.na_setup_config['dnsDomain']) hosts = { '127.0.0.1' : ('localhost'), '::1' : ('ip6-localhost', 'ip6-loopback'), 'fe00::0' : ('ip6-localnet'), 'ff00::0' : ('ip6-mcastprefix'), 'ff02::1' : ('ip6-allnodes'), 'ff02::2' : ('ip6-allrouters'), self.host_ip : (fqhn, self.na_setup_config['hostName']), } hosts_file = '' for ip in hosts: hosts_file += "%s\t" %ip if type(hosts[ip]) == tuple: for name in hosts[ip]: hosts_file += "%s\t" %name else: hosts_file += "%s" %hosts[ip] hosts_file += "\n" # Write /etc/hosts if not self.configFiles.backUpOriginalConfig('/etc/hosts'): raise HostnameSetupError('/etc/hosts doesn\'t exist! Your OS is broken, I can\'t continue!') try: fh = open('/etc/hosts', 'w') fh.write(hosts_file) fh.close() except: raise HostnameSetupError('Failed to write /etc/hosts') # Write /etc/hostname if not self.configFiles.backUpOriginalConfig('/etc/hostname'): raise HostnameSetupError('/etc/hostname doesn\'t exist! Your OS is broken, I can\'t continue!') try: fh = open('/etc/hostname', 'w') fh.write("%s\n" %self.na_setup_config['hostName']) fh.close() except: raise HostnameSetupError('Failed to write /etc/hostname') hostname_info = nixcommon.runExternalProcess('hostname -F /etc/hostname') if not hostname_info['return_code'] == 0: raise HostnameSetupError(hostname_info['std_err']) return
def setPermissions(self): from lib import nixcommon # Add openldap to sasl group (OpenLDAP can't access SASL mux pipe otherwise) userMod = nixcommon.runExternalProcess("/usr/sbin/usermod -G sasl openldap") if not userMod["return_code"] == 0: raise PermissionsSetError(userMod) return
def removeGroup(self, groupname): """ Removes the added group. Used for rollback procedures. """ groupdel = '%s %s' %(paths.General['groupdel'], groupname) groupdel_info = nixcommon.runExternalProcess(groupdel) if groupdel_info['return_code']: raise RollbackError(groupdel_info['stderr'])
def restart(self): """ Stops and starts the krb5kdc daemon. """ kdc_info = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'restart')) if kdc_info['return_code']: raise KDCDaemonOpError('Init script returned a bad status.', kdc_info) return
def restart(self): """ Restarts the kadmind daemon by stopping and starting it. """ kdcInfo = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'restart')) if kdcInfo['return_code']: raise KDCDaemonOpError('Init script returned a bad status.', kdcInfo) else: return True
def runOpenSSL(self, args): """ Runs the openssl command-line tool with the passed arguments. :param args: Argument string for openssl. """ cmd = '%s %s' %(paths['openssl'], args) openssl = nixcommon.runExternalProcess(cmd) return openssl
def installNixAuthPackageKeys(self): """ Installs the latest package signing key(s) from pi.nixauth.org (required for apt to install packages without errors). """ key_import_cmd = 'wget -O - http://pi.nixauth.org/package-signer.gpg.key | apt-key add -' key_import_info = nixcommon.runExternalProcess(key_import_cmd) if key_import_info['return_code']: raise KeyInstallError('Error installing gpg for apt:\n%s' %key_import_info['stderr'])
def restart(self): """ Stops and starts the saslauthd server. """ sasl_info = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'restart')) if sasl_info['return_code']: raise DaemonOpFailed('Init script returned a bad status.', sasl_info) return
def removeUser(self, username): """ Removes the added user. Used for rollback procedures. """ userdel = '%s %s' %(paths.General['userdel'], username) userdel_info = nixcommon.runExternalProcess(userdel) if userdel_info['return_code']: raise RollbackError(userdel_info['stderr'])
def addUser(self, username): """ Adds nixAuth's UNIX user (and group atm via the -user-group switch). """ useradd = '%s -d "%s" -M -s /bin/false --user-group -c "nixAuth role account" %s' %(paths.General['useradd'], self.na_setup_config['nixAuthRoot'], username) useradd_info = nixcommon.runExternalProcess(useradd) if useradd_info['return_code']: raise AddUserError(useradd_info['stderr'])
def _runRndc(self, cmd_line): """ Runs the requested command on rndc and returns the output. Protected function, this is not part of the API. """ # Format a proper rndc command and run it rndc_cmd = "%s %s" %(self._rndc, re.escape(cmd_line)) rndc_info = nixcommon.runExternalProcess(rndc_cmd) self.logger.debug('_runRndc: %s: returned: %s' %(rndc_cmd,rndc_info['return_code'])) return rndc_info
def isRunning(self): """ Checks to see if the ntpd daemon is running. :return: ``True`` if daemon is running, ``False`` otherwise. """ ntpd_info = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'status')) # Non-zero means the daemon isn't running if ntpd_info['return_code']: return False # Zero means daemon is running elif not ntpd_info['return_code']: return True
def start(self): """ Starts the kadmind daemon, if it is not already running. """ # if self.isRunning(): # return True kdcInfo = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'start')) # Daemon was already running if kdcInfo['return_code'] == 1: return True # Bad Status, fail properly elif kdcInfo['return_code']: raise KDCDaemonOpError('Init script returned a bad status.', kdcInfo) else: return True
def stop(self): """ Stops the ntpd daemon if it's running. """ if not self.isRunning(): return ntpd_info = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'stop')) # Daemon was already running if ntpd_info['return_code'] == 1: return # Bad Status, fail properly elif ntpd_info['return_code']: raise NTPDaemonOpError('Stop failed. Init script returned a bad status.') return
def restart(self): """ Restarts the ntpd daemon. """ if not self.isRunning(): return ntpd_info = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'restart')) # Restart failed if ntpd_info['return_code']: raise NTPDaemonOpError('Restart failed. Init script returned a bad status.') # Zero means we're good elif ntpd_info['return_code']: return return
def stop(self): """ Stops the saslauthd server, if it is running. """ if not self.isRunning(): return sasl_info = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'stop')) # Daemon was already stopped if sasl_info['return_code'] == 1: return # Bad Status, fail properly elif sasl_info['return_code']: raise DaemonOpFailed('Init script returned a bad status.', sasl_info) return
def stop(self): """ Stops the krb5kdc daemon, if running. """ # if not self.isRunning(): # return True kdc_info = nixcommon.runExternalProcess("%s %s" %(self.init_script, 'stop')) # Daemon was already stopped if kdc_info['return_code'] == 1: return # Bad Status, fail properly elif kdc_info['return_code']: raise KDCDaemonOpError('Init script returned a bad status.', kdc_info) return
def start(self): """ Starts named if it isn't running. This function calls the BIND init script since rndc isn't available to start the nameserver. """ self.logger.info('Attempting to start named') if self.isRunning(): self.logger.info('named is already running, doing nothing') return # Run the init script to start the nameserver init_script_cmd = "%s start" %self.init_script named_info = nixcommon.runExternalProcess(init_script_cmd) if named_info['return_code']: self.logger.error('named failed to start: %s' %named_info['stderr']) raise DaemonControlError('Init script did not complete properly. STDERR follows:\n%s\n' %named_info['stderr']) self.logger.info('named started successfully') return
def test_runExternalProcess(self, subprocess_mock): command = '/bin/ls' # This matches subprocess.PIPE since it's used in the default parameter values (which are not mockable) PIPE_value = -1 # Set up all needed values (incl a fake class for Popen) subprocess_mock.Popen.return_value.__enter__ = lambda s: s subprocess_mock.Popen.return_value.__exit__ = mock.Mock() subprocess_mock.Popen.return_value.communicate.return_value = ('TEST_STDOUT', 'TEST_STDERR') subprocess_mock.Popen.poll.return_value = 100 subprocess_mock.PIPE = PIPE_value nixcommon.subprocess = subprocess_mock return_values = nixcommon.runExternalProcess(command) nixcommon.subprocess.Popen.assert_called_once_with(command, shell=True, stderr=PIPE_value, stdout=PIPE_value) nixcommon.subprocess.Popen.return_value.communicate.assert_called_once_with() nixcommon.subprocess.Popen.poll.assert_called_once_with(nixcommon.subprocess.Popen.return_value) # Check the return values assert return_values['stdout'] == 'TEST_STDOUT' assert return_values['stderr'] == 'TEST_STDERR' assert return_values['return_code'] == 100
def rollback(self): """ naSetup API call. Rolls back all configuration changes performed by the :meth:`run` method. """ # Reset DNS resolution self.configFiles.restoreOriginalConfig('/etc/resolv.conf') # Remove DNS zone and config rcNameserver = bind.BIND() # Server control object rcNameserver.removeZone(self.domain) # Hostname setup rollback self.configFiles.restoreOriginalConfig('/etc/hosts') self.configFiles.restoreOriginalConfig('/etc/hostname') # Make sure we reload the hostname or weird stuff will happen hostname_info = nixcommon.runExternalProcess('hostname -F /etc/hostname') if not hostname_info['return_code'] == 0: raise HostnameSetupError(hostname_info['std_err']) return
def isTimeValid(self): """ Uses ntpdate to check the time against a ntp.org pool. This gives us the best chance to reach an active server. """ cmd = '%s -q 0.pool.ntp.org' %paths['ntpdate'] ntpdate_info = nixcommon.runExternalProcess(cmd) if ntpdate_info['return_code']: raise NTPInvalidTimeError("ntpdate returned an error: %s" %ntpdate_info['stderr']) time_info = ntpdate_info['stdout'].strip().split('\n')[-1:] # Get the final ntpdate output, with offset srv_info,offset = time_info[0].split('offset') if offset.strip() > config['Settings']['max_time_offset']: raise NTPInvalidTimeError("Time skew is greater than defined limit. Set: %s sec Actual: %s" %( config['Settings']['max_time_offset'],offset.strip())) return #--- #---
def authentication( self ): """ Tests authentication with saslauthd. This test requires that Kerberos is configured correctly! :return: ``True`` if passed, ``False`` otherwise """ testsaslauthd = self.app_paths.getConfigItem( 'SASL', 'testsaslauthd' ) cmd = '%s -r %s -u %s -p %s' %(testsaslauthd, self.na_setup_config['kerberosRealm'], self.na_setup_config['userAccount']['username'], self.na_setup_config['userAccount']['password']) testsaslauthd_info = nixcommon.runExternalProcess( cmd ) if not testsaslauthd_info['return_code'] == 0: raise AuthenticationFailed( testsaslauthd_info ) stdOut = testsaslauthd_info['stdout'].split( "\n" ) for line in stdOut: auth_info = line.split( ' ' ) if auth_info[1] == 'OK' and auth_info[2] == '"Success."': return True return False
def isRunning(self): """ Determines if the saslauthd server is running. :return: True if running, False otherwise """ running = False try: pidFile = open(config['Locations']['pidFile'], 'r') except IOError: return False saslPid = pidFile.read() sasl_info = nixcommon.runExternalProcess("/bin/ps ax | grep %s" %saslPid) stdOut = sasl_info['stdOut'].split("\n") for line in stdOut: if line[:len(saslPid.strip())] == saslPid.strip(): running = True break return running
def arePeersValid(self): """ Uses the ntpq program to ensure that the ntp server's peers are all available and of a reasonable stratum level (<=4 by default). """ # Run ntpq to fetch the peers list cmd = '%s -c lpeer localhost' %paths['ntpq'] ntpq_info = nixcommon.runExternalProcess(cmd) if ntpq_info['return_code']: raise NTPInvalidPeersError("ntpq returned an error: %s" %ntpq_info['stderr']) peers = ntpq_info['stdout'].strip().split('\n')[2:] # Fetch only the peer info, no decoration or headers if len(peers) < 1: raise NTPInvalidPeersError('No peers available.') for peer in peers: # remote,referenceid,stratum,t,when,poll,reach,delay,offset,jitter peer_info = peer.strip().split() if not (peer_info[2] <= config['Settings']['max_peer_stratum']): # Check stratum raise NTPStratumToHighError("Peer %s is stratum %s. Peers must be stratum %s or lower." %(peer_info[0], peer_info[2], config['Settings']['max_peer_stratum'])) return
if na_run_from: log.debug('nasetup run from %s' %na_run_from) con.prints('Running from %s' %na_run_from) elif not nixcommon.runningAsRoot(): log.error('Exiting due to lack of root privileges') con.prints('This script must be run as root! Exiting...') exit(-1) # ----------------------------------------------------------------------------- # System Clock Sync # ----------------------------------------------------------------------------- con.prints('Setting system clock to a public time server...') time_server = 'time.nixauth.org' ntpdate = nixcommon.runExternalProcess('ntpdate ' + time_server) # Need to localize this for people not in the US if not ntpdate['return_code']: log.warn('Failed to sync the system clock using NTP from server %s' %time_server) con.printError("Syncing this computer\'s clock to the ntp.org pool %s failed!" %time_server, False) else: log.info('Synced the system clock to %s with ntpdate' %time_server) # ----------------------------------------------------------------------------- # Set up the distro config # ----------------------------------------------------------------------------- distro = distrodetermine.whichDistro() if not distro['supported']: raise UnsupportedDistroError() log.debug('Symlinking distro and integration config')
con.prints(con.color('ERROR', 'red')) else: con.prints(con.color('OK', 'green'), no_newline = True) con.prints() # If we're missing files, alert the user and quit if needInstall: log.error('Detected missing applications: %s' %missing_apps) message = 'The following applications are missing: \n%s' %missing_apps con.printStatus('Error', message, quiet_mode) # Prevent recursive f*ckery if not na_run_from == 'napkginstall': if con.askBoolQuestion('Would you like me to run napkginstaller to install these applications'): log.info('Running package installer to fix missing packages') pkg_install_status = nixcommon.runExternalProcess('./napkginstall.py', True, None, None ) if not pkg_install_status['return_code']: log.error('naPkgInstall returned a non-zero code: %s' %pkg_install_status['return_code']) con.prints('Package installer returned an error, I can\'t continue.') exit(3) # TODO: Um... the generation needs to be re-run at this point else: exit(3) else: log.error('naPkgInstall failed to install some packages. This error is fatal, exiting') con.prints('Some applications were not installed by napkginstall!') exit(3) else: log.info('Writing paths config') # Store the paths to their config file
con.prints('Installing packages for %s, this could take a while...' %group_name) try: log.info('Installing packages for app group: %s' %group_name) distro_helper.installPackagesInGroup(group_name) except not KeyboardInterrupt: exception_info = sys.exc_info() log.error('Encountered error while installing packages: %s' %exception_info) con.printException(exception_info, 'Package install error occurred...', exit_on_err=True, exit_value=8) else: log.info('No missing packages installed, skipping installs') #------------------------------------------------- # Wrap Up #------------------------------------------------- log.info('All packages installed, starting PathBuilder') con.prints('\nLooks like everything we need is installed. Starting PathBuilder...') # Record the location of the binaries in the config path_builder = nixcommon.runExternalProcess('%s/bin/pathbuilder.py --narunfrom napkginstall' %NA_ROOT, stdout=None, stderr=None) if path_builder['return_code']: con.prints('PathBuilder did not complete properly, exiting package installer.') log.error('PathBuilder exited with non-zero code: %s' %path_builder['return_code']) exit(9) # Everything is installed! con.prints('\nAll required packages are installed and paths are set!') log.info('naPkgInstall completed') exit(0)