Keep IPv6 stack enabled in IPv4 firewall mode #13
5 changed files with 59 additions and 19 deletions
3
.github/workflows/bash-syntax.yml
vendored
3
.github/workflows/bash-syntax.yml
vendored
|
|
@ -26,6 +26,9 @@ jobs:
|
||||||
- name: Test step 1 selections
|
- name: Test step 1 selections
|
||||||
run: bash tests/test-step1-selection.sh
|
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
|
- name: Test master language selection
|
||||||
run: bash tests/test-master-language.sh
|
run: bash tests/test-master-language.sh
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ The script does not request or store model-provider API keys. After installation
|
||||||
|
|
||||||
## Firewall and SSH notes
|
## 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.
|
- 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.
|
- 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.
|
- The home directory, `.ssh`, and `authorized_keys` ownership and permissions are checked on every run.
|
||||||
|
|
|
||||||
|
|
@ -164,11 +164,11 @@ sysctl vm.swappiness
|
||||||
|
|
||||||
Для входящих подключений можно выбрать:
|
Для входящих подключений можно выбрать:
|
||||||
|
|
||||||
- только IPv4 — IPv6 отключается в UFW и системно через `sysctl`; этот режим рекомендуется, если 3x-ui используется только по IPv4;
|
- только IPv4 — публичные входящие IPv6-порты закрываются через UFW, но IPv6-стек ядра не отключается; этот режим рекомендуется, если 3x-ui используется только по IPv4;
|
||||||
- только IPv6 — управляемые скриптом порты открываются лишь для IPv6;
|
- только IPv6 — управляемые скриптом порты открываются лишь для IPv6;
|
||||||
- IPv4 + 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 без привязки к конкретной сетевой конфигурации не выполняется.
|
В режиме «только IPv6» системный IPv4 остаётся доступен для исходящих соединений и работы менеджера пакетов, но входящие управляемые порты по IPv4 закрываются UFW. Полное переносимое отключение IPv4 в Linux без привязки к конкретной сетевой конфигурации не выполняется.
|
||||||
|
|
||||||
|
|
|
||||||
25
ochenstarik-server-2.sh
Normal file → Executable file
25
ochenstarik-server-2.sh
Normal file → Executable file
|
|
@ -87,7 +87,7 @@ choose_ip_mode() {
|
||||||
esac
|
esac
|
||||||
|
|
||||||
printf '\nВыберите семейства IP для входящих подключений:\n'
|
printf '\nВыберите семейства IP для входящих подключений:\n'
|
||||||
printf ' 1) Только IPv4 — IPv6 отключается системно и в UFW\n'
|
printf ' 1) Только IPv4 — входящие IPv6-порты закрываются через UFW\n'
|
||||||
printf ' 2) Только IPv6 — открытые входящие порты доступны лишь по IPv6\n'
|
printf ' 2) Только IPv6 — открытые входящие порты доступны лишь по IPv6\n'
|
||||||
printf ' 3) IPv4 + IPv6\n'
|
printf ' 3) IPv4 + IPv6\n'
|
||||||
while :; do
|
while :; do
|
||||||
|
|
@ -206,19 +206,6 @@ set_ufw_ipv6_setting() {
|
||||||
fi
|
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() {
|
enable_ipv6_systemwide() {
|
||||||
local sysctl_path
|
local sysctl_path
|
||||||
local -a sysctl_paths=(/proc/sys/net/ipv6/conf/*/disable_ipv6)
|
local -a sysctl_paths=(/proc/sys/net/ipv6/conf/*/disable_ipv6)
|
||||||
|
|
@ -230,6 +217,11 @@ enable_ipv6_systemwide() {
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restore_ipv6_stack_if_managed() {
|
||||||
|
[[ -e "$IPV6_SYSCTL_FILE" ]] || return 0
|
||||||
|
enable_ipv6_systemwide
|
||||||
|
}
|
||||||
|
|
||||||
check_session_safety() {
|
check_session_safety() {
|
||||||
local client_ip client_port server_ip server_port
|
local client_ip client_port server_ip server_port
|
||||||
|
|
||||||
|
|
@ -284,9 +276,10 @@ apply_ip_mode_and_firewall() {
|
||||||
|
|
||||||
check_session_safety
|
check_session_safety
|
||||||
if [[ "$IP_MODE" == ipv4 ]]; then
|
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
|
delete_managed_ufw_rules
|
||||||
set_ufw_ipv6_setting no
|
|
||||||
disable_ipv6_systemwide
|
|
||||||
else
|
else
|
||||||
enable_ipv6_systemwide
|
enable_ipv6_systemwide
|
||||||
[[ "$IP_MODE" != ipv6 ]] || verify_ipv6_connectivity
|
[[ "$IP_MODE" != ipv6 ]] || verify_ipv6_connectivity
|
||||||
|
|
|
||||||
44
tests/test-step2-ipv4-firewall.sh
Executable file
44
tests/test-step2-ipv4-firewall.sh
Executable 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'
|
||||||
Loading…
Reference in a new issue