#!/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 } # 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 </dev/null 2>&1 || grep -qi 'dhcpcd' /etc/resolv.conf 2>/dev/null } # 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}" } # Ecrit les serveurs DNS dans /etc/resolv.conf quand ce fichier n'est pas gere par lien symbolique. write_resolv_conf() { local dns_servers="$1" local server if [[ -L /etc/resolv.conf ]]; then echo "/etc/resolv.conf est un lien symbolique, il ne sera pas modifie directement." return 0 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 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)" 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/resolv.conf write_network_interfaces "${interface}" "${ip_cidr}" "${gateway}" "${dns_servers}" write_dhcpcd_conf "${interface}" "${ip_cidr}" "${gateway}" "${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 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 } main "$@"