Das hier beschriebene Tutorial zeigt anhand eines Beispiels, wie ihr ein etwas komplizierteres Routing nutzen koennt. In diesem Beispiel soll ein Client ins Internet gelangen, indem er ueber einen lokalen Server weiter an einen entfernten Server geleitet wird und dieser wiederrum die Anfragen weiter an das Internet leitet. Dies soll natuerlich bis zu dem entfernten Server auf sichere Weise geschehen. Eine etwas ausfuehrlichere Beschreibung und mehrere Beispiele findet ihr unter lartc.org/lartc.html.
Einige Voraussetungen stellt dieses Tutorial schon.
* ihr solltet wissen wie man ein Standardgateway einstellt
* ein wenig Erfahrung im Umgang mit Linux haben (ssh Nutzung etc.)
* die Kreativitaet das hier gelesene im eigenem Netzwerk umzusetzen ;)
Das Netzwerk ist in zwei Teile aufgeteilt, einmal dem lokalen Netzwerk und dem entfernten Netzwerk. Das entfernte Netzwerk besteht aus einem einzigen Computer, der eine direkte Anbindung an das Internet hat. Dieser ist mit der IP xxx.xxx.xxx.xxx erreichbar. Das lokale Netzwerk besteht aus einem Client (mit der IP 192.168.0.103), einem lokalen Server (mit der IP 192.168.0.2) und einem Router (mit der IP 192.168.0.1). Der lokale Server hat mittels OpenVPN einen VPN-Tunnel mit dem entfernten Server aufgebaut. Diese beiden Computer koennen sich durch diesen Tunnel ueber 10.0.0.2 (lokaler Server) und 10.0.0.1 (entfernter Server) erreichen.
Der Client wird in diesem Aufbau als Standardgateway den lokalen Server nutzen um ins Internet zu gelangen. Die Schwierigkeit bei dem Routen besteht hierbei, dass der lokale Server einmal einen Standardgateway ins Internet braucht (also zu dem Router 192.168.0.1) um das VPN mit dem entfernten Server aufzubauen, und er muss wissen, dass er alle Anfragen die von dem Client ans Internet gerichtet sind nicht an den Router weitergeben soll, sondern ueber den VPN-Tunnel zu dem entfernten Server und dieser wiederrum in das Internet.
Zunaechst muessen wir OpenVPN einrichten und einen Tunnel zwischen dem lokalen Server und dem entfernten Server aufbauen. Zuerst fangen wir mit dem entfernten Server an (auf ihm befindet sich Debian). Dort installieren wir nun OpenVPN:
ENTFERNTERSERVER:~# apt-get install openvpn
Dannach muesst ihr die Einstellungen fuer diese OpenVPN Verbindung vornehmen. Diese erstellen und speichern wir in dem Ordner /etc/openvpn.
ENTFERNTERSERVER:~# cd /etc/openvpn
ENTFERNTERSERVER:/etc/openvpn# touch testtunnel.conf
In die Datei testtunnel.conf wird dies hier eingetragen:
dev tun
ifconfig 10.0.0.1 10.0.0.2
secret static.key
proto udp
port 1194
verb 3
status /var/log/openvpn-testtunnel.log
Da OpenVPN hier nur ein Mittel zum Zweck ist, gehe ich nicht weiter auf die hier benutzten Einstellungen ein. Diese koennt ihr selber im Internet nachlesen, z.B. unter sarwiki.informatik.hu-berlin.de/OpenVPN_(deutsch).
Wenn wir dieses erledigt haben, brauchen wir allerdings noch einen Key fuer die VPN-Verbindung. Dazu fuehren wir dieses hier aus:
Nun haben wir die Konfiguration auf dem Debian System fast abgeschlossen. Jetzt muessen wir das System nur noch so einstellen, dass OpenVPN diese Verbindung auch nach einem Neustart aufbaut. Dazu muessen wir die Datei /etc/default/openvpn editieren:
AUTOSTART="testtunnel"
Dies muss in die Datei eingetragen werden, damit OpenVPN beim Starten auch diese Verbindung aufbaut.
Nun konfigurieren wir OpenVPN auf dem lokalen Server. Auf diesem Server befindet sich ein OpenSUSE 10.2. Hier installieren wir nun auch OpenVPN ueber Yast (dies brauche ich wohl nicht weiter zu erklaeren) und erstellen wieder die Datei testtunnel.conf in /etc/openvpn.
LOKALERSERVER:~$ cd /etc/openvpn
LOKALERSERVER:/etc/openvpn# touch testtunnel.conf
In diese Datei wird nun dieses hier eingetragen:
remote ENTFERNTERSERVER 1194
proto udp
dev tun
ifconfig 10.0.0.2 10.0.0.1
secret static.key
verb 3
status /var/log/openvpn-testtunnel.log
Nun brauchen wir auch noch den auf dem entfernten Server erstellten Key. Ich ueberlasse es euch wie ihr ihn uebertragt. Wenn ihr nun den Key in der Datei static.key in dem Ordner /etc/openvpn habt, brauchen wir bei OpenSUSE nun nichts weiter machen. OpenVPN fuehrt automatisch alle Verbindungen in dem Ordner /etc/openvpn beim Neustart aus.
Jetzt brauchen wir nur noch bei beiden Computern OpenVPN neuzustarten. Beachtet allerdings hierbei, dass auf dem Router 192.168.0.1 im lokalen Netzwerk, der Port 1194 udp freigegeben sein muss.
Jetzt koennen wir noch testen ob wir von dem entfernten Server den lokalen Server anpingen koennen:
TUX@ENTFERNTERSERVER:~$ ping 10.0.0.2 -c4
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=63.1 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=55.8 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=56.8 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=56.4 ms
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3009ms
rtt min/avg/max/mdev = 55.838/58.049/63.127/2.956 ms
Sehr gut, wir haben nun einen VPN-Tunnel zwischen dem lokalen Server und dem entfernten Server aufgebaut.
So, nun da wir das VPN zwischen dem lokalen Server und dem entfernten Server eingerichtet haben, wollen wir natuerlich auch das Routing einrichten. Da es sich bei dem entfernten Server um ein Debian System handelt, muessen wir nun noch folgenden Eintrag bearbeiten, damit das IP-forwarding auch nach einem Neustart funktioniert. In der Datei /etc/network/options muss dies hier eingetragen werden (oder zumindest von "no" auf "yes" gestellt werden).
ip_forward=yes
Damit wir nicht neustarten muessen, um das IP-forwarding zu aktivieren, geben wir diesen Befehl ein:
Beachtet, dass das IP-forwarding auf jedenfall nach einem Neustart aktiviert sein muss, um das Routing durchzufuehren. Solltet ihr kein Debian System an dieser Stelle haben und die Datei /etc/network/options existiert nicht, dann tragt den Befehl zur manuellen Ausfuehrung in das folgende Skript mit ein.
Nun benoetigen wir noch ein Skript, welches die Routing Regeln und die Firewall bei jedem Neustart richtig einstellt. Dazu erstellen wir auf dem entfernten Server eine Datei in den Ordner /etc/init.d
ENTFERNTERSERVER:~# cd /etc/init.d
ENTFERNTERSERVER:/etc/init.d# touch advanced-routing
Wenn ihr die Datei erstellt habt, tragt ihr dieses in die Datei ein:
#!/bin/bash
case "$1" in
start)
echo "configure meinlokales routing..."
route add -net 192.168.0.0/24 gw 10.0.0.2
Dies ist eine ganz Normale Case-Anweisung, die nur auf den Parameter "start" die Befehle ausfuehrt.
route add -net 192.168.0.0/24 gw 10.0.0.2 sagt dem entfernten Server, dass er das Netz 192.168.0.0/24 durch das Gateway 10.0.0.2 erreicht. Somit schickt er alle Beantwortungen der Anfragen, die vom lokalen Netzwerk kommen und ihn erreichen, durch den Tunnel zurueck an den lokalen Server.
iptables -A POSTROUTING -t nat -j MASQUERADE -o eth0 -s 192.168.0.0/24 weist den entfernten Server an, alle ausgehenden Pakete zu maskieren. Somit ersetzt er die Source IP Adresse der Pakete mit seiner eigenen, so das die Antworten auf die Anfragen an ihn zurueck kommen. Er demaskiert diese Antworten wieder, indem er nun die Destination IP Adresse mit der alten Source IP Adresse ersetzt und die Pakete an diese IP weiterschickt. Falls ihr weitere Moeglichkeiten von iptables wissen wollt oder mal genauer nachlesen wollt, koennt ihr es unter www.netfilter.org tuen.
Damit der entfernte Server dieses Skript auch beim Starten ausfuehrt, muessen wir dieses noch fuer die einzelnen Runlevel eintragen. Da es nur gestartet werden muss, nicht gestoppt werden brauch, werden wir es nur fuer das Starten eintragen.
Da nun der entfernte Server eingerichtet ist, muessen wir den lokalen Server einstellen. Dieses ist komplizierter. Als erstes muessen wir einen neuen table in der Datei /etc/iproute2/rt_tables eintragen. Hier fuegt ihr dieses hinzu:
200 TESTTABLE
Wenn ihr dieses getan habt, erstellt ihr genau wie auf dem entfernten Server eine Datei in dem Verzeichnis /etc/init.d:
LOKALERSERVER:~# cd /etc/init.d
LOKALERSERVER:/etc/init.d# touch advanced-routing
In diese Datei schreibt ihr diesen Inhalt:
#!/bin/bash
case "$1" in
start)
echo "allow ip forwarding..."
echo 1 > /proc/sys/net/ipv4/ip_forward
echo "configure meinlokales routing..."
ip route add default via 10.0.0.1 dev tun0 table TESTTABLE
#hier die einzelnen Clients eintragen, die durch den Tunnel geroutet werden sollen
ip rule add from 192.168.0.103 table TESTTABLE
echo "configure IPtables..."
iptables -A INPUT -i tun0 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -i tun0 -j ACCEPT
iptables -A INPUT -i tun0 -j REJECT
;;
*)
echo "only use /etc/init.d/advanced-routing start"
;;
esac
exit 0
Dieses ist, wie auf dem entfernten Server, eine ganz normale Case-Anweisung, die nur auf dem Parameter "start" hin die Befehle ausfuehrt.
echo 1 > /proc/sys/net/ipv4/ip_forward sagt dem lokalen Server, dass er das IP-forwarding aktivieren soll. Dieses hier waere eine Moeglichkeit die ihr Nutzen koennt um es zu aktivieren, wenn ihr keine /etc/network/options wie bei Debian habt.
ip route add default via 10.0.0.1 dev tun0 table TESTTABLE dieses hier ist die einzige Regel, die in den table TESTTABLE eingetragen wird. Diese Regel besagt, dass die default route ueber das Gateway 10.0.0.1 (also der entfernte Server) und durch das Device tun0 (dieses ist unser Tunnel-Device von OpenVPN) gehen soll.
ip rule add from 192.168.0.103 table TESTTABLE hiermit fuegen wir die Regel hinzu, dass alle Anfragen von 192.168.0.103 (unserem CLIENT) den table TESTTABLE durchlaufen soll und die dort aufgefuehrten Regeln nutzen soll. Hierbei ist es sehr wichtig, dass jeder CLIENT (falls ihr mehrere verwendet) einen eigenen Eintrag in dem Skript bekommt. Ihr muesst dafuer ja nur die IP Adresse immer aendern. Man koennte es theoretisch auch so machen, dass der lokale Server fuer das ganze Netz 192.168.0.0/24 die Regeln aus dem table TESTTABLE nutzt, dadurch wuerden wir aber unseren lokalen Server im lokalen Netzwerk nicht mehr erreichen. Deshalb duerfen die Regeln in dem table TESTTABLE nie fuer den lokalen Server (192.168.0.2) angewendet werden.
iptables -A INPUT -i tun0 -m state --state ESTABLISHED,RELATED -j ACCEPT laesst nur Verbindungen zu, die vom lokalen Server aufgebaut werden oder schon aufgebaut sind. Dadurch verhindern wir, dass man von dem entfernten Server durch den VPN-Tunnel auf den lokalen Server zugreift. Sollte es wirklich mal sein, dass der entfernte Server uebernommen wird, haben wir so wenigstens noch das lokale Netzwerk geschuetzt.
iptables -A INPUT -p icmp -i tun0 -j ACCEPT dieses beantwortet Pings die durch den VPN-Tunnel kommen. Da OpenVPN ab und zu mit einem Ping ueberprueft, ob die Verbindung noch besteht, muessen wir diese zulassen. Auch so ist es gut, wenn man mal an dem VPN-Tunnel arbeiten muss, ueberpruefen zu koennen, ob die beiden Server sich erreichen koennen.
iptables -A INPUT -i tun0 -j REJECT verhindert alle uebrigen Verbindungen, die durch den VPN-Tunnel kommen. Die Reihenfolge der drei iptables Regeln sind wichtig, deshalb sollte diese Reihenfolge nicht veraendert werden. Iptables handelt die Regeln von oben her ab und als letztes trifft diese Regel hier zu, die alle Verbindungen durch den VPN-Tunnel verbietet.
Natuerlich sollten wir hier auch nicht vergessen, dass das Skript auch executable ist:
Damit der lokale Server dieses Skript auch beim Starten ausfuehrt, muessen wir dieses noch fuer die einzelnen Runlevel eintragen. Dieses koennt ihr wie bei dem entfernten Server machen oder (weil es sich bei dem lokalen Server um einen OpenSUSE 10.2 System handelt) ihr stellt es im Runlevel Editor in Yast ein.
Wenn wir dieses getan haben, sind wir auch schon fertig mit den Einstellungen am lokalen Server. Jetzt muessen wir nur noch einmal das Skript manuell starten, damit wir nicht neustarten muessen:
Nun wird auf dem CLIENT (192.168.0.103) der Standardgateway auf 192.168.0.2 (also den lokalen Server) gestellt. Als Nameserver koennt ihr eure standard Nameserver von eurem Anbieter verwenden.
Nun testen wir die Verbindung ins Internet von unserem CLIENT aus.
CLIENT:~# traceroute heise.de
traceroute to heise.de (193.99.144.80), 30 hops max, 40 byte packets
1 192.168.0.2 (192.168.0.2) 0.172 ms 0.112 ms 0.194 ms
2 10.0.0.1 (10.0.0.1) 56.634 ms 58.987 ms 59.120 ms
3 fragw.gatewayrouter.net (84.16.224.1) 58.286 ms 57.434 ms 56.740 ms
4 89-149-218-25.internetserviceteam.com (89.149.218.25) 55.675 ms 56.477 ms 55.980 ms
5 r7-r5.gatewayrouter.net (89.149.218.2) 56.262 ms 55.688 ms 56.415 ms
6 de-cix.ffm.plusline.net (80.81.192.132) 61.327 ms 58.383 ms 58.600 ms
7 heise1.f.de.plusline.net (213.83.46.195) 59.617 ms 57.574 ms 60.065 ms
8 heise1.f.de.plusline.net (213.83.46.195)(N!) 59.722 ms * *
Wir sehen hier ganz genau, dass unser erster hop (also unsere erste Station auf dem Weg zu heise.de)
1 192.168.0.2 (192.168.0.2) 0.172 ms 0.112 ms 0.194 ms
unser lokaler Server ist. Bei dem naechsten hop
2 10.0.0.1 (10.0.0.1) 56.634 ms 58.987 ms 59.120 ms
handelt es sich um unseren entfernten Server, der durch den VPN-Tunnel erreicht wird. Somit laufen unsere Pakete auch genau den Weg zu heise.de, den wir benutzen wollen. Allerdings zeigt uns dieser Test nicht, auf welchem Weg die Pakete zurueckkommen. Um das zu testen, nutzen wir auf dem entfernten Server das Programm tcpdump. Wir geben auf dem entfernten Server diesen Befehl ein:
ENTFERNTERSERVER:~# tcpdump -ni tun0 icmp
und auf unserem CLIENT fuehren wir einen Ping nach heise.de aus:
TUX@CLIENT:~$ ping heise.de -c4
PING heise.de (193.99.144.80) 56(84) bytes of data.
64 bytes from redirector.heise.de (193.99.144.80): icmp_seq=1 ttl=249 time=65.4 ms
64 bytes from redirector.heise.de (193.99.144.80): icmp_seq=2 ttl=249 time=60.9 ms
64 bytes from redirector.heise.de (193.99.144.80): icmp_seq=3 ttl=249 time=59.5 ms
64 bytes from redirector.heise.de (193.99.144.80): icmp_seq=4 ttl=249 time=60.7 ms
--- heise.de ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3009ms
rtt min/avg/max/mdev = 59.511/61.675/65.465/2.277 ms
Nun schauen wir uns die Ausgabe an, die uns tcpdump durch den Ping geliefert hat:
tcpdump: WARNING: arptype 65534 not supported by libpcap - falling back to cooked socket
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type LINUX_SLL (Linux cooked), capture size 96 bytes
13:16:44.076582 IP 192.168.0.103 > 193.99.144.80: ICMP echo request, id 47659, seq 1, length 64
13:16:44.079567 IP 193.99.144.80 > 192.168.0.103: ICMP echo reply, id 47659, seq 1, length 64
13:16:45.081769 IP 192.168.0.103 > 193.99.144.80: ICMP echo request, id 47659, seq 2, length 64
13:16:45.083353 IP 193.99.144.80 > 192.168.0.103: ICMP echo reply, id 47659, seq 2, length 64
13:16:46.083665 IP 192.168.0.103 > 193.99.144.80: ICMP echo request, id 47659, seq 3, length 64
13:16:46.085946 IP 193.99.144.80 > 192.168.0.103: ICMP echo reply, id 47659, seq 3, length 64
13:16:47.085493 IP 192.168.0.103 > 193.99.144.80: ICMP echo request, id 47659, seq 4, length 64
13:16:47.087220 IP 193.99.144.80 > 192.168.0.103: ICMP echo reply, id 47659, seq 4, length 64
Der ICMP request zeigt, dass eine Anfrage von der IP 192.168.0.103 (unserem CLIENT) an die IP 193.99.144.80 (heise.de) durch den VPN-Tunnel gekommen ist. Und der ICMP reply zeigt, dass der entfernte Server die Antwort die von 193.99.144.80 (heise.de) gekommen ist an 192.168.0.103 (unserem CLIENT) durch den VPN-Tunnel zurueck schickt. Nun kann man deutlich erkennen, dass alle Anfragen ins Internet von unserem CLIENT durch den VPN-Tunnel geschickt werden. Das Routing ist nun fertig eingerichtet.
Falls wir nun einen weiteren Client dem Routing hinzufuegen wollen, ist nicht viel arbeit zu tun. Als erstes, muessen wir unserem neuen CLIENT2 mit der IP 192.168.0.101 als Standardgateway unseren lokalen Server (192.168.0.2) einstellen. Wenn wir dieses getan haben, fuegen wir dem Skript advanced-routing auf dem lokalen Server (192.168.0.2) eine neue Zeile hinzu:
[...]
#hier die einzelnen Clients eintragen, die durch den Tunnel geroutet werden sollen
ip rule add from 192.168.0.103 table TESTTABLE
ip rule add from 192.168.0.101 table TESTTABLE
[...]
ip rule add from 192.168.0.101 table TESTTABLE ist die neue Zeile die wir hinzufuegen muessen. Dieses laesst sich beliebig fortfuehren, mit allen Clients die diese Route nutzen sollen. Da wir den lokalen Server aber nicht neustarten wollen, um das Routing zu nutzen, fuehren wir diese Zeile einmal manuell aus:
LOKALERSERVER:~# ip rule add from 192.168.0.101 table TESTTABLE
Nun nutzt CLIENT2 (192.168.0.101) auch unsere eingerichtete Route.