The Millhouse Group Blog
Software Development in the 21st Century
Sunday, 30 March 2025
Upgrading to the Mikrotik RouterBoard RB2011, Part 2 - DHCP
This is Part 2 of my RB2011 series - [here's Part 1](https://blog.themillhousegroup.com/2025/01/upgrading-to-mikrotik-routerboard.html). You can find the [whole series here](https://blog.themillhousegroup.com/search/label/routerboard).
## DNSMasq Replacement
First, let's replace the `dnsmasq` instance on the Raspberry Pi (which does DHCP, DNS and ad-blocking via Pi-Hole-style `0.0.0.0` resolution). As mentioned before, this will free up the Pi to just be a server again, rather than a critical bit of network infrastructure, but also:
- allow the DHCP pool to be modified from a web UI (easier if using a phone in a pinch)
- make the current DHCP client list visible in a browser
- allow DHCP modifications without having to restart DNSmasq
### DHCP on RouterOS v7
Some other nice properties of the [Mikrotik DHCP server](https://help.mikrotik.com/docs/spaces/ROS/pages/24805500/DHCP#DHCP-DHCPServer) include:
#### Multiple pools with different behaviour
- I like the idea of having "trusted" devices (with a known MAC and fixed IP address) getting better bandwidth, and this is entirely possible with the `rate-limit` property of (leases](https://help.mikrotik.com/docs/spaces/ROS/pages/24805500/DHCP#DHCP-Leases)
- It would be nice to also represent this status with some bit-flagging in the IP address itself, as per my [cunning scheme](https://blog.themillhousegroup.com/2011/04/ultimate-ubuntu-build-server-guide-part_25.html)
#### Event scripting
- you can run a [script upon lease assignment or de-assignment](https://help.mikrotik.com/docs/spaces/ROS/pages/24805500/DHCP#DHCP-DHCPServerProperties)
- could be handy to trigger presence-like automated behaviour
To minimise interruption to network users, we'll do this in a staged manner, gradually moving services over to the RB2011.
### Move to a static IP for the RouterBoard
This is critically important, and if you don't do it right, you're highly-likely to end up with the dreaded unreachable router. Ask me how I know...
To do this in one fell swoop, switch to the Quick Set tab in the top of the web interface. Make the configuration mode __Router__ but leave the Address Acquisition settings alone - we're not going to be using `ETH1`, for our intents and purposes, it's cursed. Then just fill out the Local Network IP settings you'd like the RB2011 to have, but turn off DHCP Server at this stage (we're not ready for that yet) and NAT - our existing gateway device does NAT for us.
Hit *Apply Configuration*, say a small prayer to `${DEITY}` and reload the page (at the new IP address if you changed it). Give it a minute or two before you punt it across the room...
### DHCP Server with single address pool
Next up, we'll set up a DHCP server to do that aspect of what we're currently doing with DNSMasq - a single address pool for unspecified random (a.k.a "guest") devices for which we don't have a mapping, plus a bunch of fixed IPs for known devices. For a smooth switchover, first we'll bring the lease times of everyone getting leases from the Pi *down* to a short interval, to get them roughly "in sync".
Backup the old `dnsmasq.conf` on the Pi:
```
% cd /etc
% sudo cp dnsmasq.conf dnsmasq.conf.pre-rb2011
```
Bring the lease time DOWN for everyone, by going into `dnsmasq.conf` and changing the `dhcp-range` (for random/guest devices) and each `dhcp-host` entry (for known devices) like this:
```
dhcp-range=10.240.0.64,10.240.0.96,1h
...
dhcp-host=00:11:22:33:86:7e,somebox,10.240.0.100,2h
```
to
```
dhcp-range=10.240.0.64,10.240.0.96,5m
...
dhcp-host=00:11:22:33:86:7e,somebox,10.240.0.100,5m
```
Restart dnsmasq for the changes to take effect:
```
pi % sudo service dnsmasq restart
```
Now over on the RB2011, we can run the built-in DHCP Server "wizard" from the SSH prompt:
```
[admin@MikroTik] > /ip/dhcp-server/setup
Select interface to run DHCP server on
dhcp server interface: bridge
Select network for DHCP addresses
dhcp address space: 10.0.0.0/8
Select gateway for given network
gateway for dhcp network: 10.240.0.1
Select pool of ip addresses given out by DHCP server
addresses to give out: 10.240.0.32-10.240.0.63
Select DNS servers
dns servers: 10.240.0.200
Select lease time
lease time: 1800
[admin@MikroTik] >
```
But then immediately disable it:
```
[admin@MikroTik] > /ip/dhcp-server/print
Columns: NAME, INTERFACE, ADDRESS-POOL, LEASE-TIME
# NAME INTERFACE ADDRESS-POOL LEASE-TIME
0 dhcp1 bridge dhcp_pool1 30m
[admin@MikroTik] > /ip/dhcp-server/disable numbers: 0
```
We can now fine-tune the config at our leisure, and enable it once we're entirely ready.
__Set the `domain` so clients have a FQDN:__
```
[admin@MikroTik] > /ip/dhcp-server/network/set numbers=0 domain=home.themillhousegroup.com
```
__Ensure each client gets an ARP table entry:__
```
[admin@MikroTik] > /ip/dhcp-server/set numbers=0 add-arp=yes
```
### Known MAC addresses
I have a reasonably-long list of device MAC addresses (about 30) that I want to have "stable" IP addresses; this is a "static lease" in Mikrotik-speak.
A typical dnsmasq host line looks like:
```
dhcp-host=00:11:22:33:86:7e,somebox,10.240.0.100,15m
```
Each line becomes (for example):
```
[admin@MikroTik] > /ip/dhcp-server/lease/add mac-address=00:11:22:33:86:7e comment=somebox address=10.240.0.100 lease-time=900
```
But note the `hostname` from DNSMasq has just become a comment. To get the nice behaviour that DNSMasq gives us where a DNS entry is created, we need to also add a DNS entry:
```
[admin@MikroTik] > /ip/dns/static/add name=somebox address=10.240.0.100 ttl=900
```
30 entries was too much like manual labour for me, so here's a [little shell script in a Gist](https://gist.github.com/themillhousegroup/41fba50b448ba8f10166decbe2fcc890). Feed it your `dnsmasq.conf` and it'll spit out all the configuration lines you'll need to make your Mikrotik work like DNSMasq did; i.e. a static DHCP server lease and a static DNS entry. Paste the output into your RouterOS SSH session, and confirm that you've got a nice list in the UI at _IP_ -> _DNS_ -> _Static_.
Once you're all set, we're going to take down dnsmasq's DHCP and bring up the RouterOS service as close-together as possible. To do this, first you'll need to tell dnsmasq to NOT listen on the usual interface. On my Pi running Raspbian Buster, this is unfortunately not something predictable like `eth1`, but rather something you'll need to copy-paste from the output of `ifconfig`; e.g.:
```
pi $ ifconfig
enxb827ebfdad60: flags=4163 mtu 1500
inet 10.240.0.200 netmask 255.255.255.0 broadcast 10.240.0.255
```
Edit `/etc/dnsmasq.conf`, adding that ethernet identifier to the `no-dhcp-interface` line (if there is one)
```
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP and TFTP on it.
no-dhcp-interface=lo,enxb827ebfdad60
```
And now it's swap-over time:
```
pi % sudo service dnsmasq restart
```
```
[admin@MikroTik] > /ip/dhcp-server/enable numbers: 0
```
Then watch the Pi's `/var/lib/misc/dnsmasq.leases` get shorter and the Mikrotik's _IP_ -> _DHCP Server_ -> _Leases_ start to fill up!
Don't forget to backup your settings!
The new DHCP responses still point to the Pi for DNS resolution; we'll configure that next time...
Labels:
dhcp,
dnsmasq,
gist,
github,
homeautomation,
markdown-enabled,
mikrotik,
routerboard
Saturday, 22 February 2025
Introducing ... Blitzcore!
My family has become quite addicted to an excellent little card game called [Dutch Blitz](https://en.wikipedia.org/wiki/Dutch_Blitz). It's easy to learn, compact, reasonably quick to play (although the exact length of an entire game can vary *wildly*...) and it's lots of fun.
The only downside is the scoring system, which involves pen-and-paper and certain amount of maths, making the scorekeeper role unappealing to younger players.
To this end, I'm pleased to release [Blitzcore](https://blitzcore.themillhousegroup.com) ("Blitz-Score") to the world, allowing your phone to do the maths for you and present a clear picture of the score [1](#foot-1).
Paintakingly designed to fit onto exactly one phone-sized browser page like [all good mobile-first webapps](https://blog.themillhousegroup.com/2023/08/searching-for-next-spa.html), it uses bright colours that match the game, big easy-to-hit buttons, and just-in-time calculation to ensure the score is always accurate, no matter what you just edited.
Your player name and colour preferences are stored in LocalStorage to make it even quicker to get going next time.
This is my first "big" (not really) webapp in [Svelte 5](https://svelte.dev/) and [SvelteKit](https://svelte.dev/docs/kit/introduction) and it's been a pretty delightful experience. I `push` to my Github private repo and Netlify (free tier) picks it up and publishes to the world in about 45 seconds. The page load time is absolutely stupendous - Svelte/SvelteKit/Vite is absolutely killing it here.
The only trouble I've ever had has been occasionally Svelte *not* reacting to a change when expected, which so-far has been 100% due to my failure to wrap derived values in [the `$derived()` rune](https://svelte.dev/docs/svelte/$derived). It can be quite a pain to track down these things, which seemed to mostly be around manipulating array entries (using `slice` and friends), but I got there in the end. Enjoy!
---
1 To be clear, this is not a version of the game itself - the creators have actually [attempted to do this for mobile devices](https://dutchblitz.com/), which I have never tried ... [⤴︎](#foot-1-src)
Labels:
dev,
dutch-blitz,
markdown-enabled,
netlify,
node.js,
svelte,
typescript
Wednesday, 22 January 2025
Upgrading to the Mikrotik RouterBoard RB2011, Part 1
For my home network, I needed a firewall that offers an API that I can call from elsewhere (on the trusted side of the network). Think "default block ALL", then an API call comes in to temporarily open one port to one IP address for an hour, before reverting back to "block all".
There are precious few network devices that actually offer this capability; at first this seemed surprising but then I guess there's less overlap between _"I configure secure small networks"_ and _"I write backend code"_ than I thought.
Fortunately, Mikrotik's [RB2011UiAS-RM](https://mikrotik.com/product/RB2011UiAS-RM) (RB2011 for short) is an absolute network Swiss Army Knife, capable of a heap of stuff that I've had a long-suffering Raspberry Pi 3B doing - things like DNSMasq (and domain blocking à la Pi-Hole) and WireGuard VPN termination. I'd like to take those tasks off the trusty Pi, leaving it to be a true application server rather than a bit of network infrastructure. Of course there are many devices that can perform these tasks, but the Mikrotik stands above the rest with its [control-plane API](https://help.mikrotik.com/docs/spaces/ROS/pages/47579162/REST+API), which lets me do the dynamic-firewall thing I need, rackmountability (I 3d-printed the ears) and the fact that it is **fanless** and thus completely silent in operation.
_**Aside**: Here's a little tip if you find something a bit niche, but a bit overpriced, on eBay. **Stick a watch on it**. Eventually, the seller will get notified by eBay and prompted to offer *you*, the watcher, a 10% discount. This has been my strategy for low-urgency nerd items for a while and it works a treat._
Here's how I've got it going.
# Finding it
Firstly (and this may just be some leftover configuration on my secondhand device that a factory reset didn't clear out for some reason), I couldn't get the device to respond in *any* way over the network until the following conditions were *all* true:
- Port `ETH1` plugged in to my existing network
- IP address allocated to `ETH1` via DHCP
- Port `ETH2` plugged into my laptop (Wifi OFF)
- IP address allocated to laptop via DHCP over that link
This is counter to every bit of documentation (Mikrotik-official or otherwise) I've found online that says the router will factory-reset to `192.168.88.1` and will run a DHCP server to hand out `192.168.88.0/24` IPs to connected clients.
Once I'd discovered (via looking at DNSMasq logs on the Pi) that the router was coming online in this way, it was actually a pleasure to use, as it's always annoying having to flip back-and-forth between multiple networks while setting these things up. Effectively the router's web UI is accessible at whatever IP you want to give its `ETH1` MAC via DHCP, and the rest of the `ETH` ports just come up as a "bridge", making the device feel like a simple 10/100/1000 switch, that happens to have a nice UI.
On the subject of UI, Mikrotik does offer a native control application [(WinBox)](https://mikrotik.com/download) but it's not really needed unless you're having major issues finding your device on the network (but see above for some hints!) as it can do some "neighbour discovery" stuff. For me, my 2012 MacBook Pro is running too ancient a MacOS to even consider it. The Web UI ("`webfig`") plus SSH are easily enough for me. After decades of absolutely awful consumer-grade router web UIs, `webfig` is snappy, modern enough, well-considered and bug-free.
# Upgrade to v7 and Initial setup
The RB2011 has 128Mb of RAM, allowing it to be [upgraded to Version 7 of RouterOS](https://help.mikrotik.com/docs/spaces/ROS/pages/115736772/Upgrading+to+v7) (important, because that's the version in which the REST API becomes available). Going from v6.49.17 to v7 is as easy as going to _System -> Packages -> Check for Updates_ and switching the _Channel_ to `upgrade`. `v7.12.1` shows up (in January 2025 at least) and is just a _Download and Install_ away. From there, we're on the v7 train and can go as bleeding-edge as desired. For me the `stable` channel seemed like a safe bet, so I further updated to `7.17` (January 2025).
### Backup
Once we've done that, we should also start [backing up](https://help.mikrotik.com/docs/spaces/ROS/pages/40992852/Backup) the router config after every successful stage of setup. Log in as `admin` via SSH and just do `[admin@MikroTik] > /system/backup/save`. You can see the file from the *Files* top-level menu in the UI or in the console with `/file print`:
```
[admin@MikroTik] /file> print
# NAME TYPE SIZE LAST-MODIFIED
0 skins directory 1970-01-01 11:00:05
1 pub directory 2019-10-23 12:13:14
2 auto-before-reset.backup backup 44.1KiB 1970-01-01 11:00:06
3 MikroTik-20250122-2026.backup backup 28.2KiB 2025-01-22 20:26:13
[admin@MikroTik] /file>
```
### Turn off unwanted services
RouterOS runs a [number of IP services](https://help.mikrotik.com/docs/spaces/ROS/pages/328229/IP+Services) that we neither want nor need; turning them off can only be of benefit; `telnet` and `ftp` are ancient and insecure; we've already established that `winbox` is surplus to requirements. Eventually, it would be good to only allow the `-ssl` versions of the `www` and `api` services, but we'll leave them for now. `ssh` is always wanted.
So to begin, let's see what we have:
`/ip/service/print`:
```
Flags: X - DISABLED, I - INVALID
Columns: NAME, PORT, CERTIFICATE, VRF, MAX-SESSIONS
# NAME PORT CERTIFICATE VRF MAX-SESSIONS
0 telnet 23 main 20
1 ftp 21 main 20
2 www 80 main 20
3 ssh 22 main 20
4 X www-ssl 443 none main 20
5 api 8728 main 20
6 winbox 8291 main 20
7 api-ssl 8729 none main 20
```
Now we can turn them off:
`/ip/service/disable telnet`
`/ip/service/disable ftp`
`/ip/service/disable winbox`
We can also reduce the maximum number of concurrent sessions to something more realistic. It's probably unimportant, but it shows off a cool feature of the command-line:
```
/ip/service/set max-sessions=3
numbers: 2,3,4,5,7
```
...we can apply the same setting change to all the different services in one go. That's really cool. Here's what we have now:
```
Flags: X - DISABLED, I - INVALID
Columns: NAME, PORT, CERTIFICATE, VRF, MAX-SESSIONS
# NAME PORT CERTIFICATE VRF MAX-SESSIONS
0 X telnet 23 main 20
1 X ftp 21 main 20
2 www 80 main 3
3 ssh 22 main 3
4 X www-ssl 443 none main 3
5 api 8728 main 3
6 X winbox 8291 main 20
7 api-ssl 8729 none main 3
```
**Reminder**: Time to backup again!
Saturday, 28 December 2024
End of year 2024 wrapup
Just a quick one; not a great deal of personal development or great strides forward this year, but we can always live in hope for next year.
This marks *25 years* of being a professional software developer. I've gone from writing C in `vim` on a [100MHz 128Mb HP-UX workstation](https://blog.themillhousegroup.com/2021/12/computers-i-have-known-part-4.html) to letting __GitHub Copilot__ help me write TypeScript in VSCode on an 11-core M3 16Gb MacBook Pro at umpteen GHz.
I've Created, Read, Updated and Deleted (see what I did there?) a *lot* of code, and I still love it. I have rejected any offers to move into the "people management" side of things, and hope to continue to be "on the tools" for my entire career. How much longer will that last? Dunno - but certainly not another 25 years!
Saturday, 30 November 2024
Mac - when your disk is really, really full
My wife's 2012 (OSX Catalina, 10.15) MacBook Air has been struggling recently with a hard disk that seems to have no hesitation in filling itself to the absolute brim (like `78kb space remaining`). The problem is that APFS, being a journaling file system, wants to write a record of your attempt to call `rm ~/Downloads/stupid-big-file.mp4`, but doesn't have the space to do so - preventing the `rm` from running, and so escaping this situation is far harder than it should be.
This has happened a number of times now, and after various attempts to use ["Target Disk Mode"](https://support.apple.com/en-au/guide/mac-help/mchlp1443/mac) via a daisy-chain of Thunderbolt-to-FireWire adapters, Apple Disk Utility in Recovery Mode, and a [GParted LiveCD](https://gparted.org/livecd.php) to mount the drive, this typically-unassuming but excellent [Stack Exchange answer](https://apple.stackexchange.com/a/371323/117415) has ended up being my saviour. It always takes me ages to re-stumble upon it because my Googling is typically for phrases like "resize APFS partition" which leads you to [articles like this one](https://www.macobserver.com/tips/resize-your-apfs-container/) which is totally overkill for the situation.
The **TL;DR** is - you can safely use the `diskutil` utility to `remove` the volume that is named `VM`, and APFS will then automatically resize the overfull "Macintosh HD" volume to get that space back.
Just be aware that the Mac will always prefer to have *some* VM, so the long-term solution is probably to keep several tens of Gb free so that the two volumes can coexist in harmony.
While I'm linking to useful Apple-disaster-recovery-related Stack answers, while you're in Recovery Mode and trying to figure out what files to nuke, Apple helpfully removes the link to `du`. [This answer gives the full path](https://superuser.com/a/1279144/386135) (which on the aforementioned 2012 Mac is actually `/Volumes/Macintosh\ HD/usr/bin/du` as per one of the comments).
Sunday, 20 October 2024
Google Home Minis (1st Gen) bricked
As of this month, I have two out of my fleet of three *Google Home Mini (Gen 1)*s out of commission. Neither will boot; one slows a lonely green LED when the reset button is hit, the other nothing at all (and pulls massive power from USB while it boots).
There is a [truly MASSIVE thread](https://www.googlenestcommunity.com/t5/Speakers-and-Displays/HOME-mini-UNRESPONSIVE-thread-quot-Google-home-mini-4-dots-stuck-problem/m-p/558790/page/209876543210) about this over on the Google Nest forum. The *TL;DR* is this: if you contact Google about this issue, they will stall and give you the runaround. Eventually if you persist, they will get you to confirm the purchase date of the device(s), at which point they will either send you a new device or close the conversation with __"[it is|they are] out of warranty"__.
The speculation on the forums is that Google are remote-bricking these devices as they reach their warranty period. This truly saddens me but given the well-documented Google anti-enthusiasm for long-term product support, it makes perfect sense. These devices acheived their aim of massive market penetration and have become almost-indispensable around my house, with the excellent Spotify and Chromecast integrations being used multiple times per day. The very low purchase price (indeed *$free* in one case) made it a no-brainer to dot (hah!) them around the place.
The only working *Home Mini* left is a little newer. How much longer will it survive? I can't imagine there is any way to block it from receiving that remote kill-code from the mothership without completely nerfing its internet access, so it's just a ticking time bomb now.
There's also a highly visible lack of supply for the obvious replacement, the *Nest Mini (Gen 2)*. I suspect Google is letting these evaporate without replacement, so they can introduce a *Gen 3* model, considerably more expensive, for all the people who they've locked into the Google Nest/Home ecosystem and who now, funnily enough, need to replace a fleet of devices. Damn.
Draw your own conclusions about how Google's tracking with _"Don't Be Evil"_ at this point, as tonnes of still-useful electronics make their way into landfill.
Sunday, 8 September 2024
UnSAFe at any speed
I first encountered the [Scaled Agile Framework for enterprise](https://scaledagileframework.com/), (from here-on referred to as SAFe) in 2014, when the large enterprise I was working for decided it was what was needed in order to ship solutions faster. (*Spoiler:* it wasn't, it didn't help in the slightest, it made us considerably slower, at considerable expense). I'll let you peruse their website at your leisure, but before you go, remember the tenets of the [Agile Manifesto](https://agilemanifesto.org/) (emphasis mine):
- Individuals and interactions over *processes* and tools
- Working software over comprehensive *documentation*
- Customer collaboration over contract negotiation
- Responding to change over *following a plan*
Now look at this, the ***SAFe 6.0*** I-don't-even-know-what-to-call-it diagram:
All I see is prescriptive *processes*, *documentation* and *plans*. You don't "do" agile development by signing up for expensive certifications that basically allow you to continue to ship 4-times-a-year but call yourself an agile workplace when you're recruiting. You also won't fool *anyone* which half a clue during the recruitment process.
Just one more example. This is grabbed from a PDF from [Accenture](https://sai2.wpengine.com/wp-content/uploads/delightful-downloads/2020/01/Key_Accenture_Learning_on_Scaled_and_Distributed_Agile_August-18-for-SAFe.pdf), who are [fully on-board with SAFe](https://scaledagile.com/case_study/accenture/), I suspect because:
- Acronym
- Includes the word Agile
- You can get certified for it (at great expense)
- It seems to make sense if you don't look at it too closely
- It actually makes you go slower than before (more billable hour$)
Ready? Here we go. This is how easy it is to "integrate" agile and waterfall:
So simple! It's super-cool that they like, don't have any interdependencies whatsoever! So clean!
Labels:
accenture,
agile,
facepalm,
markdown-enabled,
safe,
scaledagile,
snakeoil
Subscribe to:
Posts (Atom)