Keep IPv6 stack enabled in IPv4 firewall mode

This commit is contained in:
ochenstarik-ui 2026-07-15 23:11:53 +07:00
parent bc0b29a1b1
commit be2f83c498
5 changed files with 59 additions and 19 deletions

View file

@ -26,6 +26,9 @@ jobs:
- name: Test step 1 selections
run: bash tests/test-step1-selection.sh
- name: Test step 2 IPv4 firewall mode
run: bash tests/test-step2-ipv4-firewall.sh
- name: Test master language selection
run: bash tests/test-master-language.sh

View file

@ -115,7 +115,7 @@ The script does not request or store model-provider API keys. After installation
## Firewall and SSH notes
- Step 2 supports IPv4-only, IPv6-only, and dual-stack UFW rules.
- Step 2 supports IPv4-only, IPv6-only, and dual-stack UFW rules. IPv4-only mode blocks public inbound IPv6 through UFW without disabling the kernel IPv6 stack.
- A repeated step 2 run can change the IP-family mode or add ports without reinstalling everything.
- Step 3 reads the SSH port chosen in step 2 and creates an administrator with sudo access.
- The home directory, `.ssh`, and `authorized_keys` ownership and permissions are checked on every run.

View file

@ -164,11 +164,11 @@ sysctl vm.swappiness
Для входящих подключений можно выбрать:
- только IPv4 — IPv6 отключается в UFW и системно через `sysctl`; этот режим рекомендуется, если 3x-ui используется только по IPv4;
- только IPv4 — публичные входящие IPv6-порты закрываются через UFW, но IPv6-стек ядра не отключается; этот режим рекомендуется, если 3x-ui используется только по IPv4;
- только IPv6 — управляемые скриптом порты открываются лишь для IPv6;
- IPv4 + IPv6 — порты открываются для обоих семейств.
Выбор сохраняется в `/etc/ochenstarik-server/ip-family.conf`, а список управляемых портов — в `/etc/ochenstarik-server/ufw-managed-ports.conf`. При переходе на режим с одним семейством старые правила этих портов пересоздаются. Скрипт не разрешит отключить семейство адресов, через которое работает текущая SSH-сессия. Для режима IPv6 также проверяются глобальный IPv6-адрес и маршрут по умолчанию.
Выбор сохраняется в `/etc/ochenstarik-server/ip-family.conf`, а список управляемых портов — в `/etc/ochenstarik-server/ufw-managed-ports.conf`. При переходе на режим с одним семейством старые правила этих портов пересоздаются. Скрипт не разрешит отключить семейство адресов, через которое работает текущая SSH-сессия. Для режима IPv4 UFW остаётся с поддержкой IPv6, чтобы политика `deny incoming` продолжала закрывать публичный IPv6. Для режима IPv6 также проверяются глобальный IPv6-адрес и маршрут по умолчанию.
В режиме «только IPv6» системный IPv4 остаётся доступен для исходящих соединений и работы менеджера пакетов, но входящие управляемые порты по IPv4 закрываются UFW. Полное переносимое отключение IPv4 в Linux без привязки к конкретной сетевой конфигурации не выполняется.

25
ochenstarik-server-2.sh Normal file → Executable file
View file

@ -87,7 +87,7 @@ choose_ip_mode() {
esac
printf '\nВыберите семейства IP для входящих подключений:\n'
printf ' 1) Только IPv4 — IPv6 отключается системно и в UFW\n'
printf ' 1) Только IPv4 — входящие IPv6-порты закрываются через UFW\n'
printf ' 2) Только IPv6 — открытые входящие порты доступны лишь по IPv6\n'
printf ' 3) IPv4 + IPv6\n'
while :; do
@ -206,19 +206,6 @@ set_ufw_ipv6_setting() {
fi
}
disable_ipv6_systemwide() {
cat > "$IPV6_SYSCTL_FILE" <<'EOF'
# Managed by ochenstarik-server-2.sh
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOF
chmod 644 "$IPV6_SYSCTL_FILE"
sysctl -p "$IPV6_SYSCTL_FILE" >/dev/null
[[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null || true)" == 1 ]] \
|| die "Не удалось отключить IPv6"
}
enable_ipv6_systemwide() {
local sysctl_path
local -a sysctl_paths=(/proc/sys/net/ipv6/conf/*/disable_ipv6)
@ -230,6 +217,11 @@ enable_ipv6_systemwide() {
done
}
restore_ipv6_stack_if_managed() {
[[ -e "$IPV6_SYSCTL_FILE" ]] || return 0
enable_ipv6_systemwide
}
check_session_safety() {
local client_ip client_port server_ip server_port
@ -284,9 +276,10 @@ apply_ip_mode_and_firewall() {
check_session_safety
if [[ "$IP_MODE" == ipv4 ]]; then
restore_ipv6_stack_if_managed
set_ufw_ipv6_setting yes
ufw reload >/dev/null 2>&1 || true
delete_managed_ufw_rules
set_ufw_ipv6_setting no
disable_ipv6_systemwide
else
enable_ipv6_systemwide
[[ "$IP_MODE" != ipv6 ]] || verify_ipv6_connectivity

View file

@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -Eeuo pipefail
script_path="${1:-ochenstarik-server-2.sh}"
[[ -f "$script_path" ]] || {
printf '[x] Missing script: %s\n' "$script_path" >&2
exit 1
}
if grep -q 'disable_ipv6 = 1' "$script_path"; then
printf '[x] Step 2 must not disable the IPv6 kernel stack for IPv4 mode\n' >&2
exit 1
fi
if grep -q 'disable_ipv6_systemwide' "$script_path"; then
printf '[x] Step 2 still references the old system-wide IPv6 disable helper\n' >&2
exit 1
fi
if ! grep -q 'restore_ipv6_stack_if_managed' "$script_path"; then
printf '[x] Step 2 should restore IPv6 when a legacy managed sysctl file exists\n' >&2
exit 1
fi
ipv4_block="$(
awk '
/if \[\[ "\$IP_MODE" == ipv4 \]\]; then/ { capture=1 }
capture { print }
capture && /^ else$/ { exit }
' "$script_path"
)"
if [[ "$ipv4_block" != *'set_ufw_ipv6_setting yes'* ]]; then
printf '[x] IPv4 mode must keep UFW IPv6 support enabled so deny incoming applies to IPv6\n' >&2
exit 1
fi
if [[ "$ipv4_block" != *'delete_managed_ufw_rules'* ]]; then
printf '[x] IPv4 mode must delete old managed IPv6 allow rules before applying IPv4-only rules\n' >&2
exit 1
fi
printf '[+] Step 2 IPv4 firewall behavior looks correct\n'