Table des matières

OpenVPN

Pour la faire simple, c’est globalement le bordel. On a commencé à fournir des VPNs alors que l’on avait pas encore de plan d’adressage, du coup on a des /32 hors du /27 de VPN qui se baladent. C’est pour cela que l’on a de l’iBGP vers nominoe.
Nous remarquerons que ça ne dérange pas du tout OpenVPN d’avoir des IPs en dehors de son /27.

Configuration serveur

J’ai impunément piqué l’idée d’ARN de factoriser les confs, c’est quand même vachement plus pratique quand je dois faire des modifications. Ainsi, on a trois fichier : common.ovpn qui contient la conf en commun, tcp.conf pour la partie TCP et upd.conf pour la partie UDP.

common.ovpn

# Serveur
mode server
port 1194
tun-ipv6
#local 89.234.186.3 # Adresse d'écoute pour les connexions entrantes

# Contournement des soucis de fragmentation
mssfix 1300

# On utilise la meilleure méthode de chiffrement
cipher aes-256-cbc

# Certificats
ca /etc/ssl/grifon/vpn.grifon.fr.CA-OPENVPN.crt
cert /etc/ssl/grifon/vpn.grifon.fr.OPENVPN.crt
key /etc/ssl/grifon/vpn.grifon.fr.OPENVPN.key
dh dh4096.pem

# Clé partagée (0 pour serveur, 1 pour client ; oui, même fichier côté client
# et côté serveur)
tls-auth ta.key 0

# Réseau
push "dhcp-option DNS 89.234.186.4" # Serveur DNS
push "dhcp-option DNS 2a00:5884::7" # Serveur DNS
keepalive 10 120

# Scripts pour ajouter les bonnes routes sur judicael
script-security 3
client-connect /etc/openvpn/client-connect # Utilisé pour IPv4
client-disconnect /etc/openvpn/client-disconnect # Utilisé pour IPv4

#
# server implique push topology et tls-server
#
topology subnet
server 89.234.186.64 255.255.255.224
push "explicit-exit-notify" # Pour pas que le serveur attende un timeout

# Configuration IPv6
push "tun-ipv6"
ifconfig-ipv6 2a00:5884:0:3::/64 2a00:5884:0:3::

# Securité
user openvpn # Le serveur ne tourne pas en tant que root
group openvpn
persist-key
persist-tun
comp-lzo # On compresse
client-config-dir /etc/openvpn/ccd # Chaque client a ses IP spécifiées ici

# Auth
auth-user-pass-verify "/etc/openvpn/auth.sh /etc/openvpn/passwd" via-env
verify-client-cert none
username-as-common-name
ccd-exclusive # Si le client n'a pas de fichier dans /etc/openvpn/ccd, on refuse la connexion

# Log
verb 3
mute 20
status /etc/openvpn/openvpn-status.log # Liste des clients connectés
#log-append /var/log/openvpn.log # Logs

# routage
up "/etc/openvpn/up.sh"

tcp.conf

# COMMON CONFIGURATION 
config /etc/openvpn/common.ovpn

proto tcp6-server
dev tun-tcp0
daemon ovpn-server-tcp
learn-address /etc/openvpn/setup-address-tcp.sh # Utilisé pour IPv6

udp.conf

# COMMON CONFIGURATION
config /etc/openvpn/common.ovpn

proto udp6
dev tun-udp0
daemon ovpn-server-udp
learn-address /etc/openvpn/setup-address.sh # Utilisé pour IPv6

fragment 1300

# https://winaero.com/blog/speed-up-openvpn-and-get-faster-speed-over-its-channel/
sndbuf 0
rcvbuf 0
push "sndbuf 0"
push "rcvbuf 0"

Fichiers maisons utilisés par OpenVPN

Tous les fichiers qui suivent doivent être exécutables.

auth.sh

Son but est de vérifier le mot de passer envoyé par le client par rapport au contenu de /etc/openvpn/passwd.

#!/bin/sh
 
htpasswd -bv "$1" "${username}" "${password}"

client-connect

Ce fichier ajoute la route /32 pour les clients en dehors du /27 du plan d’adressage

#!/bin/sh
set -e
 
# debug
#set -x
#exec &> /tmp/client-connect.log
 
[ -z "$ifconfig_pool_remote_ip" ] || sudo /bin/ip route add $ifconfig_pool_remote_ip/32 dev $dev proto static
 
routed_IPv4_addr=$(awk '/^iroute / { print $2 }' /etc/openvpn/ccd/${username})
routed_IPv4_netm=$(awk '/^iroute / { print $3 }' /etc/openvpn/ccd/${username})
 
if [ -n "${routed_IPv4_addr}" ]; then
	if [ -n "${routed_IPv4_netm}" ]; then
		sudo /bin/ip route add \
			${routed_IPv4_addr}/${routed_IPv4_netm} dev ${dev} \
			proto static
	else
		sudo /bin/ip route add \
			${routed_IPv4_addr}/32 dev ${dev} proto static
	fi
fi

client-disconnect

Comme avant sauf que l’on enlève la route (en même temps, vu le nom du fichier…)

#!/bin/sh
set -e
[ -z "$ifconfig_pool_remote_ip" ] || sudo /bin/ip route del $ifconfig_pool_remote_ip/32 dev $dev proto static || true

setup-address.sh

Là c’est la version du future (donc IPv6), qui fait les deux en même temps.

#!/bin/bash
 
action="$1"
addr="$2"
grep -qE "^2a00:.*" <<< "$addr"
if [ $? -eq 0 ]
then
case "$action" in
        add )
                      sudo /bin/ip route add $addr dev tun0 proto static metric 1024
        ;;
        delete)
          sudo /bin/ip route del $addr dev tun0 proto static metric 1024 || true
        ;;
esac
fi

Redirection des ports

Pour que le VPN puisse être joint depuis tous les ports, nous devons les rediriger vers le 1194. En effet, OpenVPN ne sait pas écouter sur plusieurs ports, et ce serait un peu lourd d’avoir un processus par port. Nous utilisons pour cela le module NAT d’iptables.
Nous avons aussi un daemon L2TP et IPsec qui tourne, nous ne voulons pas rediriger le trafic à destination de ces ports OpenVPN, donc nous mettons juste un -j ACCEPT avant la règle de NAT. Absolument tous les ports TCP sont redirigés, il n’est donc pas possible de se connecter en SSH au serveur VPN en IPv4.

iptables -t nat -A PREROUTING -d 89.234.186.190/32 -p udp -m udp --dport 1:65535 -j DNAT --to-destination 89.234.186.190:1194
iptables -t nat -A PREROUTING -d 89.234.186.190/32 -p tcp -m tcp --dport 1:65535 -j DNAT --to-destination 89.234.186.190:1194
iptables -A INPUT -s 89.234.186.0/24 -d 172.16.0.0/12 -j DROP
iptables -A FORWARD ! -s 172.16.0.0/12 -d 172.16.0.0/12
iptables -A OUTPUT -s 172.16.0.0/12 -d 89.234.186.0/24 -j DROP
ip6tables -A PREROUTING -d 2a00:5884:0:2:ffff:ffff:ffff:ffff/128 -p tcp -m tcp --dport 1:65535 -j DNAT --to-destination [2a00:5884:0:2:ffff:ffff:ffff:ffff]:1194
ip6tables -A PREROUTING -d 2a00:5884:0:2:ffff:ffff:ffff:ffff/128 -p udp -m udp --dport 1:65535 -j DNAT --to-destination [2a00:5884:0:2:ffff:ffff:ffff:ffff]:1194

Gestion des logs

Par défaut, openvpn écrit ses logs dans /var/log/daemons.log. Nous voulons un fichier séparé car ils sont très vite très gros avec tous les bots qui tentent de se connecter au VPN en TCP en croyant tomber sur un serveur mail/web/whateverd. J’ai donc écrit un fichier de conf rsyslog :

if $programname startswith 'ovpn-server-' then {
	-/var/log/openvpn.log
}

Configuration client

Mot de passe

Si un client veut un mot de passe particulier, il peut donner la sortie de htpasswd -n $username

Fichier de conf client

Je ne copie pas ici la clé partagée (ta.key) parce que ça n’aurait aucun sens. Elle est envoyée à l’adhérent lors de la création de son compte.

# OpenVPN chez Grifon

# Informations de connexion
client
dev tun
proto udp
#proto tcp-client
port 1194
remote vpn.grifon.fr 1194
remote-random
resolv-retry infinite
remote-cert-tls server
route-delay 2
nobind
persist-key

# Configuration pour contourner les soucis de fragmentation
fragment 1300
mssfix 1300

# Chiffrement le plus élevé
cipher aes-256-cbc

# Clé partagée (à ne pas distribuer)
tls-auth ta.key 1
comp-lzo

redirect-gateway def1 bypass-dhcp # À commenter pour ne pas utiliser le VPN comme connexion IPv4 par défaut
route-ipv6 2000::/3               # À commenter pour ne pas utiliser le VPN comme connexion IPv6 par défaut

# Auth
auth-user-pass

# CA
<ca>
-----BEGIN CERTIFICATE-----
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
2kSONme/cdZptUNl2KZzs+xYBeyUPSRE
-----END CERTIFICATE-----
</ca>

Particularité pour les clients hors du /27

OpenVPN ajoute les routes IPv4 0.0.0.0/1 via 89.234.186.65 dev tun0 et 128.0.0.0/1 via 89.234.186.65 dev tun0, sauf que dans le cas du /32, 89.234.186.65 n’est pas directement joignable. Il faut donc ruser avec un up.sh :

#!/bin/sh
 
dev=$1
 
ip route add 89.234.186.65/32 dev $dev

Ajouter un VPN

alarig@judicael:~$ add-ovpn -h
Usage:
	-h, --help	Affiche ce message d’aide
	-u, --user	Identifiant de connexion au VPN
	-i, --id	Numéro servant à la génération des IPs
	-m, --email	Adresse mail à qui envoyer les informations de connexion

Exemple :

ovpn-add -u toto -m toto@super.bzh
Ce qui configurera les adresses ip:

Si vous êtes curieux, voici le script :

#!/bin/sh
 
usage() {
	printf "Usage:\n"
	printf "\t-h, --help	Affiche ce message d’aide\n"
	printf "\t-u, --user	Identifiant de connexion au VPN\n"
	printf "\t-i, --id	Numéro servant à la génération des IPs\n"
	printf "\t-m, --email	Adresse mail à qui envoyer les informations de
connexion\n"
}
 
OPTS=$(getopt -o u:,m:,h -l user:,email:,help -- "$@")
 
if [ $? != 0 ]; then
	exit 1
fi
 
if [ $1 ]; then
	if [ $1 = '-h' ] || [ $1 = '--help' ]; then
		usage
		exit 0
	elif [ $# -lt 4 ]; then
		echo "Paramètre manquant"
		usage
		exit 1
	fi
else
	echo "Paramètre manquant"
	usage
	exit 1
fi
 
eval set -- "$OPTS"
 
while :; do
	case "$1" in
		-h | --help)	usage;
				exit 0;;
		-u | --user)	user="$2";
				shift 2;;
		-m | --email)	EMAIL="$2";
				shift 2;;
		--)		shift; break;;
	esac
done
 
mailregex=$(echo $EMAIL | egrep '[^.]+@[^.]+\.[^.]+')
if [ ${#mailregex} != ${#EMAIL} ]; then
	echo "Adresse mail invalide"
	exit 1
fi
 
#ipv6="2a00:5884:$((8300+$IPid))"
#ipv4="89.234.186.$IPid"
 
# API
USERAPI='user'
PASSWORD='password'
SUBNETID_VPNIPv4='76'
SUBNETID_VPNIPv6='36'
URLAPI="https://ipam.grifon.fr/api/${USERAPI}/"
 
# Curl
CURL="$(which curl) -s -k -X"
HEADERHTTP='Content-type: application/x-www-form-urlencoded'
 
# Get Token
APITOKEN=$(${CURL} POST --user ${USERAPI}:${PASSWORD} --header "${HEADERHTTP}" \
        ${URLAPI}/user/ | jq '.data.token' | sed 's/\"//g')
#echo $APITOKEN
# Get last IP
# IPv4
LAST_VPN_V4=$(${CURL} POST --header "token: ${APITOKEN}" \
        --header "${HEADERHTTP}" \
        ${URLAPI}/addresses/first_free/$SUBNETID_VPNIPv4/ \
        --data "description=VPN de $FIRSTNAME ${LASTNAME}" \
        | jq .data | sed 's/\"//g')
echo $LAST_VPN_V4
 
# IPv6
LAST_VPN_V6=$(${CURL} POST --header "token: ${APITOKEN}" \
        --header "${HEADERHTTP}" \
        ${URLAPI}/subnets/$SUBNETID_VPNIPv6/first_subnet/48 \
        --data "description=VPN de $FIRSTNAME $LASTNAME" \
        --data 'isFull=1' \
        | sed 's/\\//' | jq .data | sed 's/\"//g')
echo $LAST_VPN_V6
FIRST_IPv6="${LAST_VPN_V6%/48}1"
 
password="$(head -c 12 /dev/urandom | base64)"
 
echo "1 - Génération du mot de passe" >&2
htpasswd -nb $user $password | grep -v "^$" >> /etc/openvpn/passwd
 
echo "2 - Génération de la configuration" >&2
echo "# IPv4
ifconfig-push $LAST_VPN_V4 255.255.255.224
 
# IPv6
ifconfig-ipv6-push ${FIRST_IPv6}/112
iroute-ipv6 ${LAST_VPN_V6}
route-ipv6 $LAST_VPN_V6 $FIRST_IPv6" > /etc/openvpn/ccd/$user
 
echo "3 - Envoi du mail" >&2
SUBJECT=$(perl -wse "use utf8; use Encode qw(encode); print encode(\"MIME-Q\",\
\"Votre VPN vient d’être créé\");")
 
echo "From: Adminsys grifon <adminsys@grifon.fr>
To: $EMAIL
Content-Type: text/plain; charset=UTF-8
Subject: $SUBJECT
 
Bonjour,
Votre VPN vient d’être créé.
 
Vous avez l’IPv4 $LAST_VPN_V4 et le subnet IPv6 $LAST_VPN_V6.
Votre identifiant est $user et votre mot de passe est $password
Si vous souhaitez utiliser un mot de passe que nous ne connaissons pas, vous
pouvez nous envoyer la sortie de 'htpasswd -n $user'.
 
Si vous voulez un reverse DNS sur votre VPN, merci d’envoyer un mail à
adminsys@grifon.fr contenant le serveur qui fera autorité sur vos zones DNS.
 
Vous trouverez ci-dessous un exemple de configuration côté client ainsi que la
clé pré-partagée
 
 
##### DÉBUT DU FICHIER DE CONFIGURATION #####
 
$(cat /etc/openvpn/client.conf.template)
 
##### FIN DU FICHIER DE CONFIGURATION #####
 
##### DÉBUT DE LA CLÉ PRÉ-PARTAGÉE #####
 
$(cat /etc/openvpn/ta.key)
 
##### FIN DE LA CLÉ-PARTAGÉE #####
Cette clé partagée est là pour éviter les attaques par force brute, merci de ne
pas la diffuser.
 
Cordialement,
-- 
Les adminsys de grifon" | /usr/sbin/sendmail -f adminsys@grifon.fr -t
 
 
SUBJECT=$(perl -wse "use utf8; use Encode qw(encode); print encode(\"MIME-Q\",\
\"Création du VPN de $user\");")
 
echo "From: Adminsys grifon <adminsys@grifon.fr>
To: adminsys@grifon.fr
Content-Type: text/plain; charset=UTF-8
Subject: $SUBJECT
 
IPv4 : $LAST_VPN_V4
IPv6 : $LAST_VPN_V6
 
" | /usr/sbin/sendmail -f adminsys@grifon.fr -t