def process_loop(self):
        # _logger.debug('{0} processing loop called'.format(self.get_name()))

        if self._startup:
            self._startup = False

            task = QueueTask(
                TASK_FIREWALL_INSERT_RULES,
                src_module=self.get_name(),
                dest_module=SilentDuneClientFirewallModule().get_name(),
                data=self.create_logging_rules())
            self.send_parent_task(task)

        if self._log_handle:

            # See if we have iptables log event data
            data = self._log_handle.readline()

            while data:

                if self.node_info.platform == 'linux' and 'kernel: SDC' in data:

                    # See tests/iptables_logging_test.py for example log events.
                    ev = IPLogEvent(data)

                    for mod_name in self._subscribers:
                        # _logger.debug('{0}: sending {1} log event.'.format(self.get_name(), mod_name))
                        task = QueueTask(TASK_LOG_EVENT,
                                         src_module=self.get_name(),
                                         dest_module=mod_name,
                                         data=ev)
                        self.send_parent_task(task)

                data = self._log_handle.readline()
Exemple #2
0
    def check_service(self, name):
        """
        Check the service for rules and add them to the firewall.
        :param name: Service discovery module name
        """
        module_name, class_name = name.rsplit('.', 1)

        _logger.debug('{0}: Loading service object {1}'.format(self.get_name(), class_name))

        module = import_by_str(name)
        cls = module(config=self.config)
        disabled = getattr(self, cls.get_config_property_name())
        if type(disabled) is str:  # Python 2.7 returns string type from getattr(), Python 3.4 returns bool.
            disabled = ast.literal_eval(disabled)

        # _logger.debug('Property: {0}: Value: {1}'.format(cls.get_config_property_name(), disabled))
        # See if this discovery service has been disabled. Name value must match one of our property names.
        if disabled:
            _logger.debug('{0}: {1} service disabled by config.'.format(self.get_name(), class_name))
            return 0

        rules, slot = cls.discover()

        if rules:

            # See if we already have saved rules for this slot id
            if slot in self._mss_slots:
                if self.rules_have_changed(self._mss_slots[slot], rules):

                    _logger.debug('{0}: {1}: Rules have changed, notifying firewall manager.'.format(
                        self.get_name(), class_name))

                    # Notify the firewall module to delete the old rules.
                    task = QueueTask(TASK_FIREWALL_DELETE_RULES,
                                     src_module=self.get_name(),
                                     dest_module=SilentDuneClientFirewallModule().get_name(),
                                     data=self._mss_slots[slot])
                    self.send_parent_task(task)
                else:
                    return 0

            # Save rules so we can check against them next time.
            self._mss_slots[slot] = rules

            # Notify the firewall module to reload the rules.
            task = QueueTask(TASK_FIREWALL_INSERT_RULES,
                             src_module=self.get_name(),
                             dest_module=SilentDuneClientFirewallModule().get_name(),
                             data=rules)
            self.send_parent_task(task)
        else:
            _logger.info('{0}: {1}: service did not return any rules.'.format(
                self.get_name(), class_name))

            return 0

        return len(rules)
    def process_loop(self):
        # TODO: Things to do occasionally; Hash rule files and compare to hashes saved on server to look for tampering.
        #if self.t_seconds > 45:
        #    print "debug 1 message"

        if self.t_seconds > 120:
            print "debug 2 message"
            task = QueueTask(
                TASK_FIREWALL_RELOAD_RULES,
                src_module=self.get_name(),
                dest_module=SilentDuneClientFirewallModule().get_name())
            self.send_parent_task(task)

        pass
    def process_loop(self):

        # Print a debug message every 45 seconds
        if self.timed_event('_t_debug_message', 45):
            _logger.debug('{0} 45 second debug message'.format(
                self.get_name()))

        # Tell the firewall module to reload the firewall rules every 2 minutes.
        if self.timed_event('_t_reload_task', 120):
            # Notify the firewall module to reload the rules.
            task = QueueTask(
                TASK_FIREWALL_RELOAD_RULES,
                src_module=self.get_name(),
                dest_module=SilentDuneClientFirewallModule().get_name())
            self.send_parent_task(task)
    def add_url_rule_to_firewall(self, rules):
        """
        Add rules allowing access immediately to the firewall.
        :param rules: rules list
        """
        if not rules:
            return

        # Notify the firewall module to reload the rules.
        task = QueueTask(
            TASK_FIREWALL_INSERT_RULES,
            src_module=self._parent.get_name(),
            dest_module=SilentDuneClientFirewallModule().get_name(),
            data=rules)
        self._parent.send_parent_task(task)

        time.sleep(
            2)  # Give the firewall manager time to add the rule to the kernel
    def allow_remote_config_access(self):

        rules = list()

        if self._uri.scheme == 'http':
            rules.append(self.get_http_rule_by_hostname())
        elif self._uri.scheme == 'https':
            rules.append(self.get_https_rule_by_hostname())
        elif self._uri.scheme == 'ftp':
            rules.append(self.get_ftp_rule_by_hostname())

        # Notify the firewall module to reload the rules.
        task = QueueTask(
            TASK_FIREWALL_INSERT_RULES,
            src_module=self.get_name(),
            dest_module=SilentDuneClientFirewallModule().get_name(),
            data=rules)
        self.send_parent_task(task)
    def _add_server_access_rule(self):
        """
        Add a management server connection rule so we can connect
        :return:
        """

        rule = create_iptables_egress_ingress_rule(
            self._server,
            self._port,
            'tcp',
            Slots.silentdune_server,
            transport=ipt.TRANSPORT_AUTO)

        # Notify the firewall module to reload the rules.
        task = QueueTask(
            TASK_FIREWALL_INSERT_RULES,
            src_module=self.get_name(),
            dest_module=SilentDuneClientFirewallModule().get_name(),
            data=rule)
        self.send_parent_task(task)
    def update_node_firewall_rules_from_server(self):
        """
        Retrieve the node bundle rules from the server and send them to the Firewall module.
        :return:
        """

        # TODO: Save current bundle machine subsets and then look for any orphaned sets and remove them
        # from the firewall.

        if self._bundle_machine_subsets:
            # Until the orphan rule check is in place, just tell the firewall to delete all rules.
            task = QueueTask(
                TASK_FIREWALL_DELETE_RULES,
                src_module=self.get_name(),
                dest_module=SilentDuneClientFirewallModule().get_name(),
                data=self._bundle_machine_subsets)
            self.send_parent_task(task)

        # Get updated bundle information.
        self._node_bundle, status_code = self._sds_conn.get_node_bundle_by_node_id(
            self._node.id)
        self._bundle, status_code = self._sds_conn.get_bundle_by_id(
            self._node_bundle.bundle)

        if not self._download_bundleset():
            if self._sds_conn.request_error:
                _logger.error(
                    '{0}: request to server failed, changing state to not connected'
                    .format(self.get_name()))
                self._connected = False
                return False

        if self._bundle_machine_subsets and len(
                self._bundle_machine_subsets) > 0:

            rules = self.flatten_rules(self._bundle_machine_subsets)

            # Check to see if we are in lockdown mode. If so filter out all
            if self._locked:
                data = list()
                for i in rules:
                    if i.slot <= self._globals.lockdown_slot_level or i.slot >= self._globals.rejection_slot_level:
                        data.append(i)
            else:
                data = rules

            # Notify the firewall module to reload the rules.
            task = QueueTask(
                TASK_FIREWALL_INSERT_RULES,
                src_module=self.get_name(),
                dest_module=SilentDuneClientFirewallModule().get_name(),
                data=data)
            self.send_parent_task(task)

            # Notify the firewall module to reload the rules.
            # task = QueueTask(TASK_FIREWALL_RELOAD_RULES,
            #                  src_module=self.get_name(),
            #                  dest_module=SilentDuneClientFirewallModule().get_name())
            # self.send_parent_task(task)

            # Reset the node rule bundle check timer
            self._t_next_check = self.t_seconds

        else:
            _logger.error(
                '{0}: No rules downloaded from server, unable to update firewall module.'
                .format(self.get_name()))
            return False

        return True
    def process_loop(self):

        if self._startup:
            self._startup = False

            self._add_server_access_rule()
            time.sleep(
                3
            )  # Give the firewall time to add the rules before we attempt to connect

            self.service_connect_to_server()

            if self._sds_conn.status == ConnStatus.not_registered:
                _logger.error(
                    '{0} this node is not registered, unable to download rules'
                    .format(self.get_name()))

        # If we are not registered, just return
        if self._sds_conn and self._sds_conn.status == ConnStatus.not_registered:
            return

        # If we are not connected to the server, try reconnecting every 60 seconds.
        if not self._connected:
            if self.timed_event('_t_connection_retry', 60):
                self._t_connection_retry = self.t_seconds
                self.service_connect_to_server()

        if self._connected:

            # Check to see if the connection to the server has just started.
            if self._connection_start:

                # Get the global preferences
                self.get_global_preferences()

                # Notify the server we are active now.
                self._node.active = True
                self.update_server_node_info(active=True)

                # Set our lock down mode value.
                self.set_lockdown_mode()

                # Update our firewall with rules from the server.
                self.update_node_firewall_rules_from_server()

                self._connection_start = False

                # Subscribe to log events
                task = QueueTask(
                    TASK_SUBSCRIBE_LOG_EVENTS,
                    src_module=self.get_name(),
                    dest_module=SilentDuneClientLoggingModule().get_name())
                self.send_parent_task(task)

                _logger.debug('Next server check in {0} seconds.'.format(
                    self._t_check_interval))

            # Check to see if the node rule bundle information has changed.
            if self.timed_event('_t_next_check', self._t_check_interval):
                _logger.debug('Next server check in {0} seconds.'.format(
                    self._t_check_interval))

                self._node, status_code = self._sds_conn.get_node_by_machine_id(
                    self.node_info.machine_id)

                # Check to see if we need to update our firewall rules bundle.
                if self._node.sync:
                    _logger.info(
                        '{0}: Found signal to update the firewall rules bundle.'
                        .format(self.get_name()))

                    self.set_lockdown_mode()
                    self.update_node_firewall_rules_from_server()

                    # Update our information with the server.
                    self.update_server_node_info(sync=False)
    def apply_remote_config(self):

        rules = list()

        try:

            if self._uri.scheme == 'file':
                with open(self._uri.path) as handle:
                    rq = handle.read().decode('utf-8')
            else:
                rq = requests.get(self._remote_config_url, verify=False)

                if rq.status_code != 200:
                    _logger.error(
                        '{0}: unable to retrieve remote configuration'.format(
                            self.get_name()))
                    return False
        except:
            _logger.error('{0}: unable to retrieve remote config rules'.format(
                self.get_name()))
            return False

        try:
            if self._uri.scheme == 'file':
                rc = RemoteConfig(json.loads(rq))
                rule_text = rq
            else:
                rc = RemoteConfig(rq.json())
                rule_text = rq.text

        except:
            _logger.error('{0}: parsing configuration json failed'.format(
                self.get_name()))
            return False

        # See if we should check the hash value against a DNS TXT lookup hash value
        if self._remote_config_dns_name:

            if not rc.hash_method or rc.hash_method not in [
                    'sha1', 'sha256', 'sha512'
            ]:
                _logger.error(
                    '{0}: unsupported hashing algorithm in remote config ({1})'
                    .format(self.get_name(), rc.hash_method))
                return False

            if rc.hash_method == 'sha1':
                hash_value = hashlib.sha1(rule_text).hexdigest()
            elif rc.hash_method == 'sha256':
                hash_value = hashlib.sha256(rule_text).hexdigest()
            elif rc.hash_method == 'sha512':
                hash_value = hashlib.sha512(rule_text).hexdigest()

            if self.get_remote_dns_hash() != hash_value:
                _logger.error(
                    '{0}: remote DNS TXT hash value does not match calculated hash'
                    .format(self.get_name()))
                return False

        _logger.debug('{0}: deleting rules for slot {1}'.format(
            self.get_name(), self._slot_config_apply))
        task = QueueTask(
            TASK_FIREWALL_DELETE_SLOT,
            src_module=self.get_name(),
            dest_module=SilentDuneClientFirewallModule().get_name(),
            data=self._slot_config_apply)
        self.send_parent_task(task)

        for rule in rc.rules:

            try:

                # See if we need to try and resolve the host name
                if not is_valid_ipv6_address(
                        rule.host) and not is_valid_ipv6_address(rule.host):

                    addrs = self.get_ip_from_hostname(rule.host, rule.port)

                    if not addrs:
                        _logger.error(
                            '{0}: error: unable to resolve rule host {1}'.
                            format(self.get_name(), rule.host))
                        return False

                    for addr in addrs:
                        _logger.debug(
                            '{0}: adding rules slot: {1}, ip address: {2}, port: {3}, protocol: {4}'
                            .format(self.get_name(), self._slot_config_apply,
                                    addr, rule.port, rule.protocol))
                        rules.append(
                            create_iptables_remote_config(
                                addr,
                                rule.mask,
                                rule.port,
                                rule.protocol,
                                self._slot_config_apply,
                                rule.uid,
                                rule.gid,
                                transport=ipt.TRANSPORT_AUTO))

                else:
                    _logger.debug(
                        '{0}: adding rules slot: {1}, ip address: {2}, port: {3}, protocol: {4}'
                        .format(self.get_name(), self._slot_config_apply,
                                rule.host, rule.port, rule.protocol))
                    rules.append(
                        create_iptables_remote_config(
                            rule.host,
                            rule.mask,
                            rule.port,
                            rule.protocol,
                            self._slot_config_apply,
                            rule.uid,
                            rule.gid,
                            transport=ipt.TRANSPORT_AUTO))

            except:
                _logger.error('{0}: parsing remote config rule failed.'.format(
                    self.get_name()))
                return False

        if rules:
            task = QueueTask(
                TASK_FIREWALL_INSERT_RULES,
                src_module=self.get_name(),
                dest_module=SilentDuneClientFirewallModule().get_name(),
                data=rules)
            self.send_parent_task(task)

        return True
    def check_service(self, name):
        """
        Check the service for rules and add them to the firewall.
        :param name: Service discovery module name
        """
        module_name, class_name = name.rsplit('.', 1)

        _logger.debug('{0}: Loading auto discover object {1}'.format(
            self.get_name(), class_name))

        module = import_by_str(name)
        cls = module(config=self.config)
        disabled = getattr(self, cls.get_config_property_name())
        if type(
                disabled
        ) is str:  # Python 2.7 returns string type from getattr(), Python 3.4 returns bool.
            disabled = ast.literal_eval(disabled)

        # _logger.debug('Property: {0}: Value: {1}'.format(cls.get_config_property_name(), disabled))
        # See if this discovery service has been disabled. Name value must match one of our property names.
        if disabled:
            _logger.debug(
                '{0}: {1} discovery service disabled by config.'.format(
                    self.get_name(), class_name))
            return 0

        rules, slot = cls.discover(self)

        rules = self.flatten_rules(rules)

        if rules:

            # Notify the firewall module to delete the old rules.
            task = QueueTask(
                TASK_FIREWALL_DELETE_SLOT,
                src_module=self.get_name(),
                dest_module=SilentDuneClientFirewallModule().get_name(),
                data=slot)
            self.send_parent_task(task)

            # Notify the firewall module to load the new rules.
            task = QueueTask(
                TASK_FIREWALL_INSERT_RULES,
                src_module=self.get_name(),
                dest_module=SilentDuneClientFirewallModule().get_name(),
                data=rules)
            self.send_parent_task(task)

            time.sleep(1)  # Let the firewall apply the rule changes
        else:
            _logger.info(
                '{0}: {1}: discovery service did not return any rules.'.format(
                    self.get_name(), class_name))

            _logger.debug('SLOTS: {0}: {1}'.format(Slots.ntp, slot))

            # If there were no rules discovered for NTP, open up access to all NTP servers.
            # In self._t_ntp_check_interval seconds we will check to see if any NTP servers are active.
            if slot == Slots.ntp and is_service_running('ntpd'):
                self._all_ntp_access_enabled = True
                _logger.debug(
                    '{0}: Asking Firewall Module to enable generic NTP access.'
                    .format(self.get_name()))
                task = QueueTask(
                    TASK_FIREWALL_ALLOW_ALL_NTP_ACCESS,
                    src_module=self.get_name(),
                    dest_module=SilentDuneClientFirewallModule().get_name())
                self.send_parent_task(task)

            return 0

        return len(rules)
    def process_loop(self):

        if self._startup:

            # Try resolving example.org, if we get an error try enabling generic all DNS access.
            # This block may be called multiple times during startup to make sure DNS is working.
            try:
                resolve_hostname('example.org', ipt.TRANSPORT_AUTO)
            except socket.gaierror:

                # If we have reached here, we have no DNS access at all.
                if self._all_dns_access_enabled:
                    # raise OSError('No external DNS access available. Unable to complete auto discovery.')
                    _logger.error(
                        '{0}: No external DNS available. Waiting 5 minutes to try again.'
                        .format(self.get_name()))
                    time.sleep(300)
                    return

                _logger.debug(
                    '{0}: Asking Firewall Module to enable generic DNS access.'
                    .format(self.get_name()))

                # Tell the firewall manager to enable generic all DNS access.
                self._all_dns_access_enabled = True

                task = QueueTask(
                    TASK_FIREWALL_ALLOW_ALL_DNS_ACCESS,
                    src_module=self.get_name(),
                    dest_module=SilentDuneClientFirewallModule().get_name())
                self.send_parent_task(task)

                return

            self._startup = False
            self.discover_services()

            # Tell the firewall manager to remove generic all DNS access rules.
            if self._all_dns_access_enabled:
                self._all_dns_access_enabled = False
                _logger.debug(
                    '{0}: Asking Firewall Module to disable generic DNS access.'
                    .format(self.get_name()))
                task = QueueTask(
                    TASK_FIREWALL_DISABLE_ALL_DNS_ACCESS,
                    src_module=self.get_name(),
                    dest_module=SilentDuneClientFirewallModule().get_name())
                self.send_parent_task(task)

        # After the check interval has passed, check services again.
        if self.timed_event('_t_all_service_check',
                            self._t_all_check_interval):
            self.discover_services()

        # See we need to remove the all access ntp rules.
        if self._all_ntp_access_enabled and self.timed_event(
                '_t_ntp_service_check', self._t_ntp_check_interval):

            rule_count = self.check_service(NTP_DISCOVERY_SERVICE)

            # If we found NTP rules, tell the firewall manager to remove generic all NTP access rules.
            if self._all_ntp_access_enabled and rule_count > 0:

                self._all_ntp_access_enabled = False
                _logger.debug(
                    '{0}: Asking Firewall Module to disable generic NTP access.'
                    .format(self.get_name()))
                task = QueueTask(
                    TASK_FIREWALL_DISABLE_ALL_NTP_ACCESS,
                    src_module=self.get_name(),
                    dest_module=SilentDuneClientFirewallModule().get_name())
                self.send_parent_task(task)