Hetzner - DokuWiki

KVM mit Nutzung aller IPs aus Subnetz

Spezielles KVM-Setup auf Debian Lenny.

KVM mit Nutzung aller IPs aus Subnetz.png

Inhaltsverzeichnis

Einleitung

Der eine oder andere wird nach dem Lesen der Anleitung entnervt sagen: "Das ist mir viel zu kompliziert".

WICHTIG: Um Frustration und Enttäuschung zu vermeiden bitte ich Euch: Lest die Anleitung einmal komplett durch, bevor ihr Euren Server auf den Kopf stellt - dann wisst Ihr wenigstens, was Euch erwartet.

Weitere Anleitungen zum Thema KVM findet ihr in der Kategorie Virtualisierung.

Wie bei allen Anleitungen, die man im Internet findet, gilt auch hier: Verwendung der Anleitung auf eigene Gefahr.

Besonderheiten

Diese Anleitung richtet sich an jene Systemadministratoren, die KVM auf Ihrem Hetzner-Server unter den folgenden Randbedingungen betreiben wollen:

  • es findet kein NAT statt
    • jede virtuelle Maschine hat mindestens eine öffentliche IP-Adresse
  • die physikalische Maschine routet den Traffic
    • Routing des Traffics zwischen einzelnen VMs
    • Routing des Traffics zwischen VMs und Internet
    • folglich wird auch kein Proxy-ARP benötigt
    • da wir routen und nicht switchen, kann iptables auf dem Hostsystem zum Einsatz kommen
      • zentrale Firewall-Regeln auf dem Host anstatt in jedem einzelnen Gast
      • Firewall-Regeln für Traffic zwischen den VMs ist möglich
  • es gehen keine IP-Adressen "verloren"
    • die Netzwerk- und Broadcast-Adresse des zusätzlichen IP-Netzes können genutzt werden
    • es fällt keine IP-Adresse dadurch weg, dass eine IP des zusätzlichen Netzes als Gateway herhalten muss
      • es sind wirklich alle IP-Adressen des zusätzlichen IP-Netzes nutzbar
  • es wird auf den Einsatz von libvirt für die Netzwerkkonfiguration verzichtet
    • libvirt verwaltet mit dieser Anleitung nur die Gäste selbst, keine Netze
  • als Betriebssystem auf dem Host wird Debian Lenny verwendet

Voraussetzungen

  • Root-Server von Hetzner
  • zusätzliches IP-Netz von Hetzner
  • Minimalinstallation von Debian Lenny, idealerweise mit LVM
    • zusätzlich folgende Pakete und ihre Abhängigkeiten:
      • bridge-utils
      • dhcp3-server
      • iptables
      • kvm

Host-System

Netzwerk-Konfiguration

Es empfiehlt sich, die aktuelle Netzwerkkonfiguration des Servers zu notieren. Insbesondere das Default-Gateway ist wichtig.

Das Default-Gateway kann man der Ausgabe von

ip route

(aus Paket iproute) oder

route -n

entnehmen.

IP-Adressen

Nehmen wir an, Ihr habt folgende Daten notiert bzw. von Hetzner bekommen:

  • Server
    • Haupt-IP: xx.yy.99.146
    • Default-Gateway: xx.yy.99.129
  • IP-Netz
    • IP: xx.yy.240.64
    • Maske: 255.255.255.240
    • Broadcast: xx.yy.240.79

Diese Daten heißen im Klartext:

Euer Server mit der IP xx.yy.99.146 sendet alle Pakete, die ins Internet adressiert sind, an das Gateway xx.yy.99.129. Hetzner routet die zusätzlichen IP-Adressen xx.yy.240.64 - xx.yy.240.79 auf die Haupt-IP xx.yy.99.146 (in der Routing-Tabelle des Hetzner-Routers steht: das Netz xx.yy.240.64/28 befindet sich hinter xx.yy.99.146).

/etc/network/interfaces

Die Datei /etc/network/interfaces auf Eurem Hostsystem sollte nun folgendermaßen aussehen:

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static
        address xx.yy.99.146
        netmask 255.255.255.255
        gateway xx.yy.99.129
        pointopoint xx.yy.99.129

Hier legen wir die IP-Adresse Eures Servers sowie die des Default-Gateways statisch fest.

Die Netzmaske 255.255.255.255 sorgt dafür, dass wir Pakete nach draußen immer an das Default-Gateway von Hetzner adressieren - auch wenn wir mit einem Racknachbarn sprechen wollen.

Die Zeile pointopoint (Achtung: da steht nicht pointtopoint) ist notwendig wegen der restriktiven Netzmaske: damit wir das Gateway jetzt überhaupt noch erreichen können, ist eine Host-Route notwendig. Genau das erledigt diese Zeile.


Wenn wir wirklich alle zusätzlichen IP-Adressen nutzen wollen und uns keine "verloren" gehen soll, können wir das IP-Netz leider nicht einfach komplett auf eine Bridge routen. Wir müssen wohl oder übel für jede einzelne IP-Adresse in dem zusätzlichen Netz eine einzelne Route anlegen.

Doch auch das reicht noch nicht: Da wir zwischen einzelnen VMs nicht switchen sondern routen wollen, dürfen die virtuellen Maschinen mit ihren Netzwerkinterfaces nicht in ein und die selbe Bridge gesteckt werden (falls doch, könnte nämlich VM1 vorbei an der Firewall auf dem Host-System auf VM2 zugreifen).

Fazit

Für jede IP-Adresse aus dem zusätzlichem IP-Netz

  1. legen wir eine eigene Bridge an
  2. setzen wir einen Eintrag in der Routing-Tabelle
  3. geben der Bridge eine IP-Adresse der Form 172.30.X.1/24 (dazu später mehr: DHCP-Server für virtuelle Maschinen)

Eintrag in /etc/network/interfaces für die erste zusätzliche IP-Adresse xx.yy.240.64

auto br64
iface br64 inet static
        address 172.30.64.1
        netmask 255.255.255.0
        pre-up brctl addbr $IFACE
        post-up route add -host xx.yy.240.64 $IFACE
        post-down brctl delbr $IFACE

Erklärung:

Die Bridge br64 erhält die IP-Adresse 172.30.64.1/24 (dazu später mehr: DHCP-Server für virtuelle Maschinen), pre-up legt die Bridge an, post-up setzt die Route (Klartext: IP-Adresse xx.yy.240.64 befindet sich direkt an Interface br64) und post-down sorgt dafür, dass die Bridge gelöscht wird, wenn das Interface runtergefahren wird.

Diese Blöcke wiederholen sich nun in der /etc/network/interfaces bis zur letzten Adresse des IP-Netzes:

auto br79
iface br79 inet static
        address 172.30.79.1
        netmask 255.255.255.0
        pre-up brctl addbr $IFACE
        post-up route add -host xx.yy.240.79 $IFACE
        post-down brctl delbr $IFACE

DHCP-Server für virtuelle Maschinen

Da viele Installer mit der Host-Route nicht zurecht kommen, setzen wir auf dem Hostsystem einen DHCP-Server auf und vergeben private IP-Adressen nach RFC1918 per DHCP an unsere VMs.

/etc/dhcp3/dhcpd.conf

authoritative;
default-lease-time              3600;
max-lease-time                  3600;
ddns-update-style               ad-hoc;
log-facility                    local7;
use-host-decl-names             on;

option subnet-mask              255.255.255.0;
option domain-name              "lan";
option domain-name-servers      213.133.100.100, 213.133.99.99, 213.133.98.98;

subnet 172.30.64.0 netmask 255.255.255.0 {
        option routers          172.30.64.1;
        range                   172.30.64.10 172.30.64.200;
}

subnet 172.30.65.0 netmask 255.255.255.0 {
        option routers          172.30.65.1;
        range                   172.30.65.10 172.30.65.200;
}

...

subnet 172.30.78.0 netmask 255.255.255.0 {
        option routers          172.30.78.1;
        range                   172.30.78.10 172.30.78.200;
}

subnet 172.30.79.0 netmask 255.255.255.0 {
        option routers          172.30.79.1;
        range                   172.30.79.10 172.30.79.200;
}

Für jede Bridge bzw. IP-Netz wird ein DHCP-Subnet deklariert, welches für DHCP-Clients in diesen Netzen das zu nutzende Gateway definiert.

Somit bekommt z.B. die virtuelle Maschine, deren "Netzwerkkarte" in die Bridge br64 eingefügt wird, vom DHCP-Server eine der IP-Adresse aus dem Bereich 172.30.64.10-200, als Default-Gateway 172.30.64.1 und als DNS-Server die 3 von Hetzner (213.133.100.100, 213.133.99.99 und 213.133.98.98) zugewiesen.

/etc/default/dhcp3-server

Damit wir nur auf unseren Bridge-Interfaces auf DHCP-Requests hören und antworten (vorallem nicht auf eth0 - da geht's zu Hetzner), sieht unsere Datei /etc/default/dhcp3-server entsprechend aus:

INTERFACES="br64 br65 br66 br67 br68 br69 br70 br71 br72 br73 br74 br75 br76 br77 br78 br79"

Euren DHCP-Server solltet Ihr mit

/etc/init.d/dhcp3-server restart

nun mit der neuen Config starten.

Firewall

Ein rudimentäres und unvollständiges Firewall-Skript (Paketfilter ist der passendere Name) wäre z.B. folgendes:

#!/bin/sh
it="/sbin/iptables"

# Mit Leerzeichen getrennte Aufzaehlung Eurer Zusatz-IPs und Zusatz-Subnetze
#
# Beispiel:
#
# MY_NET="192.0.2.8/29  192.0.2.44  192.0.2.128/29"
MY_NET="xx.yy.240.64/28"

# Mit Leerzeichen getrennte Aufzaehlung Eurer internen DHCP-Netze
MY_NET_DHCP="172.30.0.0/16"

# Haupt-IP des Servers
HAUPT_IP="xx.yy.99.146"


#
# INPUT
#


# DHCP-Requests von den VMs
$it -A INPUT -p udp --dport 67 -i br+                    -j ACCEPT

# andere Regeln fuer INPUT

# Rest verwerfen
#$it -A INPUT -j DROP


#
# FORWARD
#


# Pakete die zu bestehenden Verbindungen gehoeren
$it -A FORWARD -m state --state RELATED,ESTABLISHED      -j ACCEPT

# VMs (Zusatz-IP/Zusatz-Netz) --> Internet
for NET in $MY_NET
do
  $it -A FORWARD -i br+ -o eth0 -s $NET                  -j ACCEPT
done

# VMs (DHCP) --> Internet
for NET in $MY_NET_DHCP
do
  $it -A FORWARD -i br+ -o eth0 -s $NET                  -j ACCEPT
done

# VMs <--> VMs
# hier kann Traffic zwischen VMs reglementiert werden

# VMs --> VMs
for SOURCE_NET in $MY_NET $MY_NET_DHCP
do
  for DEST_NET in $MY_NET
  do
    $it -A FORWARD -i br+ -o br+ -s $SOURCE_NET -d $DEST_NET -j ACCEPT
  done
done

# Internet --> VMs
for NET in $MY_NET
do
  $it -A FORWARD -i eth0 -o br+ -d $NET                  -j ACCEPT
done

# Rest verwerfen
$it -A FORWARD -j DROP


#
# POSTROUTING
#


# Masquerading
# Verstecken der privaten, internen DHCP-Netze hinter der Haupt-IP
for NET in $MY_NET_DHCP
do
  $it -t nat -A POSTROUTING -o eth0 -s $NET -j SNAT --to-source $HAUPT_IP
done


#
# Routing
#


# Routing aktivieren
echo 1 > /proc/sys/net/ipv4/ip_forward

Das an Eure Bedürfnisse angepasste Skript lässt sich z.B. als /usr/local/bin/firewall ablegen und in /etc/rc.local starten.

Virtuelle Maschinen

Anlegen einer virtuellen Maschine

Für KVM gibt es einige Tools, um virtuelle Maschinen (bzw. deren XML-Datei) zu erzeugen.

Eine solche Datei für die virtuelle Maschine "cookie", /etc/libvirt/qemu/cookie.xml, könnte wie folgt aussehen:

<domain type='kvm'>
  <name>cookie</name>
  <uuid>00000000-2812-2009-1719-000000000067</uuid>
  <memory>2097152</memory>
  <vcpu>4</vcpu>
  <os>
    <type arch='i686' machine='pc'>hvm</type>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>
  <features>
    <acpi/>
    <apic/>
    <pae/>
  </features>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <devices>
    <emulator>/usr/bin/kvm</emulator>
    <disk type='file' device='cdrom'>
      <source file='/var/lib/libvirt/iso/grml-medium_2009.10.iso'/>
      <target dev='hda'/>
      <readonly/>
    </disk>
    <disk type='block' device='disk'>
      <source dev='/dev/vgdom/cookie'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <interface type='bridge'>
      <mac address='54:52:00:00:67:01'/>
      <source bridge='br67'/>
      <model type='virtio'/>
    </interface>
    <serial type='pty'>
      <target port='0'/>
    </serial>
    <console type='pty'>
      <target port='0'/>
    </console>
    <input type='mouse' bus='ps2'/>
    <graphics type='vnc' port='5967' autoport='no' keymap='de'/>
  </devices>
</domain>

Wichtig ist hier eigentlich nur, dass die UUID, die MAC-Adresse und der Name der VM eindeutig sind. In diesem Beispiel wird die virtuelle Maschine "cookie" mit ihrem Netzwerk-Interface in die Bridge br67 eingehangen.

Netzwerk-Konfiguration

per DHCP

Jede der virtuellen Maschinen, deren "virtuelles" Netzwerk-Interface einer der Bridges auf dem Hostsystem hinzugefügt wurde (das macht libvirt beim Start der VMs automatisch), kommt über DHCP ins Internet.

Beispiel: VM "cookie" mit Netzwerk-Interface in Bridge br67.

Per DHCP erhält die VM

Sobald die VM Pakete ins Internet sendet (vom Host aus gesehen sind das Pakete, die zu eth0 rausgehen), werden diese auf die Haupt-IP xx.yy.99.146 umgeschrieben. Da keine Port-Weiterleitungen definiert sind, sind Dienste (SSH, HTTP, ...) der virtuellen Maschine aus dem Internet nicht erreichbar.

Die Netzwerk-Konfiguration per DHCP eignet sich also hervorragend zur Installation von neuen virtuellen Maschinen.

statisch

Sobald die Installation abgeschlossen ist, kann auf die statische Netzwerkkonfiguration mit öffentlicher IP-Adresse gewechselt werden.

Beispiel: VM "cookie" mit Netzwerk-Interface in Bridge br67.

Datei /etc/network/interfaces in der VM:

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static
        address xx.yy.240.67
        netmask 255.255.255.255
        gateway xx.yy.99.146
        pointopoint xx.yy.99.146

Öffentliche IP-Adresse xx.yy.240.67 mit Netmask 255.255.255.255, Gateway ist die Haupt-IP xx.yy.99.146 des Servers (nicht das Default-Gateway xx.yy.99.129 des Host-Systems!) und wie bereits von /etc/network/interfaces bekannt bedarf es einer Host-Route zum Gateway wegen der restriktiven Netzmaske.

Die VM sowie Dienste in der VM (SSH, HTTP, ...) sind über die öffentliche IP-Adresse nun ansprechbar.

Hindernisse und Fallstricke

Alternative Methode für Firewall-Skript

Ich habe diese Anleitung (s.o.) als Vorlage für meine eigene Konfiguration genommen. Allerdings sind die zusätzlich erhältlichen Einzel-IPs ggf. fragmentiert, also nicht in einem gemeinsamen Subnetz. Daher habe ich folgendes Skript geschrieben (benutzt jedoch Bash-Features):

#!/usr/bin/env bash
IPT='echo iptables'
# In diesem Beispiel liegen überhaupt nur 2 IPs im gleichen Subnetz
# Format ist:
# [Name des Bridge-Interfaces]:[echte Ziel-IP]
VMS="_otto:88.x.y.z+0 _hugo:88.x.y+1.z+0 _horst:88.x.y+2.z+3 _bernd:88.x.y.z+4"

# Bestehende Verbindungen erlauben
$IPT -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
# Zwischen VMs und Internet
for i in $VMS; do
  # Erstmal extrahieren wir Namen und IP
  VM_NAME=${i%%:*}
  VM_ADDR=${i##*:}
  # Jetzt die Regeln Internet <--> Ziel-IP (beidseitig)
  $IPT -A FORWARD -i $VM_NAME -o eth0 -s $VM_ADDR -j ACCEPT
  $IPT -A FORWARD -i eth0 -o $VM_NAME -d $VM_ADDR -j ACCEPT
  for j in $VMS; do
    if [[ "$i" == "$j" ]]; then continue; fi # wenn identisch, nächste Runde
    # Wieder extrahieren wir Namen und IP
    VM_NAME2=${j%%:*}
    VM_ADDR2=${j##*:}
    # Jeglichen Verkehr zwischen den VMs erlauben
    $IPT -A FORWARD -i $VM_NAME -o $VM_NAME2 -s $VM_ADDR -d $VM_ADDR2 -j ACCEPT
  done
done
# Hier kann man noch den Rest verbieten usw.
# ....

Die DHCP-Konfiguration habe ich komplett weggelassen, da ich sie für meine Zwecke nicht brauche. Daher sind auch die entsprechenden Regeln nicht enthalten.

Dieses Skript macht es etwas leichter die Regeln zwischen den einzelnen VMs aufzustellen.



© 2016. Hetzner Online GmbH. Alle Rechte vorbehalten.