49. Mise en place d'un VPN sous SSH

49.1. Introduction

Il existe trois manière conventionnelles de monter un VPN. La première concerne l'utilisation de ssh comme base de chiffrement du flux allié à une connexion via le protocole point à point (PPP).

Nous allons encapsuler un tunnel capable de transporter du TCP/IP ou de l'UDP/IP par dessus une pile qui est elle-même composée par TCP/IP.

Représentation de l'objectif :


------------------------------
|	Charge (telnet, http, etc)	|
------------------------------
|		TCP		|
------------------------------
|		IP		|
------------------------------
|		PPP		|
------------------------------
|		SSH		|
------------------------------
|		TCP		|
------------------------------
|		IP		|
------------------------------

On peut clairement constater le double usage de TCP/IP.

49.2. Création d'un tunnel

Nous allons vérifier que nous disposons des outils nécessaires pour ce travail, que ce soit au niveau du noyau ou logiciel. En effet, il faut avoir activé le support du protocol PPP dans le noyau, soit directement, soit par module. Concernant les logiciels, il est nécessaire d'installer pppd ainsi que le client ssh.

Nous voulons établir un tunnel VPN selon le schéma suivant :

Figure 39. VPN simple entre deux machines

Pour les besoins du TP, je vais réutiliser Netkit qui est, je dois le dire, un formidable outil.

Petit rappel sur la procédure d'installation, vous pouvez consulter mes notes ou directement la documentation officielle en ligne.

Avant de démarrer les hôtes virtuels, il est important de mettre en place l'organisation du réseau. Pour cela, nous allons utiliser deux hôtes (hostA et hostB), ainsi qu'un routeur intermédiaire.

Figure 40. Représentation du réseau netkit

Le fichier lab.conf :


adminbdd labo-vpn # cat lab.conf
hostA[0]="networkA"

hostB[0]="networkB"

router[0]="networkA"
router[1]="networkB"

adminbdd labo-vpn # cat hostA.startup 
ifconfig eth0 195.0.0.10 netmask 255.255.255.0 broadcast 195.0.0.255 up
route add default gw 195.0.0.1
mknod /dev/ppp c 108 0

adminbdd labo-vpn # cat hostB.startup 
ifconfig eth0 196.0.0.10 netmask 255.255.255.0 broadcast 196.0.0.255 up
route add default gw 196.0.0.1
mknod /dev/ppp c 108 0

adminbdd labo-vpn # cat router.startup 
ifconfig eth0 195.0.0.1 up
ifconfig eth1 196.0.0.1 up

Démarrage du laboratoire :


adminbdd labo-vpn # lstart 

======================== Starting lab ===========================
Lab directory: /var/opt/netkit/labo-vpn
Version:       1.0
Author:        Vanvincq Clément
Email:         vanvincq.clement@gmail.com
Web:           http://www.eof.eu.org/
Description:
Exercice VPN par SSH
=================================================================
Starting "hostA"...
Starting "hostB"...
Starting "router"...

The lab has been started.
=================================================================

Au cas où, on vérifie que tout fonctionne parfaitement :


hostA:~# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 1e:b4:f2:49:63:5f  
          inet addr:195.0.0.10  Bcast:195.0.0.255  Mask:255.255.255.0
          inet6 addr: fe80::1cb4:f2ff:fe49:635f/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:11 errors:0 dropped:0 overruns:0 frame:0
          TX packets:11 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:692 (692.0 B)  TX bytes:846 (846.0 B)
          Interrupt:5

hostA:~# ping -c3 196.0.0.10
PING 196.0.0.10 (196.0.0.10) 56(84) bytes of data.
64 bytes from 196.0.0.10: icmp_seq=1 ttl=63 time=0.729 ms
64 bytes from 196.0.0.10: icmp_seq=2 ttl=63 time=0.288 ms
64 bytes from 196.0.0.10: icmp_seq=3 ttl=63 time=0.372 ms

--- 196.0.0.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.288/0.463/0.729/0.191 ms
		  
hostB:~# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 72:e1:04:ca:38:92  
          inet addr:196.0.0.10  Bcast:196.0.0.255  Mask:255.255.255.0
          inet6 addr: fe80::70e1:4ff:feca:3892/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:11 errors:0 dropped:0 overruns:0 frame:0
          TX packets:11 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:692 (692.0 B)  TX bytes:846 (846.0 B)
          Interrupt:5
		  
hostB:~# ping -c3 195.0.0.10
PING 195.0.0.10 (195.0.0.10) 56(84) bytes of data.
64 bytes from 195.0.0.10: icmp_seq=1 ttl=63 time=0.312 ms
64 bytes from 195.0.0.10: icmp_seq=2 ttl=63 time=0.236 ms
64 bytes from 195.0.0.10: icmp_seq=3 ttl=63 time=0.353 ms

--- 195.0.0.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.236/0.300/0.353/0.050 m		  

Maintenant, créons un utilisateur sur le deuxième hôte :


hostB:/hosthome# adduser vpn
Adding user `vpn' ...
Adding new group `vpn' (1001) ...
Adding new user `vpn' (1001) with group `vpn' ...
Creating home directory `/home/vpn' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
Changing the user information for vpn
Enter the new value, or press ENTER for the default
        Full Name []: 
        Room Number []: 
        Work Phone []: 
        Home Phone []: 
        Other []: 
Is the information correct? [Y/n] Y
hostB:~# /etc/init.d/ssh start
Starting OpenBSD Secure Shell server: sshd

hostA:~# ssh -l vpn 196.0.0.10
The authenticity of host '196.0.0.10 (196.0.0.10)' can't be established.
RSA key fingerprint is 53:d4:13:1a:f0:a3:e9:43:6b:04:6c:d0:bc:63:10:29.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '196.0.0.10' (RSA) to the list of known hosts.
vpn@196.0.0.10's password: 
vpn@hostB:~$

Bien, tout est prêt pour initier la connexion PPP. On va ajouter une clef RSA pour simplifier l'authentification SSH.


hostA:~/.ssh# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
/root/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
c0:e4:0a:f1:6e:0e:6b:9c:99:62:b0:52:76:05:87:30 root@hostA
The key's randomart image is:
+--[ RSA 2048]----+
|  E...o          |
|   +.*           |
|  . . =          |
|   o o .         |
|. + =   S        |
|.= X             |
|+.B .            |
|oo               |
|                 |
+-----------------+
hostA:~/.ssh# ls
id_rsa  id_rsa.pub  known_hosts
hostA:~/.ssh# ssh vpn@196.0.0.10 "mkdir -p .ssh"
vpn@196.0.0.10's password: 
hostA:~/.ssh# cat ./id_rsa.pub | ssh vpn@196.0.0.10 "cat >> .ssh/authorized_keys"
hostA:~/.ssh# ssh-agent
hostA:~/.ssh# ssh-add
Enter passphrase for /root/.ssh/id_rsa: 
Identity added: /root/.ssh/id_rsa (/root/.ssh/id_rsa)
hostA:~/.ssh# ssh-add -l
2048 c0:e4:0a:f1:6e:0e:6b:9c:99:62:b0:52:76:05:87:30 /root/.ssh/id_rsa (RSA)
hostA:~/.ssh# ssh vpn@196.0.0.10 hostname
hostB

Il reste une dernière chose à installer : la commande sudo. Normalement, je ne devrais pas à avoir le faire mais dans ma version de netkit, il y a un bug référencé qui empêche la prise en compte de la directive USE_SUDO=yes dans le fichier /$HOME_NETKIT/netkit.conf. Pour cela j'ai téléchargé le paquet binaire sudo pour la version 4 de Debian (sinon, il risquerait d'y avoir un problème de dépendance avec glibc. Pour importer ce paquet directement dans les machines virtuelles, il suffit de le placer dans le répertoire /home de la machine hôte. Ensuite, rendez-vous dans le /hosthome des machines virtuelles pour y retrouver vos documents ;)


hostA:/hosthome# dpkg -i sudo_1.7.9-1_i386.deb 
Selecting previously deselected package sudo.
(Reading database ... 31970 files and directories currently installed.)
Unpacking sudo (from sudo_1.7.9-1_i386.deb) ...
Setting up sudo (1.7.9-1) ...
Processing triggers for man-db ...
hostB:/hosthome# dpkg -i sudo_1.7.9-1_i386.deb 
Selecting previously deselected package sudo.
(Reading database ... 31970 files and directories currently installed.)
Unpacking sudo (from sudo_1.7.9-1_i386.deb) ...
Setting up sudo (1.7.9-1) ...
Processing triggers for man-db ...

Configuration du fichier /etc/sudoers pour l'utilisateur vpn :


hostB:/home/vpn# visudo
[...]
# Ajoutons cette ligne
vpn ALL=NOPASSWD: /usr/sbin/pppd

Ok, tout est prêt pour le grand test :)


hostA:~# pppd updetach noauth debug pty "ssh -l vpn -t 196.0.0.10 sudo /usr/sbin/pppd nodetach notty noauth" ipparam vpn 192.168.0.1:192.168.0.2
using channel 3
Using interface ppp0
Connect: ppp0 <--> /dev/pts/0
rcvd [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x5254a39a> <pcomp> <accomp>]
sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0xb2ebb5fa> <pcomp> <accomp>]
sent [LCP ConfAck id=0x1 <asyncmap 0x0> <magic 0x5254a39a> <pcomp> <accomp>]
rcvd [LCP ConfAck id=0x1 <asyncmap 0x0> <magic 0xb2ebb5fa> <pcomp> <accomp>]
sent [LCP EchoReq id=0x0 magic=0xb2ebb5fa]
sent [CCP ConfReq id=0x1 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
sent [IPCP ConfReq id=0x1 <compress VJ 0f 01> <addr 192.168.0.1>]
rcvd [LCP EchoReq id=0x0 magic=0x5254a39a]
sent [LCP EchoRep id=0x0 magic=0xb2ebb5fa]
rcvd [CCP ConfReq id=0x1 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
sent [CCP ConfAck id=0x1 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
rcvd [IPCP ConfReq id=0x1 <compress VJ 0f 01> <addr 0.0.0.0>]
sent [IPCP ConfNak id=0x1 <addr 192.168.0.2>]
rcvd [LCP EchoRep id=0x0 magic=0x5254a39a]
rcvd [CCP ConfAck id=0x1 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
Deflate (15) compression enabled
rcvd [IPCP ConfAck id=0x1 <compress VJ 0f 01> <addr 192.168.0.1>]
rcvd [IPCP ConfReq id=0x2 <compress VJ 0f 01> <addr 192.168.0.2>]
sent [IPCP ConfAck id=0x2 <compress VJ 0f 01> <addr 192.168.0.2>]
Cannot determine ethernet address for proxy ARP
local  IP address 192.168.0.1
remote IP address 192.168.0.2

Bien, cela fonctionne :) Vérifions quelques détails au cas où !


hostA:~# ifconfig
eth0      Link encap:Ethernet  HWaddr 1e:b4:f2:49:63:5f  
          inet addr:195.0.0.10  Bcast:195.0.0.255  Mask:255.255.255.0
          inet6 addr: fe80::1cb4:f2ff:fe49:635f/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:369 errors:0 dropped:0 overruns:0 frame:0
          TX packets:419 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:52240 (51.0 KiB)  TX bytes:58850 (57.4 KiB)
          Interrupt:5 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:460 (460.0 B)  TX bytes:460 (460.0 B)

ppp0      Link encap:Point-to-Point Protocol  
          inet addr:192.168.0.1  P-t-P:192.168.0.2  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:5 errors:0 dropped:0 overruns:0 frame:0
          TX packets:5 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3 
          RX bytes:78 (78.0 B)  TX bytes:72 (72.0 B)


hostA:~# ping -c 3 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=5.84 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=3.25 ms
64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=3.72 ms

--- 192.168.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2023ms
rtt min/avg/max/mdev = 3.259/4.276/5.842/1.125 ms

Yeah! it works! Maintenant quelques explication sur la commande :

pppd debug updetach noauth

Il se peut que la commande pppd demande le chemin complet de l’application. Dans ce cas, utilisez la commande whereis pour compléter l’information. Les trois mots suivants sont des options. L’option debug n’est présente que pour fournir des renseignements complémentaires au cas où votre connexion ne s’effectuerait pas, updetach permet de détacherpppd du tty où il a été lancé pour le transformer en daemon. Pour terminer, noaut demande à ce que pppd ne se préoccupe pas de la partie authentification. Celle-ci, comme nous l’avons déjà dit, reviendra à ssh.

pty "ssh -l login -t AdresseDistante

L’option pty permet ici de passer les commandes suivantes au shell distant que nous venons d’ouvrir. L’option -t de ssh, quant à elle, force l’allocation du pseudo-tty sur la machine distante. C’est en quelque sorte l’équivalent de ce que nous venons de voir pour pppd avec pty.

pppd noauth 192.168.254.254:192.168.253.253"

Cette dernière ligne attribue une adresse privée virtuelle à chacun des bouts du réseau. Vous pouvez faire communiquer vos machines de l’une vers l’autre sans aucun souci. Félicitations, vous venez de créer votre premier VPN.

49.3. Mettre en place l’IP_FORWARDING

L’IP_FORWARDING est un drapeau qui dit au noyau linux s’il doit laisser passer les paquets à travers la machine ou si, au contraire, il doit les stopper. Par défaut, dans les distributions actuelles, il est initialisé à 0 à chaque démarrage. Le script suivant vous permet de l’initialiser à 1 si vous souhaitez qu’une des deux machines, ou les deux, servent de passerelle pour d’autres machines de vos réseaux, ce qui permet non plus uniquement la communication d’un poste à un autre, mais également d’un réseau à un autre.


#!/bin/sh
echo "Mise en place des règles de transfert IP"
echo 1 > /proc/sys/net/ipv4/ip_forward
echo -n "/proc/sys/net/ipv4/ip_forward: "
cat /proc/sys/net/ipv4/ip_forward
for forwarding in /proc/sys/net/ipv4/conf/*/forwarding
do
      echo -n "$forwarding: ";
      interface=‘dirname $forwarding‘
      interface=‘basename $interface‘
      case "$interface" in
            ppp*|eth1)   # liste des interfaces où le transfert
                         # doit être activé
                         echo 1 > $forwarding
                         ;;
            *)           # On désactive les interfaces qui ne nécessitent
 # pas le transfert.
                         echo 0 > $forwarding
                         ;;
      esac
      cat $forwarding
done

49.4. Configuration du serveur ssh

Le serveur SSH est livré avec son fichier de configuration par défaut, nommé sshd_config. Par défaut, celui-ci écoute sur le port 22, nous allons le modifier pour qu’il écoute sur le port 9876. Ceci a pour résultat deux choses :


# vpn/etc
# Configuration spécifique du port
Port 9876
PidFile /var/run/sshd_vpn.pid
HostKey /etc/ssh/ssh_host_key
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
ServerKeyBits 768
LoginGraceTime 600
KeyRegenerationInterval 3600
# Configuration des niveaux des journaux
SyslogFacility AUTH
LogLevel INFO
RSAAuthentication yes
AllowUsers sshvpn
# Restrictions
#
IgnoreRhosts yes
IgnoreUserKnownHosts yes
PermitRootLogin no
StrictModes yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
RhostsAuthentication no
RhostsRSAAuthentication no
X11Forwarding no
PrintMotd no
KeepAlive yes

49.5. Configuration du fichier VPN

Dès lors que vos essais initiaux auront été fructueux, vous désirerez sûrement automatiser votre vpn. Pour ce faire, il est utile et propre de créer un fichier de configuration qui contiendra les variables nécessaires à la création de votre tunnel vpn :


# VPN1 Configuration File
# /opt/ssh-vpn/etc/vpn1
# Les réseaux sont connectés d’une part et d’autre,
# suite à la commande route
client_network=192.168.2.0/24
server_network=192.168.1.0/24
# Désirez vous des informations de debug ?
client_debug="no"
server_debug="yes"
# Prenez des IPs différentes pour chaque VPN nécessaire.
server_ppp_ip=192.168.254.254
client_ppp_ip=192.168.254.253
# Y a t-il une authentification PPP requise ?
client_require_pap="yes"
server_require_pap="yes"
client_require_chap="no"
server_require_chap="no"
# Besoin d’arguments pppd non-standards ? Mettez-les ici.
#client_pppd_args="usepeerdns"
#server_pppd_args="proxyarp"
# Besoin d’arguments ssh supplémentaires ? Mettez les ici
#client_ssh_args="-C"
#server_ssh_args=""