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)
def main(): setup_logging('ddns2.log') config = get_config('router', 'ddns') logging.info("starting ddns") ddns_config = config['DDNS_CONFIG'] resource4 = ddns_config['resource4'] resource6 = ddns_config['resource6'] domain = ddns_config['domain'] pppoe_interface = ddns_config['pppoe_interface'] ipv6_pool_name = ddns_config['ipv6_pool_name'] logging.info("loaded config, connecting to router") router_api = RouterOsApiPool(**(config['ROUTER_CONFIG'])).get_api() while True: try: for local_ip, resource in ((get_local_ipv4(router_api, pppoe_interface), resource4), (get_local_ipv6(router_api, ipv6_pool_name), resource6)): current_ip, record_name = get_current_record_ip( domain, resource) if local_ip is not None and current_ip != local_ip: logging.info( f"updating record {record_name} from {current_ip} to {local_ip}" ) update_record_ip(domain, resource, local_ip) heartbeat() except RouterOsApiConnectionError: logging.warning('resetting router api') router_api = RouterOsApiPool( **(get_config('router')['ROUTER_CONFIG'])).get_api() except JSONDecodeError: logging.exception('linode JSONDecodeError') except (requests.exceptions.RequestException, OSError): logging.exception('RequestException issues') except: logging.exception("error") raise finally: sleep(30)
def main(): set_routes = set() add_routes = set() setup_logging('routes.log') config = get_config('router') router_key = config['ROUTER_CONFIG'] router_api = RouterOsApiPool(**router_key).get_api() route = router_api.get_resource('/ip/route') load_existing_routes(route, set_routes) connections = router_api.get_resource('/ip/firewall/connection') while True: heartbeat() add_routes.clear() add_fast_routes(add_routes, connections) new_routes, remove_routes = load_address_list(router_api, set_routes, add_routes) if new_routes: add_new_routes(route, new_routes, remove_routes) set_routes -= remove_routes set_routes = set_routes.union(new_routes) sleep(30)
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 main_loop(session_key, session): res = session.get('http://192.168.100.1/reseau-pa3-frequencecable.html') res.raise_for_status() if 'document.write(AccessMemberLimit);' in res.text: logging.warning( "Permission denied, another user is already logged; sleeping 20 seconds" ) sleep(20) return session_key if '>location.href="/login.html"<' in res.text: logging.info("logging in to session") # login res = session.get('http://192.168.100.1/login.html') res.raise_for_status() session_key = session_key_matcher.match(res.text).group(1) logging.info("session key is {}".format(session_key)) res = session.post( f'http://192.168.100.1/postlogin.cgi?sessionKey={session_key}', data={ 'sessionKey': session_key, 'loginUsername': MODEM_USER, 'loginPassword': MODEM_PASSWORD, }) if 'var RefreshPage = ' not in res.text: logging.warning("failed to login") return session_key logging.info(f"cookie is {session.cookies}") return session_key if not session.cookies: logging.warning( f"need to force logout and log back in for reboot to work") session.get('http://192.168.100.1/logout.html').raise_for_status() return '' new_session_key = session_key_matcher.match(res.text).group(1) if new_session_key and new_session_key != session_key: session_key = new_session_key m = error_counters_matcher.match(res.text) correctable = int(m.group(1)) uncorrectable = int(m.group(2)) if uncorrectable > 1: logging.warning( "found {} correctable and {} uncorrectable errors".format( correctable, uncorrectable)) logging.warning("disabling cable modem route") router_api = RouterOsApiPool(**ROUTER_KEY).get_api() route = router_api.get_resource('/ip/route') route_id = route.get(distance='15')[0]['id'] route.set(id=route_id, disabled='no') logging.warning( "restarting cable modem; session key is {}, cookies {}".format( session_key, session.cookies)) res = session.post( f'http://192.168.100.1/reseau-pa3-frequencecable.cgi?sessionKey={session_key}', data={ 'sessionKey': session_key, 'CmStartupDsFreq': '999999', 'action': '1', }) res.raise_for_status() if 'Invalid Session Key' in res.text: logging.warning( 'Invalid Session Key error, logging out and retrying') session.get('http://192.168.100.1/logout.html').raise_for_status() return '' logging.warning("disabling eth interface on router") eth = router_api.get_resource('/interface') eth_id = eth.get(name='wan-hot')[0]['id'] eth.set(id=eth_id, disabled='yes') logging.warning("waiting 125 seconds") sleep(120) eth.set(id=eth_id, disabled='no') logging.warning("reenabling cable modem interface") sleep(5) logging.warning("reenabling cable modem route") route.set(id=route_id, disabled='yes') return session_key
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}' )
def connect(self): self.connection = RouterOsApiPool(host=self.hostname, username=self.username, password=self.password, plaintext_login=True) return self.connection
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)