snom Telefone mit OpenVPN konfigurieren

Gründe für VPN

Telefone und TK-Anlage sollten zuverlässig miteinander sicher kommunizieren können. Bei externen Telefonen wird diese Kommunikation jedoch oft durch NAT-Router und Firewalls behindert. Auch sollten selbst betriebene TK-Anlagen für fremde Geräte nur möglichst schwer / eingeschränkt erreichbar sein.
Aus diesen Gründen kann der Zugang zur TK-Anlage über eine (UDP-basierte) VPN-Verbindung sinnvoll sein.

Die erste Konfiguration solcher externen Telefone erfolgt am einfachsten über den Redirection Service der jeweiligen Hersteller. Dessen Konfiguration bieten wir (und andere) auch als Service an.

OpenVPN

Hersteller wie snom, htek und Yealink bieten Telefone an, welche einen OpenVPN-Client in ihrer Firmware integriert haben und damit ihren Traffic direkt zu einem entfernten OpenVPN-Server verschlüsselt übertragen können. Die Konfiguration unterscheidet sich je nach Hersteller: Wir fokussieren uns hier auf die Erstellung der Konfiguration für Telefone der Firma snom. Aspekte der Sicherheit, wie bspw. Zugriffskontrolle und unterschiedliche CA, sind hier unberücksichtigt.

Server-Konfiguration

Die Konfiguration eines OpenVPN-Servers kann auf unterschiedliche Weise erfolgen. Bei der verwendeten Konfiguration sollte insbesondere auf das verwendete Protokoll (UDP) und den Anschluss-Typ (tun) sowie einer separaten key-direction-Anweisung geachtet werden. Das interne LAN-Netz, indem der OpenVPN-Server steht, ist in diesem Beispiel 192.168.1.0.

Sollte der OpenVPN-Server nicht gleichzeitig das Default-Gateway oder der Server sein, auf dem die TK-Anlage als B2BUA läuft, so muss zusätzlich noch ein entsprechendes Routing eingerichtet werden, damit die IP-Pakete der TK-Anlage zum Telefon finden. Möglicherweise kann hier auch der Einsatz eines Mid-Registrar-Servers sinnvoll sein.

port 1195
proto udp
dev tun

client-config-dir /etc/openvpn/clients

ca /etc/ssl/cacert.pem
cert /etc/openvpn/openvpn_certs/my.server.crt
key /etc/openvpn/openvpn_certs/my.server.key 
dh /etc/openvpn/openvpn_certs/dh1024.pem

tls-auth /etc/openvpn/openvpn_certs/ta.key 
key-direction  	1 

# Welche IPs sollen den verbundenen Clients zugewiesen werden. 
server 10.30.0.0 255.255.255.0
mode server

cipher AES-256-CBC
push "cipher AES-256-CBC"
push "route 192.168.1.0 255.255.255.0"
push "route 10.30.0.0 255.255.255.0"
push "redirect-gateway def1 bypass-dhcp bypass-dns"

# Compress funktioniert nicht bei alter snom Firmware!
compress lzo

user nobody
group nobody   

ifconfig-pool-persist /etc/openvpn/ipp.txt
keepalive 10 60

max-clients 25

persist-key
persist-tun

status /var/log/openvpn-status.log
log-append /var/log/openvpn.log
verb 4

Client-Konfiguration

Die snom Telefone erwarten ihre VPN-Konfiguration in einem TAR-File. Nachfolgend wird beschrieben, wie dieses beispielhaft für ein snom D785 Telefon mit der Firmware 10.1.57.14 und einem bereits installierten VPN-Firmware-Patch automatisch erzeugt werden kann. Seine URL sollte im Web-Interface des Telefons unterhalb des aktivierten vpn-Parameters (AdvancedQoS/Security im Abschnitt Security) angegeben werden.

Das File wertet die im Server verwendeten Parameter für dev, port, proto, chipher, compress, ca sowie tls-auth (key-direction) aus und fügt deren Werte dynamisch (bei Dateien inline) in die Konfiguration ein. Dieses erspart einen stetigen Abgleich zwischen den Konfigurationsdateien.
Benötigt wird zudem Zugriff auf die folgenden Dateien:

DateiErklärung
/etc/ssl/private/cakey.pemCA Schlüssel zum Signieren des Telefon-Zertifikats
/etc/ssl/cacert.pemCA Zertifikat
/etc/ssl/serialSerial-Datei der CA
/etc/openvpn/openvpn.confKonfigurationsdatei des OpenVPN-Servers

#!/usr/bin/haserl-lua5.2 --shell=lua --upload-limit=256
<% 
local userserial = ENV["HTTP_USER_AGENT"]:match("; snom[^ -]*.* [0-9%.]* .* (%x%x%x%x%x%x%x%x%x%x%x%x) ") or "NO-SERIAL"
local serverfile = assert(io.open("/etc/openvpn/openvpn.conf","r"),"cannot open serverconf")
local serverconf = "\n"..serverfile:read("*all"):gsub("(#[^\n]*)",""):gsub("(\n%s*\n)","\n"):gsub("\n(%s+)","").."\n"

local clientconf = "\nclient\n"..
"resolv-retry infinite\n"..
"pull\n"..
"nobind\n\n"..
"persist-key\n"..
"persist-tun\n\n"..
"dhcp-option DNS 10.30.0.1\n"..
"dhcp-option NTP 10.30.0.1\n\n"..
"explicit-exit-notify\n\n"..
"mute-replay-warnings\n"..
"remote-cert-tls server\n\n"..
"verb 0\n\n"..
 "###   dynamic values from server config ###\n\n"..
   (serverconf:match("(port%s[^\n]*\n)") or "") ..
   (serverconf:match("(proto%s[^\n]*\n)") or "") ..
   (serverconf:match("(dev%s[^\n]*\n)")  or "") ..
   (serverconf:match("(cipher%s[^\n]*\n)") or "") ..
   (serverconf:match("(compress%s[^\n]*\n)") or serverconf:match("(compress\n)") or "") ..
   ("remote "..ENV.HTTP_HOST:match("^([^:]*)").."\n")

local ca = serverconf:match("(<ca>.*</ca>)")
if ca then
 clientconf = clientconf .. ca .."\n"
else
 ca = serverconf:match("ca%s+([^%s]+)")
 if ca then
  file = assert(io.open(ca, "r"),"Cannot open CA-File "..ca)
  clientconf  = clientconf .. "<ca>\n"..file:read("*all"):match("%-%-%-%-%-BEGIN CERTIFICATE%-%-%%-%-%-.*%-%-%-%-%-END CERTIFICATE%-%-%-%-%-").."\n</ca>\n"
  io.close(file)
 end
end

local ta = serverconf:match("(<tls%-auth>.*</tls%-auth>)")
if ta then
 clientconf = clientconf .. ta .."\n"
else
 ta = serverconf:match("tls%-auth%s+([^%s]+)")
 if ta then
  file = assert(io.open(ta, "r"),"Cannot open TLS-AUTH-File "..ta)
  clientconf  = clientconf .. "<tls-auth>\n"..file:read("*all").."\n</tls-auth>\n"
  io.close(file)
  ta = serverconf:match("tls%-auth%s+[^%s]+%s+([01])") or serverconf:match("key%-direction%s+([01])") or "0"
  clientconf = clientconf .. "key-direction "..(ta == "0" and "1" or "0").."\n"
 else
  clientconf = clientconf .. "## No TLS-AUTH-file specified \n"
 end
end

local pkHandle = io.popen("openssl genrsa 2048 2>/dev/null")
local key = pkHandle:read("*a"):sub(1,-2)
pkHandle:close()
local crtHandle = io.popen("echo \""..key.."\" | openssl req -new -sha256 -key /dev/stdin -subj \"/C=DE/ST=Hamburg/L=Hamburg/O=voiscout/OU=pbx/CN="..userserial.."/serialNumber="..userserial..
      "\" | openssl x509 -days 7600 -sha256 -CA /etc/ssl/cacert.pem -CAkey /etc/ssl/private/cakey.pem -req -CAcreateserial -CAserial /etc/ssl/serial 2>/dev/null" )
clientconf = clientconf.."<cert>\n"..crtHandle:read("*a"):sub(1,-2).."\n</cert>\n<key>\n"..key.."\n</key>"
crtHandle:close()
%>
content-type: application/x-tar

<%
 local pkHandle = io.popen("mkdir -p /tmp/"..userserial .." && cd /tmp/"..userserial.."/  && echo \""..clientconf .."\" > vpn.cnf && tar cf - vpn.cnf 2>/dev/null")
 io.write(pkHandle:read("*a"):sub(1,-2))
 pkHandle:close()
%>