====== 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’[[reseau:bgp:ibgp|iBGP]] vers [[machines:grifon:nominoe]]. \\
Nous remarquerons que ça ne dérange pas du tout OpenVPN d’avoir des IPs en dehors de son /27.
===== PKI =====
On utilise la CA de grifon (dispo sur [[machines:grifon:rda]]), le certificat est vallable un an et pour le signer:
openssl x509 -req -days 365 -in /etc/ssl/local_grifon/services/vpn.grifon.fr.csr -out /etc/ssl/local_grifon/services/vpn.grifon.fr.crt -CA /etc/ssl/local_grifon/rootCA/rootCA.crt -CAkey /etc/ssl/local_grifon/rootCA/rootCA.key -CAcreateserial -CAserial /etc/ssl/local_grifon/rootCA/rootCA.srl
===== Configuration serveur =====
J’ai impunément piqué l’[[http://gitlab.netlib.re/arn/arn-confs/tree/master/openvpn|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
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
-----BEGIN CERTIFICATE-----
MIIKDzCCBfegAwIBAgIJAOK5QArwwnBEMA0GCSqGSIb3DQEBCwUAMIGdMQswCQYD
VQQGEwJGUjERMA8GA1UECAwIQnJldGFnbmUxFzAVBgNVBAcMDkNlc3NvbiBTZXZp
Z25lMQ8wDQYDVQQKDAZHUklGT04xETAPBgNVBAsMCFNlcnZpY2VzMRwwGgYDVQQD
DBNjb25zdGFuY2UuZ3JpZm9uLmZyMSAwHgYJKoZIhvcNAQkBFhFjb250YWN0QGdy
aWZvbi5mcjAeFw0xODAyMTIyMjIzMzdaFw0yODAyMTAyMjIzMzdaMIGdMQswCQYD
VQQGEwJGUjERMA8GA1UECAwIQnJldGFnbmUxFzAVBgNVBAcMDkNlc3NvbiBTZXZp
Z25lMQ8wDQYDVQQKDAZHUklGT04xETAPBgNVBAsMCFNlcnZpY2VzMRwwGgYDVQQD
DBNjb25zdGFuY2UuZ3JpZm9uLmZyMSAwHgYJKoZIhvcNAQkBFhFjb250YWN0QGdy
aWZvbi5mcjCCBCIwDQYJKoZIhvcNAQEBBQADggQPADCCBAoCggQBAOGDncASqaSz
1JWHt3Vzza5DrgFxkaS5o4SXaTZHjbeq6SlXzXgDgeee8m4/zon8XcIN9BifLIyD
Mt6VK5SHr+2Z14yiMau9rY6LFQlNdaSl9POcintieqqyxmy5g/AepntbX+dYGemY
4X2FBD0CawyPEp19opSDqqV5Wf6kwBZgfb1SVveyFeJLMx7xiRSP9Lt3rKPWH8Jr
cXepYvroW2MNZ6D7MQDnBhXgaVbuwZxZnZrOpzah2hE5J1PAFj4XcJjL9ZcDQCO5
RVxn4nWVjSEQ+xO2j02Imwivl2yIDy2G6OZKeqoABFKJDzpsHG5Kq3DfocUIUgeb
uBguk8pruIaIHUOHIR8wrxnbIp4m/pRJN/M0RBngRMruaiHdjt+H9wmuDq1goa8L
fXjn25m9xbJ1w0U3ima0gJRBOjEZe1OMTgjQ/qtkTgBZ64UlP19sq3ebRoh1V284
rQpu/Gn1J1zlH7fBtLDS8bGu9xEWY2Ny9nqG1A2e/+vAb/NcJJrrhbyJ7+3RqSTZ
LQm8NGLH1F3Rshl7YRKbxNznXHi+/P0AuF+kDfLkSgd0Xto5BZWVQaOcqXEGSQBo
f4qMpnSEJRjrPYEDRgB32QKfqJy5svJ4pFt+Y1Z5oFItJhTIiruaN0ygENbw1tHq
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
-----END CERTIFICATE-----
==== 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:
* 89.234.186.42
* 2a00:5884:8342::/48
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
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
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