VPN Server einrichten

Submitted by admin on Sa, 30.09.2017 - 15:04

strongswan.png

VPN

Android hat seit Version 7 (Nougat) eine brauchbare VPN Implementierung, die man dazu verwenden kann, die Kontrolle über die eigenen Daten im Smartphone wieder zu erlangen. In diesem Beitrag wird gezeigt, wie man so ein VPN Gateway aufsetzt. Eine Warnung vorweg: Das ist nichts für Anfänger, denn das ist ein tiefer Eingriff in die Sicherheitsinfrastruktur und ein Fehler bei der Konfiguration des VPN Servers kann zu wirklichen Problemen führen.

Funktion

Auf der Netzebene ist Strongswan als VPN Lösung das zentrale Element. Unterstützt wird es durch einen DHCP Lösung, die Teil von Strongswan ist, der verschiedene Konfigurationsdaten für die Clients bereitstellt. Die Authentifizierung erfolgt über X.509 Client Zertifikate. Als besondere Eigenschaft sticht hier die Beschaffung der Zertifikate über das SCEP Protokoll hervor. Der Client erhält sein Zertifikat im einfachsten Fall per E-Mail, oder eben auch per SCEP, sofern dieser das unterstützt.

Der DNS Server Unbound hat zwei Funktionen:

  • DNS Server mit DNSSEC Validierung
  • Werbefilter, wobei die Listen von AdAway verwendet werden.

 

Der Squid Proxy dient als reiner Caching Proxy und kann als Träger für weitere Funktionen im Bereich Malware/Werbung genutzt werden. Die ganzen DNS Abfragen für den Drittcontent vieler Webangebote verlagert sich damit vom Client auf den VPN Server.

Mit Aktivierung der VPN Verbindung wird sämtlicher Traffic vom Smartphone in das Internet über die VPN Verbindung geroutet. DNS Abfragen gehen über den Unbound DNS Servers den VPN-Gateways. Manipulationen am DNS werden abgefangen, sofern die aufgerufene Domain mit DNSSEC gesichert ist.

Material

Zunächst wird ein Server benötigt, eine virtuelle Maschine reicht dafür aus. Wichtig: Die VM muss mit KVM laufen, so dass Kernelmodule nachgeladen werden können. Mit LXC oder OpenVZ Containern funktioniert das nicht. Als Plattform wurde in diesem Beispiel Arch-Linux gewählt, mit Centos7, Debian/Ubuntu und FreeBSD geht das natürlich auch. Im Falle von FreeBSD unterscheidet sich die Firewall Konfiguration jedoch.

Der Client ist fast egal, er muss nur IKE2/RSA unterstützen. Die folgenden Plattformen funktionieren damit:

  • Linux, FreeBSD und andere BSDs für die es Strongswan gibt, funktionieren ohne Einschränkungen
  • Windows 10 erfüllt auch die Voraussetzungen
  • Android ab Version 7 (Nougat)

Apple und Android bis Version 6 funktionieren nicht, weil diese Plattformen entweder kein IKE2 können (Android <=6), oder es schlichtweg kaputt ist (Apple).

Pakete

Es müssen folgende Pakete installiert werden:

  • unbound
  • squid
  • strongswan

 

Hetzner VServer

Leider ist bei den Hetzner VServern Selinux per default deaktiviert. Im File /etc/selinux/config sollte daher der Wert SELINUX=enforcing eingestellt werden. Danach muss neu gebootet werden, damit die Selinux Labels im Filesystem neu erstellt werden.

Selinux

Fall Strongswan mit aktiviertem Selinux nicht startet, sind die Schritte in diesem Abschnitt notwendig. Strongswan muss auf /dev/net/tun zugreifen können. Eine Anpassung der Selinux Konfiguration ermöglicht das. Zunächst wird ein File /tmp/cf-strongswan erstellt:

module cf-strongswan 1.0;

require {
	type ipsec_t;
	type tun_tap_device_t;
	class tun_socket create;
	class chr_file { ioctl open read write };
}

#============= ipsec_t ==============
allow ipsec_t self:tun_socket create;
allow ipsec_t tun_tap_device_t:chr_file { ioctl open read write };

Dieses File wird dann mit semodule -i cf-strongswan.pp in die vorhandenen Selinux Rulesets aufgenommen.

Konfiguration

Die Config Files sind auf Github zu finden. Dort sind auch die Scripte für SCEP und das Update der AdAway Daten für Unbound abgelegt.

Zertifikate

Wer schon eine PKI hat, kann das alles mit SCEP erledigen. Bei ip6.li sind unter Technik -> X.509 zu Zertifikaten einige Beiträge zu finden. Es spielt dabei keine Rolle, ob die Strongswan eigene PKI verwendet wird, oder eine eigene Lösung.

Die Java SCEP Lösung kann man evtl. auf Android anpassen.

Exkurs: OCSP

Für ein VPN Gateway ist die Nutzung von OCSP eine kritische Komponente im Sicherheitskonzept - und auch auch Angriffsvektor zugleich. OCSP ist derzeit der einzige Weg, in Echtzeit während der Anmeldung zu prüfen, ob das Zertifikat vom Client noch gültig ist. Bei Strongswan muss man da nichts tun, die Zertifikate müssen von der PKI nur mit einer OCSP Responder URL versehen werden, die natürlich auch funktionieren muss. Da sind wir auch schon bei dem Angriffsvektor: Ein DoS Angriff gegen den OCSP Responder kann dazu führen, dass die Zertifikate nicht als gültig erkannt werden.

Squid Proxy

Mit einem Squid-Proxy Server, der als transparenter Proxyserver mit SSL-Interception konfiguriert wird, bekommt man noch mehr Kontrolle über die Datensammelwut von Websites und Apps.

Zunächst das Config File für Squid:

#
# Recommended minimum configuration:
#

# Example rule allowing access from your local networks.
# Adapt to list your (internal) IP networks from where browsing
# should be allowed
#acl localnet src 10.0.0.0/8	# RFC1918 possible internal network
#acl localnet src 172.16.0.0/12	# RFC1918 possible internal network
#acl localnet src 192.168.0.0/16	# RFC1918 possible internal network
#acl localnet src fc00::/7       # RFC 4193 local private network range
#acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines

acl localnet src 10.1.1.1/32
acl localnet src 10.3.0.0/24
acl localnet src 172.31.1.100/32
acl localnet src 192.168.0.0/24
acl localnet src fc00::/64

acl SSL_ports port 443
acl Safe_ports port 80		# http
acl Safe_ports port 21		# ftp
acl Safe_ports port 443		# https
acl Safe_ports port 70		# gopher
acl Safe_ports port 210		# wais
acl Safe_ports port 1025-65535	# unregistered ports
acl Safe_ports port 280		# http-mgmt
acl Safe_ports port 488		# gss-http
acl Safe_ports port 591		# filemaker
acl Safe_ports port 777		# multiling http
acl CONNECT method CONNECT

#
# Recommended minimum Access Permission configuration:
#
# Deny requests to certain unsafe ports
http_access deny !Safe_ports

# Deny CONNECT to other than secure SSL ports
http_access deny CONNECT !SSL_ports

# Only allow cachemgr access from localhost
http_access allow localhost manager
http_access deny manager

# We strongly recommend the following be uncommented to protect innocent
# web applications running on the proxy server who think the only
# one who can access services on "localhost" is a local user
#http_access deny to_localhost

# Example rule allowing access from your local networks.
# Adapt localnet in the ACL section to list your (internal) IP networks
# from where browsing should be allowed

http_access allow localnet
http_access allow localhost

# And finally deny all other access to this proxy
http_access deny all

http_port 3130
http_port 3128 tproxy

####################################################################################
# ssl intercept
# Squid normally listens to port 3128
# https://wiki.squid-cache.org/ConfigExamples/Intercept/SslBumpExplicit
# /usr/lib64/squid/ssl_crtd -c -s /var/cache/squid/ssl_db
# chown -R proxy:proxy /var/cache/squid/ssl_db
# openssl dhparam -outform PEM -out dhparam.pem 2048

#http_port 3131 ssl-bump \
#  cert=/etc/squid/ssl/proxy-ca-rsa.pem \
#  generate-host-certificates=on \
#  dynamic_cert_mem_cache_size=4MB \
#  options=NO_SSLv3

https_port 3129 tproxy ssl-bump \
  cert=/etc/squid/ssl/proxy-ca-rsa.pem \
  generate-host-certificates=on \
  dynamic_cert_mem_cache_size=4MB \
  options=NO_SSLv3,SINGLE_DH_USE,SINGLE_ECDH_USE \
  tls-dh=/etc/squid/ssl/dhparam.pem \
  cipher=ECDHE-PSK-CHACHA20-POLY1305:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:DHE-RSA-CAMELLIA128-SHA:AES128-SHA:RC4-SHA:HIGH:!aNULL:!MD5:!ADH

sslcrtd_program /usr/lib64/squid/ssl_crtd -s /var/cache/squid/ssl_db -M 4MB

acl broken_sites dstdomain .example.com

# Damit der Playstore und diverse Apps funktionieren, die das Serverzertifikat selbst
# prüfen, werden diese von der TLS Interception ausgenommen

acl TrustedDestination dst 127.0.0.1/32
acl TrustedDestination dst 172.31.1.100/32
acl TrustedDestination dst 10.1.1.1/32
acl TrustedDestination dst ::1/128
acl TrustedDestination dst 74.125.0.0/16       # google
acl TrustedDestination dst 172.217.0.0/16      # google
acl TrustedDestination dst 173.194.0.0/16      # google
acl TrustedDestination dst 216.58.192.0/19     # google

acl TrustedDestination dst 52.88.0.0/13        # Amazon
acl TrustedDestination dst 54.224.0.0/12       # Amazon
acl TrustedDestination dst 52.192.0.0/11       # Amazon

acl TrustedDestination dst 23.37.48.0/20       # Akamai

acl TrustedDestination dst 178.162.216.177/32  # Telekom
acl TrustedDestination dst 46.29.100.36/32     # Telekom
acl TrustedDestination dst 84.16.232.91/32     # Telekom
acl TrustedDestination dst 84.16.232.89/32     # Telekom
acl TrustedDestination dst 62.157.140.200/32   # Telekom
acl TrustedDestination dst 54.77.11.79/32      # Telekom
#acl TrustedDestination dst 54.194.222.178/32   # Telekom

acl TrustedDestination dst 213.154.225.245/32  # CAcert
acl TrustedDestination dst 2001:7b8:3:9c::245/128  # CAcert

acl step1 at_step SslBump1
ssl_bump none broken_sites
ssl_bump none TrustedDestination
ssl_bump peek step1
ssl_bump bump all
sslproxy_foreign_intermediate_certs /etc/squid/ssl/ca.pem
#sslproxy_foreign_intermediate_certs /usr/local/etc/squid/ssl/fake-server-ca.crt

sslproxy_cert_error allow TrustedDestination
sslproxy_cert_error deny all

####################################################################################

# Uncomment and adjust the following to add a disk cache directory.
#cache_dir ufs /var/spool/squid 100 16 256

# Leave coredumps in the first cache dir
coredump_dir /var/cache/squid

#
# Add any of your own refresh_pattern entries above these.
#
refresh_pattern ^ftp:		1440	20%	10080
refresh_pattern ^gopher:	1440	0%	1440
refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
refresh_pattern .		0	20%	4320

#dns_nameservers 127.0.0.1 ::1

always_direct allow all

forwarded_for delete
via off
follow_x_forwarded_for deny all

#logformat combined %>a %[ui %[un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
#logformat combined %>a %[ui %[un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh

#access_log daemon:/var/log/squid/access.log combined

shutdown_lifetime 1 second

# Falls der Server mehrere IP Adressen hat, kann man hier angeben,
# welche genutzt werden soll
# tcp_outgoing_address <ipv6 >dresse>
# tcp_outgoing_address <ipv4 Adresse>

Da der Proxy die gleiche Root-CA verwendet, wie das VPN Gateway, muss das CA Zertifikat nicht extra in ANdroid installiert werden.

iptables

Damit der transparente Proxy Server auch verwendet wird, müssen ein paar Firewall Regeln für den Linux-Kernel gesetzt werden.

IPv4

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

-N DIVERT
-A DIVERT -j MARK --set-mark 1
-A DIVERT -j ACCEPT

-A PREROUTING -p tcp -m socket -j DIVERT
-A PREROUTING -p tcp --dport 80  -j TPROXY --tproxy-mark 0x1/0x1 --on-port 3128
-A PREROUTING -p tcp --dport 443 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 3129

COMMIT

# Completed on Sat Aug 27 15:39:35 2016
# Generated by iptables-save v1.6.0 on Sat Aug 27 15:39:35 2016
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# NAT to external IP
-A POSTROUTING -s 10.0.0.0/8 -j SNAT --to-source 172.31.1.100

COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:VPN - [0:0]

-A INPUT -i lo -j ACCEPT

-A INPUT -s 10.3.0.0/24 -j ACCEPT
#-A INPUT -m conntrack --ctstate INVALID -s 10.3.0.0/24 -j LOG --log-prefix "invalid_dropped: " --log-level 4
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p udp -m multiport --dports 500,4500 -j ACCEPT

-A INPUT -j DROP

-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 10.3.0.0/24 -j ACCEPT

-A FORWARD -j REJECT

COMMIT

IPv6

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

-N DIVERT
-A DIVERT -j MARK --set-mark 1
-A DIVERT -j ACCEPT

-A PREROUTING -p tcp -m socket -j DIVERT
-A PREROUTING -p tcp --dport 80  -j TPROXY --tproxy-mark 0x1/0x1 --on-port 3128
-A PREROUTING -p tcp --dport 443 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 3129

COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# NAT to external IP, if provider does not provide a prefix
-A POSTROUTING -s fc00::/16 -j SNAT --to-source 2a00:1::2

COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:VPN - [0:0]
:ICMP - [0:0]

-A INPUT -i lo -j ACCEPT

-A INPUT -s fc00::/16 -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

-A INPUT -p ipv6-icmp -j ICMP

-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT

-A INPUT -s fc00::/16 -j LOG --log-prefix "ip6_input_dropped: " --log-level 4
-A INPUT -j DROP

-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s fc00::/16 -j ACCEPT

-A FORWARD -j LOG --log-prefix "ip6_forward_dropped: " --log-level 4
-A FORWARD -j REJECT

-A ICMP -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT
# Allow some other types in the ICMP chain, but rate limit.
-A ICMP -p icmpv6 --icmpv6-type echo-request -m limit --limit 900/min -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type echo-reply -m limit --limit 900/min -j ACCEPT
# Allow others ICMPv6 types but only if the hop limit field is 255.
-A ICMP -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
-A ICMP -p icmpv6 --icmpv6-type redirect -m hl --hl-eq 255 -j ACCEPT
-A ICMP -p icmpv6 -j LOG --log-prefix "dropped IN ICMPv6"
-A ICMP -p icmpv6 -j DROP

COMMIT

Systemd

Damit die Rules auch geladen werden, muss der Systemd noch ein Config File bekommen, sofern nicht distributionseigene Tools genutzt werden können.

[Unit]
Description=iptables
Documentation=man:iptables(8)
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/iptables-restore /etc/iptables/active4 ; /sbin/ip6tables-restore /etc/iptables/active6

[Install]
WantedBy=multi-user.target

Es wird noch eine Routing Tabelle für den transparenten Proxy gebraucht:

[Unit]
Description=tproxy
Documentation=man:ip(8)
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes

ExecStart=\
  /usr/bin/ip -f inet  rule add fwmark 1 lookup 100 ; \
  /usr/bin/ip -f inet6 rule add fwmark 1 lookup 100 ; \
  /usr/bin/ip -f inet  route add local default dev eth0 table 100 ; \
  /usr/bin/ip -f inet6 route add local default dev eth0 table 100

ExecStop=\
  /usr/bin/ip -f inet  route del local default dev eth0 table 100 ; \
  /usr/bin/ip -f inet6 route del local default dev eth0 table 100 ; \
  /usr/bin/ip -f inet  rule del fwmark 1 lookup 100 ; \
  /usr/bin/ip -f inet6 rule del fwmark 1 lookup 100

[Install]
WantedBy=multi-user.target

Android

Leider weist Android viele Lücken im Bereich der Remote Managements auf. So wird z.B. die automatische Konfiguration von Proxy Servern nicht unterstützt. Beim Firefox muss man in about:config folgende Paramter ändern:

  • network.proxy.type =2
  • network.proxy.autoconfig_url = http://wpad/proxy.pac
  • network.proxy.autoconfig_retry_interval_max = 86400

Ebenso muss die VPN Konfiguration manuell vorgenommen werden. Es sind festzulegen:

  • Protokoll: IKE2/RSA
  • DNS Server: Interne IP-Adresse, die bei aktiviertem VPN erreichbar sein muss
  • IP-Adresse (nicht DNS Name) des VPN-Servers im Internet

Mit diesen Einstellungen kann das VPN als "immer aktiv" gewählt werden. Es ist übrigens bemerkenswert, dass selbst bei gleicher Android Version (z.B. 7.1) manche Smartphones IKE2 unterstützen (z,B. Samsung S7 mit Telekom Android) und andere (z.B. LineageOS auf Google Nexus 6) nicht. Es gibt jedoch im Playstore die kostenlose App Strongswan, um dieses Manko zu beheben.

Wird die Strongswan App verwendet, dann muss in den erweiterten Einstellungen als "Server_identität" der DN des Serverzertifikats angegeben werden, der Hostname reicht nicht. Beispiel: CN=vpn.example.com, nicht jedoch vpn.example.com. Die Nutzung der Strongswan App ist den meisten Fällen die bessere Lösung.