Donnerstag, 25. Juli 2013

Bereitstellung von Webserverdiensten (http, https) auf einer virtuellen Maschine mit nginx als Reverse Proxy (Teil 1)

Problembeschreibung

Ein Serversystem mit öffentlicher IP (200.200.200.200) virtualisiert mehrere Gast(Server)systeme welche HTTP, MAIL, SVN(SSH) usw. bereitstellen. Als Virtualisierungslösung verwenden wir kvm mit libvirt. Als Netzwerkmodus wird "nat" verwendet, da die Gäste keine öffentlichen Adressen bekommen sollen. Die Standardkonfiguration lässt in diesen Modus die Kommunikation des Gasts nach aussen hin zu (MASQUERADING), schirmt diesen jedoch beim Zugriff auf ihn selbst ab.
Wir haben auf den Gastsystem (192.168.100.10, 192.168.100.11) bereits HTTP Server (lighttp, apache) eingerichtet. Diese warten auf Port 80 auf eingehende Anfragen. Die Webserver sollen über www.mindfab.net (192.168.100.10) und über cms.mindfab.net (192.168.100.11) erreichbar sein. Dieses Problem lässt sich mit einfachen Portforwarding nicht lösen, da wir in Abhängigkeit von dem Servernamen (www.mindfab.net oder cms.mindfab.net) auf unterschiedliche IP-Adressen (http Server) zugreifen. Als Lösung dieses Problems bietet sich nginx, ein open source web Server und reverse proxy Server an.

Lösungsansatz

Als erstes sollten wir sicherstellen das unsere HTTP Server auf den Gästen ordnungsgemäss funktionieren und über die entsprechenden IP Adressen erreichbar sind. Dies kann man bspw. mit lynx überprüfen. Von unseren Host System aus überprüfen wir also folgendes...
lynx http://192.168.100.10
lynx http://192.168.100.11
Bei beiden Kommandos sollte die "default" Webseite erscheinen.

Als nächstes installieren wir nginx auf unseren Host...

apt-get install nginx
und beginnen mit der entsprechenden Konfiguration.

vim /etc/nginx/conf.d/default.conf
## 192.168.100.10 -> www.mindfab.net ##
upstream wwwmindfabnet  {
      server 192.168.100.10:80;
}
 
## 192.168.100.11 -> cms.mindfab.net ##
upstream cmsmindfabnet  {
      server 192.168.100.11:80;
}
 
## Start www.mindfab.net ##
server {
    listen       200.200.200.200:80;
    server_name  www.mindfab.net;
 
    access_log  /var/log/nginx/www.mindfab.net.access.log;
    error_log  /var/log/nginx/www.mindfab.net.error.log;
    root   /usr/share/nginx/www;
    index  index.html index.htm;
 
    ## send request back to www.mindfab.net ##
    location / {
     proxy_pass  http://wwwmindfabnet;     
     proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
     proxy_redirect off;
     proxy_buffering off;
     proxy_set_header        Host            $host;
     proxy_set_header        X-Real-IP       $remote_addr;
     proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
   }
}
## End www.mindfab.net ##
 
## START cms.mindfab.net ##
server {
   listen      200.200.200.200:80;
   server_name cms.mindfab.net;
   access_log  /var/log/nginx/cms.mindfab.net.access.log;
   error_log   /var/log/nginx/cms.mindfab.net.error.log;
   root        /usr/local/nginx/www;
   index       index.html;
 
   location / {
        proxy_pass  http://cmsmindfabnet;        
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_set_header        Host            cms.mindfab.net;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
## END cms.mindfab.net  ##
 Nun starten wir den Daemon neu und testen anschliessend den Reverse Proxy.
service nginx restart
lynx http://www.mindfab.net # sollte uns den Inhalt des Webservers auf 192.168.100.10 anzeigen
lynx http://cms.mindfab.net # sollte uns den Inhalt des Webservers auf 192.168.100.11 anzeigen
Anmerkung: Selbstverständlich müssen wir sicherstellen das www.mindfab.net und cms.mindfab.net beide auf die IP Adresse 200.200.200.200 auflösen. Dies können wir durch eine entsprechende Konfiguration unseres DNS Servers erreichen. Für Testzwecke kann man aber auch zwei Einträge in seine /etc/hosts Datei anlegen.

Im 2.Teil werden wir unseren Reverse Proxy um SSL erweitern.

Referenzen:

  • http://www.cyberciti.biz/tips/using-nginx-as-reverse-proxy.html

Freitag, 19. Juli 2013

Portforwarding an Guest VM unter KVM mit libvirt und iptables

Problembeschreibung

Ein Serversystem mit öffentlicher IP virtualisiert mehrere Gast(Server)systeme welche HTTP, MAIL, SVN(SSH) usw. bereitstellen. Als Virtualisierungslösung verwenden wir kvm mit libvirt. Als Netzwerkmodus wird "nat" verwendet, da die Gäste keine öffentlichen Adressen bekommen sollen. Die Standardkonfiguration lässt in diesen Modus die Kommunikation des Gasts nach aussen hin zu (MASQUERADING), schirmt diesen jedoch beim Zugriff auf ihn selbst ab. Dieses Problem können wir durch den gezielten Einsatz von Einträgen in den PREROUTING und FORWARDING Bereichen von iptables lösen.

Im folgenden heisst unser Gastsystem "ssh-server" und hat die Gast-IP im privaten Subnetz von "192.168.100.10". Auf den Gast läuft ein SSH Server auf Port 22 dieser soll von aussen über Port 10000 über die öffentliche IP erreichbar sein.

Lösungsansatz

Libvirt  unterstützt seit Version 0.8.0 hook Scripte. Diese werden bspw. beim starten/beenden/usw. des libvirt deamons oder auch beim starten/beenden/usw. von virtuellen Maschinen über qemu aufgerufen. Diese Funktionalität werden wir nutzen um die Gast spezifischen Firewall regeln zu realisieren.

Als erstes erstellen wir also ein qemu hook Script und machen dies ausführbar.

touch /etc/libvirt/hooks/qemu
chmod +rx /etc/libvirt/hooks/qemu
Libvirt  ruft dann später das Script u.a. beim Starten und Stoppen von Gästen mit folgenden Parametern auf
  1. Objekt (z.B. Name des Gasts)
  2. Operation (z.B. start, stopped, reconnect, etc.)
  3. Unter-Operation oder "-"
  4. Extra-Argument oder "-"
Eine umfangreiche Dokumentation über Hook Scripte und deren Argumente findet sich auf der libvirt Webseite http://www.libvirt.org/hooks.html.

Wir editieren nundas Script folgendermassen.

#!/bin/bash

Guest="ssh-server"
Guest_ip="192.168.100.10"

if [[ $1 = $Guest ]]
then
        if [[ $2 = "stopped" || $2 = "reconnect" ]]
        then
                iptables -t nat -D PREROUTING -p tcp --dport 10000 -j DNAT --to $Guest_ip:22
                iptables -D FORWARD -d $Guest_ip/32 -p tcp -m state --state NEW -m tcp \
                --dport 22 -j ACCEPT
        fi
        if [[ $2 = "start" || $2 = "reconnect" ]]
        then
                iptables -t nat -I PREROUTING -p tcp --dport 10000 -j DNAT --to $Guest_ip:22
                iptables -I FORWARD -d $Guest_ip/32 -p tcp -m state --state NEW -m tcp \
                --dport 22 -j ACCEPT
        fi
fi
Nun stoppen wir ggf. den Gast und machen das Script durch Neustarten des libvirt Deamons bekannt.
virsh destroy ssh-server
/etc/init.d/libvirt-bin restart
virsh start ssh-server
Wenn alles funktioniert hat sollten keinerlei Fehlermeldungen im Log auftauchen und die iptables die entsprechenden Regeln enthalten.
tail -f /var/log/libvirt/libvirtd.log

iptables -L
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination        
ACCEPT     tcp  --  anywhere             192.168.100.10       state NEW tcp dpt:ssh

iptables -L -t nat

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination        
DNAT       tcp  --  anywhere             anywhere             tcp dpt:10000 to:192.168.100.10:22

Referenzen

  • http://www.jimscode.ca/index.php/component/content/article/19-linux/142-linux-port-forwarding-to-guest-libvirt-vms
  • http://www.libvirt.org/hooks.html