The Brothers WISP 112 – UBNT Payment Gateway, Automation Layering, MTK netPower16P



This week Greg, Mike, Thomas, Tommy, and Andrew Cox talk for 2 HOURS about all sorts of things.

This week we talk about:
**Cambium Deal Link**
Ole made a great first ansible playbook – it updates his unimus version!
Greg has ansible playbooks for updating catalyst switch IOSs
Greg has a playbook that connects to mikrotiks, does pings/traceroutes, then aggregates the info.
Greg makes a tutorial on installing Hashicorp vault(using ansible) for the lab and playbooks for working with it.
Always remember strain relief on your connections – Brock and others having ethernet stability issues due to pressure on the connector.
Cox found a small 12V UPS for $25
Mikrotik 4011 mystery lockups – A few people report down clocking a troublesome unit helps “system routerboard settings set cpu-frequency=1200”
Mikrotik netPower16P – 16 port AF/AT outdoor PoE switch
Ubiquiti Payment Gateway
40 Tb optical comb
VCSA 10/10 RCE
Zerodium stops accepting Apple exploits – there is too many
Unimus 2.0.0 release
Quickbooks “Pay Online” blocked my credit card due to no SCA – Thanks…
Canon beta webcam software
Greg will always remember a bag of pubes.

Here’s the video:(if you don’t see it, hit refresh)

Making your router talk – MikroTik and Telegram Bot Scripting

While there are existing ways (SNMP/SMS) to run scripts on RouterOS via external means, I’ve been meaning to show off a system I built based around Telegram Messenger – as it’s a relatively common one, and has a flexible API for interfacing with.



I began this with the older MikroTik 4096 character variable limit in mind, intending to process 1 or 2 messages at a time, but found half way through that this no longer applies (yay) – so as many as 100 messages or more could be pulled down at the same time and churned through the processing script.

Because we’re running this based around a single-threaded processing script it’s not going to be the fastest implementation, but I’m hoping this is a good start for anyone looking to expand on the functions I’ve added here.

At present the system works as follows:

A scheduler entry called “telegram_bot” runs every 50 seconds by default. It’s designed not to hammer the telegram servers or exceed rate limits – but will be ramped up in the event messages are seen.

The scheduler runs the script “telegram_bot_main” which ensures all required functions are loaded and sets the BOTID (see: https://core.telegram.org/bots for how to create one) and your telegramID (an ID unique to each user). This will be used to determine if the messenger gets authenticated or unauthenticated access. The main script also updates the scheduler delay (which is set to 10 seconds when a message first arrives, and slowly increases back up to 50 seconds if no further updates are seen).

Important note: A function called “sendlog” is called throughout these scripts in order to allow easy debugging – this is the best place to define any logging/put/debugging options you want to spit out – and then off again when you want the script to run silently in the background.

If updates are found – these are processed through the “telegram_bot_processupdates” script which determines what to do next, until the list of updates has been completed – when a list of updates has been processed, a call is sent back to telegram to confirm the updateID of the last message processed – this is then used as an offset for the subsequent messages to be sent by telegram. You wouldn’t want to run 2 routers on the same BOT ID with this in place as it would mean only one router might see the message.

Updates are processed one message at a time, and if a message is detected to be from a group (for example someone has accidentally added the bot to a group chat) the bot will leave this chat by calling the “telegram_bot_leavechat” function.

If an update is detected to be from an authed user (matching the “telegramID” mentioned above) then the received command is run against the “telegram_bot_message_auth” script, otherwise against the “telegram_bot_message_unauth” script.

The authenticated one is pretty basic for now – it allows the user to run any existing script on the router including variables. Say for example you had a script/function that would send a copy of the router backup to an email address you could trigger this remotely.

The unauthenticated one is where I’ve had some fun for now – putting in a few basic commands that allow a remote user to trigger functions on the router. These are:

wifi: show current number of wireless registrations
internet: show current speed of ether1 on the router
blink: make a light on the router blink
beep: for devices that have it, make the router speaker beep
ping <ip address>: ping an IP address from the router and print back the latency

Now yes, these are very simple commands – but I’ve done this specifically so I can leave this bot open to the public to access and play with – the list of commands you could add is up to your imagination.

Once a command is issued – the script “telegram_bot_sendmessage” is called to issue a response to the user, with the result of their command – this is only attempted once and otherwise simply fails, but it would be possible to queue these up also and attempt to process in order.

I did for a short time toy with the idea of porting Zork to work on RouterOS before remembering I have a full-time job, a wife and much worse programming skills than routing ones.. 🙂

You can message my router by telegramming @AURouter_bot – note the first response will take up to a minute to appear, then subsequent ones will appear faster.

Scripts in their entirety here:

This can be copied and pasted into RouterOS v6.46:

Don’t forget to update the BOTID + TELEGRAM USER ID in telegram_bot_main or you won’t be able to retrieve updates, or you’ll only be responding to unauthed messages.

/system scheduler add interval=50s name=telegram_bot on-event=":local scriptname \"telegram_bot_main\"\r\
    \n#:if ([:len [/system script job find script=\$\"scriptname\"]] > 0) do={\r\
    \n#:log warning \"\$scriptname Already Running - killing old script before continuing\"\r\
    \n#:foreach counter in=[/system script job find script=\$\"scriptname\"] do={\r\
    \n#/system script job remove \$counter\r\
    \n#}\r\
    \n#}\r\
    \n/system script run \$scriptname" policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-time=startup
/system script
add dont-require-permissions=yes name=telegram_bot_main policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":global botID \"BOT_ID_STUFF_GOES_HERE\" \r\
    \n:global mychat \"YOURTELEGRAM_ID_GOES_HERE\"\r\
    \n:global urlStart \"https://api.telegram.org/bot\"\r\
    \n\r\
    \n#max updates to pull down at once\r\
    \n:local updatelimit 100\r\
    \n#set to 50s or less\r\
    \n:local maxpolldelay 50\r\
    \n\r\
    \n#Functions\r\
    \n:global processupdates [:parse [/system script get telegram_bot_processupdates source]]\r\
    \n:global messageauth [:parse [/system script get telegram_bot_message_auth source]]\r\
    \n:global messageunauth [:parse [/system script get telegram_bot_message_unauth source]]\r\
    \n:global sendmessage [:parse [/system script get telegram_bot_sendmessage source]]\r\
    \n:global sendlog [:parse [/system script get telegram_bot_log source]]\r\
    \n:global leavechat [:parse [/system script get telegram_bot_leavechat source]]\r\
    \n:global updateoffset [:parse [/system script get telegram_bot_updateoffset source]]\r\
    \n\r\
    \n\$sendlog msg=\"=====Beginning Cycle=====\"\r\
    \n\r\
    \n:global telegramdelay\r\
    \n:local telegramdelaycurrent [/system scheduler get [find name=telegram_bot] interval]\r\
    \n\r\
    \n:local fetchURL (\"/getUpdates\\\?limit=\" . \$updatelimit . \"&allowed_updates=message\")\r\
    \n\$sendlog msg=(\"GETURL: \" . \$fetchURL); :set fetchURL (\$urlStart . \$botID . \$fetchURL);\r\
    \n:local content [/tool fetch url=\$fetchURL as-value output=user]\r\
    \n\r\
    \n#if new message exists send to updateprocessingqueue\r\
    \n:if ((\$content->\"status\") = \"finished\" && [:len (\$content->\"data\")] > 33) do={\r\
    \n  :local contentdata (\$content->\"data\")\r\
    \n  \$processupdates updatecontent=(\$contentdata)\r\
    \n} else={\r\
    \n  \$sendlog msg=(\"Status: \" .(\$content->\"status\") . \" - No new data to process\")\r\
    \n  :if (\$telegramdelay < \$maxpolldelay) do={\r\
    \n    :set telegramdelay (\$telegramdelay + 10)\r\
    \n\t\$sendlog msg=(\"Increased delay to \" . \$telegramdelay . \"s\")\r\
    \n  }\r\
    \n}\r\
    \n\r\
    \n:if ([:pick \$telegramdelaycurrent 6 [:len \$telegramdelaycurrent]] != \$telegramdelay) do={\r\
    \n  /system scheduler set [find name=telegram_bot] interval=(\"00:00:\" . \$telegramdelay)\r\
    \n}"
add dont-require-permissions=no name=telegram_bot_sendmessage policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#Functions\r\
    \n:global sendlog \r\
    \n\r\
    \n\$sendlog msg=\"RUN: sendmessage\"\r\
    \n\r\
    \n:global urlStart\r\
    \n:global botID\r\
    \n:local content\r\
    \n:local fetchURL\r\
    \n\r\
    \n\$sendlog msg=(\"Sending message to \$chatid\")\r\
    \n\r\
    \n:set fetchURL (\"/sendmessage\\\?chat_id=\" . \$chatid . \"&text=\" . \$text)\r\
    \n\$sendlog msg=(\"GETURL: \" . \$fetchURL); :set fetchURL (\$urlStart . \$botID . \$fetchURL);\r\
    \n:set content [/tool fetch url=\$fetchURL as-value output=user]\r\
    \n\r\
    \n:if ((\$content->\"status\") = \"finished\") do={\r\
    \n  \$sendlog msg=\"Message sent successfully\"\r\
    \n} else={\r\
    \n  \$sendlog msg=\"Message sent failed\"\r\
    \n}\r\
    \n\$sendlog msg=\"END: sendmessage\""
add dont-require-permissions=no name=telegram_bot_message_unauth policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#Functions\r\
    \n:global sendmessage\r\
    \n:global sendlog\r\
    \n\r\
    \n\$sendlog msg=\"RUN: message_unauth\"\r\
    \n\r\
    \n:local unauthcmd 0;\r\
    \n:local tmsg\r\
    \n\r\
    \n##### Commands that can be run unauthenticated ####\r\
    \n\r\
    \n  :if (message = \"wifi\") do={:set unauthcmd 1;\r\
    \n    \$sendlog msg=\"Response: wifi\"\r\
    \n    :local registrations [:len [/caps-man registration-table find]]\r\
    \n    :set tmsg \"There are \$registrations wireless registrations\"\r\
    \n  }\r\
    \n  \r\
    \n  :if (message = \"internet\") do={:set unauthcmd 1;\r\
    \n    \$sendlog msg=\"Response: internet\"\r\
    \n    :local internetspeed\r\
    \n    /interface monitor-traffic ether1 once do={:set internetspeed ((\$\"rx-bits-per-second\"/1000) . \"kbps/\" . (\$\"tx-bit\
    s-per-second\"/1000) . \"kbps\")}\r\
    \n    :set tmsg \"Current internet bandwidth: \$internetspeed\"\r\
    \n  }\r\
    \n  \r\
    \n  :if (message = \"blink\") do={:set unauthcmd 1;\r\
    \n    \$sendlog msg=\"Response: blink\"\r\
    \n\t:blink\r\
    \n    :set tmsg \"Somewhere far away you're making a light blink.. aren't you fancy!\"\r\
    \n  }\r\
    \n  \r\
    \n  :if (message = \"beep\") do={:set unauthcmd 1;\r\
    \n    \$sendlog msg=\"Response: beep\"\r\
    \n\t:beep \r\
    \n    :set tmsg \"Keep that up and you're going to drive the network admin mad!\"\r\
    \n  }\r\
    \n  \r\
    \n  :if (message ~\"^ping \") do={:set unauthcmd 1;\r\
    \n    \$sendlog msg=(\"Response: ping specified host\")\r\
    \n    :local pingrx\r\
    \n    :local pingrtt\r\
    \n    :local pinghost [:pick \$message 5 [:len \$message]]\r\
    \n    :do {\r\
    \n      /tool flood-ping count=5 [\$pinghost] do={:set pingrtt (\$\"max-rtt\"); :set pingrx (\$\"received\");}\r\
    \n    } on-error={\r\
    \n      \$sendlog msg=(\"Ping command failed\")\r\
    \n      :set pingrx 0;\r\
    \n    }\r\
    \n    :if (\$pingrx > 0) do={\r\
    \n      :set tmsg (\"PONG: Max \" . \$pingrtt . \"ms from \" . \$pinghost . \" with \" . \$received . \"/5 responses\")\r\
    \n    } else={\r\
    \n      :set tmsg (\"PONG: No response from \$pinghost\")\r\
    \n    }\r\
    \n  }\r\
    \n  \r\
    \n##### Final command if no unathenticated command is matched ####\r\
    \n  \r\
    \n  :if (\$unauthcmd = 0) do={\r\
    \n    \$sendlog msg=\"Response: No valid unauth cmd\"\r\
    \n    :set tmsg \"Invalid command, try: wifi,internet,blink,ping <ip>,beep (all lower case)\"\r\
    \n  }\r\
    \n  \r\
    \n## Send message ##\r\
    \n  :if ([:len \$tmsg] > 0) do={\r\
    \n    \$sendlog msg=(\"Trigger sendmessage function for \$chatid with content: \$tmsg\")\r\
    \n    \$sendmessage chatid=(\$chatid) text=(\$tmsg)\r\
    \n  } else={\r\
    \n    \$sendlog msg=\"No response to send\"\r\
    \n  }\r\
    \n\r\
    \n"
add dont-require-permissions=no name=telegram_bot_log policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\
    "#Comment this line out if you don't want logging to happen\r\
    \n:log info \"\$msg\""
add dont-require-permissions=no name=telegram_bot_processupdates policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#Functions\r\
    \n:global messageauth\r\
    \n:global messageunauth\r\
    \n:global sendmessage\r\
    \n:global sendlog\r\
    \n:global leavechat\r\
    \n:global updateoffset\r\
    \n\r\
    \n\$sendlog msg=\"RUN: processupdates\"\r\
    \n\r\
    \n:global mychat\r\
    \n:global urlStart\r\
    \n:global botID\r\
    \n:global telegramdelay\r\
    \n:local fetchURL\r\
    \n:local content\r\
    \n:local start 0\r\
    \n:local end 0\r\
    \n:local update \"string\"\r\
    \n:local message \"string\"\r\
    \n:local chatid \"string\"\r\
    \n:local chattype \"string\"\r\
    \n\r\
    \n:local nextupdatestart\r\
    \n\r\
    \n:local processcontent (\$updatecontent)\r\
    \n:local contentchunk\r\
    \n\$sendlog msg=(\"Process queue length: \" . [:len \$processcontent])\r\
    \n\r\
    \n:while ([:len \$processcontent] > 0) do={\r\
    \n  \$sendlog msg=(\"Proccess length remaining: \" . [:len \$processcontent])\r\
    \n\r\
    \n#Determine if multiple updates are present (update1)\r\
    \n  :set start [:find \$processcontent \"\\22update_id\\22:\"]\r\
    \n  :set start (\$start + 12)\r\
    \n  :set nextupdatestart ([:find \$processcontent \"\\22update_id\\22:\" \$start] -1)\r\
    \n  :if (\$nextupdatestart < 1) do={:set nextupdatestart [:len \$processcontent]}\r\
    \n  \$sendlog msg=(\"Start location: \$start | Next update start: \$nextupdatestart\")\r\
    \n  :set contentchunk [:pick \$processcontent (\$start - 14) (\$nextupdatestart)]\r\
    \n\r\
    \n#breakup contentchunk into component variables\r\
    \n  :set start [:find \$contentchunk \"\\22update_id\\22:\" 0]\r\
    \n  :set start (\$start + 12)\r\
    \n  :set end [:find \$contentchunk \",\" \$start]\r\
    \n  :set update ([:pick \$contentchunk \$start \$end])\r\
    \n  \r\
    \n  \$sendlog msg=(\"Update ID: \$update\")\r\
    \n  \r\
    \n  :set start [:find \$contentchunk \"\\22text\\22:\" 0]\r\
    \n  :set start (\$start  + 8)\r\
    \n  :set end [:find \$contentchunk \"\\22\" \$start]\r\
    \n  :set message [:pick \$contentchunk \$start \$end]\r\
    \n  \r\
    \n  \$sendlog msg=(\"Received Message: \$message\")\r\
    \n  \r\
    \n  :set start [:find \$contentchunk \"\\22id\\22:\"]\r\
    \n  :set start (\$start + 5)\r\
    \n  :set end [:find \$contentchunk \",\" \$start]\r\
    \n  :set chatid [:pick \$contentchunk \$start \$end]\r\
    \n  \r\
    \n  :set start [:find \$contentchunk \"\\22chat\\22:\"]\r\
    \n  :set start [:find \$contentchunk \"\\22type\\22:\"]\r\
    \n  :set start (\$start + 8)\r\
    \n  :set end [:find \$contentchunk \",\" \$start]\r\
    \n  :set chattype [:pick \$contentchunk (\$start) (\$end -2)]\r\
    \n \r\
    \n  \$sendlog msg=(\"From Chat ID: \$chatid - Type: \$chattype\")\r\
    \n  \r\
    \n#is a group\? Leave and update offset\r\
    \n:if (\$chattype != \"private\") do={\r\
    \n  \$leavechat leaveroom=(\"\$chatid\") leaveupdateid=(\$update)\r\
    \n} else={\r\
    \n#is authed user\?\r\
    \n  :if (\$chatid = \$mychat) do={\r\
    \n    \$sendlog msg=(\"Run message_auth for \$chatid\")\r\
    \n    \$messageauth message=(\$message) chatid=(\$chatid)\r\
    \n  } else={\r\
    \n    \$sendlog msg=(\"Run message_unauth for \$chatid\")\r\
    \n    \$messageunauth message=(\$message) chatid=(\$chatid)\r\
    \n  }\r\
    \n}\r\
    \n#Trim content \r\
    \n  :set \$processcontent [:pick \$processcontent (\$nextupdatestart) [:len \$processcontent]]\r\
    \n\r\
    \n#end of while loop\r\
    \n}\r\
    \n\r\
    \n#send update offset\r\
    \n\$updateoffset updateid=(\$update)\r\
    \n\r\
    \n:if (\$telegramdelay > 10) do={\r\
    \n  :set telegramdelay (10)\r\
    \n  \$sendlog msg=(\"Reset delay to minimum \" . \$telegramdelay . \"s\")\r\
    \n}\r\
    \n\r\
    \n\$sendlog msg=\"END: processupdates\""
add dont-require-permissions=no name=telegram_bot_message_auth policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#Functions\r\
    \n:global sendmessage\r\
    \n:global sendlog \r\
    \n\r\
    \n\$sendlog msg=\"RUN: message_auth\"\r\
    \n\r\
    \n:local authcmd 0;\r\
    \n:local tmsg\r\
    \n\r\
    \n\r\
    \n  :if ([:len [/system script find name=\$message]] > 0) do={\r\
    \n    \$sendlog msg=(\"Script: \$message run by \$chatid\")\r\
    \n    /system script run \$message\r\
    \n  } else={\r\
    \n    :if (\$message = \"List\") do={\r\
    \n      \$sendlog msg=\"listing scripts\"\r\
    \n      :local scriptnames\r\
    \n      :foreach counter in=[/system script find] do={\r\
    \n        :set scriptnames (\$scriptnames . \",\" . [/system script get \$counter name])\r\
    \n      }\r\
    \n      :local authmessage \"Scripts: \$scriptnames\"\r\
    \n      \$sendmessage chatid=\$chatid text=\$authmessage\r\
    \n    } else={\r\
    \n      \$sendlog msg=\"Unknown cmd\"\r\
    \n      \$sendmessage chatid=\$chatid text=\"Unknown cmd\"\r\
    \n    }      \r\
    \n  }"
add dont-require-permissions=no name=telegram_bot_leavechat policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#Functions\r\
    \n:global sendlog \r\
    \n:global updateoffset\r\
    \n\$sendlog msg=\"RUN: leavechat\"\r\
    \n\r\
    \n:global urlStart\r\
    \n:global botID\r\
    \n:local content\r\
    \n:local fetchURL\r\
    \n\r\
    \n:set fetchURL (\"/leaveChat\\\?chat_id=\" . \$leaveroom)\r\
    \n\$sendlog msg=(\"GETURL: \" . \$fetchURL); :set fetchURL (\$urlStart . \$botID . \$fetchURL);\r\
    \n:execute {:set content [/tool fetch url=\$fetchURL as-value output=user]}\r\
    \n\r\
    \n\$updateoffset updateid=(\$leaveupdateid)\r\
    \n"
add dont-require-permissions=no name=telegram_bot_updateoffset policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#Functions\r\
    \n:global sendlog \r\
    \n\$sendlog msg=\"RUN: updateoffset\"\r\
    \n\r\
    \n:global urlStart\r\
    \n:global botID\r\
    \n:global telegramdelay\r\
    \n:local fetchURL\r\
    \n:local content\r\
    \n\r\
    \n:local update (\$updateid + 1)\r\
    \n\r\
    \n#send update offset\r\
    \n:set fetchURL (\"/getUpdates\\\?offset=\" . \$update . \"&limit=1&allowed_updates=message\")\r\
    \n\$sendlog msg=(\"GETURL: \" . \$fetchURL); :set fetchURL (\$urlStart . \$botID . \$fetchURL);\r\
    \n:set content [/tool fetch url=\$fetchURL as-value output=user]\r\
    \n\r\
    \n:if ((\$content->\"status\") = \"finished\") do={\r\
    \n  \$sendlog msg=\"Update offset success\"\r\
    \n} else={\r\
    \n  \$sendlog msg=\"Update offset failed\"\r\
    \n}"

If you have any questions – or come up with some unique ideas for what you can do with something like this, share them here!

Greg Talks 11 – Hollywood Dreams, Post-Covid Handshake, Media

Greg talks to Justin Burdine.

This week we talk about:
IT in Hollywood
New post Covid handshakes.
Working from home tips: have a dedicated space, follow normal routine, dress for work, voip phone, vpn via router, take some laps. If you need noise try music or podcasts, videos are pretty distracting.
The Last Leg on Channel4 in the UK.
Friday night dinner new season on the 27th.
Man I still really like Seinfeld. I can still watch and really enjoy it.
Great British pottery throw down…it’s alright.
Sales engineering is baller.
I got corrugated plastic, like in signs, to make some PB paddle cases for travel – not that I’ll be doing that anytime soon.

Join the patron only slack at http://patreon.com/thebrotherswisp

Here’s the video:(if you don’t see it, hit refresh)

Bob Beck Interview on OpenBSD, libTLS, LibreSSL with Tom Smyth at EuroBSDCon 2018



Bob Beck shares his experience of participating in the OpenBSD project. He patiently discusses aspects of the project he likes and enlightens us about the some of the methodologies OpenBSD use to root out bugs in the OS / general Eco System. Thanks Bob.

Join the patron only slack at http://patreon.com/thebrotherswisp

Here’s the video:(if you don’t see it, hit refresh)

The Brothers WISP 101 – Cheap OTDR, UBNT Data Collection, IPv6 Tracing



This week Greg,Tomas, Nick, and Tommy talk about networking and listen to traffic driving by Tomas’ house.

This week we talk about:
– George A had a cheap SM OTDR he likes…I wonder if he still does?
– Use caution with cambium 3000s and it looks like 4.4.2 firmware is “pretty good” so far.
Several of us have played with the Mikrotik Audience so far: Review sample from ISPSupplies!
– IPv6 tracing – imcpv6 vs. udp
– Unifi LTE – LTE gateway
– Unifi Dream Machine – new router AP for the house
– Ubiquiti UAP-Beacon HD mesh AP
– UBNT stealthily enables data collection
– Why does Nick B maintain so many random domains?
– Tomas is introducing a “Linux-desktop-only” policy at Unimus – should be fun times…

Here’s the video:(if you don’t see it, hit refresh)