OpenVPN mit erweiterter Authentifizierung und Autorisierung

Der Artikel beschreibt die Konfiguration von OpenVPN mit zusätzlichen Funktionen:

  • Token-Zertifikate für die primäre Authentifizierung (Rutoken als Beispiel)
  • LDAP-Backend für die sekundäre Authentifizierung (am Beispiel von ActiveDirectory)
  • Filtern interner Ressourcen, die Benutzern zur Verfügung stehen (über iptables)

Außerdem wird beschrieben, wie Sie Clients für Linux, Windows und MacOS konfigurieren.

Server-Setup


Installieren Sie OpenVPN


Nehmen Sie das Skript Nyr / openvpn-install und führen Sie es von root aus.

git clone https://github.com/Nyr/openvpn-install.git cd openvpn-install 

Der Startvorgang wird einige Fragen stellen.

  • udp-Protokoll
  • Port 1194
  • DNS-Server - Lokal
  • externe IP-Gateway-Adresse im Internet, über die der VPN-Server verfügbar sein wird

Es gibt auch eine sicherheitsverbesserte Version des ursprünglichen Skripts - github.com/Angristan/OpenVPN-install . Es hat mehr Verschlüsselungseinstellungen mit Erklärungen, warum.

Benutzerverwaltung


Hinzufügen
Wenn keine Token verwendet werden, wird der Benutzer über dasselbe Skript hinzugefügt. Das Skript generiert im Wesentlichen eine benutzerdefinierte ovpn-Konfiguration und fügt dort ein vom Stammzertifikat signiertes Zertifikat ein.

Wenn Token verwendet werden (siehe Abschnitt über Token unten), wird das Zertifikat manuell ausgeschrieben, basierend auf der Anforderung des Zertifikats, das auf dem Token generiert wird. Die Benutzerkonfiguration muss manuell aus der vorhandenen Vorlage erfolgen (aus derselben Vorlage, aus der das Konfigurationsskript generiert wird). Die Vorlage finden Sie hier /etc/openvpn/client-common.txt . Es ist nicht im openvpn-Paket enthalten und wird vom Skript während des Konfigurationsprozesses generiert.

Löschen
Das Entfernen von Benutzern erfolgt über dasselbe Installationsskript. Das Zertifikat wird der CRL hinzugefügt, die neue CRL wird auf den VPN-Server übertragen. Der Server betrachtet alle Zertifikate in CRL als ungültig und lehnt die Annahme ab.

So widerrufen Sie ein Zertifikat manuell:

 cd /etc/openvpn/easyrsa #   ./easyrsa revoke $CLIENT #   crl ./easyrsa gen-crl #   crl rm -rf /etc/openvpn/crl.pem #    cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem # openvpn    crl,       nobody chown nobody:nobody /etc/openvpn/crl.pem 

Filtern verfügbarer Hosts für Clients


Clients müssen durch die Hosts eingeschränkt werden, auf die sie im Netzwerk zugreifen können, wenn sie eine Verbindung zu openvpn herstellen.

Manuell

Die Idee ist, Pakete auch auf der tun0 Schnittstelle tun0 , in die sie von Clients kommen, und sie zu filtern, bevor sie in NAT gelangen. Nach NAT gibt es keinen Grund, sie zu filtern - alle haben eine OpenVPN-Server-IP-Adresse im internen Netzwerk. Vor dem Einstieg in NAT haben Pakete für jeden Benutzer eine eigene eindeutige IP-Adresse (Informationen zur Entsprechung von IP-Adressen und Benutzern finden Sie in der Datei /etc/openvpn/ipp.txt ).

Pakete, die das System passieren (nicht direkt von ihm kommen und nicht eingehen, dh vom System weitergeleitet werden), werden von der FORWARD-Tabelle verarbeitet. Tabellen in iptables werden von oben nach unten verarbeitet. Wenn keine der Regeln in der Tabelle zu einer Entscheidung über das Schicksal des Pakets geführt hat, wird die Standardregel ausgelöst.

Vorbereiten der FORWARD-Tabelle:

 #   iptables -F FORWARD #     FORWARD -    iptables -P FORWARD DROP #     iptables -I FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT 

Beispielregeln für einen bestimmten Client. Da die Standardregel für die Tabelle DROP ist, bleiben nur die Host + Port-Paare zulässig, wo Sie können. Ermöglichen Sie den Zugriff auf den Port auf dem Host + Pingen Sie den Host selbst an:

 iptables -I FORWARD -s 10.8.0.3 -i tun0 -d 10.0.2.3 -p tcp --dport 443 -j ACCEPT iptables -I FORWARD -s 10.8.0.3 -i tun0 -d 10.0.2.3 -p icmp --icmp-type echo-request -j ACCEPT 

Im obigen Beispiel kann Host 10.8.0.3 auf Port 443 von Host 10.0.2.3 zugreifen.

So schließen Sie den Zugriff:

 #         iptables -L FORWARD --line-numbers #     iptables -D FORWARD { } 

Dann müssen Sie alle Regeln für einen bestimmten Client finden und löschen.

Während des Debuggens können Sie bequem überprüfen, welche Regeln funktionieren. Jede Regel hat einen Zähler für verarbeitete Pakete.

 #  ,      watch iptables -nvL FORWARD #     iptables -Z FORWARD 

Automatisch

Ein openvpn-Server kann Skripts für bestimmte Aktionen ausführen. Insbesondere beim Verbinden und Trennen von Clients. Skripte können auf alles geschrieben werden, wenn sie nur ausführbar sind. Innerhalb des Skripts übergeben Umgebungsvariablen alle möglichen Parameter an die aktuelle Verbindung. Wir interessieren uns für Variablen:

  • common_name (Name des Inhabers des Zertifikats; was wird beim Erstellen des Zertifikats in das Feld common name common_name )
  • ifconfig_pool_remote_ip (Client-IP-Adresse auf tun0)
  • script_type (welches Ereignis ist script_type - verbinden oder trennen).

Sie benötigen Root-Rechte, um iptables zu verwalten. Openvpn setzt nach dem Verbinden die Rechte auf niemanden zurück und führt Skripte aus. Es ist schlecht, niemandem zu erlauben, etwas unter sudo zu tun, und es ist besser, kein Sternchen in den Regeln zu verwenden, aber irgendwie muss man dem Benutzer erlauben, iptables zu steuern.

 # /etc/sudoers.d/50_openvpn # #    nobody ALL = NOPASSWD: /sbin/iptables -A FORWARD* #     nobody ALL = NOPASSWD: /sbin/iptables -L FORWARD* #    nobody ALL = NOPASSWD: /sbin/iptables -D FORWARD* 

In der Serverkonfiguration müssen Sie die Berechtigung zum Ausführen von Dateien von Drittanbietern hinzufügen und zwei Hooks aktivieren, die für das Verbinden und Trennen des Benutzers verantwortlich sind.

 script-security 2 client-connect /etc/openvpn/bin/hosts.rb client-disconnect /etc/openvpn/bin/hosts.rb 

Das Skript selbst, das die Konfigurationen liest und die Regeln für iptables anwendet. Das Skript funktioniert nach denselben Prinzipien wie im vorherigen Abschnitt beschrieben.

/openvpn/bin/hosts.rb
 #!/usr/bin/ruby # -*- coding: utf-8 -*- require 'pp' def log(string) puts 'hosts.rb: ' + string end def parse_config_file(name) config_path = "hosts/#{name}" unless File.exist?(config_path) puts "There is no specific configuration for #{name}." p name exit 0 end config_source = IO.read(config_path).split("\n") config = config_source.inject([]) do |result,line| ip, port, protocol = line.split(/\s+/) result << { ip: ip, port: port, protocol: protocol || 'tcp' } end end def get_config(name) user_config = parse_config_file(name) if user_config everybody_config = parse_config_file('everybody') end everybody_config + user_config end def apply_rule(rule) command = "sudo iptables #{rule}" log(command) system(command) end def remove_rule(number) command = "sudo iptables -D FORWARD #{number}" log(command) system(command) end def allow_target(source_ip, options) #         . apply_rule("-A FORWARD -s #{source_ip} -i tun0 -d #{options[:ip]} -p #{options[:protocol]} --dport #{options[:port]} -j ACCEPT") #       apply_rule("-A FORWARD -s #{source_ip} -i tun0 -d #{options[:ip]} -p icmp --icmp-type echo-request -j ACCEPT") end def clear_targets(source_ip) #      FORWARD,  source_ip. rules_exist = true while rules_exist table = `sudo iptables -L FORWARD --line-number`.split("\n") the_line = table.find do |line| fields = line.split(/\s+/) ip = fields[4] ip == source_ip end if the_line number = the_line.split(/\s+/)[0] remove_rule(number) else rules_exist = false end end end ################################################################################ script_type = ENV['script_type'] log(script_type) name = ENV['common_name'] source_ip = ENV['ifconfig_pool_remote_ip'] case script_type when 'client-connect' config = get_config(name) config.each{|target| allow_target(source_ip, target)} when 'client-disconnect' clear_targets(source_ip) else puts "Unknown script type #{script_type}." end 

Regeln werden in Dateien gespeichert, die dem allgemeinen Namen der Zertifikate im Ordner /etc/openvpn/hosts . Sie geben genau an, welche IP-Adressen für einen bestimmten Client verfügbar sind. Trennzeichen - eine beliebige Anzahl von Leerzeichen. Die IP-Adresse, der Port und das Protokoll (tcp oder udp) werden über das Trennzeichen geschrieben.

 10.0.0.24 53 udp 10.0.0.25 53 udp 10.0.2.3 443 tcp 

Daher sollte die folgende Struktur im Ordner /etc/openvpn

Bin── bin
│ └ hosts.rb
├── Hosts
│ ├ Benutzer1
│ ├ Benutzer2
Everybody └ alle
├── server.conf
└── ...

User1 und user2 sind Dateien im obigen Format. Sie beschreiben, auf welche Hosts der Benutzer mit dem entsprechenden allgemeinen Namen Zugriff hat.

Es gibt eine weitere zusätzliche Datei für alle Benutzer, die Regeln enthält, die für alle Clients gelten, sofern für diese Clients eine separate Konfigurationsdatei vorhanden ist. Das heißt, wenn der Benutzer eine Liste von Hosts hat, auf die er gehen kann, werden diese Liste und die Hosts, die in everybody angewendet. Wenn nicht, dann everybody nicht everybody anwendbar. Beispielsweise kann ein DNS-Server bequem in diese Datei eingefügt werden.

Protokollierung

Das Installationsskript enthält nur die Protokollierung aktueller Verbindungen ( status) . Damit ein reguläres Protokoll angezeigt wird, müssen Sie der Serverkonfiguration ( /etc/openvpn/server.conf ) eine Zeile hinzufügen:
 log-append /var/log/openvpn.log 


LDAP

Es gibt ein openvpn-auth-ldap- Plugin, mit dem Sie einen Benutzer erneut über LDAP authentifizieren können.

Paket liefern:

 sudo yum install openvpn-auth-ldap 

Zur server.conf hinzufügen:

 plugin /usr/lib64/openvpn/plugin/lib/openvpn-auth-ldap.so "/etc/openvpn/ldap.conf" 

Erstellen Sie eine Konfiguration für ldap in /etc/openvpn/ldap.conf :
 <LDAP> URL ldaps://{LDAP_DOMAIN_HERE} Timeout 15 TLSEnable no FollowReferrals yes BindDN "BIND_DN_HERE" Password "BIND_PASSWORD_HERE" </LDAP> <Authorization> BaseDN "{BASE_DN_HERE}" SearchFilter "(&(sAMAccountName=%u)(objectClass=organizationalPerson)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))" RequireGroup false </Authorization> 

Fügen Sie die Zeile zur benutzerdefinierten ovpn-Konfiguration hinzu:

 auth-user-pass 

Daher wird der Benutzer zuerst nach dem Benutzernamen und dem Kennwort der Domäne und dann nach der PIN des Tokens gefragt. Wenn einer dieser Schritte fehlschlägt, wird die Verbindung nicht hergestellt.

Die Beschreibung der Optionen für ldap.conf befindet sich im Plugin-Repository . Es unterstützt die Authentifizierung durch Gruppenmitgliedschaft, aber ich habe es nicht getestet.

Geschwindigkeit


Die größte Geschwindigkeitssteigerung ergibt den UDP-Modus. Dies wird in allen Handbüchern empfohlen. Der Punkt ist, dass es keinen Sinn macht, eine TCP-Client-Verbindung in einem TCP-Kanal zu starten. Ein TCP auf dem Client reicht aus, um die Pakete korrekt zuzustellen. Wenn Pakete im udp-Kanal verloren gehen, steuert die Client-TCP-Verbindung die Zustellungsanpassung.

Die Geschwindigkeit erhöht sich zumindest, weil nicht auf die Bestätigung der Zustellung jedes Pakets im Kanal gewartet werden muss. Es gibt ein zweites Problem mit TCP - ein Client-TCP-Paket passt höchstwahrscheinlich nicht in ein Paket des VPN-Kanals. Die MTU ist dieselbe, aber dem Client-Paket müssen Header hinzugefügt werden. Infolgedessen müssen Sie pro Benutzerpaket zwei Pakete innerhalb des VPN-Kanals senden.

Die Verwendung von TCP ist sinnvoll, wenn dies auf andere Weise nicht möglich ist. Zum Beispiel, wenn VPN über den SSH-Kanal arbeitet.

Beispiel einer vollständigen Serverkonfiguration


example-server.conf
 port 1194 proto tcp dev tun sndbuf 0 rcvbuf 0 ca ca.crt cert server.crt key server.key dh dh.pem tls-auth ta.key 0 topology subnet server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 10.0.0.25" push "dhcp-option DNS 10.0.0.24" keepalive 10 120 cipher AES-256-CBC comp-lzo user nobody group nobody persist-key persist-tun status openvpn-status.log verb 3 crl-verify crl.pem log-append /var/log/openvpn.log script-security 2 client-connect /etc/openvpn/bin/hosts.rb client-disconnect /etc/openvpn/bin/hosts.rb 


Token-Setup


PKCS # 11 Bibliothek


Um mit Token arbeiten zu können, benötigen Sie eine spezielle Bibliothek. Die Bibliothek wird sowohl zum Erstellen von Schlüsselpaaren als auch für die eigentliche Verbindung benötigt. Download für alle Plattformen über den Link .

Überall dort, wo später librtpkcs11ecp.so gefunden wird, muss genau diese Bibliothek heruntergeladen und an einem geeigneten Ort abgelegt werden.

Erstellen eines Zertifikats für ein Token


Generieren Sie ein Schlüsselpaar für das Token. Der Parameter id ist hier die Seriennummer des Steckplatzes auf dem Token, in den das Schlüsselpaar passt.

 pkcs11-tool --module /usr/lib64/librtpkcs11ecp.so --keypairgen --key-type rsa:2048 -l --id 01 

Stellen Sie eine Zertifikatanforderung für den öffentlichen Schlüssel. Beim Erstellen einer Zertifikatanforderung werden die Lebensdauer des Zertifikats und der allgemeine Name festgelegt, mit denen die verfügbaren IP-Adressen im Netzwerk gefiltert werden. Der allgemeine Name muss mit der Anmeldung in ActiveDirectory übereinstimmen, damit keine Verwirrung entsteht.

 openssl openssl> engine -t dynamic -pre SO_PATH:/usr/lib64/openssl/engines/pkcs11.so -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -pre MODULE_PATH:/usr/lib64/librtpkcs11ecp.so openssl> req -engine pkcs11 -new -key slot_0-id_01 -keyform engine -out /home/john/good.req 

Die empfangene Anfrage muss in den /etc/openvpn/easy-rsa/pki/reqs/ . Die Dateierweiterung muss erforderlich sein.
Konvertieren einer Anfrage in ein Zertifikat:

 cd /etc/openvpn/easy-rsa/ ./easyrsa sign-req client good 

Danach wird im Ordner /etc/openvpn/easy-rsa/pki/issued/ ein Zertifikat mit demselben Namen, aber der Erweiterung crt /etc/openvpn/easy-rsa/pki/issued/ .

Vor der Aufzeichnung muss das Zertifikat in DER konvertiert werden:

 openssl x509 -in /home/user/user-cert.pem -out /home/user/user-cert.crt -outform DER 

Schreiben eines Zertifikats für ein Token:

 pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -l -y cert -w /home/user/user-cert.crt --id 45 --label TEST 

Es basiert auf dem Artikel „Verwenden von Rutoken EDS mit OpenSSL (RSA)“ .

Token zur Authentifizierung verwenden


Suchen Sie die ID des Zertifikats, das dem Server präsentiert werden soll:

 $ openvpn --show-pkcs11-ids /usr/lib64/librtpkcs11ecp.so The following objects are available for use. Each object shown below may be used as parameter to --pkcs11-id option please remember to use single quote mark. Certificate DN: /CN=User1 Serial: 490B82C4000000000075 Serialized id: aaaa/bbb/41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600 

Wir sind hier an einer serialisierten ID interessiert.

Optionen, die in der ovpn-Konfiguration eingegeben werden müssen, damit Token aufgenommen werden:

 pkcs11-providers /usr/lib64/librtpkcs11ecp.so pkcs11-id 'aaaa/bbb/41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600' 

Die Option pkcs11-id muss in einfache Anführungszeichen gesetzt werden.

Dieses Handbuch ist auf allen Plattformen sinnvoll. Sie müssen den Pfad zur Bibliothek und die Zertifikat-ID auf dem Token angeben. Die Bibliothek kann etwas anders aufgerufen werden, sei .dll , nicht .so , aber die Bedeutung ist dieselbe.

In diesem Fall müssen Sie die Abschnitte cert und key aus der ovpn-Datei entfernen, da das Zertifikat und der private Schlüssel dem Token entnommen werden.

Die vollständige Client-Konfiguration (für Windows) sieht folgendermaßen aus:

client.ovpn
client
dev tun
proto tcp
sndbuf 0
rcvbuf 0
remote 78.47.37.247 22222
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-CBC
comp-lzo
setenv opt block-outside-dns
key-direction 1
verb 3

pkcs11-providers "c://Windows//System32//rtPKCS11ECP.dll"
pkcs11-id 'Aktiv\x20Co\x2E/Rutoken\x20ECP/342b871d/Rutoken/01'

-----BEGIN CERTIFICATE-----
{CERT_HERE}
-----END CERTIFICATE-----


<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
{KEY_HERE}
-----END OpenVPN Static key V1-----
</tls-auth>


Geschrieben basierend auf "Hinzufügen einer Dual-Faktor-Authentifizierung zu einer OpenVPN-Konfiguration mithilfe clientseitiger Smartcards".

Kundeneinrichtung


Linux


Openvpn hat einen Fehler, der den Benutzer daran hindert, den PIN-Code vom Token einzugeben, wenn das Paket mit systemd-Unterstützung erstellt wird. Da systemd in letzter Zeit überall war, werden alle Pakete, die bereits in den Repositorys verfügbar sind, mit seiner Unterstützung kompiliert. Clients unter Linux müssen das Paket selbst sammeln. Hier ist eine Beispielkonfiguration, die für mich unter Arch Linux funktioniert hat:

 ./configure \ --prefix=/usr \ --sbindir=/usr/bin \ --enable-iproute2 \ --enable-pkcs11 \ --enable-plugins \ --enable-x509-alt-username 

Mit dem folgenden Befehl können Sie überprüfen, ob openvpn mit oder ohne systemd erstellt wurde:

 openvpn --version | grep --color enable_systemd 

Mas os


Unter Mac OS gibt es nur einen kostenlosen Client - Tunnelblink .

Er weiß nicht, wie er einen PIN-Code von einem Token von GUI eingeben soll. Der Fehler wird zum Beispiel hier beschrieben - https://groups.google.com/forum/#!topic/tunnelblick-discuss/f_Rp_2nV-x8 Umgangen durch Starten von openvpn über die Konsole. Dies ist nicht überraschend, da der offizielle Client für Windows dies ebenfalls nicht weiß.

Auch unter Mac OS (im Gegensatz zu Windows) sind zusätzliche Skripte erforderlich, um das Netzwerk zu konfigurieren. Wenn Sie openvpn einfach über die Konsole ausführen, funktioniert DNS nicht (möglicherweise wird nur DNS angezeigt).

TunnelBlick verfügt über diese Netzwerkkonfigurationsskripts. Sie müssen nur beim Herstellen und Trennen der Verbindung aufgerufen werden. Was Sie zur ovpn-Konfiguration hinzufügen müssen:

 script-security 2 up "/Applications/Tunnelblick.app/Contents/Resources/client.up.tunnelblick.sh -9 -d -f -m -w -ptADGNWradsgnw" down "/Applications/Tunnelblick.app/Contents/Resources/client.down.tunnelblick.sh -9 -d -f -m -w -ptADGNWradsgnw" 

Ein Beispielskript zum Starten einer openvpn-Verbindung, das auf den Desktop gestellt und mit der Maus gestoßen werden kann:

 #!/bin/bash tunnelblick=/Applications/Tunnelblick.app/Contents/Resources/openvpn/openvpn-2.4.2-openssl-1.0.2k sudo $tunnelblick/openvpn --config $tunnelblick/user.ovpn 

Windows


Unter Fenstern scheint alles zu funktionieren. Der offizielle Client weiß nicht, wie er den PIN-Code vom Token eingeben soll, er schafft es, VPN von Hand über die Konsole zu öffnen.

Das Wichtigste ist, alles unter dem Administrator zu erledigen. Führen Sie das Client-Installationsprogramm als Administrator aus. Starten Sie ein Terminal, in dem openvpn auch mit Administratorrechten gestartet wird, da es sonst die Netzwerkschnittstelle nicht steuern kann.

Unter Windows sollte der Pfad zur Bibliothek für die Arbeit mit Token über doppelte Schrägstriche aufgezeichnet werden. Dies gilt sowohl für die ovpn-Konfiguration als auch für die --show-pkcs11-ids in der Befehlszeile.

 pkcs11-providers "c://Windows//System32//rtPKCS11ECP.dll" pkcs11-id 'Aktiv\x20Co\x2E/Rutoken\x20ECP/342b871d/Rutoken/01' 

Source: https://habr.com/ru/post/de433250/


All Articles