KarelWintersky

KarelWintersky / add_ip.sh

Last active 13 hours ago

Like 0
add_ip.sh Raw
1#!/usr/bin/env bash
2#
3# add-ip.sh — Автоматическое добавление IP-адресов на Linux-сервер
4#
5# Поддерживает: Ubuntu (netplan), Debian/Astra (/etc/network/interfaces),
6# CentOS/AlmaLinux (NetworkManager/nmcli), Arch Linux (systemd-networkd)
7#
8# Использование:
9# sudo bash add-ip.sh
10#
11# Скрипт запросит нужные данные и применит конфигурацию автоматически.
12
13set -euo pipefail
14
15# ─── Цвета ───────────────────────────────────────────────────────────────────
16RED='\033[0;31m'
17GREEN='\033[0;32m'
18YELLOW='\033[1;33m'
19CYAN='\033[0;36m'
20NC='\033[0m'
21
22info() { echo -e "${CYAN}[INFO]${NC} $*"; }
23ok() { echo -e "${GREEN}[OK]${NC} $*"; }
24warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
25err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
26die() { err "$@"; exit 1; }
27
28# ─── Проверка root ───────────────────────────────────────────────────────────
29if [[ $EUID -ne 0 ]]; then
30 die "Скрипт нужно запускать от root: sudo bash $0"
31fi
32
33# ─── Определение дистрибутива ────────────────────────────────────────────────
34detect_distro() {
35 if [[ -f /etc/os-release ]]; then
36 . /etc/os-release
37 DISTRO_ID="${ID:-unknown}"
38 DISTRO_LIKE="${ID_LIKE:-}"
39 else
40 die "Не удалось определить дистрибутив (/etc/os-release не найден)"
41 fi
42
43 case "$DISTRO_ID" in
44 ubuntu) DISTRO_FAMILY="ubuntu" ;;
45 debian) DISTRO_FAMILY="debian" ;;
46 astra) DISTRO_FAMILY="debian" ;;
47 centos|almalinux|rocky|rhel|fedora)
48 DISTRO_FAMILY="centos" ;;
49 arch|manjaro|endeavouros)
50 DISTRO_FAMILY="arch" ;;
51 *)
52 # Fallback: проверяем по ID_LIKE
53 if [[ "$DISTRO_LIKE" == *"debian"* ]]; then
54 DISTRO_FAMILY="debian"
55 elif [[ "$DISTRO_LIKE" == *"rhel"* || "$DISTRO_LIKE" == *"centos"* ]]; then
56 DISTRO_FAMILY="centos"
57 else
58 die "Неподдерживаемый дистрибутив: $DISTRO_ID ($DISTRO_LIKE)"
59 fi
60 ;;
61 esac
62 info "Дистрибутив: $DISTRO_ID (семейство: $DISTRO_FAMILY)"
63}
64
65# ─── Определение интерфейса ─────────────────────────────────────────────────
66detect_interface() {
67 # Берём первый нелупбэк интерфейс с IPv4 или IPv6 адресом (кроме виртуальных)
68 IFACE=$(ip -o link show up | awk -F': ' '{print $2}' | grep -v '^lo$' | grep -v '^docker' | grep -v '^br-' | grep -v '^veth' | head -n1)
69 if [[ -z "$IFACE" ]]; then
70 die "Не удалось определить сетевой интерфейс. Укажите вручную."
71 fi
72 info "Сетевой интерфейс: $IFACE"
73}
74
75# ─── Сбор данных от пользователя ────────────────────────────────────────────
76collect_data() {
77 echo ""
78 echo -e "${CYAN}══════════════════════════════════════════════════════════════${NC}"
79 echo -e "${CYAN} Добавление IP-адресов на сервер${NC}"
80 echo -e "${CYAN}══════════════════════════════════════════════════════════════${NC}"
81 echo ""
82
83 # Текущие адреса
84 info "Текущие IP-адреса на интерфейсе $IFACE:"
85 ip addr show "$IFACE" | grep -E 'inet ' | sed 's/^/ /'
86 echo ""
87
88 # IPv4
89 read -rp "Добавить IPv4-адрес? (например 192.0.2.70/24) [пусто = пропустить]: " IPV4_ADDR
90 if [[ -n "$IPV4_ADDR" ]]; then
91 # Извлекаем префикс если не указан
92 if [[ "$IPV4_ADDR" != *"/"* ]]; then
93 IPV4_ADDR="${IPV4_ADDR}/24"
94 warn "Маска не указана, используем /24"
95 fi
96 read -rp "IPv4 шлюз (первый адрес в сети, например 192.0.2.1): " IPV4_GW
97 [[ -z "$IPV4_GW" ]] && die "Шлюз обязателен для IPv4"
98 HAS_IPV4=true
99 else
100 HAS_IPV4=false
101 fi
102
103 # IPv6
104 read -rp "Добавить IPv6-адрес? (например 2001:db8:a:a::a/128) [пусто = пропустить]: " IPV6_ADDR
105 if [[ -n "$IPV6_ADDR" ]]; then
106 if [[ "$IPV6_ADDR" != *"/"* ]]; then
107 IPV6_ADDR="${IPV6_ADDR}/128"
108 warn "Маска не указана, используем /128"
109 fi
110 read -rp "IPv6 шлюз (например 2001:db8:a:a::1): " IPV6_GW
111 [[ -z "$IPV6_GW" ]] && die "Шлюз обязателен для IPv6"
112 HAS_IPV6=true
113 else
114 HAS_IPV6=false
115 fi
116
117 if ! $HAS_IPV4 && ! $HAS_IPV6; then
118 die "Ни один адрес не указан. Выход."
119 fi
120
121 # DNS
122 read -rp "DNS-серверы через запятую (по умолчанию 1.1.1.1,1.0.0.1): " DNS_INPUT
123 if [[ -z "$DNS_INPUT" ]]; then
124 DNS_V4=("1.1.1.1" "1.0.0.1")
125 DNS_V6=("2606:4700:4700::1111" "2606:4700:4700::1001")
126 else
127 IFS=',' read -ra DNS_V4 <<< "$DNS_INPUT"
128 DNS_V6=("${DNS_V4[@]}")
129 fi
130
131 # Подтверждение
132 echo ""
133 echo -e "${YELLOW}══════════════════════════════════════════════════════════════${NC}"
134 echo -e "${YELLOW} Итого:${NC}"
135 echo -e "${YELLOW} Интерфейс: ${NC}$IFACE"
136 $HAS_IPV4 && echo -e "${YELLOW} IPv4: ${NC}$IPV4_ADDR шлюз: $IPV4_GW"
137 $HAS_IPV6 && echo -e "${YELLOW} IPv6: ${NC}$IPV6_ADDR шлюз: $IPV6_GW"
138 echo -e "${YELLOW} DNS v4: ${NC}${DNS_V4[*]}"
139 echo -e "${YELLOW} DNS v6: ${NC}${DNS_V6[*]}"
140 echo -e "${YELLOW}══════════════════════════════════════════════════════════════${NC}"
141 echo ""
142 read -rp "Всё верно? Применить? (y/N): " CONFIRM
143 [[ "$CONFIRM" =~ ^[YyДд]$ ]] || die "Отмена пользователем."
144}
145
146# ─── Общие шаги: sysctl + cloud-init ────────────────────────────────────────
147setup_sysctl_nonlocal_bind() {
148 info "Настройка ip_nonlocal_bind..."
149 local SYSCTL_FILE="/etc/sysctl.d/99-nonlocal-bind.conf"
150 cat > "$SYSCTL_FILE" <<'EOF'
151net.ipv4.ip_nonlocal_bind = 1
152net.ipv6.ip_nonlocal_bind = 1
153EOF
154 sysctl --system >/dev/null 2>&1
155 ok "ip_nonlocal_bind включён"
156}
157
158disable_cloud_init_network() {
159 local CLOUD_CFG="/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg"
160 if [[ ! -f "$CLOUD_CFG" ]]; then
161 info "Отключение cloud-init network config..."
162 mkdir -p /etc/cloud/cloud.cfg.d/
163 echo 'network: {config: disabled}' > "$CLOUD_CFG"
164 ok "cloud-init network config отключён"
165 else
166 ok "cloud-init network config уже отключён"
167 fi
168}
169
170# ─── Ubuntu (netplan) ───────────────────────────────────────────────────────
171configure_ubuntu() {
172 info "Конфигурация через netplan..."
173
174 # Бэкап существующих файлов
175 for f in /etc/netplan/*.yaml; do
176 if [[ -f "$f" && "$f" != *"99-ipv"* ]]; then
177 local backup="${f}-backup-$(date +%Y%m%d%H%M%S)"
178 mv "$f" "$backup"
179 info "Создан бэкап: $backup"
180 fi
181 done
182
183 # --- IPv4 ---
184 if $HAS_IPV4; then
185 cat > /etc/netplan/99-ipv4.yaml <<YAML
186network:
187 version: 2
188 renderer: networkd
189 ethernets:
190 ${IFACE}:
191 dhcp4: false
192 addresses:
193 - "${IPV4_ADDR}"
194 routes:
195 - to: "0.0.0.0/0"
196 via: "${IPV4_GW}"
197 nameservers:
198 addresses:
199$(printf ' - "%s"\n' "${DNS_V4[@]}")
200YAML
201 chmod 600 /etc/netplan/99-ipv4.yaml
202 ok "Создан /etc/netplan/99-ipv4.yaml"
203 fi
204
205 # --- IPv6 ---
206 if $HAS_IPV6; then
207 cat > /etc/netplan/99-ipv6.yaml <<YAML
208network:
209 version: 2
210 renderer: networkd
211 ethernets:
212 ${IFACE}:
213 accept-ra: false
214 dhcp6: false
215 addresses:
216 - "${IPV6_ADDR}"
217 routes:
218 - to: "::/0"
219 via: "${IPV6_GW}"
220 on-link: true
221 nameservers:
222 addresses:
223$(printf ' - "%s"\n' "${DNS_V6[@]}")
224YAML
225 chmod 600 /etc/netplan/99-ipv6.yaml
226 ok "Создан /etc/netplan/99-ipv6.yaml"
227 fi
228
229 # Отключение dhcpcd6
230 if systemctl list-unit-files | grep -q 'dhcpcd6.service'; then
231 systemctl disable dhcpcd6.service 2>/dev/null || true
232 fi
233 if systemctl list-unit-files | grep -q 'dhclient6.service'; then
234 systemctl disable dhclient6.service 2>/dev/null || true
235 fi
236
237 systemctl daemon-reload 2>/dev/null || true
238
239 setup_sysctl_nonlocal_bind
240 disable_cloud_init_network
241
242 info "Применение netplan..."
243 netplan --debug apply 2>&1 || warn "netplan apply вернул ошибку — попробуйте перезагрузку"
244 ok "Netplan конфигурация применена"
245}
246
247# ─── Debian/Astra (/etc/network/interfaces) ──────────────────────────────────
248configure_debian() {
249 info "Конфигурация через /etc/network/interfaces..."
250
251 local IFACE_FILE="/etc/network/interfaces"
252 local BACKUP="${IFACE_FILE}-backup-$(date +%Y%m%d%H%M%S)"
253 [[ -f "$IFACE_FILE" ]] && cp "$IFACE_FILE" "$BACKUP" && info "Бэкап: $BACKUP"
254
255 # Генерируем новый конфиг
256 {
257 echo "auto lo"
258 echo "iface lo inet loopback"
259 echo ""
260
261 if $HAS_IPV4; then
262 echo "auto ${IFACE}"
263 echo "allow-hotplug ${IFACE}"
264 echo "iface ${IFACE} inet static"
265 echo -e "\taddress ${IPV4_ADDR}"
266 echo -e "\tgateway ${IPV4_GW}"
267 echo -e "\tdns-nameservers ${DNS_V4[*]}"
268 echo ""
269 fi
270
271 if $HAS_IPV6; then
272 echo "auto ${IFACE}"
273 echo "allow-hotplug ${IFACE}"
274 echo "iface ${IFACE} inet6 static"
275 echo -e "\taddress ${IPV6_ADDR}"
276 echo -e "\tgateway ${IPV6_GW}"
277 echo -e "\tdns-nameservers ${DNS_V6[*]}"
278 fi
279 } > "$IFACE_FILE"
280
281 ok "Записан $IFACE_FILE"
282
283 # Бэкап cloud-init файла
284 local INTERFACES_D="/etc/network/interfaces.d"
285 if [[ -d "$INTERFACES_D" ]]; then
286 for f in "$INTERFACES_D"/*; do
287 [[ -f "$f" ]] && mv "$f" "/root/$(basename "$f")-backup-$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
288 done
289 fi
290
291 # Отключение dhcpcd6
292 if systemctl list-unit-files | grep -q 'dhcpcd6.service'; then
293 systemctl disable dhcpcd6.service 2>/dev/null || true
294 fi
295 if systemctl list-unit-files | grep -q 'dhclient6.service'; then
296 systemctl disable dhclient6.service 2>/dev/null || true
297 fi
298
299 systemctl daemon-reload 2>/dev/null || true
300
301 setup_sysctl_nonlocal_bind
302 disable_cloud_init_network
303
304 info "Перезапуск networking..."
305 systemctl restart networking.service 2>&1 || warn "networking restart вернул ошибку — попробуйте перезагрузку"
306 ok "Конфигурация применена"
307}
308
309# ─── CentOS/AlmaLinux (NetworkManager / nmcli) ──────────────────────────────
310configure_centos() {
311 info "Конфигурация через NetworkManager (nmcli)..."
312
313 # Определяем имя соединения
314 local CONN_NAME
315 CONN_NAME=$(nmcli -t -f NAME,DEVICE connection show | grep ":${IFACE}$" | head -n1 | cut -d: -f1)
316 if [[ -z "$CONN_NAME" ]]; then
317 die "Не удалось найти соединение для интерфейса $IFACE. Проверьте 'nmcli connection show'"
318 fi
319 info "Имя соединения: $CONN_NAME"
320
321 # Бэкап текущих настроек
322 local CONN_DIR="/etc/NetworkManager/system-connections"
323 local CONN_FILE=""
324 for f in "$CONN_DIR"/*; do
325 if [[ -f "$f" ]] && grep -q "interface-name=${IFACE}" "$f" 2>/dev/null; then
326 CONN_FILE="$f"
327 break
328 fi
329 done
330 if [[ -n "$CONN_FILE" ]]; then
331 local BACKUP="${CONN_FILE}-backup-$(date +%Y%m%d%H%M%S)"
332 cp "$CONN_FILE" "$BACKUP"
333 info "Бэкап: $BACKUP"
334 fi
335
336 # IPv4
337 if $HAS_IPV4; then
338 nmcli connection modify "$CONN_NAME" ipv4.addresses "$IPV4_ADDR"
339 nmcli connection modify "$CONN_NAME" ipv4.gateway "$IPV4_GW"
340 nmcli connection modify "$CONN_NAME" ipv4.method manual
341 nmcli connection modify "$CONN_NAME" ipv4.dns "${DNS_V4[0]}"
342 for dns in "${DNS_V4[@]:1}"; do
343 nmcli connection modify "$CONN_NAME" +ipv4.dns "$dns"
344 done
345 ok "IPv4 настроены: $IPV4_ADDR, шлюз $IPV4_GW"
346 fi
347
348 # IPv6
349 if $HAS_IPV6; then
350 nmcli connection modify "$CONN_NAME" ipv6.addresses "$IPV6_ADDR"
351 nmcli connection modify "$CONN_NAME" ipv6.gateway "$IPV6_GW"
352 nmcli connection modify "$CONN_NAME" ipv6.method manual
353 nmcli connection modify "$CONN_NAME" ipv6.dns "${DNS_V6[0]}"
354 for dns in "${DNS_V6[@]:1}"; do
355 nmcli connection modify "$CONN_NAME" +ipv6.dns "$dns"
356 done
357 ok "IPv6 настроены: $IPV6_ADDR, шлюз $IPV6_GW"
358 fi
359
360 # Бэкап старых ifcfg файлов
361 local NETSCRIPTS="/etc/sysconfig/network-scripts"
362 if [[ -d "$NETSCRIPTS" ]]; then
363 for f in "$NETSCRIPTS"/ifcfg-*; do
364 [[ -f "$f" && "$f" != *"backup"* ]] && mv "$f" "/root/$(basename "$f")-backup-$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
365 done
366 fi
367
368 setup_sysctl_nonlocal_bind
369 disable_cloud_init_network
370
371 info "Перезапуск NetworkManager..."
372 systemctl restart NetworkManager.service 2>&1 || warn "NetworkManager restart вернул ошибку — попробуйте перезагрузку"
373 ok "Конфигурация применена"
374}
375
376# ─── Arch Linux (systemd-networkd) ──────────────────────────────────────────
377configure_arch() {
378 info "Конфигурация через systemd-networkd..."
379
380 local NETDIR="/etc/systemd/network"
381 mkdir -p "$NETDIR"
382
383 # Бэкап старых конфигов
384 for f in "$NETDIR"/*.network; do
385 [[ -f "$f" ]] && mv "$f" "${f}_old" 2>/dev/null || true
386 done
387
388 # Включаем нужные сервисы
389 systemctl enable --now systemd-networkd.service 2>/dev/null || true
390 systemctl enable --now systemd-resolved.service 2>/dev/null || true
391
392 # IPv4
393 if $HAS_IPV4; then
394 cat > "$NETDIR/10-static-ipv4.network" <<EOF
395[Match]
396Name=${IFACE}
397
398[Network]
399Address=${IPV4_ADDR}
400Gateway=${IPV4_GW}
401$(printf 'DNS=%s\n' "${DNS_V4[@]}")
402DHCP=no
403EOF
404 ok "Создан $NETDIR/10-static-ipv4.network"
405 fi
406
407 # IPv6
408 if $HAS_IPV6; then
409 cat > "$NETDIR/20-static-ipv6.network" <<EOF
410[Match]
411Name=${IFACE}
412
413[Network]
414Address=${IPV6_ADDR}
415$(printf 'DNS=%s\n' "${DNS_V6[@]}")
416DHCP=no
417IPv6AcceptRA=no
418
419[Route]
420Destination=::/0
421Gateway=${IPV6_GW}
422GatewayOnLink=yes
423EOF
424 ok "Создан $NETDIR/20-static-ipv6.network"
425 fi
426
427 setup_sysctl_nonlocal_bind
428
429 info "Перезапуск systemd-networkd..."
430 systemctl restart systemd-networkd 2>&1 || warn "systemd-networkd restart вернул ошибку — попробуйте перезагрузку"
431 ok "Конфигурация применена"
432}
433
434# ─── Проверка результата ────────────────────────────────────────────────────
435verify() {
436 echo ""
437 info "Проверяем IP-адреса на интерфейсе $IFACE:"
438 echo ""
439 ip addr show "$IFACE" | grep -E 'inet ' | sed 's/^/ /'
440 echo ""
441 ok "Готово! Рекомендуется перезагрузить сервер для проверки: sudo reboot"
442}
443
444# ─── main ────────────────────────────────────────────────────────────────────
445main() {
446 detect_distro
447 detect_interface
448 collect_data
449
450 case "$DISTRO_FAMILY" in
451 ubuntu) configure_ubuntu ;;
452 debian) configure_debian ;;
453 centos) configure_centos ;;
454 arch) configure_arch ;;
455 esac
456
457 verify
458}
459
460main "$@"
461