Blog

Connexion d'un client l2tp/IPsec sous Linux

Écrit le 13 05 2013 par Kévin MET _

Dans ce billet qui est la suite de celui-ci nous allons voir comment connecter un client linux à un VPN l2tp/IPsec via xl2tpd et openswan. Nous allons également voir comment superviser cette connexion et tenter de la remettre en place automatiquement en cas de problème via des scripts Bash.

Installation et configuration d'IPsec

Pour commencer nous allons installer openswan en le compilant. Les étapes sont les mêmes que dans le précédents tutoriels, je vais donc simplifier pour cette fois :


# wget --no-check-certificate https://s3-ap-northeast-1.amazonaws.com/openswanjp/openswan-2.6.38.tar.gz
# tar xvzf openswan-2.6.38.tar.gz
# cd openswan-2.6.38/
# make programs
# make install

Et pour le kernel :


# for each in /proc/sys/net/ipv4/conf/*; do echo 0 > $each/accept_redirects && echo 0 > $each/send_redirects; done

On va pouvoir s'attaquer à la configuration. Comme la dernière fois, tout se passe dans /etc/ipsec.conf. Pour l'exemple, on va considérer que l'adresse IP du serveur VPN est 66.66.66.66 et que derrière ce VPN existe un subnet du type 192.168.66.0/24 auquel on souhaite avoir accès. On va également partir du principe que le client qui essaye de se connecter est derrière un NAT et qu'il a pour IP 192.168.1.1


# /etc/ipsec.conf - Openswan IPsec configuration file
# RCSID $Id: ipsec.conf.in,v 1.16 2005/07/26 12:29:45 ken Exp $

# This file:  /usr/share/doc/openswan/ipsec.conf-sample
#
# Manual:     ipsec.conf.5


version 2.0     # conforms to second version of ipsec.conf specification

config setup
	virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,!%v4:192.168.66.0/24
	nat_traversal=yes
	protostack=netkey
	oe=off
	plutoopts="--interface=eth0"
conn L2TP-PSK-VPN0
	authby=secret
	pfs=no
	auto=add
	keyingtries=3
	dpddelay=30
	dpdtimeout=120
	dpdaction=clear
	rekey=yes
	ikelifetime=8h
	keylife=1h
	type=transport
	left=192.168.1.1
	leftnexthop=%defaultroute
	leftprotoport=17/1701
	right=66.66.66.66
	rightprotoport=17/1701

Dans virtual_private on interdit une adresse appartenant au réseau 192.168.66.0/24 de se connecter, en effet ça ne pourrait pas fonctionner correctement avec une IP dans le même subnet. Pour left on utilise notre IP privée et pour right l'ip du serveur. On indique également que IPsec doit fonctionner en mode transport et qu'il doit "tunneliser" le trafic en provenance du port 1701 en udp à destination du port 1701 en udp également ( leftprotoport=17/1701 et rightprotoport=17/1701). Enfin, on nomme la connexion à ce serveur L2TP-PSK-VPN0 que l'on devra utiliser dans les commandes pour lancer ou arrêter IPsec.

Il ne reste plus que le fichier /etc/ipsec.secrets qui va simplement contenir la clé pré-partagée :


# RCSID $Id: ipsec.secrets.proto,v 1.3.6.1 2005/09/28 13:59:14 paul Exp $
# This file holds shared secrets or RSA private keys for inter-Pluto
# authentication.  See ipsec_pluto(8) manpage, and HTML documentation.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.  Suitable public keys, for ipsec.conf, DNS,
# or configuration of other implementations, can be extracted conveniently
# with "ipsec showhostkey".
192.168.1.1 66.66.66.66 : PSK "monpasswordrandomquitue"

Il n'y a pas grand chose à dire sur ce fichier de conf. On va donc passer à la partie xl2tpd

Installation et configuration de l2tp

La partie l2tp est géré par xl2tpd que l'on installe tout simplement avec :


# apt-get install xl2tpd

Pour la configuration, le fichier est /etc/xl2tpd/xl2tpd.conf


[lac vpn0]
	lns = 66.66.66.66
	ppp debug = yes
	pppoptfile = /etc/ppp/options.l2tpd.vpn0
	length bit = yes

On indique donc que le lac (L2TP Access Concentrator) se nomme vpn0 et on indique également le fichier contenant les options ppp : /etc/ppp/options.l2tpd.vpn0

Le contenu de /etc/ppp/options.l2tpd.vpn0 :


ipcp-accept-local
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
idle 1800
mtu 1410
mru 1410
defaultroute
usepeerdns
debug
lock
connect-delay 5000
name plop
password mon_beau_password

On y indique son nom d'utilisateur et son password (name plop et password mon_beau_password). Pour les autres options je vous renvoie sur le man de pppd

Démarrage et arrêt de la connexion VPN

Pour lancer la connexion VPN il faut initialiser IPsec.


# ipsec auto --up L2TP-PSK-VPN0

On devrait obtenir quelque chose dans ce style :


104 "L2TP-PSK-VPN0" #136: STATE_MAIN_I1: initiate
003 "L2TP-PSK-VPN0" #136: ignoring unknown Vendor ID payload [4f457f7e637f7679517f4a5a]
003 "L2TP-PSK-VPN0" #136: received Vendor ID payload [Dead Peer Detection]
003 "L2TP-PSK-VPN0" #136: received Vendor ID payload [RFC 3947] method set to=115 
106 "L2TP-PSK-VPN0" #136: STATE_MAIN_I2: sent MI2, expecting MR2
003 "L2TP-PSK-VPN0" #136: NAT-Traversal: Result using draft-ietf-ipsec-nat-t-ike (MacOS X): i am NATed
108 "L2TP-PSK-VPN0" #136: STATE_MAIN_I3: sent MI3, expecting MR3
003 "L2TP-PSK-VPN0" #136: received Vendor ID payload [CAN-IKEv2]
004 "L2TP-PSK-VPN0" #136: STATE_MAIN_I4: ISAKMP SA established {auth=OAKLEY_PRESHARED_KEY cipher=aes_128 prf=oakley_sha group=modp2048}
117 "L2TP-PSK-VPN0" #137: STATE_QUICK_I1: initiate
004 "L2TP-PSK-VPN0" #137: STATE_QUICK_I2: sent QI2, IPsec SA established transport mode {ESP=>0x2c6414b5 <0x8fa973f2 xfrm=AES_128-HMAC_SHA1 NATOA=none NATD=none DPD=enabled}

Ensuite on lance la connexion l2tp :


echo "c vpn0" > /var/run/xl2tpd/l2tp-control

Si le lien xl2tpd monte bien, nous devrions avoir une nouvelle interface ppp visible via ifconfig :


ppp0      Link encap:Point-to-Point Protocol  
          inet addr:10.0.1.1  P-t-P:10.0.1.200  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1400  Metric:1
          RX packets:4 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3 
          RX bytes:81 (81.0 B)  TX bytes:72 (72.0 B)

On voit donc que notre nouvelle interface est UP et il ne reste plus qu'à ajouter une route passant par cette interface pour l'utiliser. Dans notre cas, nous voulions joindre le sous-réseau 192.168.66.0/24 :


# ip route add 192.168.66.0/24 via 10.0.1.200

Sur le serveur il ne faut pas oublié de mettre un MASQUERADE sur le subnet des interfaces ppp sinon les paquets arriveront avec des adresses en 10.0.1.0/27 sur le subnet 192.168.66.0/24 qui ne sera pas ou les renvoyer... Pour rappel, il faut donc ajouter cette règle à iptables sur le serveur :


# iptables -t nat -A POSTROUTING -s 10.0.1.0/27 -j MASQUERADE

Automatisation et Supervision du lien VPN

Pour automatisé et superviser cette connexion VPN, j'ai écrit quelques scripts que je vous livre ici :

Le premier script est un script qui permet de superviser la connexion. Il est très commenté pour en faciliter la compréhension. Il faut l'adapter à votre situation en indiquant en début de script les valeurs qui correspondent à votre connexion. En gros, le script check la connexion VPN en pinguant toutes les 30 secondes l'adresse de l'interface ppp. Si il détecte un problème sur la partie IPsec il relance la connexion IPsec, si il détecte un problème sur l2tp, il relance la connexion l2tp. Il vous envoie un mail en cas de problème et envoie les logs dans /var/log/vpn0.log par défaut (N'oubliez pas de faire un logrotate). Bref, il est plutôt sympa ce petit script 😎


#!/bin/bash

# Supervise l2tp/ipsec connection like a champ !
# Author : Kévin MET (https://mnt-tech.fr/)

FAILMAX=2 # nombres maximum de fails avant de relancer
KEEPALIVE=30 # nombres de secondes entres les controles
NEXTRESTART=1800 # duree en secondes pendant laquelle on ne relance qu'un seule fois le vpn
IPSEC_LOCK="/tmp/ipsec_$$" # lock de la relance ipsec
L2TP_LOCK="/tmp/l2tp_$$" # lock de la relance l2tp
ADMIN="admins@exemple.fr" # adresse a contacter
VPN="vpn0" # le nom du vpn l2tp dans la conf de xl2tpd
TUNNEL="L2TP-PSK-VPN0" # le nom du tunnel ipsec dans ipsec.conf
PINGCOUNT="4" # nombre de ping envoyés à chaque passe
FAIL=0 # reset du compteur de fail
LOGFILE="/var/log/vpn0.log" # fichier de log
PPP_GW="10.0.1.200" # la gateway de l'interface ppp0
LOCAL_IP="192.168.1.1" # l'adresse ip locale
ROUTE_VIA_VPN="192.168.66.0/24 via 10.0.1.200"
 
MESSAGE=""

function l2tprestart () {
	MESSAGE="Nombres de fails maximum ($FAILMAX) atteint: relance du vpn l2tpd $VPN sur `hostname -f`. Si tu as le temps regarde la cause de cette relance."
	echo "$NOW $MESSAGE" >> $LOGFILE
	# on détruit le vpn si il est encore UP
	echo "d $VPN" > /var/run/xl2tpd/l2tp-control
	sleep 5
	# on le relance
	echo "c $VPN" > /var/run/xl2tpd/l2tp-control
	sleep 10
	# on ajoute la route vers le LAN 
	ip route add $ROUTE_VIA_VPN 
	echo "$MESSAGE" | mail -s "Redemarrage du vpn l2tp sur `hostname -f`!" $ADMIN
	# on change la date de modification du fichier lock
	touch $L2TP_LOCK
	sleep 20
}

function ipsecrestart () {
	MESSAGE="Nombres de fails maximum ($FAILMAX) atteint: relance du tunnel ipsec $TUNNEL sur `hostname -f`. Si tu as le temps regarde la cause de cette relance."
	echo  "$NOW $MESSAGE" >> $LOGFILE
	# on détruit le tunnel si il est encore UP
	ipsec auto --down $TUNNEL >> $LOGFILE
	sleep 5
	# on le relance
	ipsec auto --up $TUNNEL >> $LOGFILE
	echo "$MESSAGE" | mail -s "Redemarrage du tunnel ipsec sur `hostname -f`!" $ADMIN
	# on change la date de modification du fichier lock
	touch $IPSEC_LOCK
	sleep 20
}

while (true); do
	# on check le ping vers la gateway de l'interface ppp. c'est ça qui va determiner si le vpn est fonctionnel
	NOW=`date`
	ping=$(ping -c $PINGCOUNT $PPP_GW | grep received | cut -d ',' -f2 | cut -d ' ' -f2)
	# si on perd plus d'un ping on considère que c'est un fail 
	if [ "$ping" -lt "3" ]; then
		echo "$NOW Impossible de pinger $PPP_GW Le VPN est down" >> $LOGFILE
		# on incrémente le compteur de fail de 1
		FAIL=`echo $FAIL+1 | bc`
	else
		echo "$NOW Le ping vers $PPP_GW est OK" >> $LOGFILE
		# on reinitialise le compteur de fail
		FAIL=0
	fi

	if [ "$FAIL" -ge "$FAILMAX" ] ; then
		# on commence par checker le tunnel ipsec
		echo "Controle de la couche ipsec" >> $LOGFILE
		CHECK_IPSEC=`ipsec auto --status | grep $LOCAL_IP | grep $TUNNEL | grep -c esp`
		if [ "$CHECK_IPSEC" -ne "1" ] ; then
			echo "La couche ipsec semble KO" >> $LOGFILE
			# on verifie que le fichier lock de ipsec ne soit pas trop recent pour eviter de nombreuses relances
			if [ -f $IPSEC_LOCK ]; then
				ATMP=`stat -c"%Y" $IPSEC_LOCK`
				TIMESTAMP=`date +%s`
				SSLR=`echo "$TIMESTAMP - $ATMP" | bc` # seconds since last restart
					if [ "$SSLR" -ge "$NEXTRESTART" ]; then
						rm -f $IPSEC_LOCK
						ipsecrestart
						FAIL=0
					else
						TIME_TO_SLEEP=`echo $NEXTRESTART - $SSLR | bc`
						MESSAGE= "Nombre de fails maximum ($FAILMAX) atteint, mais le tunnel ipsec a deja ete relance il y a $SSLR secondes. Seul un redemarrage toutes les $NEXTRESTART secondes est permis.Le vpn est donc DOWN et une relance du tunel ipsec sera faite dans $TIME_TO_SLEEP secondes."
						echo "$NOW $MESSAGE" >> $LOGFILE 
						echo "$MESSAGE" | mail -s "ATTENTION probleme de vpn sur `hostname -f` !!" $ADMIN
						sleep $TIME_TO_SLEEP
					fi
			else
				ipsecrestart
				FAIL=0
			fi
		else
			# si la couche ipsec est OK on va checker la partie l2tp
			echo "La couche ipsec est OK, on commence le check de la partie l2tp" >> $LOGFILE
			CHECK_L2TP=`ifconfig | grep 'addr:10.0.1.1'`
			echo $CHECK_L2TP
			if [ -z "$check" ]; then
				echo "La couche l2tp semble KO" >> $LOGFILE
				if [ -f $L2TP_LOCK ]; then
					ATMP=`stat -c"%Y" $L2TP_LOCK`
					TIMESTAMP=`date +%s`
					SSLR=`echo $TIMESTAMP - $ATMP | bc` # seconds since last restart
					if [ "$SSLR" -ge "$NEXTRESTART" ]; then
						rm -f $L2TP_LOCK
						l2tprestart
						FAIL=0
					else
						TIME_TO_SLEEP=`echo $NEXTRESTART - $SSLR | bc`
						MESSAGE="Nombre de fails maximum ($FAILMAX) atteint, mais le vpn l2tp a deja ete relance il y a $SSLR secondes. Seul un redemarrage toutes les $NEXTRESTART secondes est permis. Le vpn est donc DOWN et une relance du vpn l2tp sera faite dans $TIME_TO_SLEEP secondes."
						echo "$NOW $MESSAGE" >> $LOGFILE
						echo "$MESSAGE" | mail -s "ATTENTION probleme de vpn sur `hostname -f` !!" $ADMIN
						sleep $TIME_TO_SLEEP
					fi
				else
					l2tprestart
					FAIL=0
				fi
			else
				MESSAGE="Les couches ipsec et l2tp viennent d'etre relancees suite a un probleme non detecte par le script. Merci d'investiguer la cause du probleme."
				echo "$NOW $MESSAGE" >> $LOGFILE
				echo "$MESSAGE" | mail -s "ATTENTION relance du vpn sur `hostname -f` !!" $ADMIN
				echo "d vpn0" > /var/run/xl2tpd/l2tp-control
				ipsec auto --down $TUNNEL
				/etc/init.d/ipsec restart
				/etc/init.d/xl2tp restart
				sleep 5
				ipsec auto --up $TUNNEL
				sleep 5
				echo "c vpn0" > /var/run/xl2tpd/l2tp-control
			fi
		fi
	fi
	sleep $KEEPVALIVE
done

J'ai également ecrit deux autres scripts, un pour le lancement de la connexion VPN et un pour l'arrêt :



#!/bin/bash

# demarrage du tunnel ipsec
ipsec auto --up L2TP-PSK-VPN0

# temporisation le temps que le tunnel soit UP
sleep 10

# lancement du script qui check ipsec
/opt/vpn/check_vpn.sh &

# lancement du vpn via xl2tpd
echo "c vpn0" > /var/run/xl2tpd/l2tp-control

# temporisation le temps que le vpn soit UP
sleep 20

# ajout de la route pour router 10.0.1.0/27 via le vpn
ip route add 192.168.66.0/24 via 10.0.1.200

#!/bin/bash

# arret du vpn l2tpd
echo "d vpn0" > /var/run/xl2tpd/l2tp-control

# temporisation le temps que le vpn soit DOWN
sleep 5

# kill du tunnel ipsec
ipsec auto --down L2TP-PSK-VPN0

# kill du script check_ipsec.sh
process_id=`ps aux | grep "check_vpn.sh" | grep -v "grep" | awk '{print $2}'`
if [ $? -eq "0" ]; then
kill $process_id
fi

C'est fini pour cet article mais n'hésitez pas à laisser vos commentaires si vous ajoutez des améliorations à mes scripts ou pour toutes autres remarques.

♥ Partage sur tes réseaux sociaux ♥
Kévin MET
Kévin MET

Auteur de ce blog et gérant de la société MNT-TECH, je publie sur ce blog lorsque le temps me le permet et lorsqu'un sujet qui me parait intéressant n'a pas encore été abordé en français. Toutes les informations techniques présentes sur cette page peuvent être réutilisées moyennant le fait de citer la source.