def start_service(): with log_context({'instance', 'opsiclientd'}): logger.essential("opsiclientd service start") #logger.debug(os.environ) servicemanager.Initialize() servicemanager.PrepareToHostSingle(OpsiclientdService) servicemanager.StartServiceCtrlDispatcher()
def run(self): with log_context({'instance': 'control pipe'}): try: while not self._stopEvent.is_set(): if self.clientInfo: self.checkConnection() else: # Old protocol with self.comLock: request = self.read() if request: logger.info("Received request '%s' from %s", request, self) response = self.processIncomingRpc(request) logger.info("Sending response '%s' to %s", response, self) self.write(response) if self.clientInfo: # Switch to new protocol self.executeRpc('blockLogin', [ self._controller._opsiclientd. _blockLogin ], with_lock=False) # pylint: disable=protected-access time.sleep(0.5) except Exception as err: # pylint: disable=broad-except logger.error(err, exc_info=True) finally: self.clientDisconnected()
def executeRpc(self, method, *params): with log_context({'instance': 'control pipe'}): if not self._clients: raise RuntimeError("Cannot execute rpc, no client connected") if method == "loginUser" and not self.credentialProviderConnected( login_capable=True): raise RuntimeError( "Cannot execute rpc, no login capable opsi credential provider connected" ) responses = [] errors = [] for client in self._clients: if method == "loginUser" and not client.login_capable: continue response = client.executeRpc(method, params) responses.append(response) if response.get("error"): errors.append(response["error"]) if len(errors) == len(responses): raise RuntimeError(", ".join(errors)) return responses
def __init__(self, eventId, **kwargs): if not eventId: raise TypeError("Event id not given") self._id = str(eventId) # pylint: disable=invalid-name # Setting context here only succeeds if id is set with log_context({'instance', f'event config {self._id}'}): self.setConfig(kwargs)
def __init__(self, service, request, resource): with log_context({'instance': 'software on demand'}): WorkerOpsiJsonRpc.__init__(self, service, request, resource) self._auth_module = None if os.name == 'posix': import OPSI.Backend.Manager.Authentication.PAM # pylint: disable=import-outside-toplevel self._auth_module = OPSI.Backend.Manager.Authentication.PAM.PAMAuthentication( ) elif os.name == 'nt': import OPSI.Backend.Manager.Authentication.NT # pylint: disable=import-outside-toplevel self._auth_module = OPSI.Backend.Manager.Authentication.NT.NTAuthentication( "S-1-5-32-544")
def executeRpc(self, method, params=None, with_lock=True): params = params or [] with log_context({'instance': 'control pipe'}): rpc_id = 1 if not self.clientInfo: return { "id": rpc_id, "error": f"Cannot execute rpc, not supported by client {self}", "result": None } request = {"id": rpc_id, "method": method, "params": params} try: if with_lock: self.comLock.acquire() # pylint: disable=consider-using-with try: request_json = toJson(request) logger.info("Sending request '%s' to client %s", request_json, self) self.write(request_json) response_json = self.read() if not response_json: logger.warning( "No response for method '%s' received from client %s", request["method"], self) return {"id": rpc_id, "error": None, "result": None} logger.info("Received response '%s' from client %s", response_json, self) response = fromJson(response_json) if method == "loginUser" and response.get("result"): # Credential provider can only handle one successful login. # Ensure, that the credential provider is not used for a # second login if it keeps the pipe connection open. self.login_capable = False return response finally: if with_lock: self.comLock.release() except Exception as client_err: # pylint: disable=broad-except logger.error(client_err, exc_info=True) return {"id": rpc_id, "error": str(client_err), "result": None}
def run(self): with log_context({'instance': 'control pipe'}): self._running = True self.setup() try: while not self._stopEvent.is_set(): try: client, client_id = self.waitForClient() if self._stopEvent.is_set(): break with self._clientLock: connection = self.connection_class( self, client, client_id) self._clients.append(connection) connection.daemon = True connection.start() except Exception as err1: # pylint: disable=broad-except logger.error(err1, exc_info=True) self.setup() except Exception as err2: # pylint: disable=broad-except logger.error(err2, exc_info=True) self._running = False self.teardown()
def run(self): # pylint: disable=too-many-locals,too-many-branches,too-many-statements with log_context({'instance': 'service connection'}): logger.debug("ServiceConnectionThread started...") self.running = True self.connected = False self.cancelled = False try: # pylint: disable=too-many-nested-blocks verify_server_cert = ( config.get('global', 'verify_server_cert') or config.get('global', 'verify_server_cert_by_ca')) ca_cert_file = config.ca_cert_file self.prepare_ca_cert_file() compression = config.get('config_service', 'compression') if "localhost" in self._configServiceUrl or "127.0.0.1" in self._configServiceUrl: compression = False verify_server_cert = False if verify_server_cert: if os.path.exists(ca_cert_file): logger.info( "Server verification enabled, using CA cert file '%s'", ca_cert_file) else: logger.error( "Server verification enabled, but CA cert file '%s' not found, skipping verification", ca_cert_file) ca_cert_file = None verify_server_cert = False tryNum = 0 while not self.cancelled and not self.connected: tryNum += 1 try: logger.notice("Connecting to config server '%s' #%d", self._configServiceUrl, tryNum) self.setStatusMessage( _("Connecting to config server '%s' #%d") % (self._configServiceUrl, tryNum)) if len(self._username.split('.')) < 3: raise Exception( f"Domain missing in username '{self._username}'" ) logger.debug( "JSONRPCBackend address=%s, verify_server_cert=%s, ca_cert_file=%s, proxy_url=%s, application=%s", self._configServiceUrl, verify_server_cert, ca_cert_file, config.get('global', 'proxy_url'), f"opsiclientd/{__version__}") self.configService = JSONRPCBackend( address=self._configServiceUrl, username=self._username, password=self._password, verify_server_cert=verify_server_cert, ca_cert_file=ca_cert_file, proxy_url=config.get('global', 'proxy_url'), application=f"opsiclientd/{__version__}", compression=compression, ip_version=config.get('global', 'ip_version')) self.configService.accessControl_authenticated() # pylint: disable=no-member self.connected = True self.connectionError = None serverVersion = self.configService.serverVersion self.setStatusMessage( _("Connected to config server '%s'") % self._configServiceUrl) logger.notice( "Connected to config server '%s' (name=%s, version=%s)", self._configServiceUrl, self.configService.serverName, serverVersion) if serverVersion and (serverVersion[0] > 4 or (serverVersion[0] == 4 and serverVersion[1] > 1)): if not os.path.exists( config.ca_cert_file ) or verify_server_cert or config.get( 'global', 'install_opsi_ca_into_os_store'): # Renew CA if not exists or connection is verified try: update_ca_cert(self.configService, allow_remove=True) except Exception as err: # pylint: disable=broad-except logger.error(err, exc_info=True) except OpsiServiceVerificationError as verificationError: self.connectionError = forceUnicode(verificationError) self.setStatusMessage( _("Failed to connect to config server '%s': Service verification failure" ) % self._configServiceUrl) logger.error( "Failed to connect to config server '%s': %s", self._configServiceUrl, verificationError) break except Exception as error: # pylint: disable=broad-except self.connectionError = forceUnicode(error) self.setStatusMessage( _("Failed to connect to config server '%s': %s") % (self._configServiceUrl, forceUnicode(error))) logger.info( "Failed to connect to config server '%s': %s", self._configServiceUrl, error) logger.debug(error, exc_info=True) if isinstance(error, OpsiAuthenticationError): fqdn = System.getFQDN() try: fqdn = forceFqdn(fqdn) except Exception as fqdnError: # pylint: disable=broad-except logger.warning( "Failed to get fqdn from os, got '%s': %s", fqdn, fqdnError) break if self._username != fqdn: logger.notice( "Connect failed with username '%s', got fqdn '%s' from os, trying fqdn", self._username, fqdn) self._username = fqdn else: break if 'is not supported by the backend' in self.connectionError.lower( ): try: from cryptography.hazmat.backends import default_backend # pylint: disable=import-outside-toplevel logger.debug( "Got the following crypto backends: %s", default_backend()._backends) # pylint: disable=no-member,protected-access except Exception as cryptoCheckError: # pylint: disable=broad-except logger.debug( "Failed to get info about installed crypto modules: %s", cryptoCheckError) for _unused in range( 3): # Sleeping before the next retry time.sleep(1) except Exception as err: # pylint: disable=broad-except logger.error(err, exc_info=True) finally: self.running = False
def main(): # pylint: disable=too-many-locals,too-many-branches,too-many-statements if len(sys.argv) != 17: print( f"Usage: {os.path.basename(sys.argv[0])} <hostId> <hostKey> <controlServerPort>" " <logFile> <logLevel> <depotRemoteUrl> <depotDrive> <depotServerUsername> <depotServerPassword>" " <sessionId> <actionProcessorDesktop> <actionProcessorCommand> <actionProcessorTimeout>" " <runAsUser> <runAsPassword> <createEnvironment>") sys.exit(1) ( # pylint: disable=unbalanced-tuple-unpacking hostId, hostKey, controlServerPort, logFile, logLevel, depotRemoteUrl, depotDrive, depotServerUsername, depotServerPassword, sessionId, actionProcessorDesktop, actionProcessorCommand, actionProcessorTimeout, runAsUser, runAsPassword, createEnvironment) = sys.argv[1:] if hostKey: secret_filter.add_secrets(hostKey) if depotServerPassword: secret_filter.add_secrets(depotServerPassword) if runAsPassword: secret_filter.add_secrets(runAsPassword) init_logging(stderr_level=LOG_NONE, stderr_format=DEFAULT_STDERR_LOG_FORMAT, log_file=logFile, file_level=int(logLevel), file_format=DEFAULT_FILE_LOG_FORMAT) log_instance = f'{os.path.basename(sys.argv[0]).rsplit(".", 1)[0]}_s{sessionId}' with log_context({'instance': log_instance}): logger.debug( "Called with arguments: %s", ', '.join( (hostId, hostKey, controlServerPort, logFile, logLevel, depotRemoteUrl, depotDrive, depotServerUsername, depotServerPassword, sessionId, actionProcessorDesktop, actionProcessorCommand, actionProcessorTimeout, runAsUser, runAsPassword, createEnvironment))) language = "en" try: language = locale.getdefaultlocale()[0].split('_')[0] except Exception as err: # pylint: disable=broad-except logger.debug("Failed to find default language: %s", err) def _(string): """ Fallback function """ return string sp = None try: logger.debug("Loading translation for language '%s'", language) sp = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if os.path.exists(os.path.join(sp, "site-packages")): sp = os.path.join(sp, "site-packages") sp = os.path.join(sp, 'opsiclientd_data', 'locale') translation = gettext.translation('opsiclientd', sp, [language]) _ = translation.gettext except Exception as err: # pylint: disable=broad-except logger.debug("Failed to load locale for %s from %s: %s", language, sp, err) createEnvironment = bool( runAsUser and createEnvironment.lower() in ('yes', 'true', '1')) actionProcessorTimeout = int(actionProcessorTimeout) imp = None depotShareMounted = False be = None depot_url = urlparse(depotRemoteUrl) try: be = JSONRPCBackend( username=hostId, password=hostKey, address=f"https://127.0.0.1:{controlServerPort}/opsiclientd") if runAsUser: if getpass.getuser().lower() != runAsUser.lower(): logger.info("Impersonating user '%s'", runAsUser) imp = System.Impersonate(username=runAsUser, password=runAsPassword, desktop=actionProcessorDesktop) imp.start(logonType="INTERACTIVE", newDesktop=False, createEnvironment=createEnvironment) elif depot_url.scheme in ("smb", "cifs"): logger.info("Impersonating network account '%s'", depotServerUsername) imp = System.Impersonate(username=depotServerUsername, password=depotServerPassword, desktop=actionProcessorDesktop) imp.start(logonType="NEW_CREDENTIALS") if depot_url.hostname.lower() not in ("127.0.0.1", "localhost", "::1"): logger.notice("Mounting depot share %s", depotRemoteUrl) set_status_message(be, sessionId, _("Mounting depot share %s") % depotRemoteUrl) # pylint: disable=no-member if runAsUser or depot_url.scheme not in ("smb", "cifs"): System.mount(depotRemoteUrl, depotDrive, username=depotServerUsername, password=depotServerPassword) else: System.mount(depotRemoteUrl, depotDrive) depotShareMounted = True logger.notice("Starting action processor") set_status_message(be, sessionId, _("Action processor is running")) # pylint: disable=no-member if imp: imp.runCommand(actionProcessorCommand, timeoutSeconds=actionProcessorTimeout) else: System.execute(actionProcessorCommand, waitForEnding=True, timeout=actionProcessorTimeout) logger.notice("Action processor ended") set_status_message(be, sessionId, _("Action processor ended")) # pylint: disable=no-member except Exception as err: # pylint: disable=broad-except logger.error(err, exc_info=True) error = f"Failed to process action requests: {err}" logger.error(error) if be: set_status_message(be, sessionId, error) if depotShareMounted: try: logger.notice("Unmounting depot share") System.umount(depotDrive) except Exception: # pylint: disable=broad-except pass if imp: try: imp.end() except Exception: # pylint: disable=broad-except pass if be: try: be.backend_exit() except Exception: # pylint: disable=broad-except pass
def showPopup(self, message, mode='prepend', addTimestamp=True, displaySeconds=0): # pylint: disable=too-many-branches,too-many-statements, too-many-locals if mode not in ('prepend', 'append', 'replace'): mode = 'prepend' port = config.get('notification_server', 'popup_port') if not port: raise Exception('notification_server.popup_port not defined') notifierCommand = config.get('opsiclientd_notifier', 'command') if not notifierCommand: raise Exception('opsiclientd_notifier.command not defined') notifierCommand = f'{notifierCommand} -s {os.path.join("notifier", "popup.ini")}' if addTimestamp: message = "=== " + time.strftime( "%Y-%m-%d %H:%M:%S") + " ===\n" + message with self._popupNotificationLock: # pylint: disable=too-many-nested-blocks if (mode in ('prepend', 'append') and self._popupNotificationServer and self._popupNotificationServer.isListening()): # Already runnning try: for subject in self._popupNotificationServer.getSubjects(): if subject.getId() == 'message': if mode == 'prepend': message = message + "\n\n" + subject.getMessage( ) else: message = subject.getMessage( ) + "\n\n" + message break except Exception as err: # pylint: disable=broad-except logger.warning(err, exc_info=True) self.hidePopup() popupSubject = MessageSubject(id='message') choiceSubject = ChoiceSubject(id='choice') popupSubject.setMessage(message) logger.notice( "Starting popup message notification server on port %d", port) try: self._popupNotificationServer = NotificationServer( address="127.0.0.1", start_port=port, subjects=[popupSubject, choiceSubject]) self._popupNotificationServer.daemon = True with log_context({'instance': 'popup notification server'}): if not self._popupNotificationServer.start_and_wait( timeout=30): raise Exception( "Timed out while waiting for notification server") except Exception as err: # pylint: disable=broad-except logger.error("Failed to start notification server: %s", err) raise notifierCommand = notifierCommand.replace( '%port%', str(self._popupNotificationServer.port)).replace( '%id%', "popup") choiceSubject.setChoices([_('Close')]) choiceSubject.setCallbacks([self.popupCloseCallback]) sessionIds = System.getActiveSessionIds() if not sessionIds: sessionIds = [System.getActiveConsoleSessionId()] for sessionId in sessionIds: desktops = [None] if RUNNING_ON_WINDOWS: desktops = ["default", "winlogon"] for desktop in desktops: try: System.runCommandInSession(command=notifierCommand, sessionId=sessionId, desktop=desktop, waitForProcessEnding=False) except Exception as err: # pylint: disable=broad-except logger.error( "Failed to start popup message notifier app in session %s on desktop %s: %s", sessionId, desktop, err) class PopupClosingThread(threading.Thread): def __init__(self, opsiclientd, seconds): super().__init__() self.opsiclientd = opsiclientd self.seconds = seconds self.stopped = False def stop(self): self.stopped = True def run(self): while not self.stopped: time.sleep(1) if time.time() > self.seconds: break if not self.stopped: logger.debug("hiding popup window") self.opsiclientd.hidePopup() # last popup decides end time (even if unlimited) if self._popupClosingThread and self._popupClosingThread.is_alive( ): self._popupClosingThread.stop() if displaySeconds > 0: logger.debug("displaying popup for %s seconds", displaySeconds) self._popupClosingThread = PopupClosingThread( self, time.time() + displaySeconds) self._popupClosingThread.start()
def run(self): with log_context({'instance': 'opsiclientd'}): try: self._run() except Exception as err: # pylint: disable=broad-except logger.error(err, exc_info=True)
def main(): # pylint: disable=too-many-statements with log_context({'instance': os.path.basename(sys.argv[0])}): parser = ArgumentParser() parser.add_argument( '--version', action='version', version=f"{__version__} [python-opsi={python_opsi_version}]") parser.add_argument( '--log-level', default=LOG_NONE, type=int, choices=range(0, 10), help= ("Set the log level. " "0: nothing, 1: essential, 2: critical, 3: errors, 4: warnings, 5: notices " "6: infos, 7: debug messages, 8: trace messages, 9: secrets")) parser.add_argument('--log-file', help="Set log file") parser.add_argument('--address', default="https://127.0.0.1:4441/opsiclientd", help="Set service address") parser.add_argument('--username', help="Username to use for service connection.") parser.add_argument( '--password', help= "Password to use for service connection (default: opsi host key).") parser.add_argument( '--timeout', type=int, default=30, help="Read timeout for the rpc in seconds (default: 30).") parser.add_argument('rpc', help="The remote procedure call to execute.") log_file = None log_level = None address = None username = None password = None rpc = None timeout = None try: args = parser.parse_args() log_file = args.log_file log_level = args.log_level address = args.address username = args.username password = args.password rpc = args.rpc timeout = max(0, args.timeout) if not username and not password: try: password = get_opsi_host_key() secret_filter.add_secrets(password) except Exception as err: # pylint: disable=broad-except raise RuntimeError( f"Failed to read opsi host key from config file: {err}" ) from err except DeprecationWarning: # Fallback to legacy comandline arguments # <username> <password> <port> [debug-log-file] <rpc> (username, password, port, rpc) = sys.argv[1:5] # pylint: disable=unbalanced-tuple-unpacking secret_filter.add_secrets(password) address = f"https://127.0.0.1:{port}/opsiclientd" if len(sys.argv) > 5: log_level = LOG_DEBUG log_file = sys.argv[4] rpc = sys.argv[5] init_logging(log_file=log_file, file_level=log_level, stderr_level=LOG_NONE, file_format=DEFAULT_FILE_LOG_FORMAT) logger.debug( "log_file=%s, log_level=%s, address=%s, username=%s, password=%s, rpc=%s", log_file, log_level, address, username, password, rpc) try: jsonrpc = JSONRPCClient( # pylint: disable=unused-variable address=address, username=username, password=password, readtimeout=timeout, compression=False) logger.notice(f"Executing: {rpc}") result = eval(f"jsonrpc.{rpc}") # pylint: disable=eval-used print(result) except Exception as err: # pylint: disable=broad-except logger.error(err, exc_info=True) print(f"Error: {err}", file=sys.stderr) sys.exit(1)