24/09/2009

Allocation d’adresses IPv4 publiques over VPN

InternetVous désirez monter une solution VPN publique sans NAT, pouvoir être joignable de l’extérieur en dépit des firewalls/NAT derrière lesquels vous vous trouvez, ou simplement vous assurer que votre trafic soit chiffré entre votre LAN et internet ?

Je vous explique ici la mise en place d’une solution d’allocation d’adresses IPv4 publiques over VPN. Je vous recommande d’avoir de bonnes notions en matière de routage avant de vous engager plus loin.

Attention : Cet article fait appel à des connaissances avancées.

Prérequis

  • Un prefix annoncé sur la table globale
  • Un routeur (celui qui annonce le prefix)
  • Un serveur dédié (qui distribuera les IPs over VPN)

Dans cet article, je prendrai les données suivantes comme exemple :

  • Le prefix sera 1.2.3.0/24
  • L’adresse publique du routeur (quagga sous FreeBSD 7.x) sera 2.2.2.2
  • L’adresse publique du serveur dédié (Linux 2.6) sera 3.3.3.3

Mise en place d’un tunnel GRE

Selon la définition de Wikipédia, « Generic Routing Encapsulation (GRE) is a tunneling protocol developed by Cisco that can encapsulate a wide variety of network layer protocol packet types inside IP tunnels, creating a virtual point-to-point link to Cisco routers at remote points over an IP internetwork. »

Ce tunnel sera établi entre le routeur et le serveur dédié. Il servira à router un subnet vers le serveur dédié. Prenons comme exemple 1.2.3.16/28. Il faut également choisir deux IPs d’interconnexion, qui doivent être des IPs publiques. Ce seront les IPs assignées à chaque bout du tunnel. Choisissons 1.2.3.225 pour le routeur et 1.2.3.226 pour le serveur (ce qui donne 1.2.3.224 comme network et 1.2.3.227 comme broadcast, un /30 donc).

Sur le routeur, voici la procédure à suivre :

  • Il faut d’abord s’assurer que le module if_gre soit chargé ou bien compilé en dur. Si ce n’est pas le cas
    # kldload if_gre
  • On crée le tunnel
    # ifconfig gre0 create
    # ifconfig gre0 tunnel 2.2.2.2 3.3.3.3
    # ifconfig gre0 inet 1.2.3.225 1.2.3.226 netmask 255.255.255.252
    # ifconfig gre0 up
  • Vérifiez également que le forwarding soit activé :
    # sysctl net.inet.ip.forwarding=1

Afin que le tunnel se monte automatiquement au boot, voici les quelques lignes à rajouter dans /etc/rc.conf :

router_enable="YES"
ifconfig_gre0="tunnel 2.2.2.2 3.3.3.3"
ifconfig_gre0="inet 1.2.3.225 1.2.3.226 netmask 255.255.255.252 up"

Le tunnel est maintenant prêt côté routeur. Passons à la configuration côté serveur :

  • Le kernel doit être compilé avec les options CONFIG_IP_ADVANCED_ROUTER et CONFIG_IP_MULTIPLE_TABLES activées.
  • On crée le tunnel
    # ip tun add gretun mode gre local 3.3.3.3 remote 2.2.2.2 ttl 64 dev eth0
    # ip addr add dev gretun 1.2.3.226 peer 1.2.3.225/30
    # ip link set dev gretun up

Le tunnel est maintenant opérationnel et chaque bout devrait être capable de pinger l’autre.

Routage d’un subnet vers le serveur

Côté routeur, il suffit d’ajouter une entrée dans la table de routage :

# route add -net 1.2.3.16/28 1.2.3.226

Côté serveur, il faut effectuer une manipulation un peu particulière. En effet, dans l’état actuel des choses, le serveur dédié risque d’avoir des problèmes s’il utilise une des IPs du prefix 1.2.3.16/28 routé. Supposons que nous avons assigné l’IP 1.2.3.16 à une interface du serveur :

# ip addr add 1.2.3.16/28 dev gretun

Si on essaye de joindre cette IP depuis l’extérieur (un ping par exemple), les packets arriveront bien jusqu’au serveur (après avoir été routés par le routeur (encore heureux)). Le problème se pose lorsque le serveur essaye de répondre : il va router les packets sortants sur l’interface par défaut et non via l’interface gretun. Cela peut marcher chez certains hébergeurs qui ne filtrent pas l’adresse source des packets sortants, mais cela n’est pas toujours le cas. Et puis c’est plus propre d’avoir la même route pour l’aller que pour le retour. Il faut donc router en fonction de l’adresse source des packets. C’est ici qu’intervient la feature de tables de routage multiple du kernel Linux.

Voici la manipulation à suivre :

# echo 200 tunnel >> /etc/iproute2/rt_tables
# ip rule add from 1.2.3.16/28 table tunnel
# ip rule add from 1.2.3.226/32 table tunnel
# ip route add default via 1.2.3.225 table tunnel

La première commande permet d’assigner un nom à un numéro de table (200 est ici un nombre arbitraire, il suffit de prendre un numéro de table qui n’est pas encore utilisé).

La deuxième commande indique que tous les packets dont l’adresse source fait partie du prefix 1.2.3.16/28 doivent être routés en consultant la table de routage “tunnel”.

La troisième commande fait la même chose que la deuxième, si ce n’est qu’elle n’affecte que les packets en provenance de l’adresse 1.2.3.226. Cette commande est nécessaire pour que l’IP d’interconnexion soit accessible depuis l’extérieur.

Enfin, la quatrième commande ajoute la route par défaut inhérente à la table “tunnel”. Les packets utiliseront dorénavant la même route pour joindre le serveur que pour en partir. Il suffit de vérifier :

# ping -I 1.2.3.16 google.com

Le ping devrait se dérouler correctement.

Allocation d’IPs publiques over VPN

Il reste à présent à mettre en place une solution VPN sur le serveur dédié. Après avoir tenté avec OpenVPN, je me suis rendu compte qu’il était incapable de prendre une autre IP principale que le .1 du subnet alloué over vpn. Cela est inacceptable dans notre cas où le subnet alloué commence à partir de .16. Je me suis alors tourné vers vtun et c’est cette solution que je vais exposer ici.

Premièrement, il faut choisir une adresse IP du subnet qu’utilisera vtund. Prenons 1.2.3.17. Une nouvelle règle de routage est ici nécessaire. En effet, les packets ayant comme adresse source 1.2.3.17 doivent être routés vers les interfaces du VPN, et non pas vers gretun comme c’est le cas actuellement :

# ip rule add from 1.2.3.17 lookup main

Cette commande fait une exception de routage parmis le subnet 1.2.3.16/28, indiquant que les packets en provenance de 1.2.3.17 doivent utiliser la table de routage principale et non la table “tunnel”. Il reste alors à rendre possible la communication entre deux IPs du même subnet :

# ip rule add from 1.2.3.16/28 to 1.2.3.16/28 lookup main

Ceci permet d’éviter que les packets soient envoyés sur l’interface gretun, ce qui est le comportement par défaut pour les packets en provenance de 1.2.3.16/28 selon la règle indiquée précédemment.

Assurez-vous également que la redirection IP est activée sur le serveur :

# echo 1 > /proc/sys/net/ipv4/ip_forward

Voici un exemple de vtund.conf pour le serveur :

options {
port 4444;
syslog daemon;
ppp           /usr/sbin/pppd;
ifconfig      /sbin/ifconfig;
route         /sbin/route;
firewall      /sbin/iptables;
ip            /sbin/ip;
}

default {
compress no;
speed 0;
}

user0 {
passwd blahblah;
type tun;
device user0;
proto tcp;
encrypt yes;
keepalive yes;

up {
ifconfig "%% 1.2.3.17 pointopoint 1.2.3.18 mtu 1500";
};
}

On alloue donc l’IP publique 1.2.3.18 au client “user0”.

Voici la configuration correspondante côté client (Linux 2.6, avec les options kernel CONFIG_IP_ADVANCED_ROUTER et CONFIG_IP_MULTIPLE_TABLES activées) :

options {
port 4444;
timeout 60;
ppp           /etc/redirectall.sh;
ifconfig      /sbin/ifconfig;
route         /sbin/route;
firewall      /sbin/iptables;
ip            /sbin/ip;
}

user0 {
passwd blahblah;
device server0;
proto tcp;
persist yes;

up {
ifconfig "%% 1.2.3.18 pointopoint 1.2.3.17 mtu 1500";
ip "rule add from 1.2.3.18 lookup tunnel";
ip "route add default via 1.2.3.17 table tunnel";
ppp "";
};

down {
ip "rule del from 1.2.3.18";
ppp "flush";
};
}

Quelques explications sont ici nécessaires. Le path indiqué par “ppp” a été modifié pour pointer sur un script permettant de rediriger tout le trafic over vpn. Il est également nécessaire d’ajouter une entrée “200 tunnel” dans le /etc/iproute2/rt_tables du client.

Voici redirectall.sh :

#!/bin/bash
if [ "$1" = "flush" ]; then
route delete -net 0.0.0.0/1
route delete -net 128.0.0.0/1
route delete -host 3.3.3.3
else
route add -host 3.3.3.3 gw `route -n | grep -E ^0.0.0.0 | awk '{print $2}'`
route add -net 0.0.0.0/1 gw 1.2.3.17
route add -net 128.0.0.0/1 gw 1.2.3.17
fi

Tout est à présent fin prêt. Lancez le daemon vtun sur le serveur et le client, et vous serez joignable directement sur l’IP assignée. Si vous ne désirez pas rediriger tout votre trafic over vpn, il suffit de commenter les deux commandes ppp dans le up { } et down { } de la configuration client.

Schéma

L’architecture ressemble à ceci :
Schema IPv4 publique over VPN

Remerciements

Merci à bragon, avec qui j’ai initialement mis en place ce système avec le /24 de GeekNode.
Merci à GeekNode pour le /28 :)

  1. Khemael
    | #1

    Un Tres bon article qui malheureusement ne traite pas des problèmes de MTU associé au GRE.

    Alors pour toi public qui veut router ton bloc vers ta connexion de pauvre sur ta MachinBox, soit rassuré, l’overhead GRE est connu :

    1500 – 20 (IP Header) – 4 (GRE), et un iptables bien placé avec un MSS a 1476 te sauveras de ces connexions TCP qui ne passaient pas alors que les ping s’échangeaient fougueusement :

    iptables -t mangle -A FORWARD -p tcp -m tcp –tcp-flags SYN,RST SYN -j TCPMSS –set-mss 1476

    :)

  2. | #2

    Merci pour cette précision.

  3. | #3

    Errata: openvpn est tout à fait capable de sélectionner une IP autre que .1, il faut simplement ne pas utiliser la directive “server” qui est en réalité un alias pour une série d’autres commandes. Il est possible d’arriver à nos fins comme ceci :

    dev tap
    mode server
    tls-server
    push “topology subnet”
    ifconfig 1.2.3.17 255.255.255.240
    push “route-gateway 1.2.3.17”

  1. | #1