openconnect appelle vpnc-script pour poser les routes et le DNS. Il vient d'un paquet distinct, dont le nom change de famille en famille et qui l'installe à des endroits différents. C'est un FICHIER qu'on cherche, pas un binaire du PATH : le contrôle des binaires ne peut donc pas le voir. Sans lui, la session s'ouvre, openconnect démarre, et l'interface tun n'apparaît jamais. La panne se manifeste trois étages au-dessus du paquet absent, sur un symptôme qui ne l'accuse pas. L'installateur nomme désormais le paquet à poser, par famille de distribution. --- EN --- openconnect calls vpnc-script to lay down routes and DNS. It ships in a separate package whose name varies by family and which installs it at different paths. It is a FILE we look for, not a binary on the PATH, so the binary check cannot see it. Without it the session opens, openconnect starts, and the tun interface never appears. The failure surfaces three stages above the missing package, on a symptom that does not accuse it. The installer now names the package to install, per distribution family. Assisted-by: Claude Opus 5
222 lines
7.7 KiB
Bash
Executable file
222 lines
7.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
#
|
|
# Paquets client VPN, par pilote de script/vpn/drivers/.
|
|
#
|
|
# sudo bash script/install/install_vpn.sh l2tp_ipsec
|
|
#
|
|
# Un groupe de paquets par pilote : le nom passé en argument est le `name` du
|
|
# pilote, et non un nom de paquet. C'est le CLI (script/vpn/vpn.py install)
|
|
# qui appelle ce script, et il ne connaît que les noms de pilotes.
|
|
#
|
|
# Ce script INSTALLE et ne configure rien : la configuration est rendue au
|
|
# montage du tunnel, dans un tmpfs, par le pilote. Il désactive tout de même
|
|
# le démarrage automatique des services — un strongSwan ou un xl2tpd lancé au
|
|
# boot tiendrait UDP 500/1701 et empêcherait l'instance dédiée au profil de
|
|
# s'attacher.
|
|
set -euo pipefail
|
|
|
|
log() { echo "[VPN] $*"; }
|
|
die() { echo "[VPN] ERREUR: $*" >&2; exit 1; }
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage : sudo bash script/install/install_vpn.sh <pilote>
|
|
|
|
Pilotes connus :
|
|
l2tp_ipsec strongSwan + xl2tpd + pppd (L2TP/IPsec à clé pré-partagée)
|
|
wireguard wireguard-tools
|
|
openvpn openvpn
|
|
openconnect openconnect + vpnc-scripts
|
|
sshuttle sshuttle (sur RHEL/Rocky/Alma : dépôt EPEL requis)
|
|
|
|
Sans argument : tous les pilotes.
|
|
USAGE
|
|
}
|
|
|
|
check_root() {
|
|
[ "$(id -u)" -eq 0 ] || die "à lancer en root : sudo bash $0 $*"
|
|
}
|
|
|
|
detect_os() {
|
|
[ -f /etc/os-release ] || die "OS indéterminable (pas de /etc/os-release)"
|
|
# shellcheck disable=SC1091
|
|
. /etc/os-release
|
|
OS="${ID}"
|
|
OS_LIKE="${ID_LIKE:-}"
|
|
log "OS détecté : ${OS}"
|
|
}
|
|
|
|
family() {
|
|
case "$OS" in
|
|
ubuntu|debian|linuxmint|pop|elementary|raspbian) echo debian; return ;;
|
|
arch|manjaro|endeavouros|artix|garuda) echo arch; return ;;
|
|
fedora|rhel|centos|almalinux|rocky) echo rhel; return ;;
|
|
opensuse*|sles|sled) echo suse; return ;;
|
|
esac
|
|
case "$OS_LIKE" in
|
|
*debian*|*ubuntu*) echo debian; return ;;
|
|
*arch*) echo arch; return ;;
|
|
*rhel*|*fedora*) echo rhel; return ;;
|
|
*suse*) echo suse; return ;;
|
|
esac
|
|
die "famille de distribution inconnue : ${OS} (ID_LIKE=${OS_LIKE})"
|
|
}
|
|
|
|
# Les paquets, par pilote puis par famille. `strongswan-starter` fournit la
|
|
# commande `ipsec` et le démon starter, que le pilote L2TP utilise ; les
|
|
# paquets `charon-systemd`/`swanctl` seuls ne la fournissent PAS.
|
|
packages_for() {
|
|
local driver="$1" fam="$2"
|
|
case "${driver}:${fam}" in
|
|
# libstrongswan-standard-plugins apporte le greffon openssl, et
|
|
# avec lui 3DES. Sans ce paquet, charon ANNONCE 3DES, le
|
|
# concentrateur le choisit — c'est souvent le seul qu'il connaisse —
|
|
# et la négociation meurt sur « ENCRYPTION_ALGORITHM 3DES_CBC not
|
|
# supported! ». Mesuré sur Ubuntu 24.04 : greffons chargés sans lui,
|
|
# « aes md5 rc2 sha1 », donc pas de 3DES.
|
|
l2tp_ipsec:debian)
|
|
echo "strongswan strongswan-starter libstrongswan-standard-plugins libcharon-extra-plugins xl2tpd ppp" ;;
|
|
l2tp_ipsec:arch) echo "strongswan xl2tpd ppp" ;;
|
|
l2tp_ipsec:rhel) echo "strongswan xl2tpd ppp" ;;
|
|
l2tp_ipsec:suse) echo "strongswan xl2tpd ppp" ;;
|
|
|
|
# wireguard-tools fournit wg ET wg-quick. Le module noyau est dans
|
|
# Linux depuis 5.6 : rien à compiler sur les distributions visées.
|
|
wireguard:*) echo "wireguard-tools" ;;
|
|
|
|
openvpn:*) echo "openvpn" ;;
|
|
|
|
# vpnc-scripts porte le script que openconnect appelle pour poser
|
|
# les routes et le DNS. Sans lui, la session s'ouvre et la machine
|
|
# ne voit rien passer.
|
|
openconnect:debian) echo "openconnect vpnc-scripts" ;;
|
|
openconnect:*) echo "openconnect" ;;
|
|
|
|
sshuttle:*) echo "sshuttle" ;;
|
|
|
|
*) die "pilote inconnu : ${driver}. Voir --help." ;;
|
|
esac
|
|
}
|
|
|
|
install_packages() {
|
|
local fam="$1"; shift
|
|
log "Installation : $*"
|
|
case "$fam" in
|
|
debian)
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update -qq
|
|
# shellcheck disable=SC2086
|
|
apt-get install -y --no-install-recommends $*
|
|
;;
|
|
arch)
|
|
# shellcheck disable=SC2086
|
|
pacman -Sy --needed --noconfirm $*
|
|
;;
|
|
rhel)
|
|
# shellcheck disable=SC2086
|
|
{ command -v dnf >/dev/null && dnf install -y $*; } \
|
|
|| yum install -y $*
|
|
;;
|
|
suse)
|
|
# shellcheck disable=SC2086
|
|
zypper --non-interactive install $*
|
|
;;
|
|
esac
|
|
}
|
|
|
|
disable_autostart() {
|
|
# Un service lancé au boot tient le port et fait échouer l'instance
|
|
# dédiée au profil. On les arrête et on les désactive : le pilote
|
|
# démarre ce dont il a besoin, quand il en a besoin.
|
|
command -v systemctl >/dev/null || return 0
|
|
for unit in xl2tpd strongswan-starter strongswan ipsec; do
|
|
if systemctl list-unit-files "${unit}.service" >/dev/null 2>&1 \
|
|
&& systemctl is-enabled "${unit}.service" >/dev/null 2>&1; then
|
|
log "désactivation de ${unit}.service (le pilote le pilote)"
|
|
systemctl disable --now "${unit}.service" >/dev/null 2>&1 || true
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Les binaires que chaque pilote exige, en miroir de `binaries` dans
|
|
# script/vpn/drivers/. Un paquet installé sans son binaire (nom changé,
|
|
# dépôt incomplet) doit être vu ICI, pas au premier montage.
|
|
binaries_for() {
|
|
case "$1" in
|
|
l2tp_ipsec) echo "ipsec xl2tpd pppd ip" ;;
|
|
wireguard) echo "wg wg-quick ip" ;;
|
|
openvpn) echo "openvpn ip" ;;
|
|
openconnect) echo "openconnect ip" ;;
|
|
sshuttle) echo "sshuttle ssh" ;;
|
|
esac
|
|
}
|
|
|
|
# Le script que openconnect appelle pour poser routes et DNS. Il vient d'un
|
|
# paquet à part, dont le nom change de famille en famille, et il s'installe à
|
|
# des endroits différents : c'est un FICHIER qu'on cherche, pas un binaire du
|
|
# PATH, donc `binaries_for` ne peut pas le voir.
|
|
#
|
|
# Sans lui, la session s'ouvre, openconnect se lance, et l'interface tun
|
|
# n'apparaît jamais : le montage échoue trois étages plus haut, sur un
|
|
# symptôme qui n'accuse pas le paquet manquant.
|
|
VPNC_SCRIPT_PATHS="
|
|
/etc/vpnc/vpnc-script
|
|
/usr/share/vpnc-scripts/vpnc-script
|
|
/usr/libexec/openconnect/vpnc-script
|
|
/usr/lib/openconnect/vpnc-script
|
|
"
|
|
|
|
verify_vpnc_script() {
|
|
local fam="$1" path package
|
|
for path in ${VPNC_SCRIPT_PATHS}; do
|
|
if [ -x "$path" ]; then
|
|
log "vpnc-script : ${path}"
|
|
return 0
|
|
fi
|
|
done
|
|
case "$fam" in
|
|
rhel|suse) package="vpnc-script" ;;
|
|
*) package="vpnc-scripts" ;;
|
|
esac
|
|
die "vpnc-script introuvable — installer le paquet ${package}"
|
|
}
|
|
|
|
verify() {
|
|
local driver="$1" fam="$2" missing=""
|
|
for b in $(binaries_for "$driver"); do
|
|
command -v "$b" >/dev/null || missing="${missing} ${b}"
|
|
done
|
|
if [ -n "$missing" ]; then
|
|
die "toujours absents après installation :${missing}"
|
|
fi
|
|
if [ "$driver" = "openconnect" ]; then
|
|
verify_vpnc_script "$fam"
|
|
fi
|
|
log "vérifié : tout est en place pour ${driver}"
|
|
}
|
|
|
|
ALL_DRIVERS="l2tp_ipsec wireguard openvpn openconnect sshuttle"
|
|
|
|
main() {
|
|
case "${1:-}" in
|
|
-h|--help) usage; exit 0 ;;
|
|
esac
|
|
check_root "$@"
|
|
detect_os
|
|
local fam drivers
|
|
fam="$(family)"
|
|
# Sans argument : tout. C'est ce que « [8] Installer les paquets
|
|
# client » demande quand on ne choisit pas de technologie.
|
|
drivers="${*:-${ALL_DRIVERS}}"
|
|
for driver in ${drivers}; do
|
|
log "── ${driver} ──"
|
|
install_packages "$fam" "$(packages_for "$driver" "$fam")"
|
|
verify "$driver" "$fam"
|
|
done
|
|
disable_autostart
|
|
log "Terminé. Monter un tunnel : ./script/vpn/vpn.py up --profile <nom>"
|
|
}
|
|
|
|
main "$@"
|