class RouterOsApi: """ Connects to the RouterOS device using the RouterOS API (with SSL by default). """ def open( self, hostname: Optional[str], username: Optional[str], password: Optional[str], port: Optional[int], platform: Optional[str], extras: Optional[Dict[str, Any]] = None, configuration: Optional[Config] = None, ) -> None: """ Connect to the device and populate the attribute :attr:`connection` with the underlying connection. Args: extras: Extra arguments for :class:`RouterOsApiPool`. """ params = { "host": hostname, "username": username, "password": password, "port": port, "plaintext_login": True, "use_ssl": True } if extras is not None and extras.get("use_ssl", True): ssl_ctx = ssl.create_default_context() ssl_ctx.verify_mode = ssl.CERT_REQUIRED if extras.get( "ssl_verify", True) else ssl.CERT_NONE ssl_ctx.check_hostname = extras.get("ssl_verify_hostname", False) if "ssl_ca_file" in extras: ssl_ctx.load_verify_locations(extras.pop("ssl_ca_file")) params["ssl_context"] = ssl_ctx extras = extras or {} params.update(extras) self._pool = RouterOsApiPool(**params) self.connection = self._pool.get_api() def close(self) -> None: """Close the connection with the device""" self._pool.disconnect()
def __init__(self, config): """Initialize the scanner.""" self.last_results = None self.host = config[CONF_HOST] self.interface = config[CONF_INTERFACE] self.address_range = config[CONF_ADDRESS_RANGE] self.port = config[CONF_PORT] self.username = config[CONF_USERNAME] self.password = config[CONF_PASSWORD] self.scan_interval = config[CONF_SCAN_INTERVAL] self.success_init = True from routeros_api import RouterOsApiPool # Establish a connection to the Mikrotik router. connection = RouterOsApiPool(self.host, username=self.username, password=self.password, port=self.port) self.client = connection.get_api() self.leases = self.client.get_resource('/ip/dhcp-server/lease').get() _LOGGER.debug('Got leases %s', str(self.leases)) # At this point it is difficult to tell if a connection is established. # So just check for null objects. if not connection.connected: self.success_init = False if self.success_init: _LOGGER.info('Successfully connected to Mikrotik device') # Reserve 2 seconds for API communication. self.ip_scan_args = { "duration": str(max(self.scan_interval.seconds - 2, 1)) } if self.address_range: self.ip_scan_args["address-range"] = self.address_range if self.interface: self.ip_scan_args["interface"] = self.interface _LOGGER.debug('ip_scan_args %s', str(self.ip_scan_args)) self._update_info() else: _LOGGER.error( 'Failed to establish connection to Mikrotik device with IP: %s', self.host)
class RouterAPIConnection: ''' Base wrapper interface for the routeros_api library ''' def __init__(self, router_name, config_entry): self.router_name = router_name self.config_entry = config_entry self.last_failure_timestamp = self.successive_failure_count = 0 ctx = None if self.config_entry.use_ssl and self.config_entry.no_ssl_certificate: ctx = ssl.create_default_context() ctx.set_ciphers('ADH:@SECLEVEL=0') self.connection = RouterOsApiPool( host=self.config_entry.hostname, username=self.config_entry.username, password=self.config_entry.password, port=self.config_entry.port, plaintext_login=True, use_ssl=self.config_entry.use_ssl, ssl_verify=self.config_entry.ssl_certificate_verify, ssl_context=ctx) self.connection.socket_timeout = config_handler.system_entry( ).socket_timeout self.api = None def is_connected(self): if not (self.connection and self.connection.connected and self.api): return False try: self.api.get_resource('/system/identity').get() return True except (socket.error, socket.timeout, Exception) as exc: self._set_connect_state(success=False, exc=exc) return False def connect(self): connect_time = datetime.now() if self.is_connected() or self._in_connect_timeout( connect_time.timestamp()): return try: print( f'Connecting to router {self.router_name}@{self.config_entry.hostname}' ) self.api = self.connection.get_api() self._set_connect_state(success=True, connect_time=connect_time) except (socket.error, socket.timeout, Exception) as exc: self._set_connect_state(success=False, connect_time=connect_time, exc=exc) #raise RouterAPIConnectionError def router_api(self): if not self.is_connected(): self.connect() return self.api def _in_connect_timeout(self, connect_timestamp, quiet=True): connect_delay = self._connect_delay() if (connect_timestamp - self.last_failure_timestamp) < connect_delay: if not quiet: print( f'{self.router_name}@{self.config_entry.hostname}: in connect timeout, {int(connect_delay - (connect_timestamp - self.last_failure_timestamp))}secs remaining' ) print( f'Successive failure count: {self.successive_failure_count}' ) return True if not quiet: print( f'{self.router_name}@{self.config_entry.hostname}: OK to connect' ) if self.last_failure_timestamp > 0: print( f'Seconds since last failure: {connect_timestamp - self.last_failure_timestamp}' ) print( f'Prior successive failure count: {self.successive_failure_count}' ) return False def _connect_delay(self): mktxp_entry = config_handler.system_entry() connect_delay = ( 1 + self.successive_failure_count / mktxp_entry.delay_inc_div) * mktxp_entry.initial_delay_on_failure return connect_delay if connect_delay < mktxp_entry.max_delay_on_failure else mktxp_entry.max_delay_on_failure def _set_connect_state(self, success=False, connect_time=datetime.now(), exc=None): if success: self.last_failure_timestamp = 0 self.successive_failure_count = 0 print( f'{connect_time.strftime("%Y-%m-%d %H:%M:%S")} Connection to router {self.router_name}@{self.config_entry.hostname} has been established' ) else: self.api = None self.successive_failure_count += 1 self.last_failure_timestamp = connect_time.timestamp() print( f'{connect_time.strftime("%Y-%m-%d %H:%M:%S")} Connection to router {self.router_name}@{self.config_entry.hostname} has failed: {exc}' )
class API: def __init__(self, hostname, username, password): # Router Credentials self.hostname = hostname self.username = username self.password = password self.connection = None self.api = None ######################################################################################################################## def connect(self): self.connection = RouterOsApiPool(host=self.hostname, username=self.username, password=self.password, plaintext_login=True) return self.connection ######################################################################################################################## def get_api(self): try: self.api = self.connection.get_api() return self.api except RouterOsApiConnectionError: self.api = None ######################################################################################################################## def check_api(self): if self.api: try: self.api.get_resource('log').get() return self.api except RouterOsApiConnectionError: self.api = None return self.api ######################################################################################################################## def add_ips(self, address, list): if not self.check_api(): return None prefix = self.api.get_resource('ip/firewall/address-list') prefix.add(address=address, list=list, timeout='35d') ######################################################################################################################## def check_log(self): if not self.check_api(): print("Failed attemtps could not create API instance, finnishing up script") return None logged_attempts = list() saved_attempts = list() log = self.api.get_resource('log').get() for entry in log: if "denied winbox/dude connect from" in entry["message"]: ip = re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", entry["message"])[0] logged_attempt = dict() logged_attempt["id"], logged_attempt["ip"] = entry["id"], ip regex_string = "Modify your IP specifications Here" white_list = re.findall(regex_string, ip) if not white_list: logged_attempts.append(logged_attempt) if len(saved_attempts) > 0: for saved_attempt in saved_attempts: if len(logged_attempts) > 0: for logged_attempt in logged_attempts: if saved_attempt["id"] != logged_attempt["id"]: saved_attempts.append(logged_attempt) else: for logged_attempt in logged_attempts: saved_attempts.append(logged_attempt) return saved_attempts ######################################################################################################################## def attempt_counter(self, attempts): if attempts: ip_counter = list() tested_ips = list() for attempt in attempts: attempt_ip = attempt["ip"] if len(ip_counter) > 0: if attempt_ip not in tested_ips: new_counter = dict() new_counter["ip"], new_counter["counter"] = attempt_ip, 1 ip_counter.append(new_counter) tested_ips.append(new_counter["ip"]) else: for ip_counter_entry in ip_counter: if attempt_ip == ip_counter_entry["ip"]: ip_counter_entry["counter"] += 1 else: new_ip = dict() new_ip["ip"] = attempt_ip new_ip["counter"] = 1 ip_counter.append(new_ip) tested_ips.append(new_ip["ip"]) return ip_counter ip_counter = None return ip_counter ######################################################################################################################## ######################################################################################################################## def create_address_list(self, attempts): COMMAND_PATH = '/sys/script/' COMMAND_ID = 'ip_block' exsisting_ips = list() if not self.check_api(): print("Error connecting to API while creating starting address list") return None if not attempts: return None blocked_subnet = self.api.get_resource('/ip/firewall/address-list').get() for subnet in blocked_subnet: if subnet["list"] == "BLOCKED_SUBNETS": exsisting_ips.append(subnet["address"]) if attempts: for attempt in attempts: ip = attempt["ip"] if ip not in exsisting_ips: print(ip) self.add_ips(address=ip, list="BLOCKED_SUBNETS") ip_inst = BlockedIP(ip, datetime.datetime.now()) IPs.insert_IP(ip_address=ip_inst.IP, date_time=ip_inst.date_time) self.connection.disconnect() ######################################################################################################################## def full_sequence(self): self.connect() self.get_api() print(self.api) if not self.check_api(): return None log = self.check_log() attempt_counter = self.attempt_counter(log) self.create_address_list(attempt_counter)