Home Server from a laptop - homer

I recently acquired a Lenovo L470 laptop for cheap, and I will be using it as a hypervisor to run my home automation VM on it.

Basically the benefit is that it consumes low power, has a built-in UPS, a built-in keyboard and screen.

First, I create a USB drive that will be used to boot arch install media.

Wrote the iso disk image to the drive following the arch wiki. Then I booted in, had to change the password so that I can log in through ssh.

Ran this script on the target machine

Then I booted up the new system, but network was not available.

First I thought that the driver was not loaded, but that wasn't the case. It turned out that the install script was not configuring the right interface. The install script was used originally for virtual machines, and in those cases the name of the interface was enp1s0 but given that this was a physical machine, the assigned device name was different.

this part of the script was not good

[Match]
Name=enp1s0

[Network]
DHCP=yes

So I have corrected it to wired0:

$ cat /etc/systemd/network/20-wired.network 
[Match]
Name=wired0

[Network]
DHCP=yes

And added an udev rule so that if a network device is seen with the particular mac address, always the name wired0 will be assigned:

$ cat /etc/udev/rules.d/10-network.rules 
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="XX:XX:XX:XX:XX:XX", NAME="wired0", ENV{NM_UNMANAGED}="1"

The last part, the ENV{NM_UNMANAGED}="1" is there, so that network manager should not manage it. It is really important in the case when you have a graphical environment.

Also configured a bridge device, as that will be the network to which all my virtual machines will be connected.

# cat /etc/systemd/network/pubbr.netdev 
[NetDev]
Name=pubbr
Kind=bridge
# cat /etc/systemd/network/pubbr.network 
[Match]
Name=pubbr

[Network]
Address=192.168.114.1/24
ConfigureWithoutCarrier=yes

When doing networkctl it was still showing as no-carrier, but this could be ignored. I believe once a device is added to the bridge, it will become routable

$ networkctl 
IDX LINK           TYPE     OPERATIONAL SETUP     
  1 lo             loopback carrier     unmanaged
  2 pubbr          bridge   no-carrier  configured
  3 wired0         ether    routable    configured
  4 wwp0s20f0u9i12 wwan     off         unmanaged
  5 wlp5s0         wlan     off         unmanaged

Then I installed libvirt, an qemu-base. Later it turned out that one needs to install qemu-full, and also when I tried to connect to this machine remotely with a virt-manager, it turned out that I also need openbsd-netcat as well. So this is the final list of packages that need to be installed in order to be able to run virtual machines:

pacman -S libvirt qemu-full openbsd-netcat

Making myself part of libvirt and starting services:

# gpasswd --add matelakat libvirt
# systemctl enable libvirtd.service
# systemctl start libvirtd.service
# systemctl start virtlogd.service

For my virtual machines I also need a dns and a dhcp server, so I install dnsmasq

pacman -S dnsmasq

I had to change the following bits:

bind-interfaces
interface pubbr
dhcp-range=192.168.114.50,192.168.114.150,12h

Then enable/start it:

systemctl start dnsmasq.service
systemctl enable dnsmasq.service

Bash completion is also a nice-to have thing

pacman -S bash-completion

Firewall. I decided I am not going to install shorewall this time, I will learn something that is more mainstream.

sudo pacman -S firewalld
sudo systemctl enable --now firewalld.service

Added wired0 to the public zone, and set it up so that it does masquerading, and also by default it drops incoming connections.

firewall-cmd --zone=public --add-interface=wired0 --permanent
firewall-cmd --zone=public --add-masquerade --permanent
firewall-cmd --zone=public --set-target=DROP --permanent

Then created a new zone, named it pubbr:

firewall-cmd --permanent --new-zone=pubbr
firewall-cmd --zone=pubbr --add-interface=pubbr --permanent
firewall-cmd --zone=pubbr --add-source=192.168.114.0/24 --permanent
firewall-cmd --zone=pubbr --add-service={dhcp,dns} --permanent
firewall-cmd --zone=pubbr --add-masquerade --permanent

And introduced a new policy that will enable the flow in between pubbr and public

firewall-cmd --new-policy NAT_pubbr_to_ext --permanent
firewall-cmd --permanent --policy NAT_pubbr_to_ext --add-ingress-zone pubbr
firewall-cmd --permanent --policy NAT_pubbr_to_ext --add-egress-zone public
firewall-cmd --permanent --policy NAT_pubbr_to_ext --set-target ACCEPT

Also given that this is a laptop, I wanted to disable the actions when the lid was closed, so the computer keeps running:

in /etc/systemd/logind.conf do the following:

HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore

And then

systemctl reload systemd-logind.service

This article was very useful to set up the firewall: - https://eldon.me/arch-linux-based-home-router-part-iii-firewalld-configuration/

Forward port

# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" destination address="192.168.0.94" forward-port port="2222" protocol="tcp" to-port="22" to-addr="192.168.114.120"'
# firewall-cmd --reload

NTP Client

I will use the simple systemd based ntp client

First, let's check the status

$ timedatectl show --all
Timezone=Europe/Budapest
LocalRTC=no
CanNTP=yes
NTP=no
NTPSynchronized=no
TimeUSec=Sat 2024-12-14 06:59:06 CET
RTCTimeUSec=Sat 2024-12-14 06:59:29 CET

Edit /etc/systemd/timesyncd.conf

[Time]
NTP=0.hu.pool.ntp.org 1.hu.pool.ntp.org 2.hu.pool.ntp.org 3.hu.pool.ntp.org
FallbackNTP=0.hu.pool.ntp.org 1.hu.pool.ntp.org 2.hu.pool.ntp.org 3.hu.pool.ntp.org

Then start and enable it:

$ sudo systemctl enable --now systemd-timesyncd

Let's see it running

$ timedatectl show --all
Timezone=Europe/Budapest
LocalRTC=no
CanNTP=yes
NTP=yes
NTPSynchronized=yes
TimeUSec=Sat 2024-12-14 07:04:12 CET
RTCTimeUSec=Sat 2024-12-14 07:04:12 CET