Mikrotik CHRs on LXD
Another reason to love this hypervisor · September 5th, 2022
I really love Mikrotik gear, it's just super flexible, extremely affordable, and frankly rock solid when configured right. Sure it's not a next gen firewall like a Fortinet, but it does a bang up job of routing. Once you've wrapped your head around how to configure them you find all of the bells and whistles you could possibly want on every device you could imagine. But despite the fact that you can grab a quick 100MB capable firewall for about $20, or a pocket sized Map for $40, and hell even a 1GB firewall for $60 sometimes you just don't want to or need to spend the money. Or maybe you've got a single firewall in your homelab and you need something to test configurations/automation on. One of those devices could be a great solution, but we can also just throw up a Cloud Hosted Router in a VM.
I've been running an LXD cluster for almost a year now (just one month shy!) and it's been rock solid for both containers and virtual machines. I absolutely love how simple it is to spin systems up. No need for fancy GUI programs like virtmanager to deploy systems. I suppose raw qemu or virsh isn't so far from LXC commands, but I've found that LXC seems to stick better than either. The point is though, for years I've just taken the CHR iso off of MikroTik website and throw it into a KVM with virtmanager, and since upgrading to LXD I hadn't stopped to figure out how to get those running.
Well here I am, at the tail end of a long weekend where I've taken a little bit of time to indulge my curiosity and teach myself about how to configure vlans on Mikrotik's, and all I've got to practice on is my Hex firewall. While I don't mind potentially taking the network offline to dabble, I don't think my wife or son would appreciate it. So CHRs I shall have!
Setting up the VM
First things first, make a directory on each of your LXD nodes with the ISO for the CHR. I use /ISO on mine, but it's not important so long as you have a simple common path to reference on each node.
mkdir /ISO curl https://download.mikrotik.com/routeros/7.5/mikrotik-7.5.iso -o /ISO/mikrotik-7.5.iso
The above will get us started, but I really want to keep these in sync, so I threw together a quick little Nim program that will check the Mikrotik release RSS feed and pull down the latest npk, mib, and iso files for me. This way I can just setup a cronjob to make sure I always have the latest and greatest Mikrotik things in my lab.It's a bit quick and dirty, but I couldn't resist sharing more nim on the blog.
import std/[httpclient, strutils], FeedNim #nim c -d:ssl -d:release ftik.nim #Pull Changelog RSS feed, extract first item's version proc fetchVer(rss: string): string = let feed = getRSS(rss) verstr = feed.items.title.split(' ') #Should pull as RouterOS 7.5 [Stable], split on whitespace to return 7.5 return verstr.strip() #Download an item to the cwd proc fetch(item: string, url: string) = var client = newHttpClient() echo "Fetching " & $item try: var file = open($item, fmwrite) defer: file.close() file.write(client.getContent(url & $item)) echo($item & " fetched.") except IOError as err: echo("Failed to download " & $item & ": " & err.msg) #Fetch CHR iso, npk, and mib proc main(): void = let version = fetchVer("https://mikrotik.com/current.rss") url = "https://download.mikrotik.com/routeros/" & $version & "/" #https://download.mikrotik.com/routeros/7.5/mikrotik.mib fetch("mikrotik.mib", $url) #https://download.mikrotik.com/routeros/7.5/mikrotik-7.5.iso fetch("mikrotik-" & $version & ".iso", $url) #https://download.mikrotik.com/routeros/7.5/all_packages-x86-7.5.zip fetch("all_packages-x86-" & $version & ".zip", $url) main()
Right distractions aside, back to the lxc stuff. Now that we've got our ISO on the LXD nodes we can actually launch the VM. And maybe unsurprisingly if you've ever used LXC before, it is dead simple. This is almost exactly what you'd do to get a graphical Ubuntu install going, and slightly less work than getting a Windows VM running.
> Start an empty VM lxc init CHR --empty --VM -c security.secureboot=false > Add the CHR iso to the new VM lxc config device add CHR iso disk source=/ISO/mikrotik-7.5.iso boot.priority=10 > Start that sucker with a graphical console lxc start CHR --console=vga
Boom, done. Three steps and you've got yourself a Mikrotik CHR on your LXD node! Just run through the system installation the ISO provides and you'll be off. That setup will give your 1GB of ram, a 10GB drive, and a single CPU. Pretty standard VM settings in LXC. You can use lxc config to add more or less ram, disk, cpu depending on what you want/need. Frankly for what I'm using this for, 10GB of disk and 1GB ram is already overkill. I probably should have set some limits. Not important, what is important is that this VM isn't as integrated as your typical Ubuntu system is. You won't be able to do an lxc exec CHR sh and pop open a remote shell on your CHR. This is because the virtual Tik isn't running the LXD agent, and there's no way for the hypervisor to broker that connection for you. You'll need to use lxc console, webfig, winbox, or good old SSH. I guess technically you can also use telnet, but it's 2022, lets not use telnet unless we absolutely must..
After the VM comes up you'll note that there's a single interface by default (ether1) and that CHRs come with no configuration. They're wonderful blank slates of "figure it out for yourself". You probably at minimum want your CHR to have networking though right? If you're note sure what to do with a CHR, this will get you a DHCP client on ether1 so you can at least ping the thing and start to poke and prod.
> Connect to the CHR lxc console CHR > Once inside the Tik set a dhcp-client on ether1 /ip/dhcp-client add interface=ether1 > Verify you got an IP address /ip/addr pr > Exit the console /quit
Super duper simple stuff right? But a one nic router doesn't make too much sense does it? I tried for about an hour to get a bridge setup and attached to the CHR VM, but didn't have much success. When attaching the interface with a bridge parent LXD appears to send an ip tun tap command to the container/VM to configure the interface. This obviously won't work on a CHR since it obfuscates all of the Linux internals. It would be ideal to have a virtualized switch inside of an LXD environment. I've seen other blogs that detail setting up complex IPv6 networks with openwrt at the core of it, all on top of LXC. I'm sure it's possible, I just need to do some more digging to find out how. I'll have to make another post when I figure it out, the OVN network type likely holds the key, or maybe I just need to take a look at openwrt next!