Layer 3 VPN mit OpenSSH

OpenSSH ist in der Lage über Tun-Interfaces ein Layer 3 VPN herzustellen. Dies ist eine sehr einfache Variante, besonders schön ist das man SSH auch in vielen Fällen über einen Proxy verwenden kann.

Szenario

Aufbau:

  • Firewall (intern: 10.10.0.254, extern: 55.66.77.88, macht PAT über externes Interface)
  • SSHVPN VM ist eine VMware Maschine auf meinem VMware Server (intern: 10.10.0.122, tun0: 10.255.255.1, macht PAT über internes Interface)
  • Mein PC (Ubuntu 10.10 Maschine mit NX intern: 10.10.0.80)
  • Mein Netbook in dem Beispiel irgendwo im Internet mit UMTS Stick (Ubuntu 10.10 Netbook Edition)
  • Auf der Firewall ist ein NAT Portforwarding für TCP Port 22 auf 10.10.0.122 eingerichtet

Der Client

Falls noch kein Schlüsselpaar für root existiert muss eines angelegt werden.

toor@sshvpn-vm:~$ sudo ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/root/.ssh/id_rsa.
Your public key has been saved in /home/root/.ssh/id_rsa.pub.
The key fingerprint is:
05:d2:94:2e:16:fd:96:29:5c:6d:13:b5:55:8f:ce:31 root@sshvpn-vm
The key's randomart image is:
+--[ RSA 2048]----+
|      .+o. ..o. +|
|      ..+.. +  +.|
|       + o.+ .E .|
|      o +.=  o o |
|     . .So    o  |
|                 |
|                 |
|                 |
|                 |
+-----------------+
toor@sshvpn-vm:~$

Am einfachsten ist es ohne Passwort. Meine Empfehlung: Unbedingt mit Passwort machen, ist sicherer 😉 Den Namen unverändert lassen, auch den Speicherort. id_rsa wird im .ssh Verzeichnis beim Verbindungsaufbau vom ssh Client automatisch verwendet sofern vorhanden.

Anpassen der /etc/network/interfaces Datei

Folgenden Absatz an das Ende der Datei anfügen.

iface tun0 inet static
      pre-up ssh -S /var/run/sshvpn-tun-ctrl -M -f -w 0:0 55.66.77.88 true
      pre-up sleep 5
      address 10.255.255.2
      pointopoint 10.255.255.1
      netmask 255.255.255.252
      up route add -net 10.10.0.0 netmask 255.255.255.0 gw 10.255.255.1 tun0
      post-down ssh -S /var/run/sshvpn-tun-ctrl -O exit 55.66.77.88

Der Server

Anpassen der /etc/ssh/sshd_config Datei

Hinzufügen / Anpassen folgender Optionen

PermitTunnel point-to-point
PermitRootLogin forced-commands-only

PermitRootLogin existiert bei Ubuntu Systemen (steht auf Yes) schon bei anderen Distris kann das anders sein. Falls die PermitRootLogin existiert die orginale einfach auskommentieren.

Anpassen der /root/.ssh/authorized_keys Datei

tunnel="0",command="/sbin/ifdown tun0;/sbin/ifup tun0" ssh-rsa AAAA.... root@sshvpn-vm

Ab ssh-rsa muss alles ersetzt werden durch den Inhalt aus dem generierten öffentlichen Schüssel vom Client. (/root/.ssh/id_rsa.pub)

Anpassen der /etc/network/interfaces Datei

Folgenden Absatz an das Ende der Datei anfügen.

iface tun0 inet static
      address 10.255.255.1
      netmask 255.255.255.252
      pointopoint 10.255.255.2

IP Forwarding auf dem Server aktivieren

$ sysctl -w net.ipv4.ip_forward=1

Für eine dauerhaftes IP Forwarding muss mann noch die /etc/sysctl.conf anpassen.

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

NAT aktivieren

Ich habe auf meinem SSHVPN Server NAT aktiviert damit ich ohne Routing arbeiten kann, das ist einfacher.

Folgendes Script in /etc/init.d anlegen.

iptables.nat.sshvpn:

/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Aktivieren für Systemstart:

$  update-rc.d iptables.nat.sshvpn defaults

Test des VPN Tunnels

Auf dem Client kann der Tunnel mit folgendem Kommando gestartet werden.

$ sudo ifup tun0

Der SSH Client baut dann die Verbindung zum Server auf und richtet das Tunnelinterface ein.

Ein einfacher Ping zeigt das es funktioniert hat.

$  ping 10.10.0.80
PING 10.10.0.80 (10.10.0.80) 56(84) bytes of data.
64 bytes from 10.10.0.80: icmp_req=1 ttl=64 time=99.57 ms
64 bytes from 10.10.0.800: icmp_req=2 ttl=64 time=90.238 ms
64 bytes from 10.10.0.80: icmp_req=3 ttl=64 time=90.217 ms
64 bytes from 10.10.0.80: icmp_req=4 ttl=64 time=90.181 ms

Viel Spaß mit euren eigenen SSH VPNs 😉

Automatische Proxy Zuweisung über WPAD/PAC

In meiner Testumgebung verwende ich verschiedene Domains die nicht über einen Proxy gehen sollen. Da die manuelle Pflege an meinen Maschinen mir zu umständlich erscheint habe ich mich für den Proxy PAC (Proxy Auto Configuration) weg entschieden. Normalerweise wäre es ja ganz einfach, einfach bei jeder Domain als TLD .local verwenden und als Filter in der  Proxykonfiguration *.local ausschließen. Leider habe ich aber auch .com, usw. im Einsatz und ein Filter auf *.com wäre denkbar unpraktisch 😉 Die Lösung: PAC-File zentral auf meinem Server und bekanntgabe via WPAD.

PAC-File (wpad.dat)

function FindProxyForURL(url, host) {

   var proxy_yes = "PROXY proxy.lanbugs.local:3128";
   var proxy_wlan_yes = "PROXY proxy-wlan.lanbugs.local:3128";
   var proxy_no = "DIRECT";
   var proxy_failback = "PROXY proxy.lanbugs.local:3128; DIRECT";

   // Muss ins Internet direkt
   if (shExpMatch(url,"www.lanbugs.de/*")) {return proxy_yes;}
   if (shExpMatch(url,"www.thoma.cc/*")) {return proxy_yes;}

   // Interne Webseiten
   if (shExpMatch(url,"*.lanbugs.local/*")) {return proxy_no;}
   if (shExpMatch(url, "*.lanbugs.de/*")) {return proxy_no;}
   if (shExpMatch(url, "*.thoma.cc/*")) {return proxy_no;}

   // Homenet
   if (isInNet(host, "10.10.4.0", "255.255.255.0")) {
      return proxy_yes;
   }

   // WLAN
   if (isInNet(host, "10.10.5.0", "255.255.255.0")) {
      return proxy_wlan_yes;
   }

   // Server
   if (isInNet(host, "10.10.4.200", "255.255.255.255"){
     return proxy_no;
   }

   return proxy_failback;
}

Meine PAC Konfiguration mancht folgendes:

  • www.lanbugs.de wird weiterhin zum Proxy gesendet alles andere wird direkt über den internen DNS auf interne Webserver umgeleitet
  • www.thoma.cc wird weiterhin zum Proxy gesendet alles andere wird direkt über den internen DNS aus interne Webserver umgeleitet
  • *.lanbugs.local wird komplett vom Proxy ausgeschlossen
  • Mein Homenet wird komplett über den Proxy proxy.lanbugs.local abgewickelt
  • Mein WLAN Netz wird komplett über den Proxy proxy-wlan.lanbugs.local abgewickelt
  • Für den Server 10.10.4.200 geht nicht über den Proxy
  • Für alle anderen wird versucht über den Proxy zu gehen funktioniert das nicht versucht der Browser es direkt

WPAD Konfiguration

WPAD lässt sich über DNS, DHCP und als manuelles File verteilen, leider unterstützen nicht alle Browser alle Wege.

WPAD Kompatibilitätsliste

Browser DNS DHCP Manuelles File
Internet Explorer 6.0 (SP1) und höher JA JA JA
Firefox 1.5.x JA NEIN JA
Firefox 2.5.x JA NEIN JA
Opera NEIN NEIN JA
NetScape JA NEIN JA
Safari ? ? JA

Vorbereiten des Webservers für WPAD/PAC File

Mein Server läuft mit Ubuntu 8.04 LTS. Bei den Ubuntu / Debian Versionen liegt die MIME Type Konfiguration unter /etc/apache2/mods-enabled.

/etc/apache2/mods-enabled/mime.conf

AddType application/x-ns-proxy-autoconfig .dat
AddType application/x-ns-proxy-autoconfig .pac

Desweiteren sollte die Dateiendung noch in /etc/mime.types bekannt gegeben werden.

application/x-ns-proxy-autoconfig dat
application/x-ns-proxy-autoconfig pac

Das PAC File liegt im root-Verzeichnis des Webservers /var/www als proxy.pac und wpad.dat. Alternativ habe ich noch einen Vorschlag gelesen nur die proxy.pac anzulegen und die Datei via redirect unter wpad.dat erreichbar zu machen. Geschmackssache ….

WPAD via DNS

WPAD via DNS geht über verschiedene Wege. Entweder über einen A/CNAME Record oder über einen TXT/SRV Eintrag. Es kann nicht schaden alle möglichkeiten anzulegen. Per DHCP wird in meinem Netz die Domain lanbugs.local als Primäres DNS Suffix mitgegeben, folglich muss ein Eintrag für wpad.lanbugs.local angelegt werden.

Auszug aus der Zone lanbugs.local:

...
wpad           IN A                       10.10.4.200
wpad.tcp       IN SRV       0 0 80        10.10.4.200
               IN TXT                     "service: wpad:http://wpad.lanbugs.local/wpad.dat"
...

WPAD via DHCP

Der DHCP Server muss mit der Option 252 erweitert werden. In dieser Option wird die URL der wpad.dat eingefügt.

option wpad code 252 = text;

subnet 10.10.4.0 netmask 255.255.255.0 {
…
option wpad “http://wpad.lanbugs.local/wpad.dat ”;
…
}

subnet 10.10.5.0 netmask 255.255.255.0 {
…
option wpad “http://wpad.lanbugs.local/wpad.dat ”;
…
}

WPAD via Manuelles File

Falls alles nicht funktionieren  sollte lässt sich das File auch manuell Eintragen.

Firefox:

Extras -> Einstellungen -> Erweitert -> Netzwerk -> Verbindung (Einstellungen) -> Automatische Proxy-Konfigurations-URL: http://wpad.lanbugs.local/proxy.pac

Internet Explorer:

Extras -> Internet Optionen -> Verindungen -> LAN-Einstellungen (Einstellungen) -> (x) Automatisches Konfigurationsskript verwenden: http://wpad.lanbugs.local/proxy.pac

Chrome:

Nutzt die Einstellungen vom Internet Explorer

Opera:

Einstellungen -> Netzwerk -> Proxyserver -> (x) Automatische Proxykonfiguration verwenden (Skript): http://wpad.lanbugs.local/proxy.pac

Finale

Damit die automatische Zuweisung klappt sollte der Browser auch so eingestellt sein das er eine WPAD Konfiguration zulässt.

Firefox:

Extras -> Einstellungen -> Erweitert -> Netzwerk -> Verbindung (Einstellungen) -> (x) Die Proxy-Einstellungen für dieses Netzwerk automatisch erkennen

Internet Explorer:

Extras -> Internet Optionen -> Verindungen -> LAN-Einstellungen (Einstellungen) -> (x) Automatische Suche der Einstellungen

Chrome:

Nutzt die Einstellugen vom Internet Explorer

Opera:

Hier scheint nur der manuelle Weg zu bleiben

Postfix als Frontend für einen Microsoft Exchange Server

Für einen Kunden habe ich einen Postfix eingerichtet als Frontend Server für seine Exchange Infrastruktur. Die Mailadressen werden automatisch aus dem Active Directory ausgelesen und in einem Hash-File gespeichert. Vorteil ist das der Postfix so nur gültige Mailaliases zulässt. Der Frontend macht auch noch weitere Überprüfungen. (postgrey, policy-weight, etc.)

Folgendes Script wird für das Auslesen der Aliase aus dem AD verwendet:

#!/usr/bin/perl -T -w

# Version 1.02

# This script will pull all users' SMTP addresses from your Active Directory
# (including primary and secondary email addresses) and list them in the
# format "user@example.com OK" which Postfix uses with relay_recipient_maps.
# Be sure to double-check the path to perl above.

# This requires Net::LDAP to be installed.  To install Net::LDAP, at a shell
# type "perl -MCPAN -e shell" and then "install Net::LDAP"

use Net::LDAP;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant ( "LDAP_CONTROL_PAGED" );

# Enter the path/file for the output
$VALID = "/etc/postfix/example_recipients";

# Enter the FQDN of your Active Directory domain controllers below
$dc1="domaincontroller1.example.com";
$dc2="domaincontroller2.example.com";

# Enter the LDAP container for your userbase.
# The syntax is CN=Users,dc=example,dc=com
# This can be found by installing the Windows 2000 Support Tools
# then running ADSI Edit.
# In ADSI Edit, expand the "Domain NC [domaincontroller1.example.com]" &
# you will see, for example, DC=example,DC=com (this is your base).
# The Users Container will be specified in the right pane as
# CN=Users depending on your schema (this is your container).
# You can double-check this by clicking "Properties" of your user
# folder in ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=Users,DC=example,DC=com
# which would be $hqbase="cn=Users,dc=example,dc=com"
# Note:  You can also use just $hqbase="dc=example,dc=com"
$hqbase="cn=Users,dc=example,dc=com";

# Enter the username & password for a valid user in your Active Directory
# with username in the form cn=username,cn=Users,dc=example,dc=com
# Make sure the user's password does not expire.  Note that this user
# does not require any special privileges.
# You can double-check this by clicking "Properties" of your user in
# ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=user,CN=Users,DC=example,DC=com
# which would be $user="cn=user,cn=Users,dc=example,dc=com"
# Note: You can also use the UPN login: "user\@example.com"
$user="cn=user,cn=Users,dc=example,dc=com";
$passwd="password";

# Connecting to Active Directory domain controllers
$noldapserver=0;
$ldap = Net::LDAP->new($dc1) or
   $noldapserver=1;
if ($noldapserver == 1)  {
   $ldap = Net::LDAP->new($dc2) or
      die "Error connecting to specified domain controllers $@ \n";
}

$mesg = $ldap->bind ( dn => $user,
                     password =>$passwd);
if ( $mesg->code()) {
    die ("error:", $mesg->code(),"\n","error name: ",$mesg->error_name(),
        "\n", "error text: ",$mesg->error_text(),"\n");
}

# How many LDAP query results to grab for each paged round
# Set to under 1000 for Active Directory
$page = Net::LDAP::Control::Paged->new( size => 990 );

@args = ( base     => $hqbase,
# Play around with this to grab objects such as Contacts, Public Folders, etc.
# A minimal filter for just users with email would be:
# filter => "(&(sAMAccountName=*)(mail=*))"
         filter => "(& (mailnickname=*) (| (&(objectCategory=person)
                    (objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))
                    (&(objectCategory=person)(objectClass=user)(|(homeMDB=*)
                    (msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=contact))
                    (objectCategory=group)(objectCategory=publicFolder)(objectClass=msExchDynamicDistributionList) ))",
          control  => [ $page ],
          attrs  => "proxyAddresses",
);

my $cookie;
while(1) {
  # Perform search
  my $mesg = $ldap->search( @args );

# Filtering results for proxyAddresses attributes
  foreach my $entry ( $mesg->entries ) {
    my $name = $entry->get_value( "cn" );
    # LDAP Attributes are multi-valued, so we have to print each one.
    foreach my $mail ( $entry->get_value( "proxyAddresses" ) ) {
     # Test if the Line starts with one of the following lines:
     # proxyAddresses: [smtp|SMTP]:
     # and also discard this starting string, so that $mail is only the
     # address without any other characters...
     if ( $mail =~ s/^(smtp|SMTP)://gs ) {
       push(@valid, $mail." OK\n");
     }
    }
  }

  # Only continue on LDAP_SUCCESS
  $mesg->code and last;

  # Get cookie from paged control
  my($resp)  = $mesg->control( LDAP_CONTROL_PAGED ) or last;
  $cookie    = $resp->cookie or last;

  # Set cookie in paged control
  $page->cookie($cookie);
}

if ($cookie) {
  # We had an abnormal exit, so let the server know we do not want any more
  $page->cookie($cookie);
  $page->size(0);
  $ldap->search( @args );
  # Also would be a good idea to die unhappily and inform OP at this point
     die("LDAP query unsuccessful");
}
# Only write the file once the query is successful
open VALID, ">$VALID" or die "CANNOT OPEN $VALID $!";
print VALID @valid;
# Add additional restrictions, users, etc. to the output file below.
#print VALID "user\@example.com OK\n";
#print VALID "user1\@example.com 550 User unknown.\n";
#print VALID "bad.example.com 550 User does not exist.\n";

close VALID;

Quelle: http://www-personal.umich.edu/~malth/gaptuning/postfix/

Ich habe das Perlscript in /etc/postfix abgelegt. Rechte sollten auch 700 sein. Diese 6 Parameter müssen geändert werden.

$VALID = "/etc/postfix/exchange_recipients";

$dc1="dc01.lanbugs.intra";
$dc2="dc02.lanbugs.intra";

$hqbase="cn=Users,dc=lanbugs,dc=intra";

$user="cn=sys-read-aliases-mailfront,cn=Users,dc=lanbugs,dc=intra";
$passwd="geheim123";

Das Script wird als Cronjob jede Stunde ausgeführt. Hierzu muss nur ein File mit dem Namen run_read_aliases_from_exchange.sh mit den Rechten 700 im Verzeichnis /etc/cron.hourly angelegt werden. (Debian/Ubuntu)

run_read_aliases_from_exchange.sh:

#!/bin/sh

cd /etc/postfix ; ./getadsmtp.pl && postmap exchange_recipients

In der /etc/postfix/main.cf kann dann das neue Hash-File als relay_recipient_map verwendet werden.

main.cf:

...
relay_recipient_maps = hash:/etc/postfix/exchange_recipients
relay_domains = lanbugs.de
transport_maps = hash:/etc/postfix/transport
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.1.1.20/32
...

relay_domains erlaubt allgemein die Domain lanbugs.de. mynetworks wurde noch um die IP-Adresse des Exchange Servers erweitert damit dieser E-Mails zur Außenwelt senden kann. Die transport_maps sorgen dafür dass alle Mails an lanbugs.de an den Exchange Server weiter transportiert werden.

/etc/postfix/transport: (nach dem Anlegen postmap /etc/postfix/transport nicht vergessen!!!!)

langbugs.de      smtp:[10.1.1.20]:25

10.1.1.20 ist der interne Exchange Server.

Viel Spaß mit dem Postfix Frontend 😉

Massenbackup für Switche via Telnet & TFTP

Das Beispiel habe ich für Huaweiswitche erstellt. Es geht scheinbar auch über SNMP aber per Telnet die Befehle absetzen geht bei jedem Hersteller.

Folgende Software wird benötigt:

  • TFTP Server (z.B. 3CDaemon -> http://support.3com.com/software/utilities_for_windows_32_bit.htm)
  • Tera Term (hier ttpmacro -> http://www.heise.de/software/download/teraterm_pro/51776)

Das einzige was man wissen muss sind die Befehle wie man die Konfiguration bei dem entsprechenden Switch sichert. Bei Huawei liegt per Default die Konfiguration in vrpcfg.cfg. Mit dem Kommando tftp x.x.x.x put flash:/vrpcfg.cfg xxxx.cfg kann man die Konfig auf einen TFTP-Server schicken. Das Macro muss also auf verschiedene Situationen warten und dann Kommandos schicken. Das Macro speichert die Konfigurationen unter der jeweiligen IP-Adresse. Eine Erweiterung das noch ein Datum mit in dem Dateinamen eingebaut wird ist auch möglich.

Bei Huawei sieht das wie folgt aus:

>> Username:
<< Username senden
>> Password:
<< Password senden
>> <Switch-1>
<< Komando tftp ... senden
>> <Switch-1>
<< quit senden

Batch-Datei (get_cfgs.bat):

cls
for /F "eol=; tokens=1,2,3,4 delims=, " %%i in (devices.txt) do ( 

teraterm\ttpmacro.exe get_cfgs.ttl %%i

)

Geräte (devices.txt):

172.20.6.11
172.20.6.12
172.20.6.13
...

ttl Macro Script für Tera Term Macro Programm (get_cfg.ttl):

; Host & Port
host = param2

; Username
username = 'admin'

; Password
password = 'admin'

strconcat host ':23 /nossh'
connect host

wait 'Username:'
sendln username
wait 'Password:'
sendln password
wait '>'
kommando = 'tftp 172.20.16.113 put flash:/vrpcfg.cfg '
strconcat kommando param2
strconcat kommando '.cfg'

sendln kommando

wait '>'

sendln 'quit'

Die Macrosprache von Tera Term ist sehr einfach und lässt sich auf andere Hersteller leicht umbiegen.

Viel Spaß 😉