ok
Direktori : /opt/imunify360/venv/share/imunify360/scripts/ |
Current File : //opt/imunify360/venv/share/imunify360/scripts/rules_checker.py |
""" Usage /opt/imunify360/venv/bin/python3 rules_checker.py <action> choose an action from ACTIONS map e.g. /opt/imunify360/venv/bin/python3 rules_checker.py recreate Actions: - `recreate` - recreates rules if needed and checks ipsets consistent - `clear` - waits RulesChecker stop and destroys all rules and ipsets Actions based on lazy_init plugin placed in im360.plugins.protector.lazy_init """ import asyncio from pathlib import Path import os import pickle import sys import time from defence360agent.internals import logger as lg from defence360agent.internals.global_scope import g from defence360agent.model import instance, tls_check from defence360agent.contracts.config import Model, Merger from im360.contracts.config import IPSET_LISTS_PATH from im360.files import WHITELISTS, Index from im360.internals.core import ip_versions from im360.internals.strategy import Strategy from im360.plugins.protector import RULES_CHECK_IN_PROGRESS from im360.plugins.protector.lazy_init import ( RulesChecker, RealProtector, ) from im360.subsys import smtp_blocking from logging import getLogger logger = getLogger("rules-checker") STATE = {"last_ipset_check": 0.0} DAY = 24 * 60 * 60 async def _check_for_config_change(rc: RulesChecker, rp: RealProtector): """Checking that config state is consistent with the current state.""" rp._rules_checker = rc await rp._rules_checker.check_smtp_state_and_reset() await rp._on_config_update_unlocked(None) async def recreate_rules(rc: RulesChecker, rp: RealProtector): """Recreates rules if needed and checks ipsets consistent.""" logger.info("Checking that need to recreate rules") # TODO: check if we need to check it too often # for Python implementation we do it only once per day if time.time() - STATE["last_ipset_check"] < DAY: logger.info("Skip ipsets check") else: await rc._check_ipsets_consistent() STATE["last_ipset_check"] = time.time() await rc.recreate_rules_if_needed() await _check_for_config_change(rc, rp) logger.info("IP sets verification and initialization completed") async def check_config_update(rc: RulesChecker, rp: RealProtector): """Checking config update.""" logger.info("Checking config update") await _check_for_config_change(rc, rp) logger.info("Completed") async def recreate_rules_on_strategy_change( rc: RulesChecker, rp: RealProtector ): """Recreates rules if needed and checks ipsets consistent.""" logger.info("Checking that need to recreate rules on strategy change") await rc.recreate_rules_if_needed() logger.info( "Firewall rules recreated due to StrategyChange %s", Strategy.current ) async def check_ipsets_consistent(rc: RulesChecker, rp: RealProtector): """Check ipsets consistent.""" logger.info("Checking ipsets consistent") await rc._check_ipsets_consistent() STATE["last_ipset_check"] = time.time() if any(sets for sets in rc.outdated_ipsets.values()): await rc.recreate_rules_if_needed() await _check_for_config_change(rc, rp) logger.info("Completed") async def clear_everything(rc: RulesChecker, rp: RealProtector): """Clear rules and ipsets on stop.""" logger.info("Clear rules and ipsets") rc.should_stop() await rc.wait() await rc.clear_everything() logger.info("Completed") def setup_environment(): """Setup environment for rules checker.""" lg.reconfigure() ip_versions.init() instance.db.init(Model.PATH) instance.db.execute_sql("ATTACH ? AS resident", (Model.RESIDENT_PATH,)) instance.db.execute_sql("ATTACH ? AS ipsetlists", (IPSET_LISTS_PATH,)) if os.environ.get("DEBUG") == "true": g.DEBUG = True Index.add_type(WHITELISTS, "whitelist/v2", 0o770, 0o660, all_zip=True) class RealProtectorState: """RealProtector state to save and restore.""" def __init__(self, _ws, _pb_dmv, _pbm, _lic): self._webshield_status = _ws self._port_blocking_deny_mode_values = _pb_dmv self._port_blocking_mode = _pbm self.last_ipset_check = _lic def __str__(self) -> str: return ( "RealProtectorState(" f"_webshield_status={self._webshield_status}, " f"_port_blocking_mode={self._port_blocking_mode}, " "_port_blocking_deny_mode_values" f"={self._port_blocking_deny_mode_values}," f"last_ipset_check={self.last_ipset_check})" ) class RulesCheckerState: def __init__(self, interface_conf, ipset_outdated_events, outdated_ipsets): self._interface_conf = interface_conf self._ipsets_outdated_events = ipset_outdated_events self.outdated_ipsets = outdated_ipsets def __str__(self) -> str: return ( "RulesCheckerState(" f"_interface_conf={self._interface_conf}, " f"_ipsets_outdated_events={self._ipsets_outdated_events})" f"outdated_ipsets={self.outdated_ipsets})" ) class SmtpSettingsState: def __init__(self, active_settings): self._active_settings = active_settings def __str__(self) -> str: return f"SmtpSettingsState(_active_settings={self._active_settings})" def restore_state(rp: RealProtector, rc: RulesChecker): """Restore RealProtector state.""" Strategy.current = Strategy.get() try: if REAL_PROTECTOR_STATE.exists(): rp_state = pickle.load(REAL_PROTECTOR_STATE.open("rb")) rp._webshield_status = rp_state._webshield_status rp._port_blocking_deny_mode_values = ( rp_state._port_blocking_deny_mode_values ) rp._port_blocking_mode = rp_state._port_blocking_mode STATE["last_ipset_check"] = rp_state.last_ipset_check except Exception as e: logger.error("Failed to restore RealProtector state: %s", e) try: if RULES_CHECKER_STATE.exists(): rc_state = pickle.load(RULES_CHECKER_STATE.open("rb")) rc.active_interface_conf = rc_state._interface_conf rc._ipsets_outdated_events = rc_state._ipsets_outdated_events rc.outdated_ipsets = rc_state.outdated_ipsets except Exception as e: logger.error("Failed to restore RulesChecker state: %s", e) try: if SMTP_BLOCKING_STATE.exists(): smtp_state = pickle.load(SMTP_BLOCKING_STATE.open("rb")) for idx, version in enumerate(smtp_blocking.ip_versions.enabled()): smtp_instance = smtp_blocking.SMTPBlocking(version) smtp_instance.active_settings = smtp_state._active_settings[ idx ] except Exception as e: logger.error("Failed to restore SMTPBlocking state: %s", e) return rp, rc def save_state(rp: RealProtector, rc: RulesChecker): """Save RealProtector state.""" rp_state = RealProtectorState( rp._webshield_status, rp._port_blocking_deny_mode_values, rp._port_blocking_mode, STATE["last_ipset_check"], ) rc_state = RulesCheckerState( rc.active_interface_conf, rc._ipsets_outdated_events, rc.outdated_ipsets, ) smtp_state = SmtpSettingsState(smtp_blocking.get_active_settings_list()) pickle.dump(rp_state, REAL_PROTECTOR_STATE.open("wb")) pickle.dump(rc_state, RULES_CHECKER_STATE.open("wb")) pickle.dump(smtp_state, SMTP_BLOCKING_STATE.open("wb")) ACTIONS = { "recreate": recreate_rules, "clear": clear_everything, "config-update": check_config_update, "strategy-change": recreate_rules_on_strategy_change, "ipsets-consistent": check_ipsets_consistent, } REAL_PROTECTOR_STATE = Path("/var/imunify360/.realprotector.state") RULES_CHECKER_STATE = Path("/var/imunify360/.ruleschecker.state") SMTP_BLOCKING_STATE = Path("/var/imunify360/.smtp_blocking.state") def main(action): try: RULES_CHECK_IN_PROGRESS.touch() except Exception as e: logger.error("Failed to create RULES_CHECK_IN_PROGRESS file: %s", e) tls_check.reset() setup_environment() try: Merger.update_merged_config() except Exception as e: logger.error("Failed to update merged config: %r", e) loop = asyncio.get_event_loop() rp, rc = restore_state(RealProtector(), RulesChecker(loop)) if action not in ACTIONS: print( f"Invalid action: {action}. " f"Choose from {', '.join(ACTIONS.keys())}" ) RULES_CHECK_IN_PROGRESS.unlink(missing_ok=True) sys.exit(1) action = ACTIONS[action] loop.run_until_complete(action(rc, rp)) save_state(rp, rc) RULES_CHECK_IN_PROGRESS.unlink(missing_ok=True) logger.info("Script finished") if __name__ == "__main__": if len(sys.argv) != 2: print( "Please provide one action as command line argument." f" {', '.join(ACTIONS.keys())}" ) sys.exit(1) main(sys.argv[1])