455 lines
12 KiB
Bash
Executable File
455 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
set -euo pipefail
|
|
|
|
if [[ "${EUID}" -ne 0 ]]; then
|
|
echo "Ce script doit etre lance en root."
|
|
echo "Exemple: sudo $0"
|
|
exit 1
|
|
fi
|
|
|
|
# Cree une copie horodatee d'un fichier avant modification.
|
|
backup_file() {
|
|
local file="$1"
|
|
if [[ -e "${file}" ]]; then
|
|
cp -a "${file}" "${file}.bak.$(date +%Y%m%d-%H%M%S)"
|
|
fi
|
|
}
|
|
|
|
# Demande une valeur obligatoire et recommence tant que la saisie est vide.
|
|
ask_required() {
|
|
local prompt="$1"
|
|
local value
|
|
|
|
while true; do
|
|
read -r -p "${prompt}: " value
|
|
if [[ -n "${value}" ]]; then
|
|
printf '%s\n' "${value}"
|
|
return 0
|
|
fi
|
|
echo "Valeur obligatoire." >&2
|
|
done
|
|
}
|
|
|
|
# Demande une valeur facultative et retourne une chaine vide si l'utilisateur valide sans saisie.
|
|
ask_optional() {
|
|
local prompt="$1"
|
|
local value
|
|
|
|
read -r -p "${prompt}: " value
|
|
printf '%s\n' "${value}"
|
|
}
|
|
|
|
# Verifie qu'un hostname respecte une forme DNS simple compatible Debian.
|
|
is_valid_hostname() {
|
|
local value="$1"
|
|
[[ "${value}" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$ ]]
|
|
}
|
|
|
|
# Verifie qu'une valeur est une adresse IPv4 composee de quatre octets valides.
|
|
is_valid_ipv4() {
|
|
local value="$1"
|
|
local octets
|
|
IFS='.' read -r -a octets <<< "${value}"
|
|
|
|
[[ "${#octets[@]}" -eq 4 ]] || return 1
|
|
for octet in "${octets[@]}"; do
|
|
[[ "${octet}" =~ ^[0-9]+$ ]] || return 1
|
|
(( octet >= 0 && octet <= 255 )) || return 1
|
|
done
|
|
}
|
|
|
|
# Verifie qu'une valeur est une adresse IPv4 avec prefixe CIDR valide.
|
|
is_valid_cidr() {
|
|
local value="$1"
|
|
local ip prefix
|
|
|
|
[[ "${value}" == */* ]] || return 1
|
|
ip="${value%/*}"
|
|
prefix="${value#*/}"
|
|
|
|
is_valid_ipv4 "${ip}" || return 1
|
|
[[ "${prefix}" =~ ^[0-9]+$ ]] || return 1
|
|
(( prefix >= 1 && prefix <= 32 ))
|
|
}
|
|
|
|
# Demande un hostname facultatif et valide la saisie quand elle est renseignee.
|
|
ask_optional_hostname() {
|
|
local value
|
|
|
|
while true; do
|
|
value="$(ask_optional "Nom de la machine (laisser vide pour ne pas modifier)")"
|
|
if [[ -z "${value}" ]] || is_valid_hostname "${value}"; then
|
|
printf '%s\n' "${value}"
|
|
return 0
|
|
fi
|
|
echo "Nom invalide. Utilisez lettres, chiffres et tirets, sans tiret au debut ou a la fin." >&2
|
|
done
|
|
}
|
|
|
|
# Demande une adresse IPv4 obligatoire avec le libelle fourni en parametre.
|
|
ask_ipv4() {
|
|
local prompt="$1"
|
|
local value
|
|
|
|
while true; do
|
|
value="$(ask_required "${prompt}")"
|
|
if is_valid_ipv4 "${value}"; then
|
|
printf '%s\n' "${value}"
|
|
return 0
|
|
fi
|
|
echo "Adresse IPv4 invalide." >&2
|
|
done
|
|
}
|
|
|
|
# Demande une adresse CIDR facultative pour activer ou ignorer la configuration reseau.
|
|
ask_optional_cidr() {
|
|
local value
|
|
|
|
while true; do
|
|
value="$(ask_optional "Adresse IP avec prefixe CIDR (ex: 192.168.1.50/24, laisser vide pour ne pas modifier le reseau)")"
|
|
if [[ -z "${value}" ]] || is_valid_cidr "${value}"; then
|
|
printf '%s\n' "${value}"
|
|
return 0
|
|
fi
|
|
echo "Adresse CIDR invalide." >&2
|
|
done
|
|
}
|
|
|
|
# Demande un ou plusieurs serveurs DNS et valide chaque adresse IPv4.
|
|
ask_dns_servers() {
|
|
local value server
|
|
|
|
while true; do
|
|
value="$(ask_required "Serveur(s) DNS separes par des espaces (ex: 1.1.1.1 8.8.8.8)")"
|
|
for server in ${value}; do
|
|
if ! is_valid_ipv4 "${server}"; then
|
|
echo "Serveur DNS invalide: ${server}" >&2
|
|
continue 2
|
|
fi
|
|
done
|
|
printf '%s\n' "${value}"
|
|
return 0
|
|
done
|
|
}
|
|
|
|
# Propose d'utiliser la gateway comme DNS, sinon demande une saisie DNS classique.
|
|
ask_dns_servers_with_gateway_default() {
|
|
local gateway="$1"
|
|
local answer
|
|
|
|
read -r -p "Utiliser la gateway ${gateway} comme serveur DNS ? [Y/n]: " answer
|
|
if [[ -z "${answer}" || "${answer}" =~ ^[yY]$ ]]; then
|
|
printf '%s\n' "${gateway}"
|
|
return 0
|
|
fi
|
|
|
|
ask_dns_servers
|
|
}
|
|
|
|
# Liste les interfaces reseau disponibles et demande celle a configurer.
|
|
choose_interface() {
|
|
local interfaces value
|
|
|
|
mapfile -t interfaces < <(find /sys/class/net -mindepth 1 -maxdepth 1 -printf '%f\n' | grep -v '^lo$' | sort)
|
|
if [[ "${#interfaces[@]}" -eq 0 ]]; then
|
|
echo "Aucune interface reseau detectee." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Interfaces disponibles:" >&2
|
|
printf ' - %s\n' "${interfaces[@]}" >&2
|
|
|
|
while true; do
|
|
value="$(ask_required "Interface a configurer")"
|
|
if [[ -d "/sys/class/net/${value}" && "${value}" != "lo" ]]; then
|
|
printf '%s\n' "${value}"
|
|
return 0
|
|
fi
|
|
echo "Interface inconnue." >&2
|
|
done
|
|
}
|
|
|
|
# Demande une confirmation explicite avant d'appliquer les modifications.
|
|
confirm() {
|
|
local answer
|
|
|
|
read -r -p "Appliquer cette configuration ? [y/N]: " answer
|
|
[[ "${answer}" =~ ^[yY]$ ]]
|
|
}
|
|
|
|
# Met a jour /etc/hosts avec la nouvelle entree 127.0.1.1 du hostname.
|
|
update_hosts_file() {
|
|
local hostname="$1"
|
|
local tmp_file
|
|
|
|
tmp_file="$(mktemp)"
|
|
awk '
|
|
$1 == "127.0.1.1" { next }
|
|
{ print }
|
|
' /etc/hosts > "${tmp_file}"
|
|
printf '127.0.1.1\t%s\n' "${hostname}" >> "${tmp_file}"
|
|
install -m 0644 "${tmp_file}" /etc/hosts
|
|
rm -f "${tmp_file}"
|
|
}
|
|
|
|
# Reecrit /etc/network/interfaces avec une configuration IPv4 statique ifupdown.
|
|
write_network_interfaces() {
|
|
local interface="$1"
|
|
local ip_cidr="$2"
|
|
local gateway="$3"
|
|
local dns_servers="$4"
|
|
|
|
cat > /etc/network/interfaces <<EOF
|
|
# Fichier genere par configure-debian-network.sh
|
|
source /etc/network/interfaces.d/*
|
|
|
|
auto lo
|
|
iface lo inet loopback
|
|
|
|
allow-hotplug ${interface}
|
|
auto ${interface}
|
|
iface ${interface} inet static
|
|
address ${ip_cidr}
|
|
gateway ${gateway}
|
|
dns-nameservers ${dns_servers}
|
|
EOF
|
|
}
|
|
|
|
# Indique si dhcpcd semble present et susceptible de gerer resolv.conf.
|
|
has_dhcpcd() {
|
|
[[ -e /etc/dhcpcd.conf ]] ||
|
|
[[ -e /lib/systemd/system/dhcpcd.service ]] ||
|
|
[[ -e /usr/lib/systemd/system/dhcpcd.service ]] ||
|
|
command -v dhcpcd >/dev/null 2>&1 ||
|
|
grep -qi 'dhcpcd' /etc/resolv.conf 2>/dev/null
|
|
}
|
|
|
|
# Indique si systemd-resolved semble gerer la resolution DNS.
|
|
has_systemd_resolved() {
|
|
local resolv_target
|
|
|
|
resolv_target="$(readlink -f /etc/resolv.conf 2>/dev/null || true)"
|
|
[[ "${resolv_target}" == *systemd/resolve* ]] ||
|
|
[[ -e /lib/systemd/system/systemd-resolved.service ]] ||
|
|
[[ -e /usr/lib/systemd/system/systemd-resolved.service ]]
|
|
}
|
|
|
|
# Met a jour /etc/dhcpcd.conf avec un bloc statique dedie au script.
|
|
write_dhcpcd_conf() {
|
|
local interface="$1"
|
|
local ip_cidr="$2"
|
|
local gateway="$3"
|
|
local dns_servers="$4"
|
|
local tmp_file
|
|
|
|
if ! has_dhcpcd; then
|
|
return 0
|
|
fi
|
|
|
|
touch /etc/dhcpcd.conf
|
|
tmp_file="$(mktemp)"
|
|
awk '
|
|
$0 == "# BEGIN configure-debian-network.sh" { skip = 1; next }
|
|
$0 == "# END configure-debian-network.sh" { skip = 0; next }
|
|
skip != 1 { print }
|
|
' /etc/dhcpcd.conf > "${tmp_file}"
|
|
|
|
{
|
|
printf '\n# BEGIN configure-debian-network.sh\n'
|
|
printf 'interface %s\n' "${interface}"
|
|
printf 'static ip_address=%s\n' "${ip_cidr}"
|
|
printf 'static routers=%s\n' "${gateway}"
|
|
printf 'static domain_name_servers=%s\n' "${dns_servers}"
|
|
printf '# END configure-debian-network.sh\n'
|
|
} >> "${tmp_file}"
|
|
|
|
install -m 0644 "${tmp_file}" /etc/dhcpcd.conf
|
|
rm -f "${tmp_file}"
|
|
}
|
|
|
|
# Ajoute un hook dhcpcd qui restaure les DNS apres regeneration de resolv.conf.
|
|
write_dhcpcd_dns_hook() {
|
|
local dns_servers="$1"
|
|
local resolv_target
|
|
local server tmp_file
|
|
|
|
if ! has_dhcpcd; then
|
|
return 0
|
|
fi
|
|
|
|
resolv_target="$(readlink -f /etc/resolv.conf 2>/dev/null || true)"
|
|
if [[ "${resolv_target}" == *systemd/resolve* ]]; then
|
|
return 0
|
|
fi
|
|
|
|
touch /etc/dhcpcd.exit-hook
|
|
tmp_file="$(mktemp)"
|
|
awk '
|
|
$0 == "# BEGIN configure-debian-network.sh" { skip = 1; next }
|
|
$0 == "# END configure-debian-network.sh" { skip = 0; next }
|
|
skip != 1 { print }
|
|
' /etc/dhcpcd.exit-hook > "${tmp_file}"
|
|
|
|
{
|
|
printf '\n# BEGIN configure-debian-network.sh\n'
|
|
printf 'cat > /etc/resolv.conf <<'\''EOF_DNS'\''\n'
|
|
printf '# Fichier genere par configure-debian-network.sh via dhcpcd.exit-hook\n'
|
|
for server in ${dns_servers}; do
|
|
printf 'nameserver %s\n' "${server}"
|
|
done
|
|
printf 'EOF_DNS\n'
|
|
printf '# END configure-debian-network.sh\n'
|
|
} >> "${tmp_file}"
|
|
|
|
install -m 0755 "${tmp_file}" /etc/dhcpcd.exit-hook
|
|
rm -f "${tmp_file}"
|
|
}
|
|
|
|
# Configure les DNS persistants pour systemd-resolved si ce backend est present.
|
|
write_systemd_resolved_conf() {
|
|
local dns_servers="$1"
|
|
|
|
if ! has_systemd_resolved; then
|
|
return 0
|
|
fi
|
|
|
|
install -m 0755 -d /etc/systemd/resolved.conf.d
|
|
cat > /etc/systemd/resolved.conf.d/99-configure-debian-network.conf <<EOF
|
|
# Fichier genere par configure-debian-network.sh
|
|
[Resolve]
|
|
DNS=${dns_servers}
|
|
Domains=~.
|
|
EOF
|
|
}
|
|
|
|
# Ecrit les serveurs DNS dans /etc/resolv.conf quand c'est pertinent.
|
|
write_resolv_conf() {
|
|
local dns_servers="$1"
|
|
local resolv_target
|
|
local server
|
|
|
|
if [[ -L /etc/resolv.conf ]]; then
|
|
resolv_target="$(readlink -f /etc/resolv.conf 2>/dev/null || true)"
|
|
if [[ "${resolv_target}" == *systemd/resolve* ]] || ! has_dhcpcd; then
|
|
echo "/etc/resolv.conf est un lien symbolique, il ne sera pas modifie directement."
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
{
|
|
echo "# Fichier genere par configure-debian-network.sh"
|
|
for server in ${dns_servers}; do
|
|
echo "nameserver ${server}"
|
|
done
|
|
} > /etc/resolv.conf
|
|
}
|
|
|
|
# Orchestre la collecte des informations, la confirmation et l'application des changements.
|
|
main() {
|
|
local hostname interface ip_cidr gateway dns_servers reboot_answer restart_answer network_changed
|
|
|
|
echo "Configuration reseau Debian"
|
|
echo
|
|
|
|
hostname="$(ask_optional_hostname)"
|
|
ip_cidr="$(ask_optional_cidr)"
|
|
|
|
if [[ -n "${ip_cidr}" ]]; then
|
|
interface="$(choose_interface)"
|
|
gateway="$(ask_ipv4 "Gateway (ex: 192.168.1.1)")"
|
|
dns_servers="$(ask_dns_servers_with_gateway_default "${gateway}")"
|
|
network_changed=1
|
|
else
|
|
interface=""
|
|
gateway=""
|
|
dns_servers=""
|
|
network_changed=0
|
|
fi
|
|
|
|
if [[ -z "${hostname}" && "${network_changed}" -eq 0 ]]; then
|
|
echo "Aucune modification demandee."
|
|
exit 0
|
|
fi
|
|
|
|
echo
|
|
echo "Resume:"
|
|
if [[ -n "${hostname}" ]]; then
|
|
echo " Nom machine : ${hostname}"
|
|
else
|
|
echo " Nom machine : non modifie"
|
|
fi
|
|
if [[ "${network_changed}" -eq 1 ]]; then
|
|
echo " Interface : ${interface}"
|
|
echo " IP : ${ip_cidr}"
|
|
echo " Gateway : ${gateway}"
|
|
echo " DNS : ${dns_servers}"
|
|
else
|
|
echo " Reseau : non modifie"
|
|
fi
|
|
echo
|
|
|
|
if ! confirm; then
|
|
echo "Annule."
|
|
exit 0
|
|
fi
|
|
|
|
if [[ -n "${hostname}" ]]; then
|
|
backup_file /etc/hostname
|
|
backup_file /etc/hosts
|
|
hostnamectl set-hostname "${hostname}"
|
|
printf '%s\n' "${hostname}" > /etc/hostname
|
|
update_hosts_file "${hostname}"
|
|
fi
|
|
|
|
if [[ "${network_changed}" -eq 1 ]]; then
|
|
backup_file /etc/network/interfaces
|
|
backup_file /etc/dhcpcd.conf
|
|
backup_file /etc/dhcpcd.exit-hook
|
|
backup_file /etc/systemd/resolved.conf.d/99-configure-debian-network.conf
|
|
backup_file /etc/resolv.conf
|
|
write_network_interfaces "${interface}" "${ip_cidr}" "${gateway}" "${dns_servers}"
|
|
write_dhcpcd_conf "${interface}" "${ip_cidr}" "${gateway}" "${dns_servers}"
|
|
write_dhcpcd_dns_hook "${dns_servers}"
|
|
write_systemd_resolved_conf "${dns_servers}"
|
|
write_resolv_conf "${dns_servers}"
|
|
fi
|
|
|
|
echo
|
|
echo "Configuration ecrite."
|
|
if [[ "${network_changed}" -eq 1 ]]; then
|
|
read -r -p "Redemarrer le service reseau maintenant ? [y/N]: " restart_answer
|
|
if [[ "${restart_answer}" =~ ^[yY]$ ]]; then
|
|
if has_dhcpcd && systemctl is-active --quiet dhcpcd; then
|
|
systemctl restart dhcpcd
|
|
echo "Service dhcpcd redemarre."
|
|
fi
|
|
if has_systemd_resolved && systemctl is-active --quiet systemd-resolved; then
|
|
systemctl restart systemd-resolved
|
|
echo "Service systemd-resolved redemarre."
|
|
fi
|
|
if systemctl restart networking; then
|
|
echo "Service networking redemarre."
|
|
else
|
|
echo "Impossible de redemarrer networking. Un redemarrage de la machine peut etre necessaire." >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Redemarrez le reseau ou la machine pour appliquer la configuration IP."
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "${hostname}" ]]; then
|
|
echo
|
|
read -r -p "Redemarrer la machine maintenant pour appliquer completement le hostname ? [y/N]: " reboot_answer
|
|
if [[ "${reboot_answer}" =~ ^[yY]$ ]]; then
|
|
echo "Redemarrage de la machine."
|
|
systemctl reboot
|
|
else
|
|
echo "Redemarrez la machine plus tard pour que le nouveau hostname soit pris en compte partout."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
main "$@"
|