UNPKG

@fibercom/routeros-api

Version:

Robust MikroTik RouterOS API client for Node.js and TypeScript

425 lines (318 loc) 9.04 kB
# @fibercom/routeros-api A TypeScript and Node.js client for the MikroTik RouterOS API. The library supports plain TCP and TLS connections, tagged concurrent commands, continuous data streams, connection retries, RouterOS traps, and convenience methods for common router operations. ## Features - TypeScript declarations and strict type checking - RouterOS API over TCP or TLS - Tagged commands on a shared connection - Long-running streams with pause, resume, and close support - Safe incremental decoding across fragmented TCP packets - RouterOS `!re`, `!done`, `!trap`, `!fatal`, and `!empty` replies - DHCP lease, firewall, route, interface, user, log, and system helpers - Legacy RouterOS challenge-response login compatibility - Configurable retries, timeouts, keepalive, and debug output ## Requirements - Node.js 14 or newer - MikroTik RouterOS API service enabled - Network and firewall access to the API port RouterOS normally uses: - `8728` for the unencrypted API service - `8729` for API-SSL ## Installation ```bash npm install @fibercom/routeros-api ``` ## Basic usage ```js const { MikrotikAPI } = require("@fibercom/routeros-api"); async function main() { const api = new MikrotikAPI({ host: "192.168.88.1", user: "api-user", password: "your-password", timeout: 10, }); api.on("error", (error) => { console.error("Router error:", error.message); }); api.on("timeout", (error) => { console.error("Router timeout:", error.message); }); try { await api.connect(); const identity = await api.getSystemIdentity(); const resources = await api.getSystemResources(); console.log({ identity, resources }); } finally { if (api.connected) await api.close(); } } main().catch(console.error); ``` ## TypeScript usage ```ts import { MikrotikAPI, IRosOptions } from "@fibercom/routeros-api"; const options: IRosOptions = { host: "192.168.88.1", user: "api-user", password: "your-password", timeout: 10, }; const api = new MikrotikAPI(options); await api.connect(); const interfaces = await api.getInterfaces(); await api.close(); ``` ## TLS connection Use RouterOS API-SSL whenever the connection crosses an untrusted network. ```js const api = new MikrotikAPI({ host: "router.example.com", user: "api-user", password: "your-password", tls: true, }); ``` When `tls` is enabled and `port` is omitted, the library uses port `8729`. You can pass Node.js TLS options instead of `true`: ```js const fs = require("fs"); const api = new MikrotikAPI({ host: "router.example.com", user: "api-user", password: "your-password", tls: { ca: fs.readFileSync("router-ca.pem"), servername: "router.example.com", rejectUnauthorized: true, }, }); ``` Port `8728` is unencrypted. Usernames, passwords, commands, and responses can be observed by anyone able to inspect that network traffic. ## Connection retries ```js await api.connectWithRetry(3, 1000); ``` Arguments: 1. Maximum connection attempts 2. Delay between attempts in milliseconds A socket timeout produces: ```text Connection timeout. Possible firewall enabled, RouterOS API service disabled, or host unreachable. ``` ## Raw RouterOS commands Use `write()` for any RouterOS API command that does not have a convenience method: ```js const addresses = await api.write([ "/ip/address/print", "?interface=bridge", ]); ``` The method accepts either one array or multiple command arguments: ```js const identity = await api.write("/system/identity/print"); await api.write( "/system/identity/set", "=name=core-router" ); ``` RouterOS response values are returned as strings in plain JavaScript objects. ## DHCP leases Get every lease: ```js const leases = await api.getDhcpLeases(); ``` Filter by an exact DHCP server name: ```js const leases = await api.getDhcpLeases("Dhcp-mgmt"); ``` RouterOS query values are case-sensitive. Use the exact server name returned by: ```js const servers = await api.write("/ip/dhcp-server/print"); ``` Add a static lease: ```js await api.addDhcpLease({ address: "192.168.88.20", macAddress: "00:11:22:33:44:55", server: "Dhcp-mgmt", comment: "Office printer", disabled: false, }); ``` Update a lease: ```js await api.setDhcpLease("*1A", { address: "192.168.88.21", comment: "Updated printer address", }); ``` Block or unblock a lease using the `Blocked` firewall address list: ```js await api.blockDhcpLeaseById("*1A", "Blocked by billing"); await api.unblockDhcpLeaseById("*1A"); ``` Some DHCP helpers update multiple RouterOS resources sequentially. If a command fails midway, earlier router changes are not automatically rolled back. ## Live interface traffic ```js const stream = api.streamInterfaceStats( { interface: "sfp-sfpplus1", interval: 1, }, (error, packet) => { if (error) { console.error(error.message); return; } console.log( packet["rx-bits-per-second"], packet["tx-bits-per-second"] ); } ); await new Promise((resolve) => setTimeout(resolve, 5000)); await stream.pause(); await new Promise((resolve) => setTimeout(resolve, 3000)); await stream.resume(); await new Promise((resolve) => setTimeout(resolve, 5000)); await stream.close(); ``` A paused stream remains registered with the API. Resuming creates a fresh RouterOS command tag. Calling `close()` or `stop()` performs final cleanup. Streams also emit events: ```js stream.on("data", (packet) => console.log(packet)); stream.on("paused", () => console.log("paused")); stream.on("started", () => console.log("started")); stream.on("done", () => console.log("done")); stream.on("close", () => console.log("closed")); stream.on("stopped", () => console.log("stopped")); stream.on("trap", (trap) => console.error("trap", trap)); stream.on("error", (error) => console.error(error)); ``` ## Convenience methods ### System - `getSystemInfo()` - `getSystemResources()` - `getSystemIdentity()` - `setSystemIdentity(name)` - `getSystemLogs(topics?)` - `streamSystemLogs(callback?)` - `reboot()` - `exportConfig(filename?)` - `importConfig(filename)` ### Interfaces and networking - `getInterfaces()` - `getIPAddresses()` - `getInterfaceStats(interfaceName?)` - `streamInterfaceStats(options, callback?)` - `getBridges()` - `getRoutes()` - `addRoute(destination, gateway, distance?)` ### DHCP - `getDhcpLeases(serverName?)` - `addDhcpLease(lease)` - `setDhcpLease(id, parameters)` - `setDhcpLeaseAndUpdateAddressLists(id, parameters)` - `removeDhcpLease(id)` - `getDhcpServersWithGateways()` - `getFreeIpsForDhcpServer(serverName, gatewayCidr)` - `blockDhcpLeaseById(id, comment?)` - `unblockDhcpLeaseById(id)` ### Firewall, wireless, and users - `getFirewallRules(chain?)` - `addFirewallRule(...)` - `getWirelessInterfaces()` - `getWirelessRegistrations()` - `streamWirelessRegistrations(callback?)` - `getUsers()` - `addUser(name, password, group?)` - `runScript(scriptName)` ## Connection options ```ts interface IRosOptions { host: string; user?: string; password?: string; port?: number; timeout?: number; tls?: boolean | TlsOptions; keepalive?: boolean; debug?: boolean; } ``` `timeout` is measured in seconds. ## Errors RouterOS and connection failures use `RosException`: ```js const { RosException } = require("@fibercom/routeros-api"); try { await api.connect(); } catch (error) { if (error instanceof RosException) { console.error(error.errno, error.message); } else { throw error; } } ``` RouterOS `!trap` replies reject command promises. Listen for the API's `error` and `timeout` events when maintaining a long-lived connection. ## Live router smoke test The repository includes a read-only test in `index.js`. It reads router system information, interfaces, DHCP leases, and exercises traffic streaming with this sequence: ```text start -> 5 seconds -> pause -> 3 seconds -> resume -> 5 seconds -> close ``` Copy `.env.example` to `.env` and configure it: ```dotenv ROUTEROS_HOST=192.168.88.1 ROUTEROS_PORT=8728 ROUTEROS_USER=api-user ROUTEROS_PASSWORD=change-me ROUTEROS_TIMEOUT=10 ROUTEROS_TLS=false ROUTEROS_DEBUG=false ROUTEROS_TEST_INTERFACE=sfp-sfpplus1 ROUTEROS_DHCP_SERVER=Dhcp-mgmt ``` Then run: ```bash npm run test:router ``` The `.env` file is ignored by Git. Do not commit router credentials. ## Development Install dependencies: ```bash npm install ``` Build the library: ```bash npm run build ``` Run strict compilation and the automated tests: ```bash npm test ``` Watch TypeScript sources: ```bash npm run watch ``` Compiled JavaScript and declarations are written to `dist/`. ## Security - Prefer API-SSL on port `8729`. - Restrict API access with RouterOS firewall rules and `/ip service` address restrictions. - Use a dedicated RouterOS user with only the permissions your application needs. - Never commit passwords, `.env` files, private keys, or router backups. - Test configuration-changing helpers on a non-production router first. ## License MIT