HomeLab #2: Immutable GitOps homelab and MikroTik hardening

rafal_opilowski1 pts1 comments

HomeLab #2: Immutable GitOps homelab + MikroTik hardening | Rafał Opiłowski

Buy me a coffee!<br>#Before we begin…

First of all, huge thanks to the Hacker News community for all the feedback Part 1 of this series received.

Before diving into the homelab setup, I want to address one comment from that thread:

View source<br>I hope there is a part 2, where they make sure the internet can't access their management interfaces...

This is a good catch. Let’s close that gap first, before getting on with the actual homelab.

#Split WAN and LAN into interface lists

RouterOS firewall rules read cleaner against named lists than against raw interface names, and the same list works for both the Ethernet WAN port and the PPPoE client on top of it.

/interface list add name=WAN<br>/interface list add name=LAN<br>/interface list member add list=WAN interface=pppoe-out1<br>/interface list member add list=WAN interface=ether1<br>/interface list member add list=LAN interface=bridge

#Drop management traffic at the input chain

The default RouterOS input chain accepts almost anything bound for the router itself. Replace it with an explicit allow-list: established and related traffic, then LAN, then nothing else.

/ip firewall filter<br>add chain=input action=accept connection-state=established,related<br>add chain=input action=drop connection-state=invalid<br>add chain=input action=accept in-interface-list=LAN<br>add chain=input action=accept protocol=icmp<br>add chain=input action=drop in-interface-list=WAN

Order matters here. The drop in-interface-list=WAN rule sits last so it only catches what the earlier accepts didn’t already let through, and it catches everything, not just Winbox or SSH.

#Turn off services you don’t use, restrict the ones you do

/ip service binds Winbox, the web interface, the API, FTP and Telnet to every interface by default. Most homelabs need at most one or two of these, and none of them need to be reachable from the WAN once the firewall rule above exists anyway. Defense in depth means doing both.

/ip service disable telnet,ftp,www,api,api-ssl<br>/ip service set winbox address=192.168.88.0/24<br>/ip service set ssh address=192.168.88.0/24

#Stop announcing yourself

MikroTik’s neighbor discovery protocol (MNDP) is how WinBox found the router by MAC address during initial setup, back in part 1. It has no business running on the WAN side once the router is configured.

/ip neighbor discovery-settings set discover-interface-list=LAN

#Don’t forget IPv6

A private IPv4 WAN address behind CGNAT gets an incidental assist from NAT: unsolicited inbound packets have no session to match, so they never reach the LAN. That’s a side effect of translation, not a firewall rule, and it says nothing about IPv6. If the ISP hands out a routable IPv6 prefix, as most dual-stack IPoE and even some DS-Lite setups do, every LAN device gets a globally reachable address, and RouterOS’s default IPv6 firewall is far more permissive than the IPv4 one. Build the same WAN/LAN split for /ipv6 firewall filter, and don’t assume CGNAT is doing IPv6’s job for you.

/ipv6 firewall filter<br>add chain=input action=accept connection-state=established,related<br>add chain=input action=drop connection-state=invalid<br>add chain=input action=accept in-interface-list=LAN<br>add chain=input action=accept protocol=icmpv6<br>add chain=input action=drop in-interface-list=WAN

With the perimeter closed, the router is ready to carry something more interesting than home traffic.

Now, time for the homelab setup.

#The hardware: a gaming laptop

The cluster runs on a Lenovo IdeaPad Y700-15ISK, a 2016 gaming laptop that would otherwise sit in a drawer.

ComponentSpecCPUIntel Core i7-6700HQ, 4 cores / 8 threads, 2.6-3.5 GHzRAM32 GB DDR4-2133 (2×16 GB, upgraded past Lenovo’s official 16 GB max)Storage2.5” SATA bay + M.2 SATA slotNetworkGigabit Ethernet, 802.11ac Wi-Fi, Bluetooth 4.1Battery60 Wh, 4-cell Li-PolymerWeight2.6 kg<br>#The software: immutable server Linux distro

My OS of choice is uCore - a Fedora-based, container-native Linux image maintained by the Universal Blue project, built for running container workloads directly on bare metal. It layers hardware drivers and container tooling on top of Fedora’s rpm-ostree base, so the same rebase mechanism that ships the OS can also ship network drivers or anything else baked into the image.

“Immutable” here doesn’t mean the filesystem never changes; it means the OS ships as one versioned, read-only tree instead of thousands of independently upgradeable packages. rpm-ostree treats /usr and the default /etc as a single atomic OSTree deployment: an update swaps the whole tree in one transaction, and a failed update rolls back to the previous deployment instead of leaving the system half-patched. /var and /home sit outside that tree and stay writable and persistent across every rebase, which is exactly why /var/lib/rancher on the second disk survives an OS update untouched even though /usr gets replaced wholesale.

uCore has no ISO of its own. It’s an...

interface list input chain action homelab

Related Articles