Der Beitrag von Michael Kofler KVM-Host mit Ubuntu einrichten hat mich mal wieder dazu motiviert meine Web-Dinge neu zu machen. Nach meinem letzten Root-Server bei Hetzner hatte ich zuletzt einen vServer bei Contabo und darauf alles rein über Docker-Container gemacht. Aber irgendwie wurde ich damit nie so richtig warm, sodass ich nun es wieder mal mit einem Dedicated-Server von Hetzner versuche.

Hierbei beginne ich mit der Ubuntu 20.04 Installation von Hetzner. Auf dem verbleibenden Platz der beiden SSD's erstelle ich einen mirrored zpool, der die Basis für die VM Disks ist. Hierfür brauchen wir erstmal

root@kvm02 ~ # apt install zfsutils-linux

um den zpool erstellen zu können.

root@kvm02 ~ # zpool create -o ashift=9 tank mirror /dev/nvme0n1p3 /dev/nvme1n1p3

Im Anschluss werden die ZFS-Filesysteme angelegt.

root@kvm02 ~ # zfs create -o mountpoint=/srv/vms tank/vms

Für Qemu-KVM werden auch ein paar Pakete benötigt.

root@kvm02 ~ # apt install qemu-kvm libvirt-daemon-system \
			     libvirt-clients qemu-utils bridge-utils \
			     dnsmasq virt-top virtinst osinfo-db-tools \
			     libosinfo-bin isc-dhcp-server libguestfs-tools

Als Netzwerk-Setup wähle ich am Liebsten ein geroutetes Setup. Vorteil im Gegensatz zur gebridgeten Variante ist, dass man keine Verrenkungen mit den MAC-Adressen machen muss, da Hetzner hier ein bisschen speziell ist. Außerdem kann man das komplette Firewalling auf dem KVM-Host selbst übernehmen und muss nicht noch auf den VMs damit anfangen. Und im Gegensatz zu einem reinen Nat-Setup hat man natürlich nur Vorteile. ;-)
Das geroutete Setup ist mit Netplan auch recht easy umzusetzen.

root@kvm02 ~ # cat /etc/netplan/01-netcfg.yaml

# Hetzner Online GmbH installimage
network:
  version: 2
  renderer: networkd
  ethernets:
    enp35s0:
      addresses:
        - xxx.yyy.zzz.123/32
        - IPV6
      routes:
        - on-link: true
          to: 0.0.0.0/0
          via: aaa.bbb.ccc.ddd
      gateway6: fe80::1
      nameservers:
        addresses:
          - 213.133.100.100
          - 213.133.98.98
          - 213.133.99.99
          - 2a01:4f8:0:1::add:1010
          - 2a01:4f8:0:1::add:9898
          - 2a01:4f8:0:1::add:9999
  bridges:
    vbr120-nic:
      interfaces: []
    vbr110:
      addresses:
        - 172.27.110.1/24
      interfaces: [vbr110-nic]
      routes:
        - to: xxx.xxx.xxx.110/32
          scope: link
    vbr111-nic:
      interfaces: []
    vbr111:
      addresses:
        - 172.27.111.1/24
      interfaces: [vbr111-nic]
      routes:
        - to: xxx.xxx.xxx.111/32
          scope: link
    vbr112-nic:
      interfaces: []
    vbr112:
      addresses:
        - 172.27.112.1/24
      interfaces: [vbr112-nic]
      routes:
        - to: xxx.xxx.xxx.112/32
          scope: link

Im Grunde genommen bleibt die IP-Adresse des Hosts auf dem normalen Interface. Dann lege ich 3 leere Bridges, bzw Bridges mit nem Dummy-Interface an. Auf diese Bridges werden die zusätzlichen IPs geroutet (jeweils mit „scope: link“). Die Bridges bekommen jeweils eine RFC1918-IP als /24. Vermutlich sollte es auch gehen alle zusätzlichen IPs auf die gleiche Bridge zu routen, allerdings trenne ich hier gerne.

Die VM bekommt dann die Bridge als Netzwerk-IF im libvirt-XML konfiguriert:

<interface type='bridge'>
      <mac address='aa:bb:cc:dd:ee:ff'/>
      <source bridge='vbr110'/>
      <target dev='vnet0'/>
      <model type='virtio'/>
      <alias name='net0'/>
      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface> 

Innerhalb der VM wird das Interface dann genau, wie beim Host, als Point-to-point konfiguriert.

# This is the network config written by 'subiquity'
network:
  ethernets:
    enp1s0:
      addresses:
      - xxx.xxx.xxx.110/32
      routes:
        - on-link: true
          to: 0.0.0.0/0
          via: 172.27.110.1
      nameservers:
        addresses:
          - 8.8.8.8
          - 1.1.1.1
  version: 2

Um die Installation der VM einfacher bewerkstelligen zu können, lasse ich den isc-dhcp-server auf allen Bridges laufen, der eine IP aus dem privaten /24 vergibt. Dies ist auch für VMs, die keine offizielle IP-Adresse bekommen, weil sie nicht von außen erreichbar sein müssen, recht praktisch.

root@kvm02 ~ # cat /etc/dhcp/dhcpd.conf

option domain-name-servers 8.8.8.8, 1.1.1.1;

default-lease-time 3600;
max-lease-time 7200;
authoritative;

subnet 172.27.110.0 netmask 255.255.255.0 {
  range 172.27.110.101 172.27.110.200;
  option routers 172.27.110.1;
  interface vbr110;
}
subnet 172.27.111.0 netmask 255.255.255.0 {
  range 172.27.111.101 172.27.111.200;
  option routers 172.27.111.1;
  interface vbr111;
}
subnet 172.27.112.0 netmask 255.255.255.0 {
  range 172.27.112.101 172.27.112.200;
  option routers 172.27.112.1;
  interface vbr112;
}

Da die VMs trotzdem ins Internet kommen sollen, braucht man noch ein Source-NAT auf dem Host.

root@kvm02 ~ # iptables -t nat -A POSTROUTING -o enp35s0 -s 172.27.110.0/24 -j MASQUERADE
root@kvm02 ~ # iptables -t nat -A POSTROUTING -o enp35s0 -s 172.27.111.0/24 -j MASQUERADE
root@kvm02 ~ # iptables -t nat -A POSTROUTING -o enp35s0 -s 172.27.112.0/24 -j MASQUERADE

Somit ist das Setup soweit fertig, sodass man Anfangen kann erste VMs auf dem Host zu deployen. Damit wird es dann in einem der nächsten Beiträge hier weiter gehen.