KVM – Anlegen eines privaten, isolierten Netzwerks mit virt-manager

Im vorhergehenden Artikel dieses Blogs
Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts
hatte ich 2 Arten der Anbindung eines KVM-Gastsystems an die physikalische Umwelt des KVM-Hostes diskutiert. Ich hatte angemerkt, dass es unter KVM/libvirt neben einem direkten Bridging zu einer physikalischen Host-NIC natürlich auch die Möglichkeit gibt, KVM-Gäste an ein sog. “Host-Only-Network” [HON] anzubinden. Intern wird dieses Netzwerk durch eine virtuelle Bridge repräsentiert. Soll man aus dem HON heraus mit der physikalischen Umwelt (LAN) kommunizieren, muss man auf dem KVM-Host Routing zwischen einer bereitgestellten virtuellen Host-NIC der Bridge zu einer physikalischen NIC des Hostes ermöglichen. Letztere leitet die Pakete dann ins LAN.

Unterlässt man das Routing (und/oder filtert man Pakete aus dem HON) auf dem Host, so befinden sich Gastsystem und Host in einem isolierten virtuellen Netz, aus dem nach außen ohne weitere Vorkehrungen nicht kommuniziert werden kann. Dem isolierten Netz können natürlich weitere Gäste beitreten.

Wegen der Nachfrage einer Leserin, zeige ich nachfolgend kurz die Anlage eines virtuellen Host-Only-Netzwerks unter KVM mittels “virt-manager”. Ich setze voraus, dass der “libvirtd”-Daemon läuft.

Man ruft als root “virt-manager” auf und geht im Übersichtsfenster auf “Edit >> Connection Details” und dort auf den Reiter “Virtual Networks”. Dort findet man unter der Übersichtsliste zu bereits vorhandenen Netzwerken, einen Button mit einem “+” Symbol zum Anlegen eines neuen Netzwerks. Ich zeige nachfolgend die Dialogsequenz:

new_network_1

new_network_2

new_network_3

new_network_4

new_network_5

Die Bridge und eine zugehörige Host-NIC tauchen dann auch in der Liste der vorhandenen Netzwerk-Devices auf. Unter Opensuse zeigt das Komamndo “wicked show all” dann etwa ein virbr-Device (virtual bridge – im Beispiel ein “virbr2”):

virbr2          device-unconfigured
      link:     #73, state device-up, mtu 1500
      type:     bridge
      addr:     ipv4 192.168.120.1/24

virbr2-nic      device-unconfigured
      link:     #74, state down, mtu 1500, master virbr2
      type:     tap, hwaddr 52:54:00:c9:bd:24

 
Das neue Netzwerk findet sich dann auch in Form einer XML-Netzwerk-Konfigurations-Datei unter “/etc/libvirt/qemu/networks/host2.xml” wieder:

<!--
WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:
  virsh net-edit host2
or other application using the libvirt API.
-->

<network>
  <name>host2&
lt;/name>
  <uuid>f47c2d04-b1d6-48bf-a6dc-a643d28b38d3</uuid>
  <bridge name='virbr2' stp='on' delay='0'/>
  <mac address='52:54:00:c9:bd:24'/>
  <domain name='host2'/>
  <ip address='192.168.120.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.120.128' end='192.168.120.254'/>
    </dhcp>
  </ip>
</network>

 
Will man eine solche Host-Only-Bridge von einem KVM-Gast aus nutzen, so muss man für diesen Gast ein entsprechendes “Netzwerk”-Device (NIC) anlegen, das der Bridge zugeordnet wird. “virt-manager” bietet auch hierfür entsprechende grafische Dialoge an. Ich gehe davon aus, dass eine virtuelle Maschine (z.B. namens “kali2”) bereits existiert. Man öffnet deren Konfigurations-Oberfläche durch Doppelklick auf den entsprechenden Eintrag in der Liste aller KVM-Instanzen unter “virt-manager”. Im sich öffnenden Fenster klickt man weiter auf den Button mit dem “i”-Symbol:

new_network_6

Danach taucht das Device auch in der XML-Konfigurationsdatei für den Gast auf – in meinem Beispiel etwa für einen Kali-Gast mit der Datei “/etc/libvirt/qemu/kali.xml” – ich zeige nur den relevanten Ausschnitt:

    <interface type='network'>
      <mac address='52:54:00:0d:3c:8b'/>
      <source network='host2'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x0d' function='0x0'/>
    </interface>

 
Danach muss man den Gast neu starten und in ihm natürlich das neu aufgetauchte Netzwerk-Interface manuell konfigurieren – falls man nicht auf DHCP setzt und/oder besondere Einstellungen benötigt. Man merke sich hierzu die MAC-Adresse, um bei mehreren NICs nicht den Überblick zu verlieren! Man achte auch auf das Default-Gateway, wenn Routing über den Host gewünscht ist.

Achtung: Das neue virtuelle Netzwerk des Gastes ist mit “ifconfig”, “ip” oder “wicked” Kommandos erst als ein “vnetN-Device sichtbar, wenn der Gast gestartet und aktiv ist. Das “N” steht dabei für eine fortlaufende Nummer, die vom System (libvirt) vergeben wird. Z.B. taucht unter “wicked show all” dann ggf. ein Device “vnet3” auf:

virbr2          device-unconfigured
      link:     #73, state device-up, mtu 1500
      type:     bridge
      addr:     ipv4 192.168.120.1/24

virbr2-nic      device-unconfigured
      link:     #74, state down, mtu 1500, master virbr2
      type:     tap, hwaddr 52:54:00:c9:bd:24
...
...
vnet3           device-unconfigured
      link:     #17, state up, mtu 1500, master virbr2
      type:     tap, hwaddr fe:54:00:0d:3c:8b

 
Man beachte, dass sowohl die Host-Nic, als auch das Device des Gastes “tap”-Devices sind. Allg. Infos zu tap-devices finden sich hier:
https://www.kernel.org/doc/Documentation/networking/tuntap.txt
https://de.wikipedia.org/wiki/TUN/TAP

Hinweise für Leser, die die Konfiguration lieber manuell und über die Kommandozeile durchführen mögen

Wie man eine virtuelle Bridge auf dem KVM-Host mittels des “brctl”-Kommandos einrichtet, benennt und wie man ihr “tap”-Devices zuordnet, habe ich im Prinzip bereits früher am Beispiel einer direkten Bridge zu einem physikalischen Device beschrieben:
Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts
Siehe aber auch hier:
http://www.linux-kvm.org/page/Networking#Configuring_Guest_Networking – Abschnitt (Private Network)

“tap”-Devices kann man manuell und temporär über das Kommando “tunctl” auf dem Host erzeugen.
Siehe etwa :
http://unix.stackexchange.com/questions/86720/can-i-create-a-virtual-ethernet-interface-named-eth0
http://serverfault.com/questions/347895/creating-tun-tap-devices-on-linux
http://www.naturalborncoder.com/virtualization/2014/10/17/understanding-tun-tap-interfaces/
http://blog.elastocloud.org/2015/07/qemukvm-bridged-network-with-tap.html

Die Zuordnung von “tap”-Devices zu einer virtuellen Linux-Bridge erfolgt über das “brctl addif”-Kommando. Nun fehlt also nur noch eine Methode, um einmal erzeugte “tap”-Devices in die Konfiguration eines Gastes einzubinden.

Ich kenne einige Leute, die starten ihre virtuellen Maschinen lieber eigenhändig und über Scripts statt über virt-manager. Dann kann die Netzwerk-Konfiguration des Gastsystems in Form passender Optionsparameter des Kommandos “qemu-system-x86_64” (mit weiteren Optionen für KVM-Unterstützung; s.u.) oder des Kommandos “qemu-kvm” zum Starten eines KVM-Gastes geschehen. Eine Übersicht über diese Möglichkeit findet man hier:
http://qemu-buch.de/de/index.php?title=QEMU-KVM-Buch/_Netzwerkoptionen/_Virtuelle_Netzwerke_konfigurieren
In abgekürzter Form auch hier :
https://bbs.archlinux.org/viewtopic.php?pid=1148335#p1148335
https://bbs.archlinux.org/viewtopic.php?pid=1424044#p1424044

Eine weitere Alternative ist hier beschrieben (s. den Abschnitt zu “Private Networking”):
http://www.linux-kvm.org/page/Networking#Configuring_Guest_Networking

Übrigens: “qemu-kvm” ist auf aktuellen Linux-Systemen meist nur ein kleines Shell-Script-Kommando, dass “qemu-system-x86_64” mit KVM-Hardware-Unterstützungsoptionen aufruft! Siehe zum Unterschied zw. “qemu-system-x86_64” und “qemu-kvm” etwa
http://www.linux-kvm.com/content/qemu-kvm-or-qemu-system-x8664%EF%BC%9F).
Die Optionen des “qemu-kvm”-Kommandos sind z.B. hier beschrieben:
https://www.suse.com/documentation/sles11/book_kvm/data/cha_qemu_running_gen_opts.html
Eine Zusammenfassung zu tap-Devices und verschiedenen Bridging-Varianten gibt auch
https://www.suse.com/documentation/sles11/book_kvm/data/cha_qemu_running_networking.html

Will man die Tools von “libvirt/virt-manager” zum Starten der virtuellen Maschine benutzen, dann kann die “manuelle” Definition von virtuellen NICs für einen KVM-Gastes aber auch mittels des “virsh edit”-Kommandos zur Manipulation des XML-Files für die
Gastkonfiguration durchgeführt werden.
Siehe:
http://serverfault.com/questions/665440/set-up-network-interfaces-in-ubuntu-for-kvm-virtual-machine

In einigen Situationen kann es auch erforderlich sein, den Gast im laufenden Betrieb um ein Netzwerkinterface zu einem neu definierten virtuellen Netz zu erweitern. Informationen hierzu findet man hier:
http://www.linuxwave.info/2014/12/hot-attach-and-hot-detach-network.html
https://kashyapc.fedorapeople.org/virt/add-network-card-in-guest.txt

Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts

Wenn man sich mit KVM beschäftigt, taucht u.U. das Thema auf, dass dem einen oder anderen KVM-Gastsystem ggf. ein virtuelles Netzwerk-Device im “bridged mode” zugeordnet werden soll. Dabei wird ein physikalisches Netzwerkdevice (NIC) des KVM-Hostes vom Gastsystem als Ressource für Verbindungen in die Netzwerkumgebung des Hosts genutzt. Das Gastsystem erhält eine IP-Adresse im physikalischen LAN des Hosts. Ich hatte ein solches Vorgehen in einem früheren Artikel als “direktes Bridging zum LAN” bezeichnet, um es von anderen Formen des Einsatzes virtueller Brigdes abzugrenzen. Wir beschreiben in diesem Artikel Elemente einer solchen Bridge-Konfiguration und gehen dann der Frage nach, wie man die Bridge mittels Linux-Kommandos manuell anlegt.

YaST legt virtuelle direkte LAN-Bridges im Rahmen einer Hypervisor-Installation automatisch an

Unter Opensuse unterstützt einen YaST nicht nur bei der Einrichtung von Netzwerk-Devices sondern auch beim grundlegenden Setup eines KVM- oder XEN-Hostes. Im Zuge eines solchen Setups (YaST2 >> “Install Hypervisor and Tools”) legt YaST automatisch für jedes vorhandene und konfigurierte physikalische Device des Hostes – z.B. enp8s0 – eine durch Gäste “direkt” nutzbare Bridge – z.B. br0 – an. Damit auch der Host über die Bridge kommunizieren kann, wird der physikalischen Schnittstelle (hier enp8s0) die IP-Adresse entzogen und dem erzeugten Bridge-Device (z.B. br0) zugeordnet. Siehe hierzu etwa:
http://libvirt.org/ formatdomain.html# elementsNICS
bzw.
http://libvirt.org/ formatdomain.html# elementsNICSBridge

Wozu ist eine direkte LAN-Bridge eines KVM-Hosts eigentlich gut?

Es gibt verschiedene Möglichkeiten KVM- (oder auch XEN-) Gastsysteme mit einer physikalischen LAN-Umgebung des Hosts zu verbinden. Eine “Direct Bridge To Lan” bietet einen bequemen, wenn auch nicht den sichersten Weg, einzelne Gäste in das LAN zu integrieren. Dabei teilen ggf. mehrere KVM Gastsysteme und der Host ein physikalisches Netzwerk-Device zur direkten Paketübermittlung in das physikalische LAN – und darüber wiederum ggf. ins Internet. Derartige direkte Bridges werden typischerweise mit einer Bezeichnung der Form “brN” (N steht für eine Nummer) versehen.

Wir leisten uns zum besseren Verständnis einen kurzen Exkurs: Die nachfolgende Skizze zeigt schematisch zwei unterschiedliche Arten der Anbindung von KVM-Gästen an physikalische Netze.

kvm_br0_2

Das Bild dient nur einer grundsätzlichen Veranschaulichung und ist nicht in jedem Detail ernstzunehmen. Wie der Ethernet-Protokollstapel für verschiedene virtuelle Devices intern tatsächlich verarbeitet wird, sei mal dahingestellt.

Direct Bridge to LAN:
Im unteren Bereich erkennt man den Einsatz eines physikalischen Ethernet-Devices “enp8s0” im Rahmen einer “Direct Bridge to LAN”.

Die Bridge ist zwar ein virtuelles Konstrukt (mit Kernel-Unterstützung); im Sinne der Zeichnung kann man sich aber vorstellen, dass sie wie ein realer Hub/Switch mit diversen Ports versehen ist, an die wiederum physikalische und virtuelle Netzwerkdevices angebunden werden. Ich habe die Ports mit den entsprechenden Bezeichnungen der angebundenen Devices versehen.

Die Bridge übernimmt dabei eine Doppel-Rolle – einerseits entspricht sie einem host-bezogenen Ethernet-Netzwerk-Device mit einer IP-Adresse und garantiert eine entsprechende Paketverarbeitung im Kernel; andererseits subsummiert sie die gesamte Logik zur Ethernet-Paketelimination bzw. -Paket-Verteilung zwischen den virtuellen Ports. Die Bridge behandelt Pakete
normalerweise und im Gegensatz zur physikalischen NIC dabei nicht im “promiscuous mode”, sondern verwirft Pakete, die angeschlossenen Devices nicht zugeordnet werden können. (Für bestimmte Zwecke kann man eine Linux-Bridge aber auch in einen “promiscuous mode” versetzen.)

Die Bridge ist bei bestimmten Einstellungen (sog. ageing-Parameter; s. die “man”-Seiten für das Kommando “brctl”) ferner in der Lage, wie ein Switch zu fungieren und Pakete gezielt und ausschließlich zu den richtigen Target-Ports weiterzuleiten. Das ist der Standard; die Bridge kann aber auch in einen HUB-Modus versetzt werden. Wegen einer weitgehenden Gästeisolation ist das aber selten wirklich wünschenswert.

Das physikalische Interface (enp8s0) wird gezielt mit der Bridge assoziiert (s.u.) und übernimmt in etwa die Funktionen eines physikalischen Uplinks. Die physikalische NIC wird bei der Bridgezuordnung automatisch in den sog. “promiscuous mode” geschaltet, da sie ja Pakete für verschiedene IP- bzw. MAC-Adressen in Empfang nehmen muss.

Die beiden dargestellten KVM-Gastsysteme nutzen die Kapazitäten der physikalischen NIC über die Bridge. Hierzu werden weitere virtuelle Netzwerk-Interfaces (tun/tap-Devices) “vnet0” und “vnet2” verwendet, die in die Konfiguration des Gastsystems aufgenommen und natürlich auch der Bridge zugeordnet werden.

Der Host selbst erhält im Beispiel über die Adresse 192.168.2.1, die br0 zugeordnet wurde, Kontakt zum umliegenden physikalischen LAN. Die Gastsysteme teilen sich die physikalischen Ressource über die Adressen 192.168.2.5 bzw. 192.168.2.6. Sie erscheinen mit diesen Adressen direkt im (physikalischen) LAN und sind (je nach Firewall-Einstellungen) von dort aus auch ansprechbar. Dem physikalischen Interface (enp8s0) selbst wird in dieser Konfiguration keine IP zugeordnet.

Alternative – Host-Only-Bridge und Routing:
Im oberen Bereich der Abbildung ist dagegen eine weitere virtuelle Bridge “virbr0” dargestellt ist, die ein reines “Host-Only Netzwerk” realisiert. Die beiden Gäste wie der Host können über (virtuelle) NICs auch mit einer solchen rein virtuellen Bridge “virbr0” verbunden werden. Das der Bridge ggf. zugeordnete virtuelle Host-Device taucht bei einer Standardvorgehensweise mit KVM oder libvirt-Tools (virt-manager !) normalerweise unter der Bezeichnung “virbr0-nic” auf. Diese Bridge-Variante, die im Grunde wie ein virtueller Switch funktioniert, wird jedoch nicht direkt mit einem physikalischen Device verbunden. Sind die Gastsysteme nur an eine solche “virbr0” angebunden, so erfordert eine Verbindung zur Außenwelt ein (ggf. NAT-) Routing auf dem Host zwischen dem virtuellen Interface “virbr0-nic” des Hostes und einer seiner physikalischen NICs. Eine solche – relativ kontrolliert und gut absicherbare – Situation trifft man häufig auf produktiven KVM-Hosts an.

Hinweis: Die Skizze dient nur der Illustration unterschiedlicher Typen virtueller Bridges – und stellt kein praktisch sinnvolles Szenario dar. Würde man die dargestellte – wenig ressourcenschonende – Situation tatsächlich realisieren, müsste man natürlich aufpassen, keine Netzwerk-Loops durch problematisches Routing zu generieren. Auch die Namensgebung pro Host und Netzwerk sowie die DHCP-Dienste für die beiden Netze müssten strikt getrennt werden. Ein Default Gateway sollte dann nur für eine Netzwerkverbindung definiert sein, hier für das physikalische LAN.

Ich mache ausdrücklich darauf aufmerksam, dass ein direktes Bridging zwar eine einfache Möglichkeit darstellt, Gastsysteme an die physikalische Umwelt anzubinden. Das Ganze ist aus Sicherheitsgründen aber durchaus problematisch – zu denken ist vor allem an ARP- und auch IP-Spoofing durch die Gastsystemnutzer. Der Host muss deshalb mit einer Kombination von iptables- und ebtables-Regeln ausgestattet werden, um die direkte Bridge abzusichern. Auch und
gerade, wenn der Host noch Routing-Aufgaben übernimmt. In produktiven Umgebungen ist eher auf Host-Only Konfigurationen mit/ohne NAT zu setzen.

Auf Test- und Übungssystemen – insbesondere lokalen Pentest-Umgebungen, bei denen Zielsysteme für Übungen im gleichen oder unterschiedlichen virtuellen Segmenten untergebracht werden – vereinfacht eine temporäre Nutzung von gebridgten Host-Devices aber durchaus die regelmäßig erforderliche Aktualisierung von bestimmten Gastsystemen – man sollte nur nicht vergessen, diese Devices unter internen Penetrationstests in seinen virtuellen Netzen abzuklemmen. Für interne Pentest-Übungen zwischen Gästen eines Virtualisierungshosts wird man dagegen Host-Only Bridges – ohne (!) aktiviertes Routing auf dem Host – nutzen, die gegenüber der Umwelt isoliert sind. Aber auch wenn Penetrationstests nach außen gerichtet werden, kann die Nutzung virtuelle Maschinen hinter einer gebridgten NIC sinnvoll sein.

Manuelles Anlegen einer direkten br0-Bridge zu einem physikalischen Device

Eine durchaus interessante Frage ist, ob und wie man unter Opensuse/SLES eine solche Bridge auch manuell anlegen kann – z.B. wenn man sich (möglicherweise über YaST) die Bridge-Konfiguration zerschossen hat. (Z.B. dadurch, dass man in einem Anflug von Unkonzentriertheit der Ethernet-Schnittstelle über YaST irgendwann wieder direkt eine IP-Adresse zugeordnet hat).

Voraussetzung – ein aktivierbares Netzwerk-Interface

Ich möchte in diesem Artikel nicht auf die manuelle Anlage eines physikalischen Ethernet-Netzwerk-Devices selbst eingehen. Hierzu müsste man für moderne Linux-Systeme den aktuellen “udev”- und “systemd”-Mechanismus, das automatische Erkennen und Identifizieren von Devices gem. entspr. Datenbanken, die Bereitstellung von Gerätedateien unter “/dev”, das (automatische) Laden zugehöriger Treiber und auch die Vergabe von “predictable” Device-Namen besprechen (und verstehen). Hier zieht man sich besser auf YaST oder die suse-spezifischen Konfigurationsdateien unter “/etc/sysconfig/network” zurück, in denen bestimmte Parameter wie die IP-Adresse für ein Device permanent hinterlegt werden.

SuSE-spezifische Informationen zur Konfiguration findet man hier:
https://www.suse.com/ de-de/ documentation/ sled11/ book_sle_admin/ data/sec_basicnet_ manconf.html
und hier
https://www.suse.com/ documentation/ sles-12 /book_sle_admin/ data/ sec_basicnet_ manconf.html
Die letzte Doku passt auch gut zu Opensuse 13.2.

Übrigens: Wer verstehen will, wie udev/systemd zu den “predictable names” für Devices gelangen, der lese folgenden Artikel
https://major.io/ 2015/08/21/ understanding-systemds-predictable-network-device-names/

und meine Zusammenfassung unter
Linux-Namensgebung für physikalische Netzwerk-Devices

Ich gehe davon aus, dass es auf unserem KVM-Host z.B. bereits ein aktiviertes Ethernet-Device – in unserem Beispiel “enp8s0” – gibt. Soll es von KVM-Gästen über eine direkte virtuelle Bridge zum LAN genutzt werden, darf ihm beim Systemstart keine IP-Adresse zugeordnet werden. Dies kann man einerseits über “YaST2 >> Netzwerkeinstellungen” für diese NIC einstellen. Alternativ kann man manuell das opensuse-spezifische File “/etc/sysconfig/network/ifcfg-enp8s0” anlegen und mit leeren Konfigurationsanweisungen editieren:

mytux:/etc/sysconfig/network # cat ifcfg-enp8s0 
BOOTPROTO='none'
BROADCAST=''
ETHTOOL_OPTIONS=''
IPADDR=''
MTU=''
NAME=''
NETMASK=''
nNETWORK=''
REMOTE_IPADDR=''
STARTMODE='auto'
DHCLIENT_SET_DEFAULT_ROUTE='yes'
PREFIXLEN=''
mytux:/etc/sysconfig/network # 

Einrichtung einer Bridge mittels des Kommandos brctl

Opensuse 13.2 nutzt im Gegensatz zu früheren Opensuse-Versionen “wicked” zum Starten von NICs etc.. Durch den “compat”-Modus von “wicked” werden aber immer noch die klassischen “ifcfg-…”-Dateien, die unter “/etc/sysconfig/network” definiert sind, unterstützt und ausgelesen.

Leider ist die zugehörige Factory-Doku von SuSE zu diesem Themenkomplex noch nicht ausgereift. Es lohnt sich daher, zunächst ein Blick in die SLES12-Doku zu werfen. Siehe hierzu:
https://activedoc.opensuse.org/ book/ opensuse-reference/ chapter-13-basic-networking
https://www.suse.com/ de-de/ documentation/ sles-12/book_sle_admin/data/sec_basicnet_ manconf.html
Siehe ergänzend zu den bereits erwähnten Quellen auch :
https://activedoc.opensuse.org/ book/ opensuse-virtualization-with-kvm/ chapter-12-running-virtual-machines-with-qemu-kvm

Man kann eine Standard-Bridge “br0” unter Opensuse natürlich mit Hilfe von YaST2 einrichten.
Es lohnt sich aber, das mal manuell zu machen, um entsprechende Befehle und suse-spezifische Konfigurationsdateien kennenzulernen. Der entscheidende Befehl hierzu ist “brctl”, zu dem man sich unbedingt mal die man-pages durchlesen sollte.

Man beachte auch: Das Tool “Network-Manager” unterstützt bislang meines Wissens keine Bridge-Strukturen – im besonderen nicht “direkte” Linux-Bridges zu physikalischen NIC_Devices.

Das nachfolgende Beispiel zeigt die Anlage einer “direkten” Host-Bridge über “brctl addbr” und die Zuordnung zu einem physikalischen Ethernet Device (für direktes Bridging) mittels “brctl addif”. [Hierbei wurde vorausgesetzt, dass dem physikalischen Device bislang keine IP-Adresse zugeordnet wurde.] Die Aktivierung des erzeugten Bridge-Devices erfolgt ganz konventionell über “ifconfig br0 up”.

Der Bridge selbst wird dann mittels “ifconfig” sehr eine IP-Adresse zugeordnet. Dies ist dann die Adresse unter der der (KVM-) Linux-Host selbst nach außen kommunizieren kann. Natürlich hätte man das alles statt mit “ifconfig” auch mit dem Kommando “ip” und seinen Optionen erledigen können. Ich überlasse diese Übung dem Leser.

mytux:~ # ifconfig enp8s0 down 
mytux:~ # ifconfig enp8s0 up 
mytux:~ # brctl addbr br0
mytux:~ # ifconfig br0 up 
mytux:~ # brctl addif br0 enp8s0
mytux:~ # brctl show
mytux:~ # ifconfig br0 192.168.2.1 netmask 255.255.255.0 broadcast 192.168.2.255
mytux:~ # ifconfig br0 

Um diese erreichte Konfiguration permanent machen, muss man noch ein entsprechendes ifcfg-File unter “/etc/sysconfig/network” mit folgendem beispielhaften Inhalt anlegen:

mytux:/etc/sysconfig/network # cat ifcfg-br0
BOOTPROTO='static'
BRIDGE='yes'
BRIDGE_FORWARDDELAY='0'
BRIDGE_PORTS='enp8s0'
BRIDGE_STP='off'
BROADCAST=''
DHCLIENT_SET_DEFAULT_ROUTE='yes'
ETHTOOL_OPTIONS=''
IPADDR='192.168.2.1/24'
MTU=''
NETWORK=''
PREFIXLEN='24'
REMOTE_IPADDR=''
STARTMODE='auto'
NAME=''
mytux:/etc/sysconfig/network #

 
Nicht vergessen sollte man die Definition
eines Default-Gateways für Routing nach außen – im Beispiel etwa über einen Host “192.168.2.10”. Das erfordert eine weitere Textdatei namens “ifroute-br0” unter “/etc/sysconfig/network”. Der Inhalt besteht nur aus einer Zeile “default 192.168.2.10 – br0”:

mytux:/etc/sysconfig/network # cat ifroute-br0 
default 192.168.2.10 - br0 
mytux:/etc/sysconfig/network # 

 
Das ganze Vorgehen lässt sich natürlich für die Anlage weiterer direkter Bridge-Devices zu anderen noch vorhandenen physikalischen Erhernet-Devices wiederholen.

Über Tools zur Einrichtung von KVM-Gastsystemen (wie virt-manager) kann man nun virtuelle NICs der Gastsysteme anlegen und der erzeugten Bridge zuordnen. Ich gehe darauf in einem weiteren kommenden Artikel ein.

Steht die Bridge erst einmal, so kann man ihren Status und aktive, zugeordnete Devices über das Kommando “brctl show br0” ansehen:

mytux # brctl show br0
bridge name     bridge id               STP enabled     interfaces
br0             8000.1c6f653dfd1e       no              enp8s0
                                                        vnet0
                                                        vnet2

 
Der Befehl “wicked show all” zeigt übrigens auch die NIC-Zuordnung zu Bridges und zudem die IP-Adressen an:

mytux: # wicked show all           
lo              up
      link:     #1, state up
      type:     loopback
      config:   compat:/etc/sysconfig/network/ifcfg-lo
      leases:   ipv4 static granted
      leases:   ipv6 static granted
      addr:     ipv4 127.0.0.1/8 [static]
      addr:     ipv6 ::1/128 [static]

enp8s0          enslaved
      link:     #2, state up, mtu 1500, master br0
      type:     ethernet, hwaddr 1d:ff:76:5c:cd:4e
      config:   compat:/etc/sysconfig/network/ifcfg-enp8s0

br0             up
      link:     #4, state up, mtu 1500 
      type:     bridge               
      config:   compat:/etc/sysconfig/network/ifcfg-br0
      leases:   ipv4 static granted    
      addr:     ipv4 192.168.2.1/24 [static]
      route:    ipv4 default via 192.168.2.10

vnet0           device-unconfigured
      link:     #12, state up, mtu 1500, master br0
      type:     tap, hwaddr fe:54:00:cc:dd:ee

vnet1           device-unconfigured
      link:     #13, state up, mtu 1500, master br0
      type:     tap, hwaddr dd:54:00:dd:ee:ff

vnet2           device-unconfigured
      link:     #14, state up, mtu 1500, master br0
      type:     tap, hwaddr fe:54:00:22:33:44

Viel Spaß beim Experimentieren mit virtuellen Bridges!

 

Linux-Namensgebung für physikalische Netzwerk-Devices

Opensuse nutzt ja seit längerem “systemd”. Zwei der größten Veränderungen, die damit einhergingen, war die intensive Nutzung von “udev” durch “systemd” im Rahmen des Systemstarts und eine resultierende Veränderung der Namensgebung für physikalische Devices – u.a. Netzwerkdevices. Alles unter dem Schlagwort “predictable device names”.

Einen großen Einfluss hatte das u.a. auf Netzwerk-Devices: An die Stelle der alten NIC-Bezeichnungen “eth0”, “eth1”, etc. traten dann auf meinem Desktop-System z.B. so illustre Ausdrücke wie “enp8s0”, “enp9s0” oder noch komplexere Namen.

Aus den netzwerk-bezogenen “udev”-Regeln in den Dateien unter “/lib/udev/rules.d” alleine konnte ich mir bislang nicht erklären, wie es zur Festlegung der Device-Namen kam. “Die “udev”-Regeln betreffen ja etliche unterschiedliche Identifikations-Bezeichnungen. Ich hatte die Namensvergabe daher bislang einfach als Ergebnis einer für mich nicht nachvollziehbaren “udev/systemd”-Logik abgetan.

In der Auseinandersetzung mit virtuellen Bridges bin ich jedoch wieder über das Thema und inzwischen auch über zwei Artikel gestolpert, die das Ganze erklären. Ich möchte an dieser Stelle gerne auf die betreffenden Artikel hinweisen:

Der erste lesenswerte Artikel stammt von Major Hayden, der der Sache mal nachgegangen ist:
https://major.io/2015/08/21/understanding-systemds-predictable-network-device-names/

Besonders hilfreich fand ich in dem zitierten Artikel den Auszug aus “src/udev/udev-builtin-net_id.c”, der einem hilft eine Zugang zu einer Namensvergabe wie “enp8s0” zu finden:

  
 * Two character prefixes based on the type of interface:
 *   en -- ethernet
 *   sl -- serial line IP (slip)
 *   wl -- wlan
 *   ww -- wwan
 *
 * Type of names:
 *   b<number>                             -- BCMA bus core number
 *   ccw<name>                             -- CCW bus group name
 *   o<index>[d<dev_port>]                 -- on-board device index number
 *   s<slot>[f<function>][d<dev_port>]     -- hotplug slot index number
 *   x<MAC>                                -- MAC address
 *   [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>]
 *                                         -- PCI geographical location
 *   [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
 *                                         -- USB port number chain

 
Der zweite Artikel betrifft die Regeln, nach denen “systemd” verschiedene Varianten der Namensgebung bis zu einer endgültigen Entscheidung für ein Netzwerk-Device durchexerziert:

http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/

Ich zitiere aus dem Artikel:

  
The following different naming schemes for network interfaces are now supported by udev natively:

    Names incorporating Firmware/BIOS provided index numbers for on-board devices (example: eno1)
    Names incorporating Firmware/BIOS provided PCI Express hotplug slot index numbers (example: ens1)
    Names incorporating physical/geographical location of the connector of the hardware (example: enp2s0)
    Names incorporating the interfaces's MAC address (example: enx78e7d1ea46da)
    Classic, unpredictable kernel-native ethX naming (example: eth0)

By default, systemd v197 will now name interfaces following policy 1) if that 
information from the firmware is applicable and available, falling back to 2) if that information from the firmware is applicable and available, falling back to 3) if applicable, falling back to 5) in all other cases. Policy 4) is not used by default, but is available if the user chooses so. 

 
Aha – das kann ich nachvollziehen.

Wer sich mit diesem Wissen ausgestattet auch mal ansehen will, was “udev” auf einem System alles an Definitionen für gefundene Netzwerk-Devices hinterlegt, nutze das Kommando

“udevadm info -e”

mit Filtern. Auf meinem Desktop-System etwa finde ich:

   
mytux:~ # udevadm info -e | grep net            
E: ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard)
E: ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
P: /devices/pci0000:00/0000:00:1c.4/0000:08:00.0/net/enp8s0
E: DEVPATH=/devices/pci0000:00/0000:00:1c.4/0000:08:00.0/net/enp8s0
E: ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard)
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/enp8s0 /sys/subsystem/net/devices/enp8s0

E: ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard)
E: ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
P: /devices/pci0000:00/0000:00:1c.5/0000:09:00.0/net/enp9s0
E: DEVPATH=/devices/pci0000:00/0000:00:1c.5/0000:09:00.0/net/enp9s0
E: ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard)
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/enp9s0 /sys/subsystem/net/devices/enp9s0

P: /devices/virtual/misc/network_latency
N: network_latency
E: DEVNAME=/dev/network_latency
E: DEVPATH=/devices/virtual/misc/network_latency
P: /devices/virtual/misc/network_throughput
N: network_throughput
E: DEVNAME=/dev/network_throughput
E: DEVPATH=/devices/virtual/misc/network_throughput
N: net/tun
E: DEVNAME=/dev/net/tun

P: /devices/virtual/net/br0
E: DEVPATH=/devices/virtual/net/br0
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/br0

P: /devices/virtual/net/lo
E: DEVPATH=/devices/virtual/net/lo
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net

P: /devices/virtual/net/virbr0
E: DEVPATH=/devices/virtual/net/virbr0
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/virbr0

P: /devices/virtual/net/virbr0-nic
E: DEVPATH=/devices/virtual/net/virbr0-nic
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/virbr0-nic

P: /devices/virtual/net/virbr1
E: DEVPATH=/devices/virtual/net/virbr1
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/virbr1

P: /devices/virtual/net/virbr1-nic
E: DEVPATH=/devices/virtual/net/virbr1-nic
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/virbr1-nic

P: /devices/virtual/net/vmnet0
E: DEVPATH=/devices/virtual/net/vmnet0
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: INTERFACE=vmnet0
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/vmnet0

P: /devices/virtual/net/vmnet1
E: DEVPATH=/devices/virtual/net/vmnet1
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
nE: INTERFACE=vmnet1
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/vmnet1

P: /devices/virtual/net/vmnet2
E: DEVPATH=/devices/virtual/net/vmnet2
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: INTERFACE=vmnet2
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/vmnet2

 
(Der erfahrene Leser erkennt, dass hier neben physikalischen Devices qemu-Bridges und auch VMware-Bridges aktiv sind. Für entsprechende Devices werden im Zusammenspiel mit den Generatoren der Virtualisierungs-Tools offenbar Standardnamen vergeben, da eindeutige Vendor- oder “geographische” Bus-Bezeichnungen nicht ermittelbar sind.)

Mein Dank gilt den Verfassern der oben genannten Artikel, die zumindest bei mir eine Verständnislücke geschlossen haben.

Vertical gaps resulting from nested inline block elements inside DIV containers – II – getting control

In the first article of this mini series

Vertical gaps resulting from nested inline block elements inside DIV containers – I

we have seen that under certain circumstances (nested) inline-blocks may lead to vertical gaps between themselves and/or between their lower border and the lower bottom of surrounding DIV containers. (Others may describe it as an unexected spacing or margin). We tried to explain this behavior:

If no other precisely defined height scale is given the (min-) height of the innermost inline element – in our case an inline block – will be interpreted as if a letter of a corresponding font-size were present. We have shown that the baseline for letters indeed is set to the bottom of the innermost inline block if no (relatively positioned) contents of the innermost inline block is given.

In this article we want to play around with our example to get an improved understanding and to find options to control and eliminate the vertical gaps. Our example code is:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
<style type="text/css">
	
	html {
		font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
		font-size: 10px;
	}
	
	div {
		position:relative;
		margin:0;
		padding:0;
	}
	
	div.header {
		float:left; 
		min-width: 30.0rem; 
		min-height: 3.2rem; 
		background-color: #DDDDDD;
	}
	
	div.hornav {
		width: auto; 
		min-height: 3.0rem;
		line-height: 3.0rem;
		border: #F00 0.1rem solid;
		background-color: black; 
		text-align: center; 
		
	}
	
	div.hornav_txt {
		display:inline-block; 
		min-width: 20.0rem;  
		min-height:2.8rem;
		line-height: 2.8rem;
		border: solid 0.1rem #FF0; 
		background-color: #FFF;
		text-align:center; 
		
	}
	
	div.mp_cont {
		display:inline-block; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:solid 0.1rem #090;
		background-color:#F00; 
		
	}
	
	div.bg{
		position: absolute; 
		min-width:10.0rem; 
		min-height:2.6rem;
		top:0;
		bottom:0;
		left:0;
		right:0;
		border:0;
		background-color:#FF0;
		opacity: 0.5;
		z-index:1;  
	}
	
	div.fg {
		position: absolute; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:0;
		z-index:2;
	}
	
</style>
</head>

<body>
        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
            </div>
        </div> 
        <div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div><span style="font-size:2.8rem; ">XÜj</span>jjgg
             	</div><span style="color:yellow;">jg</span>
            </div>
        </div>
        <p style="clear:left;" ></p>
</body>
</html>

Test 1 – line-height reduction for the inline-blocks

r
If our explanation of the gap occurrence has some truth in it we should see an effect when we reduce the line-height for the inline-blocks in their containers.
So, let us first try the following:

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt" style="line-height:0;">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
           </div>
        </div> 

 
Now, our innermost inline-block element div.mp_cont gets no line-height in its container, but it still has a min-height. The result is:

nested_inline_blocks_5

Not quite, what we wanted. At least div.mp_cont shrank to the expected height. But, still, there is no defined inline-block height available and the browser still does not know what to do about the inline element div.hornav. Hence the vertical gap between div.hornav_txt and div.hornav.

Now, we go a step further and repeat our approach one tag-level higher:

        <div class="header">
            <div class="hornav" style="line-height:0;>
                <div class="hornav_txt" style="line-height:0;">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
            </div>
        </div>
 

 
We get:

nested_inline_blocks_6

Yes! Now, we have at least found a remedy in case that we need to control the height for inline boxes with empty or absolutely positioned contents. Let us keep this option of line-height reduction in mind!

Test 2 – provide some relatively positioned contents in the innermost inline block

If our gap theory is valid we should provide an innermost inline content with a defined height to get a reasonable vertical adjustment in the end. To achieve this for our example we have to perform 2 steps:

  1. Set the CSS value of “position” for the innermost block element div.fg to “relative” instead of “absolute”.
  2. Put some inline text into div.fg

With the first step we fulfill an objective that we already discussed in our first article:

The innermost contents shall control the height expansion of all containers in a dynamical, flexible way. This is especially important for responsive layouts and CMS systems.

So, lets do the following:

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg" style="position:relative;"></div>
                	</div>
             	</div>
            </div>
        </div> 

 
Note that we eliminated the setting “line-heigt:0;” again. We get:

nested_inline_blocks_7
 
A bit frustrating, isn’t it? We gained nothing compared with our original example. But our finding is still consistent with our theory – we still have no defined height of an innermost inline element.

So, now let us look at a small wonder of CSS: We just add a text element – a tiny harmless letter
“x” –

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg" style="position:relative;">x</div>
                	</div>
             	</div>
            </div>
        </div>
 

 
and, hey, everything gets fixed in the sense of our original expectations.

nested_inline_blocks_8

Intermediate Summary

If we had started with the very reasonable setting of our last test we would never have noted any problem in our example. So, why all the fuss?

One reason is that there are a lot of articles on the Internet about uncontrollable vertical gaps related to inline block elements. Actually these gaps are understandable and controllable.

The other reason is that in templates (e.g. for CMS systems) the (future) contents of inline-blocks may not be known – and the inline block elements may intentionally be left empty. Then we would run into the described situation of our example. The use of nested inline blocks on the other side makes horizontal centering easy if the width of the innermost contents is not really known.

Two options to get control over the gaps
Now, we have seen 2 remedies that can be applied to avoid gaps between (nested) inline blocks and their containers:

  • Set the line-height in each of the container of an inline-block to zero or a sufficiently small value. (But be careful – this may have an impact on other elements on the same HTML/DOM hierarchy level.)
  • Provide an innermost inline (text) element with a defined height (e.g. given by a font-size). This innermost element may be a “&nbsp;”. Ensure that the innermost inline element can have an impact on outer containers via relative (!) positioning.

Of course, you may/must in some situations also combine these options.

Note: The use of a “&nbsp;” may be helpful for (PHP) templates as a dummy contents. Just replace this dummy text during the creation of the real page by real contents – eg. the text of a real menu point.

What about <P>-tags with different font-sizes as the innermost tags?

Ok, up to now we used plain text inside the div.fg-container. What happens when we use

-tags and change the font sizes for these tags? Well, then it may get pretty weird:

A <p> may actually create vertical gaps again – despite the fact there is enough space for it inside its block container div.fg. One of the reasons is that browsers add their own bit of white space to a <p>. This can be controlled by margin- and padding-settings. However, in our case of a a <p> inside inline-blocks even a “margin-top:0; margin-bottom:0;” does not help.

See the following example:

       
	<div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                    <div class="mp_cont" >
                        <div class="bg"></div>
                        <div class="fg">
                            <p style="font-size:1.4rem; margin-top:0rem; margin-bottom:0rem;">Ügj</p>
                        </div>
                    </div>
             	</div>
            </div>
        </div>

 
nested_inline_blocks_9

There is again (!) a small but clearly visible gap at the lower bottom. The inline text-element inside the <p>-tag affects the next inline-block element despite the fact that we have defined normal block element with sufficient vertical space in between. The gap gets bigger with the font-size. See the same example for “font-size:1.6rem;”:

nested_inline_blocks_10

For real CSS2/3 experts this may not be too surprising; for me it was.

So, how can we correct this effect? And what do we have to do, when the font-size of the encapsulated

-tag really exceeds the height and line-height of the surrounding container?

The answers are given in the following final code for our example:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
<style type="text/css">
	
	html {
		font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
		font-size: 10px;
	}
	
	div {
		position:relative;
		margin:0;
		padding:0;
	}
	
	div.header {
		float:left; 
		min-width: 30.0rem; 
		min-height: 3.2rem; 
		background-color: #DDDDDD;
	}
	
	div.hornav {
		width: auto; 
		min-height: 3.0rem;
		line-height: 0rem;
		border: #F00 0.1rem solid;
		background-color: black; 
		text-align: center; 
		margin:0; 
		padding:0;
	}
	
	div.hornav_txt {
		display:inline-block; 
		position:relative; 
		min-width: 20.0rem;  
		min-height:2.8rem;
		line-height: 0rem;
		border: solid 0.1rem #FF0; 
		background-color: #FFF;
		text-align:center; 
		margin:0; 
		padding:0;
	}
	
	div.mp_cont {
		display:inline-block; 
		position: relative; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height: 2.6rem; 
		border:solid 0.1rem #090;
		background-color:#F00;
		margin:0; 
		padding:0;
	}
	
	div.bg{
		position: absolute; 
		min-width:10.0rem; 
		/* line below not required */
		/*min-height:2.6rem;*/
		top:0;
		bottom:0;
		left:0;
		right:0;
		border:0;
		background-color:#FF0;
		opacity: 0.5;
		z-index:1;  
	}
	
	div.fg {
		position:relative; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:transparent 0.0rem solid;
		padding:0;
		margin:0;
		z-index:2;
	}
	
</style>
</head>

<body>
        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div 
class="fg"> </div>
                	</div>
             	</div>
	    </div>
        </div> 
       <div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg"><p style="font-size:1.8rem; margin-top:0rem; margin-bottom:0rem;">Ügj</p></div>
		  	</div>
                </div>
	    </div>
        </div>
        <p style="clear:left;"></p>
        <div class="header" style="margin-left:0px; margin-top:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height: normal;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
        <div class="header" style="margin-left:50px; margin-top:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height:normal;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Ügj<br>Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
        <p style="clear:left;"></p>
        <div class="header" style="margin-left:50px; margin-top:50px; min-width: 50.0rem; ">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height:normal; padding-left:1.0rem; padding-right:1.0rem;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Hallo Hallo<br>Hallo Hallo Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
</body>
</html>

 
What did we change ?

The first thing you may note is that in the CSS definitions we used our first trick:
We explicitly set the line-heights for div.hornav and div.hornav_txt to zero.

The second important point is that when the font gets too large we must adjust the line-height for the container div.fg. We use “line-height:normal;” to adjust the line-height to the height of the included contents.

So, in case you should build a CMS in which the user can choose font-sizes by himself this is something you have to take care about. You may provide a standard setting; but if in situations like in our example case the font size of a <P>-tag exceeds defined limits our page creation program has to modify style settings explicitly to guarantee a flexible height adjustment.

The result is shown in following picture:

nested_inline_blocks_12

This gives us exactly what we wanted:

  • Easy horizontal centering via the use of (nested) inline block elements (even if the content length gets
    bigger than some threshold).
  • Vertical flexibility – even if we work with font-sizes bigger than a threshold.
  • NO VERTICAL GAPS between (nested) containers of inline-block elements.

It is easy to see how we can use the discussed elements in templates to create horizontally centered menus with menu points for which the horizontal and vertical size is not known.

CSS is a bit weird sometimes – but fun, too!

PHP and web application security – bad statistics and wrong conclusions

Sometimes I have discussions with developers of a company working mainly with Java. As I myself sometimes do development work with PHP I am regarded more or less as a freak in this community. Typical arguments evolve along the lines:

“PHP does not enforce well structured OO code, no 4-layer-architecture built in, problems with scalability”, etc…. I do not take these points too seriously. I have seen very badly structured Java OO code, one can with proper techniques implement web services in a kind of logical 3rd layer on special servers and Facebook proves PHP scalability (with some effort), etc…

What is much more interesting for me these days is the question how security aspects fit into the use of different programming languages. And here comes the bad news – at least according to statistics published recently by the online magazine Hacker News – see
http://thehackernews.com/2015/12/programming-language-security.html

The statistics on OWASP 10 vulnerability types of the investigated PHP code looks extremely bad there – compared to the investigated Java code examples. I admit that this is an interesting result for hackers and that it is somewhat depressing for security aware PHP developers.

However, is this the bad statistics the fault of the programming language?

I doubt it – despite the fact that the named article recommends to “Choose Your Scripting Language Wisely”. I would rather recommend: Educate your PHP developers properly and regularly, implement a proper quality assurance with special security check steps based on vulnerability scanning tools and invest in regular code reviews. Why?

The investigation revealed especially large deviations in the fields of XSS, SQL-injection, command injection (major elements on the OWASP 10 list). The countermeasures against the named attack vectors for PHP are all described in literature and very well known (see e.g. the books “PHP Sicherheit” of C.Kunz, S.Esser or “Pro PHP Security” of Snyder, Myer, Southwell). One of the primary key elements of securing PHP applications against attacks of the named types is a thorough inspection, analysis and correction treatment of submitted GET/POST-parameters (and avoidance of string parameters wherever possible). Never trust any input and escape all output! Define exceptions wisely and rewrite sensitive string elements according to your rules. Check whether input really comes from the right origin – e.g. your own domain, etc., etc.

Whether all relevant security measures are implemented in the PHP code of a web application has therefore more to do with the mentality, technical ability, the knowledge and on the negative side with the laziness of the programmer than with the programming language itself. As at least the technical capability is a matter of education, I conclude:

Tests regarding type, value range, length and of course tests of the contents of received string variables and e.g. image source references plus sanitizing/elimination/deactivation of problematic contents as well as the proper use of respective available library functions for such tests should be part of regular PHP training programs. In addition the use of web application scanning tools like OWASP’s ZAP scanner or the Burp Suite Pro (if you have money to afford the latter) should be trained. Such tools should become part of the QA chain. As well as educated penetration testers with the perspective of the attacker …. The money a SW-company invests for such educational measures is well invested money.
See for the significant impact of education e.g.:
https://seclab.stanford.edu/websec/scannerPaper.pdf

I would regard the statistical results discussed in the “Hacker News” article much more conclusive if we were provided additional information about
the type of applications analyzed and also the size and type of the companies behind the application development. And whether and what type of QA efforts were used. This would give a much better indication of why the Java code showed more quality regarding the prevention of OWASP 10 attacks. One reason could e.g. be that Java applications very often are developed for enterprise purposes – and bigger companies typically invest more time and effort into QA …

So, another valid interpretation of the presented statistics could be that the QA for typical PHP web application SW is on average worse than for Java SW. I admit that such a finding would also be very interesting – but it does not prove that one cannot write secure Web applications with PHP or that the production of secure code is for whatever reasons easier with Java.

In addition the presented number of bugs per MB itself is questionable: if you only look at 3 bad PHP examples and 1 good Java example you may get the same type statistics – but it would be totally meaningless. The distribution of PHP-, Java-, JS-code etc. among the statistical sample is, however, nowhere discussed in the named article – neither in number of applications nor in MB percentages.

Therefore: Without further information the implied conclusion that already the proper choice of a web scripting language would help to improve security of web applications appears is misleading.

To improve the mood of PHP developers having read the article in “Hacker News”: Have a look at the results of a similar investigation presented at this link
http://info.whitehatsec.com/rs/whitehatsecurity/images/statsreport2014-20140410.pdf

See also:
https://blog.whitehatsec.com/a-security-experts-thoughts-on-whitehat-securitys-2014-web-stats-report/
https://www.scriptrock.com/blog/which-web-programming-language-is-the-most-secure
https://threatpost.com/security-begins-with-choice-of-programming-language/105441/

It may help, really !

[But keep in mind: Only trust statistics you have manipulated yourself.]