Die Reise, Teil 2 - Public IPv6, libvirt Netzwerk und etwas Verzweiflung

Posted on Sat 15 October 2016 in lxc

Prolog

Im ersten Teil haben wir LXC eingerichtet, das libvirt-Netzwerkinterface angepasst und die lokale IPv4 Adresse für einen Container fest gesetzt.

In diesem Teil geht es um die Nutzung von öffentlichen IPv6 Adressen mit LXC Containern. In meinem Beispiel bei Online.net, die jedem Kunden ein eigenes /48 Subnet zuweisen. Was will man als Sysadmin mehr?

Kleiner Haken an der Geschichte: Das /48 muss gesplittet werden. Man erhält so viele /56 Netze wie man Server hat.

IPv6 Einrichtung auf dem Host

Im folgenden Bereich richten wir IPv6 auf dem Host System. Dabei wird das in RFC3849 beschriebene IPv6 Subnet für Dokumentationen verwendet.

Nach der Einrichtung des IPv6 Subnet im Kundenbereich von Online.net besitzt man folgendes:

  • IP Block, bspw. 2001:db8:1111::/48
  • Ein /56, bspw. 2001:db8:1111:100::/56
  • Eine DUID, bspw. 00:03:00:01:00:00:00:00:00:01

Um später zusätzliche IPv4 Failover IP Adressen in Containern verwenden zu können ist es notwendig das physikalische Interface des Systems anzupassen, sodass es von einer viruellen Netzwerkbrücke verwendet wird. Die eigentliche IPv4 Adresse, als auch die IPv6 Adresse werden auf die Bridge gelegt.

Nachfolgend ist die Einrichtung in Fedora 24 beschrieben. Details sind auch im Online.net Wiki zu finden.

# cd /etc/sysconfig/network-scripts/
# cp ifcfg-eth0 ifcfg-br0

Es erfolgt die Anpassung der beiden Dateien, sodass diese wie nachfolgend aufgebaut sind. Dabei richten wir auch direkt IPv6 ein, wobei wir von unseren Beispiel Subnet direkt die erste verfügbare IPv6 Adresse verwenden möchten.

# ifcfg-eth0
UUID="18085dc-db40-372c-ab7f-b4f47ca6cec4"
ONBOOT="yes"
BOOTPROTO=none
DEVICE="eth0"
TYPE=Ethernet
NAME="System eth0"
BRIDGE=br0

# ifcfg-br0
DNS1=62.210.16.6
ONBOOT="yes"
BOOTPROTO=none
IPV6INIT="yes"
DEVICE="br0"
TYPE=Bridge
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6ADDR="2001:db8:1111:100::1/56"
NAME="System br0"
STP=no
IPADDR=192.168.2.2
PREFIX=24
GATEWAY=192.168.2.1
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes
IPV6_FAILURE_FATAL=no
UUID=2845984-db40-372c-ab7f-b4f47ca6cec4

Beim Vergleich mit der eigenen ifcfg-eth0 sollten die wichtigen Unterschiede auffallen.

Um das IPv6 Subnet verwenden zu können ist es notwendig noch eine Konfiguration für DHCPv6 Client einzurichten, welcher im Netz das IPv6 Subnetz registriert:

# vi /etc/dhcp/dhclient6.conf
interface "br0" {
   send dhcp6.client-id 00:03:00:01:00:00:00:00:00:01;
}

Damit das auch beim Systemstart geschieht wird ein zusätzliches systemd Unit File angelegt:

# vi /etc/systemd/system/dhclient.service
[Unit]
Description=dhclient to announce 00:03:00:01:00:00:00:00:00:01 IPv6
Wants=network.target
Before=network.target

[Service]
Type=forking
ExecStart=/usr/sbin/dhclient -cf /etc/dhcp/dhclient6.conf -6 -P -v br0

[Install]
WantedBy=multi-user.target

Dieses wird noch für den Systemstart aktiviert:

# systemctl enable dhclient.service

Jetzt heißt es erst einmal bereithalten für den Neustart, also KVM Console aktivieren, verbinden und den Login mit dem root-Benutzer prüfen. Das Tastaturlayout wird nicht sinnvoll übertragen, sodass man das wirklich vorher prüfen sollte!

Zusätzlich sollten über sysctl zum Systemstart einige Optionen gesetzt werden:

# vi /etc/sysctl.d/01-ipv6.conf
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.all.proxy_ndp = 1
net.ipv6.conf.all.accept_ra = 2

Nach dem Neustart

Der Neustart ist durch und wir können uns (hoffentlich) wieder per SSH zum Server verbinden. Jetzt stehen die ersten Tests an. Wir sollten mit ip erkennen, dass die Bridge als br0 existiert und unsere beiden IP Adressen verwendet. eth0 sollte keine IP Adresse mehr besitzen:

# ip a
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master br0 state UP group default qlen 1000
    link/ether 0a:0b:0c:0d:0e:0f brd ff:ff:ff:ff:ff:ff
3: eth1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 0a:0b:0c:0d:0e:0f brd ff:ff:ff:ff:ff:ff
4: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 0a:0b:0c:0d:0e:0f brd ff:ff:ff:ff:ff:ff
    inet 192.168.2.2/24 brd 192.168.2.255 scope global br0
       valid_lft forever preferred_lft forever
    inet6 2001:db8:1111:100::1/56 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::ec4:7aff:fe84:6046/64 scope link
       valid_lft forever preferred_lft forever

Wir können bestimmt auch Google über IPv6 erreichen, oder?

# ping6 -c 4 ipv6.google.com
PING ipv6.google.com(wk-in-x71.1e100.net (2a00:1450:400c:c04::71)) 56 data bytes
64 bytes from wk-in-x71.1e100.net (2a00:1450:400c:c04::71): icmp_seq=1 ttl=50 time=5.60 ms
64 bytes from wk-in-x71.1e100.net (2a00:1450:400c:c04::71): icmp_seq=2 ttl=50 time=5.59 ms
64 bytes from wk-in-x71.1e100.net (2a00:1450:400c:c04::71): icmp_seq=3 ttl=50 time=5.55 ms
64 bytes from wk-in-x71.1e100.net (2a00:1450:400c:c04::71): icmp_seq=4 ttl=50 time=5.58 ms

--- ipv6.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 5.559/5.583/5.603/0.092 ms

An dieser Stelle sind wir also bereit unseren LXC Container per IPv6 verfügbar zu machen.

LXC und IPv6 - Eine Horrorgeschichte

Eine Horrorgeschichte - vielleicht etwas übertrieben. Einige schlaflöse Nächte hat es mich allerdings doch gekostet. Die Bridge besteht, um direkt über br0 aus Containern weitere IPv4/6 Adressen zu verwenden. Für Failover IPs haben alle mir bekannten Hoster ein sehr einfaches Konzept:

  • Bridge verwenden
  • MAC Adresse des Interface im Kundencenter hinterlegen
  • Failover IP auf das Interface binden

Für IPv6 gibt es dutzende Blog- und Foreneinträge. Dank Google Translate auch in Ihrer Sprache! Oder auch: Der Nachteul bei einem französischen Hoster.

Das Problem daran: Es wird nirgends beschrieben wie Online.net sich die Nutzung der IPv6 Subnets vorstellt.

Folgendes funktioniert nicht:

  • LXC veth Netzwerk mit br0 verwenden und IPv6 direkt binden
  • Jegliche Art von viruellem Netzwerk von LXC, siehe auch in diesem Blog

Was funktionieren könnte:

  • Separates Subnet mit radvd zu den Containern announcen

Was definitiv funktioniert, wenn man statische IPv6 Adressen vergeben möchte:

  • libvirt Interface verwenden, Subnet hinzufügen und Container konfigurieren. Yeah!

Einrichtung des libvirt-Interface mit einem IPv6 Subnet

In unserem Beispiel weiter oben haben das Subnet 2001:db8:1111:100::/56 für unseren Host verwendet. Unseren Containern möchte wir statische IPv6 Adressen geben. Dafür müssen wir zuerst das /56 aufteilen. In diesem Beispiel entscheide ich mich für das /64 Subnet 2001:db8:1111:111::/64.

Im ersten Teil der Reise haben wir bereits das libvirt-Interface mit der Bezeichnung virbr0 bearbeitet. Dies geschieht nun erneut, um das neue Subnet hinzuzufügen. Nach dem IPv4 Block, welcher mit </ip> endet definieren wir die IPv6 Adresse für virbr0:

# virsh net-edit default
...
  <ip family='ipv6' address='2001:db8:1111:111::1/64' prefix='64'>
  </ip>
</network>

Im Anschluss muss das Interface neu geladen werden. Dies kann mit systemctl restart libvirtd oder über folgende Kommandos erfolgen:

# virsh net-destroy public
# virsh net-start public

Im Anschluss steht die IPv6 Adresse auf dem Interface zur Verfügung:

# ip a
...
5: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 0a:0b:0c:0d:0e:0f brd ff:ff:ff:ff:ff:ff
inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
   valid_lft forever preferred_lft forever
inet6 2001:db8:1111:111::1/64 scope global
   valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe24:4f75/64 scope link
   valid_lft forever preferred_lft forever

Ein ping vom eigenen Heimnetzwerk sollte nun bereits auf die 2001:db8:1111:111::1 möglich sein. Bevor man fortfährt sollte man das testen.

Konfiguration des LXC Containers

IPv6 auf dem Host und auf unserem viruellen Netzwerkinterface virb0. Damit haben wir doch alles, was wir brauchen. Aber Achtung: Bevor es weiter geht sollte man die /etc/sysconfig/network-scripts/ifcfg-eth0 innerhalb des Containers entfernen. Diese Datei ist nicht unbedingt notwendig. Warum steht nachfolgend beschrieben:

Im letzten Beispiel haben wir einen LXC Container namens reducto angelegt. Auch diesmal bearbeiten wir dessen Konfigurationsdatei, entfernen unter Umständen überflüssige network.-Options und konfigurieren eine IPv6 Adresse aus unserem /64 Subnet:

# vi /var/lib/lxc/reducto/config
...
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = virbr0
lxc.network.name = eth0
lxc.network.hwaddr = fe:61:2c:xx:xx:xx
lxc.network.ipv4=192.168.122.2/24
lxc.network.ipv4.gateway = auto
lxc.network.ipv6 = 2001:db8:1111:111::2/64
lxc.network.ipv6.gateway = auto

Im Anschluss kann der Container neu gestartet. Nach dem Verbinden mit dem Container sollte die Ausgabe von ip direkt die konfigurierte IPv4 und die IPv6 Adresse zeigen:

# lxc-attach --name=reducto
[root@reducto ~]# ip a
...
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether fe:61:2c:xx:xx:xx brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.122.2/24 brd 192.168.122.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2001:db8:1111:111::2 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::fc61:2cff:fe92:20c4/64 scope link
       valid_lft forever preferred_lft forever

Damit haben wir den ersten Container der direkt per IPv6 öffentlich erreichbar ist!