ok

Mini Shell

Direktori : /lib/fm-agent/library/
Upload File :
Current File : //lib/fm-agent/library/inspector.py

from datetime import datetime
import time
import agent_util
import platform
import os
import sys
import logging
import socket

try:
    # Python 2.x
    import httplib
except:
    import http.client as httplib
try:
    import psutil
except:
    psutil = None

try:
    import distro
except:
    distro = None

try:
    import json
except ImportError:
    try:
        import simplejson as json
    # it's possible that we may not need json for the action that we're taking.
    # for example, for the rpm post install script, on a python version that
    # doesn't have json, we'll get this far in the code.  but the post
    # install doesn't use json, so we're fine
    except ImportError:
        json = None


def mac_address_octets(n):
    if sys.version_info[0] == 2:
        rng = xrange
    else:
        # python 3x uses range
        rng = range

    for i in rng(6):
        yield n & 0xFF
        n >>= 8


def int_to_mac_address(n):
    """
    Expresses a decimal integer in standard MAC address format
     ex:
     7267271067680 -> '06:9c:0b:1c:48:20'
    """
    values = ["%02x" % a for a in mac_address_octets(n)]
    return ":".join(reversed(values))


def get_fqdn():
    hostname = socket.getfqdn()
    if "darwin" == sys.platform:
        """
        Mac is odd around ipv4/6 addresses - see:
        https://stackoverflow.com/questions/8625029/why-does-pythons-socket-getfqdn-return-a-long-string-that-looks-like-an-ipv6
        """
        if hostname.lower().endswith(".arpa"):
            hostname = socket.gethostname()

    return hostname


def get_server_name():
    if "darwin" != sys.platform:
        return None
    try:
        """
        For Fortisase integration, get the value we want to specify for server_name, which 
        is sent up in handshake and also for config changes. This allows us to match the 
        name provided by Forticlient.
        """
        sc_util = agent_util.which("scutil", exc=True)
        _, output = agent_util.execute_command("{} --get ComputerName".format(sc_util))
        return output.strip()
    except:
        return None


def get_platform_uuids():
    log = logging.getLogger("Inspector")
    my_platform = sys.platform.lower()
    #
    # The keys in the rv dictionary should match the textkeys
    # in the table ServerIdentifierType
    rv = {"platform": sys.platform.lower()}
    if "darwin" == my_platform:
        try:
            ioreg_bin = agent_util.which("ioreg")
            rc, output = agent_util.execute_command(
                "{} -d2 -c IOPlatformExpertDevice | grep IOPlatformUUID".format(
                    ioreg_bin
                )
            )
            if 0 == rc:
                rv["uuid"] = output.strip().split("=")[1].strip().strip('"')
            else:
                log.error("ioreg error: {}".format(rc))
        except Exception as e:
            log.info("Gather UUID failure: {}".format(str(e)))
            pass
    return rv


class Inspector(object):
    SOURCE_LIST_PATHS = ["/etc/apt/sources.list", "/etc/yum.repos.d/panopta.repo"]
    PANOPTA_REPO = "http://packages.panopta.com/"

    def __init__(self, agent):
        self.agent = agent

    def get_all_facts(self, wifi_info=None):
        facts = {}
        log = logging.getLogger("Inspector")
        try:
            facts.update(self.get_agent_facts())
            facts.update(self.get_python_facts())
            facts.update(self.get_process_facts())
            facts.update(self.get_mac_addresses())
            facts.update(self.get_hardware_facts())
            facts.update(self.get_time_facts())
            facts.update(self.get_hostname())
            facts.update(self.get_platform_facts())
            facts.update(self.get_machine_facts())
            facts.update(self.get_wifi_facts(wifi_info))
            facts.update(self.get_dem_network_facts())

            if sys.version_info >= (2, 6, 0):
                # Cloud detection, but only if we're on a new enough Python to have timeout
                facts.update(self.get_cloud_facts())
        except Exception as e:
            log.info("get_all facts: {}".format(e))

        return facts

    def get_platform_facts(self):
        facts = {}
        if "darwin" == sys.platform:
            try:
                _, output = agent_util.execute_command("sw_vers | grep ProductVersion")
                pv = output.split(":")[1].strip()
                facts["MacOSVersion"] = pv
            except:
                pass
        return facts

    def get_hostname(self):
        facts = {}
        facts["hostname"] = get_fqdn()
        return facts

    def get_time_facts(self):
        facts = {}
        try:
            retcode, output = agent_util.execute_command("ls -l /etc/localtime")
            timezone = "/".join(output.strip().split("/")[-2:])
            facts["timezone"] = timezone
        except:
            log = logging.getLogger(self.__class__.__name__)
            log.exception("Unable to get Time Zone")
        return facts

    def get_hardware_facts(self):
        """
        Gather CPU and memory specs for the machine
        """
        facts = {"hardware": {}}
        if "darwin" == sys.platform.lower():
            try:
                retcode, output = agent_util.execute_command(
                    "sysctl -a | grep machdep.cpu"
                )
                for line in output.strip().split("\n"):
                    try:
                        key, value = line.strip().split(":", 1)
                        if key in (
                            "machdep.cpu.brand_string",
                            "machdep.cpu.core_count",
                        ):
                            facts["hardware"][key] = value.strip()
                    except:
                        continue
            except:
                log = logging.getLogger(self.__class__.__name__)
                log.exception("Unable to get CPU hardware facts")

            try:
                total_mem = os.sysconf("SC_PHYS_PAGES") * os.sysconf("SC_PAGE_SIZE")
                facts["hardware"]["mem_total"] = total_mem
            except:
                log = logging.getLogger(self.__class__.__name__)
                log.exception("Unable to get memory hardware facts")

        else:
            try:
                retcode, output = agent_util.execute_command("lscpu")
                for line in output.strip().split("\n"):
                    try:
                        key, value = line.strip().split(":", 1)
                    except:
                        continue
                    key = key.strip().lower().replace("(s)", "").replace(" ", "_")
                    value = value.strip()
                    facts["hardware"][key] = value
            except:
                log = logging.getLogger(self.__class__.__name__)
                log.exception("Unable to get CPU hardware facts")

            try:
                retcode, output = agent_util.execute_command("free -m")
                for line in output.strip().split("\n"):
                    fields = line.strip().split()
                    if fields[0].lower().startswith("mem"):
                        facts["hardware"]["mem_total"] = int(fields[1])
                    if fields[0].lower().startswith("swap"):
                        facts["hardware"]["swap_total"] = int(fields[1])
            except:
                log = logging.getLogger(self.__class__.__name__)
                log.exception("Unable to get memory hardware facts")

        return facts

    def get_agent_facts(self):
        facts = {}

        # This is a little hokey, but the last time this file's metadata changed
        # *should* be when this file whas created, ie. when the Agent was
        # installed. I thought this was better than storing the install time in
        # the local database, since there is a possibility that it could get
        # corrupted.
        facts["installed_time"] = os.path.getctime(os.path.abspath(__file__))

        facts["used_manifest"] = os.path.exists(self.agent.manifest_file)

        facts["installed_from_repo"] = False
        for source_list_path in self.SOURCE_LIST_PATHS:
            if os.path.exists(source_list_path):
                try:
                    source_list = open(source_list_path)
                    facts["installed_from_repo"] = (
                        self.PANOPTA_REPO in source_list.read()
                    )
                    source_list.close()
                    if facts["installed_from_repo"]:
                        break
                except:
                    pass

        brand = "panopta"
        if "darwin" == sys.platform or os.path.exists("/etc/fm-agent"):
            brand = "fortimonitor"
        # Set the agent brand, default to Panopta unless we have an FM-Agent config file
        facts["agent_brand"] = brand
        return facts

    def get_python_facts(self):
        facts = {"python": {}}

        facts["python"]["platform"] = platform.platform()
        facts["python"]["processor"] = platform.processor()
        facts["python"]["version"] = platform.python_version()
        facts["python"]["uname"] = platform.uname()
        try:
            facts["python"]["dist"] = platform.dist()
        except AttributeError:
            # Removed in Python 3.8.
            # https://docs.python.org/2.7/library/platform.html#platform.linux_distribution
            if distro:
                facts["python"]["dist"] = " ".join(distro.linux_distribution())
        facts["python"]["sys_platform"] = sys.platform

        return facts

    def get_cloud_facts(self):
        facts = {}

        # Try Amazon
        try:
            h = httplib.HTTPConnection("169.254.169.254", timeout=5)
            h.request("GET", "/latest/dynamic/instance-identity/document")
            r = h.getresponse()
            if r.status == 200:
                data = json.loads(r.read())
                facts["cloud_provider"] = "aws"
                facts["cloud_instance_id"] = data["instanceId"]
                facts["cloud_metadata"] = data
                return facts
        except:
            pass

        # Try Google
        try:
            headers = {"Metadata-Flavor": "Google"}
            h = httplib.HTTPConnection("metadata.google.internal", timeout=5)
            h.request(
                "GET", "/computeMetadata/v1/instance/?recursive=true", headers=headers
            )
            r = h.getresponse()
            if r.status == 200:
                data = json.loads(r.read())
                facts["cloud_provider"] = "gcp"
                facts["cloud_instance_id"] = data["id"]

                # Strip out sensitive keys
                if "serviceAccounts" in data:
                    del data["serviceAccounts"]

                for key, value in data.get("attributes", {}).items():
                    if key in [
                        "/attribute",
                        "/sshkeys",
                        "/vmdnssetting",
                        "/enable-oslogin",
                    ]:
                        data["attributes"].pop(key)
                facts["cloud_metadata"] = data
                try:
                    manifest = self.agent.get_manifest()
                    enabled_gcp_attributes = (
                        manifest.get("agent", "enable_gcp_attributes") == "true"
                    )
                    facts["cloud_metadata"]["enable_gcp_attributes"] = (
                        enabled_gcp_attributes
                    )
                except Exception:
                    log = logging.getLogger(self.__class__.__name__)
                    log.exception(
                        "Unable to parse manifest file to determine gcp attributes actions."
                    )
                return facts
        except:
            pass

        # Try Azure
        try:
            headers = {"Metadata": "true"}
            h = httplib.HTTPConnection("169.254.169.254", timeout=5)
            h.request(
                "GET", "/metadata/instance?api-version=2017-04-02", headers=headers
            )
            r = h.getresponse()
            if r.status == 200:
                data = json.loads(r.read())
                facts["cloud_provider"] = "azure"
                facts["cloud_instance_id"] = data["compute"]["vmId"]
                facts["cloud_metadata"] = data
                return facts
        except:
            pass

        # No cloud detected
        return {}

    def get_process_facts(self):
        facts = {}

        if psutil is None:
            return facts

        processes = set()
        for proc in psutil.process_iter():
            processes.add(proc.name())

        facts["processes"] = list(processes)
        return facts

    def mac_address_iter(self):
        import psutil

        for iface, addrs in psutil.net_if_addrs().items():
            for addr in addrs:
                if addr.family != psutil.AF_LINK:
                    continue
                if addr.address == "00:00:00:00:00:00":
                    continue
                yield addr.address

    def get_mac_addresses(self):
        facts = {}

        try:
            import uuid

            facts["uuid_getnode"] = int_to_mac_address(uuid.getnode())
        except:
            log = logging.getLogger(self.__class__.__name__)
            log.info(
                "Unable to import uuid module. Skipping MAC address fact collection."
            )

        try:
            addrs = []
            for i, mac_addr in enumerate(self.mac_address_iter()):
                addrs.append(mac_addr)
            facts["macaddress"] = addrs
        except ImportError:
            log = logging.getLogger(self.__class__.__name__)
            log.info("Unable to import psutil, skipping MAC address fact collection")
        except:
            log = logging.getLogger(self.__class__.__name__)
            log.info("Unknown error during MAC address collection")

        return facts

    def get_machine_facts(self):
        md = {}
        if "darwin" != sys.platform:
            return md
        facts = {"machine_description": md}
        try:
            tk = "SPHardwareDataType"
            data = self.query_system_profiler(tk)
            if data:
                info = data[tk][0]
                md["serial"] = info.get("serial_number", "")
                md["model"] = info.get("machine_model", "")
        except:
            facts.clear()
        return facts

    def get_wifi_facts(self, wifi_info=None):
        md = {}
        facts = {"DEMWifiInfo": md}
        try:
            md["ssid"] = wifi_info.get("SSID", "")
            md["bssid"] = wifi_info.get("BSSID", "")
            md["channel"] = wifi_info.get("channel", "")
            md["security_algorithm"] = wifi_info.get("link auth", "")
            md["nss"] = wifi_info.get("NSS", "")
        except:
            md.clear()
        md["timestamp"] = datetime.utcnow().timestamp()
        return facts

    def get_dem_network_facts(self):
        if "darwin" != sys.platform:
            return {}
        md = []
        facts = {"DEMNetworkInterfaceConfigurations": md}
        try:
            tk = "SPNetworkDataType"
            info = self.query_system_profiler(tk)
            if info:
                network_infos = info.get(tk, [])
                for ni in network_infos:
                    router = ni.get("IPv4", {}).get("Router", None)
                    if not router:
                        continue
                    item = {
                        "name": ni.get("_name"),
                        "description": ni.get("hardware"),
                        "dns_servers": [],
                        "gateways": [router],
                    }
                    md.append(item)
                    dns_info = ni.get("DNS", None)
                    if dns_info:
                        for sa in dns_info.get("ServerAddresses", []):
                            item["dns_servers"].append(sa)

        except:
            logging.exception("get_dem_network_facts")
            facts.clear()
        return facts

    def query_system_profiler(self, info_type):
        try:
            sp = agent_util.which("system_profiler")
            from subprocess import Popen, PIPE

            d = Popen([sp, "-json", info_type], stdout=PIPE)
            data = json.loads(d.stdout.read())
            return data
        except:
            logging.exception("query_system_profiler")
            return None

Zerion Mini Shell 1.0