diff --git a/playground/mikrotik/mikrotik.py b/playground/mikrotik/mikrotik.py new file mode 100644 index 0000000..9ef92bb --- /dev/null +++ b/playground/mikrotik/mikrotik.py @@ -0,0 +1,165 @@ +""" + + AUTHOR: + + Khushal P Soonderji + + DATE: + + Sunday, 9th Feb, 2025. + + OBJECTIVE: + + To provide an easy way to configure MikroTik devices remotely. + + REFERENCES: + + N/A + + DOWNLOADS: + + N/A + +""" + + +# ***************************************************************************************************************** +# ***** **** +# *** IMPORT *** +# ***** **** +# ***************************************************************************************************************** + + +# To make sibling directories accessible for imports: +import sys +sys.path.append(".") +sys.path.append("..") + +# System-level activities: +import io +import os + +# My utils: +from utils_v2.string import json + +# For SSH: +from paramiko import SSHClient, AutoAddPolicy + + +# ***************************************************************************************************************** +# ***** **** +# *** MACROS / ONE-TIME INIT *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** VARIABLES *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** FUNCTIONS *** +# ***** **** +# ***************************************************************************************************************** + + +def ssh_exec_one(ssh: SSHClient, command: str): + + # Start by assuming failure: + result = { + "stdin": None, + "stdout": None, + "stderr": None, + "returnCode": -999, + "exception": None + } + + try: + + # Run the command: + stdin, stdout, stderr = ssh.exec_command(command) + + print(stdin) + + # Extract the results: + result["stdin"] = stdin.read().decode("utf8") + result["stdout"] = stdout.read().decode("utf8") + result["stderr"] = stderr.read().decode("utf8") + result["returnCode"] = stdout.channel.recv_exit_status() + + # Close the file-like objects: + stdin.close() + stdout.close() + stderr.close() + + # If something goes wrong: + except Exception as exception: + result["exception"] = exception + raise exception + + # Done here: + return result + + +# --------------------------------------------------------------------------------------------------------------------- + + +def ssh_test(config: dict): + + # Initialize the object: + ssh = SSHClient() + # ssh.load_host_keys("~/.ssh/known_hosts") + ssh.load_system_host_keys() + ssh.set_missing_host_key_policy(AutoAddPolicy()) + + # Log in to the SSH client: + ssh.connect( + hostname = config["nasIp"], + port = 19991, + username = config["username"], + password = config["password"] + ) + + # Execute the needed commands: + result = ssh_exec_one(ssh, "hostname") + print("EXEC ONE JSON:", json.to_string(result, default = str)) + + # Close the connection: + ssh.close() + + +# ***************************************************************************************************************** +# ***** **** +# *** MAIN PROGRAM *** +# ***** **** +# ***************************************************************************************************************** + + +if __name__ == "__main__": + + # The expected configuration structure from the database: + sample_config_json = { + "siteName": "Hirani Telcom", + # "nasIp": "102.210.175.251", + "nasIp": "wtt.ditscentre.in", + "radius": "102.210.175.242", + "secret": "abcdefgh", + # "username": "easyfi", + "username": "chaalu", + # "password": "easyfi", + "password": "hellochaalu", + "location": "Kenya", + "snmpCommunity": "abcdefgh" + } + + ssh_test(config = sample_config_json) diff --git a/readme/Mikrotik - EasyFi.md b/readme/Mikrotik - EasyFi.md new file mode 100644 index 0000000..1747871 --- /dev/null +++ b/readme/Mikrotik - EasyFi.md @@ -0,0 +1,405 @@ +# MiktoTik Configuration Steps +### We need to configure MikroTik servers for two end use cases - PPPoE or HotSpot. here are the steps to achieve them through MikroTik's REST API facility. Use the `MiktoTik (EasyFi)` Postman collection for this. +#### Authors: Bhushan, Hari, Khushal +#### Date: 20250209 + + +### General Notes: + +Private IP series has been allocated for doing carrier-grade NAT = 100.64.0.0/10 + +PPPoE user master subnet = 100.64.0.0/16 +pppoe_ip_subnet: 100.64.0.0/24 = if user is under 256 +pppoe_ip_subnet: 100.64.0.0/23 = if user is under 512 +pppoe_ip_subnet: 100.64.0.0/22 = if user is under 1024 +pppoe_ip_subnet: 100.64.0.0/21 = if user is under 2048 +PPPoE deactivated user master subnet = 10.0.0.0/16 + +HotSpot user master subnet = 100.65.0.0/16 +hotspot_ip_subnet: 100.65.0.0/24 = if user is under 256 +hotspot_ip_subnet: 100.65.0.0/23 = if user is under 512 +hotspot_ip_subnet: 100.65.0.0/22 = if user is under 1024 +hotspot_ip_subnet: 100.65.0.0/21 = if user is under 2048 + +--- + +## Steps for HotSpot + +### 1. Interface +- Enlist the available interfaces. +- Select the first one that has field `"running"` set to `"false"`, and pick its `".id"`. +- Rename it to `"hs-easyfi"` by its `".id"`. + +### 2. Create a VLAN +Create a VLAN with the following config: +```json +{ + "name": "easyfi-vlan-", + "interface": "easyfi-hs", // The name that was given in the previous step. + "vlan-id": "", // From the config received on the portal. + "disabled": "no" +} +``` + +### 3. Add a new IP Address to the VLAN interface +```json +{ + "address": "", // Refer to the general notes. + "interface": "easyfi-vlan-", // Same as the 'name' given in the previous step. + "disabled": "no" +} +``` + +### 4. Add a New IP Pool for HotSpot Users +```json +{ + "name": "easyfi-hs-pool-", + "ranges": "" // Refer to the general notes. +} +``` + +### 5. Add a New HotSpot Profile +```json +{ + "dns-name": "hs01.easyfi.net.in", + "hotspot-address": "100.65.0.1", // First from the subnet. Refer to the general notes. + "html-directory": "hotspot", + "html-directory-override": "", + "http-cookie-lifetime": "3d", + "http-proxy": "0.0.0.0:0", + "install-hotspot-queue": "false", + "login-by": "cookie,http-chap", + "name": "hs01.easyfi.net.in", + "split-user-domain": "false", + "use-radius": "true", + "nas-port-type": "wireless-802.11", + "radius-accounting": "true", + "radius-default-domain": "", + "radius-interim-update": "received", + "radius-location-id": "", + "radius-location-name": "", + "radius-mac-format": "XX:XX:XX:XX:XX:XX" +} +``` + +### 6. Add a New HotSpot Server +```json +{ + "address-pool": "easyfi-hs-pool-", // Same as the name given when creating the pool. + "addresses-per-mac": "2", + "idle-timeout": "5m", + "interface": "easyfi-vlan-", // Same as the 'name' given when creating the VLAN. + "keepalive-timeout": "none", + "login-timeout": "none", + "name": "hs01.easyfi.net.in", + "profile": "hs01.easyfi.net.in", + "disabled": "false" +} +``` + +### 7. Add a New DHCP Server +```json +{ + "address-pool": "easyfi-hs-pool-", // Same as the name given when creating the pool. + "authoritative": "yes", + "disabled": "false", + "interface": "easyfi-vlan-", // Same as the 'name' given when creating the VLAN. + "lease-time": "30m", + "name": "easyfi-hs-dhcp-", + "use-radius": "no" +} +``` + +### 8. Add a New DHCP Network +```json +{ + "address": "", // Refer to the general notes. + "gateway": "100.65.0.1", // First from the subnet. Refer to the general notes. + "netmask": "24", // Refer to the no. of users in the general notes. + "dns-server": "100.65.0.1" // First from the subnet. Refer to the general notes. +} +``` + + +### 9. Whitelist the RADIUS Server IP in the HotSpot Server +Hint: Walled-Garden IPs +```json +{ + "action": "accept", + "dst-address": "" // Received from the portal. +} +``` + +### 10. Add a New RADIUS Server: +```json +{ + "accounting-port": "1813", + "address": "", // Received from the portal. + "authentication-port": "1812", + "disabled": "false", + "protocol": "udp", + "secret": "", // Received from the portal. + "service": "ppp,login,hotspot,dhcp", + "timeout": "300ms" +} +``` + +### 11. Allow Incoming Communication from the RADIUS Server to the MikroTik Device +```json +{ + "accept": "yes", + "port": "3799" +} +``` + + +### 12. Add a New DNS Server +```json +{ + "allow-remote-requests": "true", + "servers": "8.8.8.8,8.8.4.4" +} +``` + + +### 13. Add Firewall Rules for NAT-ing +**NOTE:** Looping needed to split the bigger IP subnet into smaller subnets. +```json +{ + "action": "src-nat", + "chain": "srcnat", + "disabled": "false", + "src-address": "100.65.0.16/28", + "to-addresses": "111.222.111.111" +} +``` + +### 14. Add a New SNMP Community +```json +{ + "addresses": "::/0", + "authentication-password": "", + "authentication-protocol": "MD5", + "disabled": "false", + "encryption-password": "", + "encryption-protocol": "DES", + "name": "", + "read-access": "true", + "security": "none", + "write-access": "false" // ALWAYS FALSE +} +``` + + +--- + +## Steps for PPPoE + +### 1. Interface +- Enlist the available interfaces. +- Select the first one that has field `"running"` set to `"false"`, and pick its `".id"`. +- Rename it to `"easyfi-ppp"` by its `".id"`. + +### 2. Create a VLAN +Create a VLAN with the following config: +```json +{ + "name": "easyfi-vlan-", + "interface": "easyfi-ppp", // The name that was given in the previous step. + "vlan-id": "", // From the config received on the portal. + "disabled": "no" +} +``` + +### 3. Add a New IP Pool for PPPoE Users +```json +{ + "name": "easyfi-ppp-pool-", + "ranges": "" // Refer to the general notes. +} +``` + +### 4. Add a New PPPoE Profile +```json +{ + "address-list": "", + "bridge-learning": "default", + "change-tcp-mss": "yes", + "dns-server": "8.8.8.8", + "local-address": "102.210.175.251", // nasIP (MikroTik IP) + "name": "easyfi-ppp-profile", + "only-one": "yes", + "remote-address": "easyfi-ppp-pool-", + "use-compression": "default", + "use-encryption": "default", + "use-ipv6": "yes", + "use-mpls": "default", + "use-upnp": "default" +} +``` + + +### 5. Add a PPPoE Server +```json +{ + "address-list": "", + "bridge-learning": "default", + "change-tcp-mss": "yes", + "default": "false", + "dns-server": "8.8.8.8", + "local-address": "102.210.175.251", + "name": "easyfi-ppp-profile", + "only-one": "yes", + "remote-address": "easyfi-ppp-pool-2001", + "use-compression": "default", + "use-encryption": "default", + "use-ipv6": "yes", + "use-mpls": "default", + "use-upnp": "default" +} +``` + +### 6. Allowing User Authentication RADIUS Server +```json +{ + "accounting": "true", + "interim-update": "3m", + "use-circuit-id-in-nas-port-id": "false", + "use-radius": "true" +} +``` + +### 7. Add a New RADIUS Server: +```json +{ + "accounting-port": "1813", + "address": "", // Received from the portal. + "authentication-port": "1812", + "disabled": "false", + "protocol": "udp", + "secret": "", // Received from the portal. + "service": "ppp,login,hotspot,dhcp", + "timeout": "300ms" +} +``` + +### 8. Allow Incoming Communication from the RADIUS Server to the MikroTik Device +```json +{ + "accept": "yes", + "port": "3799" +} +``` + +### 9. Add Firewall Rules for NAT-ing +**NOTE:** Looping needed to split the bigger IP subnet into smaller subnets. +```json +{ + "action": "src-nat", + "chain": "srcnat", + "disabled": "false", + "src-address": "100.64.0.16/28", + "to-addresses": "111.222.111.111" +} +``` + + +### 10. Add a New SNMP Community +```json +{ + "addresses": "::/0", + "authentication-password": "", + "authentication-protocol": "MD5", + "disabled": "false", + "encryption-password": "", + "encryption-protocol": "DES", + "name": "", + "read-access": "true", + "security": "none", + "write-access": "false" // ALWAYS FALSE +} +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +--- + +### General Notes: + +- Master Block Private IP series used for Active Customers PPPoE and HotSpot range is 100.64.0.0/10 +- - Private IP series used for Active Customers PPPoE and is 100.64.0.0/16 +- Private IP series used for Active Customers HotSpot range is 100.65.0.0/16 +- Private IP series used for Deactivated Customers PPPoE range is 10.0.0.0/16 + +--- + +### Steps for PPPoE: + +- Create IP Pool. +- /ip/pool +- Add a new pool with the `"Name"` set to `"permitted"` and `Addresses` set to `100.64.0.0/24`. +- Add a new pool with the `"Name"` set to `"deactived"` and `Addresses` set to `10.0.0.0/16`. +- Create a PPPoE profile. +- /ppp/profiles +- Add a local address (Local Address is the WAN IP of the MikroTik) to the profile. +- Add a remote address to the profile with the `Name` set to `"permitted"`. +- Add a DNS server to the profile. +- Change `TCP MSS` to `"yes"` +- Change `Limits -> Only One` to `"yes"` +- /interface/pppoe-server +- Add a new server. +- Add service name (as per req.) +- Select the interface. +- Select the profile. +- Select authentication mechanism (chap/pap/mschap1/mschap2). +- Keepalive Timeout 900 +- /ppp/secret/ppp-authorization and accounting, select use radius and accounting. +- Go to RADIUS +- Add service (select: ppp, login, hotspot, dhcp) +- Put in the RADIUS server's addr in the address col, select UDP as the protocol, put the secret. +- Go to IP firewall. +- NAT +- Create source-map (as per req.) + +--- + +### Steps for HotSpot: + +- Create IP Pool. +- /ip/pool +- Add a new pool with the `"Name"` set to `"hotspot"` and `Addresses` set to `100.65.0.0/24`. +- IP -> Hotspot -> Add -> Server Name = "New hotspot" -> Interface -> Pool -> Name = "hotspot" -> Add +- IP -> Addresses -> Add -> Address -> First IP from the Hotspot IP Pool as the gateway (100.65.0.1/24) \ No newline at end of file