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.
On utilise la CA de grifon (dispo sur 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
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.
# 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"
# 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
# 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"
Tous les fichiers qui suivent doivent être exécutables.
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}"
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
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
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
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
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
}
Si un client veut un mot de passe particulier, il peut donner la sortie de htpasswd -n $username
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 <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----- </ca>
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
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