def _decrypt(output, encryption_settings): # type: (RawAgentData, Dict[str, str]) -> RawAgentData if output.startswith(b"<<<"): # The output is not encrypted. if encryption_settings["use_regular"] == "enforce": raise MKAgentError( "Agent output is plaintext but encryption is enforced by configuration" ) return output if encryption_settings["use_regular"] not in ["enforce", "allow"]: return output try: # simply check if the protocol is an actual number protocol = int(output[0:2]) output = TCPDataSource._decrypt_package( output[2:], encryption_settings["passphrase"], protocol) except ValueError: raise MKAgentError("Unsupported protocol version: %s" % str(output[:2])) except Exception as e: if encryption_settings["use_regular"] == "enforce": raise MKAgentError("Failed to decrypt agent output: %s" % e) # of course the package might indeed have been encrypted but # in an incorrect format, but how would we find that out? # In this case processing the output will fail return output
def _execute(self): # type: () -> RawAgentData if self._use_only_cache: raise MKAgentError( "Got no data: No usable cache file present at %s" % self._cache_file_path()) self._verify_ipaddress() output = self._fetch_raw_data( socket.socket( socket.AF_INET6 if self._host_config.is_ipv6_primary else socket.AF_INET, socket.SOCK_STREAM), ( self._ipaddress, self.port, ), self.timeout, self._logger) if not output: # may be caused by xinetd not allowing our address raise MKEmptyAgentData("Empty output from agent at TCP port %s" % self.port) if len(output) < 16: raise MKAgentError("Too short output from agent: %r" % output) output = self._decrypt(output, self._host_config.agent_encryption) return output
def _decrypt(self, output): # type: (RawAgentData) -> RawAgentData if output.startswith(b"<<<"): self._logger.debug("Output is not encrypted") if self._encryption_settings["use_regular"] == "enforce": raise MKAgentError( "Agent output is plaintext but encryption is enforced by configuration" ) return output if self._encryption_settings["use_regular"] not in [ "enforce", "allow" ]: self._logger.debug("Output is not encrypted") return output try: self._logger.debug("Decrypt encrypted output") output = self._real_decrypt(output) except MKAgentError: raise except Exception as e: if self._encryption_settings["use_regular"] == "enforce": raise MKAgentError("Failed to decrypt agent output: %s" % e) # of course the package might indeed have been encrypted but # in an incorrect format, but how would we find that out? # In this case processing the output will fail return output
def _execute(self): # type: () -> RawAgentData if self._use_only_cache: raise MKAgentError( "Got no data: No usable cache file present at %s" % self._cache_file_path()) verify_ipaddress(self._ipaddress) assert self._ipaddress with TCPDataFetcher( socket.AF_INET6 if self._host_config.is_ipv6_primary else socket.AF_INET, (self._ipaddress, self.port), self.timeout, self._host_config.agent_encryption, ) as fetcher: output = fetcher.data() if not output: # may be caused by xinetd not allowing our address raise MKEmptyAgentData("Empty output from agent at %s:%d" % (self._ipaddress, self.port)) if len(output) < 16: raise MKAgentError("Too short output from agent: %r" % output) return output raise MKAgentError("Failed to read data")
def _get_agent_info_program(self, commandline, command_stdin): # type: (Union[bytes, Text], Optional[bytes]) -> RawAgentData exepath = commandline.split()[0] # for error message, hide options! self._logger.debug("Calling external program %r" % (commandline)) p = None try: if config.monitoring_core == "cmc": p = subprocess.Popen( # nosec commandline, shell=True, stdin=subprocess.PIPE if command_stdin else open(os.devnull), stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid, close_fds=True, ) else: # We can not create a separate process group when running Nagios # Upon reaching the service_check_timeout Nagios only kills the process # group of the active check. p = subprocess.Popen( # nosec commandline, shell=True, stdin=subprocess.PIPE if command_stdin else open(os.devnull), stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, ) if command_stdin: stdout, stderr = p.communicate(input=ensure_bytestr(command_stdin)) else: stdout, stderr = p.communicate() exitstatus = p.returncode except MKTimeout: # On timeout exception try to stop the process to prevent child process "leakage" if p: os.killpg(os.getpgid(p.pid), signal.SIGTERM) p.wait() raise finally: # The stdout and stderr pipe are not closed correctly on a MKTimeout # Normally these pipes getting closed after p.communicate finishes # Closing them a second time in a OK scenario won't hurt neither.. if p: if p.stdout is None or p.stderr is None: raise Exception("stdout needs to be set") p.stdout.close() p.stderr.close() if exitstatus: if exitstatus == 127: raise MKAgentError("Program '%s' not found (exit code 127)" % exepath) else: raise MKAgentError("Agent exited with code %d: %s" % (exitstatus, stderr)) return stdout
def _execute(self) -> RawAgentData: if not self._credentials: raise MKAgentError("Missing credentials") if self.ipaddress is None: raise MKAgentError("Missing IP address") with IPMIDataFetcher(self.ipaddress, self._credentials["username"], self._credentials["password"]) as fetcher: return fetcher.data() raise MKAgentError("Failed to read data")
def _execute( self, *, selected_raw_sections: Optional[SelectedRawSections], ) -> AgentRawData: if TCPConfigurator._use_only_cache: raise MKAgentError("Got no data: No usable cache file present at %s" % self.configurator.file_cache.path) with TCPFetcher.from_json(self.configurator.configure_fetcher()) as fetcher: return fetcher.fetch() raise MKAgentError("Failed to read data")
def configure_fetcher(self) -> Dict[str, Any]: if not self.credentials: raise MKAgentError("Missing credentials") if self.ipaddress is None: raise MKAgentError("Missing IP address") return { "file_cache": self.file_cache.configure(), "address": self.ipaddress, "username": self.credentials["username"], "password": self.credentials["password"], }
def _execute(self): # type: () -> RawAgentData if not self._credentials: raise MKAgentError("Missing credentials") if self._ipaddress is None: raise MKAgentError("Missing IP address") with IPMIDataFetcher(self._ipaddress, self._credentials["username"], self._credentials["password"], self._logger) as fetcher: data = fetcher.data() return data
def _make_fetcher(self) -> IPMIFetcher: if not self.credentials: raise MKAgentError("Missing credentials") if self.ipaddress is None: raise MKAgentError("Missing IP address") return IPMIFetcher( self._make_file_cache(), address=self.ipaddress, username=self.credentials["username"], password=self.credentials["password"], )
def _sensors_section(self): # type: () -> RawAgentData if self._command is None: raise MKAgentError("Not connected") self._logger.debug("Fetching sensor data via UDP from %s:623", self._command.bmc) try: sdr = ipmi_sdr.SDR(self._command) except NotImplementedError as e: self._logger.log(VERBOSE, "Failed to fetch sensor data: %r", e) self._logger.debug("Exception", exc_info=True) return b"" sensors = [] has_no_gpu = not self._has_gpu() for number in sdr.get_sensor_numbers(): rsp = self._command.raw_command(command=0x2d, netfn=4, data=(number, )) if 'error' in rsp: continue reading = sdr.sensors[number].decode_sensor_reading(rsp['data']) if reading is not None: # sometimes (wrong) data for GPU sensors is reported, even if # not installed if "GPU" in reading.name and has_no_gpu: continue sensors.append(_parse_sensor_reading(number, reading)) return b"<<<mgmt_ipmi_sensors:sep(124)>>>\n" + b"".join( [b"|".join(sensor) + b"\n" for sensor in sensors])
def _fetch_raw_data(sock, address, timeout, logger): # type: (socket.socket, Tuple[Optional[str], int], float, logging.Logger) -> RawAgentData output_lines = [] # type: List[bytes] logger.debug("Connecting via TCP to %s:%d (%ss timeout)", address[0], address[1], timeout) try: sock.settimeout(timeout) sock.connect(address) sock.settimeout(None) logger.debug("Reading data from agent") while True: data = sock.recv(4096, socket.MSG_WAITALL) if not data: break output_lines.append(data) except socket.error as e: if cmk.utils.debug.enabled(): raise raise MKAgentError("Communication failed: %s" % e) finally: sock.close() return b''.join(output_lines)
def data(self): # type: () -> RawAgentData if self._process is None: raise MKAgentError("No process") stdout, stderr = self._process.communicate( input=ensure_bytestr(self._stdin) if self._stdin else None) if self._process.returncode == 127: exepath = self._cmdline.split()[ 0] # for error message, hide options! raise MKAgentError("Program '%s' not found (exit code 127)" % six.ensure_str(exepath)) if self._process.returncode: raise MKAgentError( "Agent exited with code %d: %s" % (self._process.returncode, six.ensure_str(stderr))) return stdout
def _get_raw_data( self, *, selected_raw_sections: Optional[SelectedRawSections], ) -> Tuple[BoundedAbstractRawData, bool]: """Returns the current raw data of this data source It either uses previously cached raw data of this data source or executes the data source to get new data. The "raw data" is the raw byte string returned by the source for AgentDataSource sources. The SNMPDataSource source already return the final info data structure. """ file_cache = self._make_file_cache() raw_data = file_cache.read() if raw_data: self._logger.log(VERBOSE, "Use cached data") return raw_data, True if raw_data is None and config.simulation_mode: raise MKAgentError("Got no data (Simulation mode enabled and no cachefile present)") self._logger.log(VERBOSE, "Execute data source") raw_data = self._execute(selected_raw_sections=selected_raw_sections) file_cache.write(raw_data) return raw_data, False
def _get_raw_data(self): # type: () -> Tuple[BoundedAbstractRawData, bool] """Returns the current raw data of this data source It either uses previously cached raw data of this data source or executes the data source to get new data. The "raw data" is the raw byte string returned by the source for CheckMKAgentDataSource sources. The SNMPDataSource source already return the final info data structure. """ raw_data = self._read_cache_file() if raw_data: self._logger.log(VERBOSE, "Use cached data") return raw_data, True elif raw_data is None and config.simulation_mode: raise MKAgentError( "Got no data (Simulation mode enabled and no cachefile present)" ) self._logger.log(VERBOSE, "Execute data source") raw_data = self._execute() self._write_cache_file(raw_data) return raw_data, False
def _raw_data(self): # type: () -> RawAgentData self._logger.debug("Reading data from agent") if not self._socket: return b"" def recvall(sock): # type: (socket.socket) -> bytes buffer = [] # type: List[bytes] while True: data = sock.recv(4096, socket.MSG_WAITALL) if not data: break buffer.append(data) return b"".join(buffer) try: output = recvall(self._socket) if not output: # may be caused by xinetd not allowing our address raise MKEmptyAgentData("Empty output from agent at %s:%d" % self._address) return output except socket.error as e: if cmk.utils.debug.enabled(): raise raise MKAgentError("Communication failed: %s" % e)
def _real_decrypt(self, output): # type: (RawAgentData) -> RawAgentData try: # simply check if the protocol is an actual number protocol = int(output[:2]) except ValueError: raise MKAgentError("Unsupported protocol version: %r" % output[:2]) encrypted_pkg = output[2:] encryption_key = self._encryption_settings["passphrase"] encrypt_digest = sha256 if protocol == 2 else md5 # Adapt OpenSSL handling of key and iv def derive_key_and_iv(password, key_length, iv_length): # type: (bytes, int, int) -> Tuple[bytes, bytes] d = d_i = b'' while len(d) < key_length + iv_length: d_i = encrypt_digest(d_i + password).digest() d += d_i return d[:key_length], d[key_length:key_length + iv_length] key, iv = derive_key_and_iv(encryption_key.encode("utf-8"), 32, AES.block_size) decryption_suite = AES.new(key, AES.MODE_CBC, iv) decrypted_pkg = decryption_suite.decrypt(encrypted_pkg) # Strip of fill bytes of openssl return decrypted_pkg[0:-decrypted_pkg[-1]]
def _execute(self): # type: () -> RawAgentData with PiggyBackDataFetcher(self._hostname, self._ipaddress, self._time_settings) as fetcher: self._summary = fetcher.summary() return fetcher.data() raise MKAgentError("Failed to read data")
def _execute(self): # type: () -> RawAgentData self._logger.debug("Calling external program %r" % (self.source_cmdline)) with ProgramDataFetcher(self.source_cmdline, self.source_stdin, self._logger) as fetcher: return fetcher.data() raise MKAgentError("Failed to read data")
def __exit__(self, exc_type, exc_value, traceback): # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]) -> bool self.close() if isinstance(exc_type, IpmiException) and not exc_value: # Raise a more specific exception raise MKAgentError("IPMI communication failed: %r" % exc_type) if not cmk.utils.debug.enabled(): return False return True
def _execute( self, *, selected_raw_sections: Optional[SelectedRawSections], ) -> AgentRawData: # TODO(ml): Do something with the selection. with ProgramFetcher.from_json(self.configurator.configure_fetcher()) as fetcher: return fetcher.fetch() raise MKAgentError("Failed to read data")
def _execute(self) -> SNMPRawData: ip_lookup.verify_ipaddress(self.ipaddress) with SNMPDataFetcher( self._make_oid_infos(), self._use_snmpwalk_cache, self._snmp_config, ) as fetcher: return fetcher.data() raise MKAgentError("Failed to read data")
def _execute( self, *, selected_raw_sections: Optional[SelectedRawSections], ) -> AgentRawData: with PiggyBackFetcher.from_json( self.configurator.configure_fetcher()) as fetcher: return fetcher.fetch() raise MKAgentError("Failed to read data")
def _execute( self, *, selected_raw_sections: Optional[SelectedRawSections], ) -> RawAgentData: self._summary = self._summarize() with PiggyBackDataFetcher(self.hostname, self.ipaddress, self._time_settings) as fetcher: return fetcher.data() raise MKAgentError("Failed to read data")
def data(self): # type: () -> RawAgentData if self._command is None: raise MKAgentError("Not connected") output = b"" output += self._sensors_section() output += self._firmware_section() return output
def _execute(self): # type: () -> RawSNMPData verify_ipaddress(self._ipaddress) with SNMPDataFetcher( self._make_oid_infos(), self._use_snmpwalk_cache, self._snmp_config, self._logger, ) as fetcher: return fetcher.data() raise MKAgentError("Failed to read data")
def _execute( self, *, selected_raw_sections: Optional[SelectedRawSections], ) -> SNMPRawData: # This is wrong configurator = cast(SNMPConfigurator, self.configurator) configurator.selected_raw_sections = selected_raw_sections # checking only # End of wrong with SNMPFetcher.from_json( self.configurator.configure_fetcher()) as fetcher: return fetcher.fetch() raise MKAgentError("Failed to read data")
def _execute(self): # type: () -> RawAgentData if self._use_only_cache: raise MKAgentError( "Got no data: No usable cache file present at %s" % self._cache_file_path()) verify_ipaddress(self._ipaddress) with TCPDataFetcher( socket.AF_INET6 if self._host_config.is_ipv6_primary else socket.AF_INET, (self._ipaddress, self.port), self.timeout, self._host_config.agent_encryption, self._logger, ) as fetcher: output = fetcher.data() if len(output) < 16: raise MKAgentError("Too short output from agent: %r" % output) return output raise MKAgentError("Failed to read data")
def _execute( self, *, selected_raw_sections: Optional[SelectedRawSections], ) -> RawAgentData: if not self._credentials: raise MKAgentError("Missing credentials") if self.ipaddress is None: raise MKAgentError("Missing IP address") if selected_raw_sections is None: # pylint: disable=unused-variable # TODO(ml): Should we pass that to the fetcher? selected_raw_section_names = {SectionName("mgmt_ipmi_sensors")} with IPMIDataFetcher( self.ipaddress, self._credentials["username"], self._credentials["password"], ) as fetcher: return fetcher.data() raise MKAgentError("Failed to read data")
def _execute( self, *, selected_raw_sections: Optional[SelectedRawSections], ) -> SNMPRawData: ip_lookup.verify_ipaddress(self.ipaddress) with SNMPDataFetcher( self._make_oid_infos( selected_raw_sections=selected_raw_sections), self._use_snmpwalk_cache, self._snmp_config, ) as fetcher: return fetcher.data() raise MKAgentError("Failed to read data")