Для тех, кто еще не успел полюбить kill switch
Приложение VPN защищает трафик, пока туннель поднят. В моменты подключения, переподключения и обрыва программы на устройстве могут выйти в интернет напрямую и показать сайтам ваш настоящий адрес. Например, Tunnelblick на Mac пропускает трафик программ мимо VPN в момент подключения, пока назначается адрес.
Надёжно это закрывается не в приложении VPN, а в самой системе, правилом «в интернет только через VPN». Такую защиту называют аварийным выключателем (kill switch).
Принцип одинаковый на всех устройствах:
- весь исходящий трафик закрыт;
- открыто только подключение к серверам VPN;
- всё, что идёт внутри туннеля, разрешено, в том числе IPv6 и DNS.
Пока VPN подключён, вы ничего не заметите. Если туннель пропадает, интернет пропадает вместе с ним, а не уходит мимо.
На компьютерах (Windows, macOS, Linux) это делается встроенным файрволом. На телефонах своего файрвола нет: на Android есть системная настройка с тем же действием, на iPhone возможности ограничены.
Утечку по IPv6 при подключённом VPN наша внутренняя инфраструктура закрывает сама, для этого ничего настраивать не нужно. Статья про другое: про моменты, когда туннель не поднят.
Перед началом
Для компьютеров нужны домены серверов VPN: только к этим серверам файрвол будет пускать напрямую. Для телефонов этот раздел можно пропустить.
1. Откройте файл конфигурации `.ovpn`, который выдал бот, в текстовом редакторе: Блокнот на Windows, TextEdit на Mac.
2. Найдите строки, которые начинаются со слова `remote`. В каждой строке после этого слова стоит домен сервера, за ним порт. Например, в строке `remote vpn1.example.com 1194` домен `vpn1.example.com`, порт `1194`.
3. Выпишите все домены. В конфиге их несколько: если один сервер недоступен, приложение подключается к другому. Если у вас несколько конфигов (например, «Россия» и «Мир»), выпишите домены из всех.
Домены `vpn1.example.com` и `vpn2.example.com` в командах ниже только пример. Замените их своими в строке, которая отмечена в каждом разделе. Если в строках `remote` у вас адреса из цифр, а не домены, впишите их так же.
Как защита работает с доменами.
Без VPN защита закрывает и DNS, а значит, приложение VPN не смогло бы узнать адрес сервера по домену. Поэтому команда при включении сама узнаёт адреса серверов, записывает их в системный файл hosts (там компьютер ищет адрес раньше, чем спрашивает DNS) и разрешает подключение только к ним. При повторном запуске на Windows и Linux прежние правила действуют, пока команда узнаёт адреса: на эти секунды открыт только DNS. На Mac защита на это время снимается.
Если VPN перестал подключаться.
Скорее всего, у серверов сменились адреса. Выполните команду из своего раздела ещё раз: она удалит старые записи и узнает новые. То же, если пришёл конфиг с другими доменами: впишите новые домены и выполните команду. Если VPN ещё подключается, запускайте команду с подключённым VPN: тогда и запросы DNS уйдут через туннель (на Windows соединение VPN при этом может на пару секунд прерваться). Если команда не сможет узнать адреса (опечатка в домене, нет интернета), на Windows и Linux новые правила не создаются, а прежняя защита остаётся как была.
Что изменится.
Без подключённого VPN интернета на устройстве не будет вообще. Это и есть защита.
Когда защиту нужно временно выключить.
В сетях Wi-Fi со входом через страницу в браузере (отель, аэропорт, кафе) страница входа не откроется. Выключите защиту, войдите в сеть, снова включите её и подключите VPN.
В каждом разделе ниже есть команда, которая выключает защиту и возвращает систему как было. Сохраните её до того, как включать. Все команды выполняются на вашем устройстве, серверы VPN они не затрагивают.
Как копировать команды.
Блок команды сначала вставьте в текстовый редактор и сверьте первую и последнюю строку: они указаны в каждом разделе. Если при копировании захватилось что-то лишнее (номер страницы, заголовок, строка с тремя обратными кавычками), удалите это: такие строки не часть команды.
Windows
Подходит для OpenVPN Connect и OpenVPN GUI. Работает на встроенном файрволе Windows: весь исходящий трафик мимо туннеля VPN закрыт, разрешены только подключение к серверам VPN и получение адреса от роутера. Правила работают по адресам, а не по сетевым адаптерам, поэтому сразу действуют для любого подключения: Wi-Fi, кабеля, USB-модема, нового адаптера, в том числе в первые секунды после переподключения к сети. Правила сохраняются после перезагрузки.
Как включить:
1. Нажмите «Пуск», введите `PowerShell`, нажмите правой кнопкой на «Windows PowerShell» и выберите «Запуск от имени администратора».
2. Скопируйте блок ниже в Блокнот и в строке `$Servers` замените домены своими. Первая строка блока начинается с `# Coeur VPN`, последняя с `Remove-Variable`.
3. Если нужен доступ к устройствам домашней сети (принтер, сетевое хранилище, телевизор), в строке `$AllowLan` поставьте `$true` вместо `$false`.
4. В Блокноте нажмите Ctrl+A и Ctrl+C, вставьте блок в окно PowerShell и нажмите Enter.
# Coeur VPN: интернет только через VPN (Windows)
$Servers = @("vpn1.example.com", "vpn2.example.com") # <- домены из строк remote
$AllowLan = $false # $true: оставить доступ к принтеру, NAS и устройствам дома
# Дальше ничего менять не нужно
$Group = "Coeur VPN kill switch"
$Mark = "# coeur-vpn-killswitch"
$Hosts = "$env:SystemRoot\System32\drivers\etc\hosts"
$Enc = [System.Text.Encoding]::GetEncoding(0)
# Свои адреса, для которых запрет не действует: туннель Coeur VPN и сам компьютер
$Local4 = "0.0.0.0-10.7.255.255", "10.9.0.0-126.255.255.255", "128.0.0.0-255.255.255.255"
$Local6 = "::2-fd42:6a7:ffff:ffff:ffff:ffff:ffff:ffff",
"fd42:6a9::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
function ConvertTo-Num([string]$ip) {
$b = [System.Net.IPAddress]::Parse($ip).GetAddressBytes()
return [long]$b[0] * 16777216 + [long]$b[1] * 65536 + [long]$b[2] * 256 + [long]$b[3]
}
function ConvertTo-Ip([long]$n) {
$p = [math]::Floor($n / 16777216), ([math]::Floor($n / 65536) % 256),
([math]::Floor($n / 256) % 256), ($n % 256)
return $p -join "."
}
# hosts читается и пишется целиком; если файл занят, 10 попыток раз в полсекунды
function Get-HostsLines {
for ($i = 0; $i -lt 10; $i++) {
try { return ,[System.IO.File]::ReadAllLines($Hosts, $Enc) }
catch { Start-Sleep -Milliseconds 500 }
}
return $null
}
function Set-HostsLines([string[]]$Lines) {
for ($i = 0; $i -lt 10; $i++) {
try { [System.IO.File]::WriteAllLines($Hosts, $Lines, $Enc); return $true }
catch { Start-Sleep -Milliseconds 500 }
}
return $false
}
# Пока узнаются адреса серверов, всё закрыто, кроме DNS; старые правила остаются до конца
$Old = @(Get-NetFirewallRule -Group $Group -ErrorAction SilentlyContinue)
$On = @($Old | Where-Object { $_.Enabled -eq "True" })
$Tmp = @()
if ($On.Count -gt 0) {
$t = @{ Group = $Group; Direction = "Outbound"; Action = "Block" }; $L0 = $Local4 + $Local6
$Tmp += New-NetFirewallRule @t -DisplayName "$Group (lookup TCP)" -Protocol TCP -LocalAddress $L0 -RemotePort "1-52", "54-65535"
$Tmp += New-NetFirewallRule @t -DisplayName "$Group (lookup UDP)" -Protocol UDP -LocalAddress $L0 -RemotePort "1-52", "54-66", "68-65535"
$Tmp += New-NetFirewallRule @t -DisplayName "$Group (lookup ICMPv4)" -Protocol ICMPv4 -LocalAddress $Local4
$Tmp += New-NetFirewallRule @t -DisplayName "$Group (lookup ICMPv6)" -Protocol ICMPv6 -LocalAddress $Local6
$On | Disable-NetFirewallRule
}
$Pins = @(); $Ips = @(); $Err = $null
$Lines = Get-HostsLines
if ($null -eq $Lines) { $Err = "Не удалось прочитать файл hosts (мешает антивирус?)." }
foreach ($d in $Servers) {
if ($Err) { break }
if ($d -match '^\d{1,3}(\.\d{1,3}){3}$') { $Ips += $d; continue }
$r = @(Resolve-DnsName -Name $d -Type A -DnsOnly -NoHostsFile -ErrorAction SilentlyContinue |
Where-Object { $_.Type -eq "A" } | ForEach-Object { $_.IPAddress })
if ($r.Count -eq 0) { $Err = "Не удалось узнать адрес $d. Проверьте интернет и домен."; break }
foreach ($ip in $r) { $Ips += $ip; $Pins += "{0} {1} {2}" -f $ip, $d, $Mark }
}
if (-not $Err -and $Ips.Count -eq 0) { $Err = "Не указан ни один сервер VPN (строка Servers)." }
if (-not $Err) {
$New = @($Lines | Where-Object { $_ -notmatch "coeur-vpn-killswitch" }) + $Pins
if (-not (Set-HostsLines $New)) { $Err = "Не удалось записать файл hosts (мешает антивирус?)." }
}
Clear-DnsClientCache
if ($Err) {
if ($On.Count -gt 0) {
$On | Enable-NetFirewallRule
$Tmp | Remove-NetFirewallRule
Write-Host "$Err Новые правила не созданы, прежняя защита осталась."
} else { Write-Host "$Err Защита не включена." }
} else {
$Keep = @()
foreach ($ip in $Ips) { $n = ConvertTo-Num $ip; $Keep += [pscustomobject]@{ S = $n; E = $n } }
if ($AllowLan) {
$Lan = "10.0.0.0/10.255.255.255", "172.16.0.0/172.31.255.255",
"192.168.0.0/192.168.255.255", "169.254.0.0/169.254.255.255",
"224.0.0.0/239.255.255.255"
foreach ($r in $Lan) {
$q = $r.Split("/")
$Keep += [pscustomobject]@{ S = (ConvertTo-Num $q[0]); E = (ConvertTo-Num $q[1]) }
}
}
$Block4 = @(); $Next = [long]0
foreach ($k in ($Keep | Sort-Object S)) {
if ($k.S -gt $Next) {
$Block4 += "{0}-{1}" -f (ConvertTo-Ip $Next), (ConvertTo-Ip ($k.S - 1))
}
if ($k.E + 1 -gt $Next) { $Next = $k.E + 1 }
}
if ($Next -le 4294967295) { $Block4 += "{0}-255.255.255.255" -f (ConvertTo-Ip $Next) }
$Block6 = @("::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
$L = $Local4 + $Local6
$R = $Block4 + $Block6
$c = @{ Group = $Group; Direction = "Outbound"; Action = "Block" }
$r1 = $c + @{ DisplayName = "$Group (TCP)"; Protocol = "TCP"
LocalAddress = $L; RemoteAddress = $R }
$r2 = $c + @{ DisplayName = "$Group (UDP)"; Protocol = "UDP"
LocalAddress = $L; RemoteAddress = $R; RemotePort = "1-66", "68-65535" }
$r3 = $c + @{ DisplayName = "$Group (ICMPv4)"; Protocol = "ICMPv4"
LocalAddress = $Local4; RemoteAddress = $Block4 }
$r4 = $c + @{ DisplayName = "$Group (ICMPv6)"; Protocol = "ICMPv6"
LocalAddress = $Local6; RemoteAddress = $Block6 }
$Rules = @($r1, $r2, $r3, $r4)
if ($AllowLan) {
# DNS в домашней сети закрыт и в этом режиме: запросы имён идут только через VPN
$LanR = @($Lan | ForEach-Object { $_.Replace("/", "-") })
$Rules += ,($c + @{ DisplayName = "$Group (LAN DNS UDP)"; Protocol = "UDP"
LocalAddress = $Local4; RemoteAddress = $LanR; RemotePort = "53" })
$Rules += ,($c + @{ DisplayName = "$Group (LAN DNS TCP)"; Protocol = "TCP"
LocalAddress = $Local4; RemoteAddress = $LanR; RemotePort = "53" })
}
foreach ($rule in $Rules) { New-NetFirewallRule @rule | Out-Null }
if ($Old.Count -gt 0) { $Old | Remove-NetFirewallRule }
if ($Tmp.Count -gt 0) { $Tmp | Remove-NetFirewallRule }
Write-Host ("Защита включена. Серверы VPN: {0}" -f (($Ips | Select-Object -Unique) -join ", "))
}
Remove-Variable Servers, AllowLan -ErrorAction SilentlyContinue
В конце появится строка «Защита включена» с адресами серверов, которые команда узнала по доменам. Если вместо неё появилась строка с ошибкой, в ней написана причина: чаще всего это опечатка в домене или нет интернета. Новые правила в этом случае не создаются, а если защита уже была включена, она остаётся прежней.
Что закрывают правила.
Весь исходящий трафик мимо туннеля, кроме серверов VPN и получения адреса от роутера (DHCP), в том числе IPv6 и запросы DNS. Это правила запрета, а в Windows запрет сильнее любых разрешений, так что их не обойдут ни программы, ни службы Windows. Закрыта и домашняя сеть: роутер, принтеры, виртуальные машины VirtualBox и Hyper-V. С `$AllowLan = $true` домашняя сеть доступна, но DNS роутера остаётся закрытым, чтобы запросы имён шли только через VPN.
Важно:
- Можно не выключать IPv6 в программе OpenVPN. Защита IPv6 в нашем VPN рассчитана в том числе и на включённый IPv6 в туннеле.
- Правила действуют, только когда включён встроенный файрвол Windows. Проверка: `Get-NetFirewallProfile | Format-Table Name, Enabled`, везде должно быть `True`. Если его заменил файрвол антивируса, такое же правило нужно сделать в нём.
- Туннель Coeur VPN правила узнают по его адресам (`10.8.x.x`). Если ваша домашняя сеть тоже использует адреса `10.8.x.x`, напишите в поддержку.
- Если VPN перестал подключаться или пришёл конфиг с другими доменами, выполните блок ещё раз. Старые правила и записи в hosts он заменит сам.
- В OpenVPN Connect дополнительно включите «Меню» → «Settings» → «Seamless Tunnel»: приложение держит туннель во время переподключений.
Временно выключить
(PowerShell от имени администратора):
Disable-NetFirewallRule -Group "Coeur VPN kill switch"
Включить снова:
Enable-NetFirewallRule -Group "Coeur VPN kill switch"
Удалить защиту полностью:
Get-NetFirewallRule -Group "Coeur VPN kill switch" -ErrorAction SilentlyContinue | Remove-NetFirewallRule
$h = "$env:SystemRoot\System32\drivers\etc\hosts"; $e = [System.Text.Encoding]::GetEncoding(0); $ok = $false
for ($i = 0; $i -lt 10 -and -not $ok; $i++) {
try {
$k = [string[]]@([System.IO.File]::ReadAllLines($h, $e) | Where-Object { $_ -notmatch "coeur-vpn-killswitch" })
[System.IO.File]::WriteAllLines($h, $k, $e); $ok = $true
} catch { Start-Sleep -Milliseconds 500 }
}
Clear-DnsClientCache
if ($ok) { "Защита удалена." } else { "Правила удалены, но файл hosts занят. Повторите команду через минуту." }
macOS
Подходит для Tunnelblick и OpenVPN Connect. Работает на встроенном файрволе macOS (pf): наружу разрешены только туннель VPN, подключение к серверам VPN и получение адреса от роутера. Именно это закрывает окно утечки в Tunnelblick во время подключения: собственный «выключатель» Tunnelblick срабатывает только после обрыва и не действует, если программа не запущена.
Как включить:
1. Откройте «Терминал» («Программы» → «Утилиты»).
2. Скопируйте блок ниже в TextEdit и в строке `SERVERS` замените домены своими (через пробел, внутри кавычек). Если нужен доступ к устройствам домашней сети (принтер, AirPlay, сетевое хранилище), в строке `ALLOW_LAN` поставьте `yes` вместо `no`. Первая строка блока должна быть `sudo /bin/sh <<'EOF'`, последняя `EOF`.
3. Вставьте блок в Терминал и нажмите Enter. Когда попросит, введите пароль от Mac (при вводе символы не отображаются).
sudo /bin/sh <<'EOF'
# Coeur VPN: интернет только через VPN (macOS)
SERVERS="vpn1.example.com vpn2.example.com" # <- домены из строк remote, через пробел
ALLOW_LAN=no # yes: оставить доступ к принтеру, AirPlay, NAS и устройствам дома
PATH=/usr/bin:/bin:/usr/sbin:/sbin
MARK="# coeur-vpn-killswitch"
TMP=$(mktemp /tmp/coeur.XXXXXX)
launchctl bootout system/net.coeurvpn.killswitch 2>/dev/null
pfctl -a com.apple/coeur -F rules 2>/dev/null
sed -i '' '/coeur-vpn-killswitch/d' /etc/hosts
dscacheutil -flushcache; killall -HUP mDNSResponder
IPS=""; FAIL=""
for d in $SERVERS; do
case "$d" in
*[!0-9.]*) A=$(dig +short +time=3 +tries=2 A "$d" | grep -E '^[0-9]+(\.[0-9]+){3}$')
for ip in $A; do echo "$ip $d $MARK" >> "$TMP"; done ;;
*) A="$d" ;;
esac
if [ -z "$A" ]; then FAIL="$d"; break; fi
IPS="$IPS $A"
done
if [ -n "$FAIL" ]; then
echo "Не удалось узнать адрес $FAIL. Проверьте интернет и домен. Защита не включена."
else
IPS=$(printf '%s\n' $IPS | sort -u | tr '\n' ' ')
[ -n "$(tail -c1 /etc/hosts)" ] && echo >> /etc/hosts
cat "$TMP" >> /etc/hosts
dscacheutil -flushcache; killall -HUP mDNSResponder
UTUN=""; i=0
while [ $i -le 15 ]; do UTUN="$UTUN utun$i"; i=$((i + 1)); done
LANS="10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 169.254.0.0/16 224.0.0.0/4"
LAN=""
[ "$ALLOW_LAN" = yes ] && LAN="pass out quick inet from any to { $LANS } no state"
cat > /etc/pf.anchors/coeur <<RULES
# Coeur VPN: интернет только через VPN
pass quick on lo0 all no state
pass out quick on {$UTUN } all no state
pass out quick inet proto udp from any port 68 to any port 67 no state
pass out quick inet from any to { $IPS} no state
$LAN
block drop out quick all
RULES
cat > /Library/LaunchDaemons/net.coeurvpn.killswitch.plist <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "
Чтобы видеть ссылки, необходимо зарегистрироваться.
">
<plist version="1.0">
<dict>
<key>Label</key>
<string>net.coeurvpn.killswitch</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>/sbin/pfctl -s info 2>/dev/null | grep -q 'Status: Enabled' || /sbin/pfctl -E
/sbin/pfctl -a com.apple/coeur -f /etc/pf.anchors/coeur</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>60</integer>
</dict>
</plist>
PLIST
pfctl -s info 2>/dev/null | grep -q 'Status: Enabled' || pfctl -E 2>/dev/null
pfctl -a com.apple/coeur -f /etc/pf.anchors/coeur 2>&1 | grep -v ALTQ
launchctl bootstrap system /Library/LaunchDaemons/net.coeurvpn.killswitch.plist
if pfctl -a com.apple/coeur -sr 2>/dev/null | grep -q 'block drop out'; then
echo "Защита включена. Серверы VPN: $IPS"
else
echo "Правила не загрузились, защита не включена. Проверьте SERVERS и повторите."
fi
fi
rm -f "$TMP"
EOF
В конце появится строка «Защита включена» со списком адресов серверов, которые команда узнала по доменам. Если вместо неё появилась строка «Защита не включена», в ней написана причина.
Блок сразу создаёт системную службу: она включает защиту после перезагрузки и дальше раз в минуту проверяет, что файрвол включён и правила на месте. Проверить правила вручную: `sudo pfctl -a com.apple/coeur -sr`. Должны показаться строки `pass`, а последней `block drop out quick all`.
Важно:
- Если VPN перестал подключаться или пришёл конфиг с другими доменами, выполните блок ещё раз. Старые правила и записи в hosts он удалит сам.
- В Tunnelblick можно дополнительно включить «VPN Details» → «Settings» → «On unexpected disconnect» → «Disable network access», а в OpenVPN Connect «Меню» → «Settings» → «Seamless Tunnel». Это не замена файрволу, а дополнение.
Временно выключить:
sudo launchctl bootout system /Library/LaunchDaemons/net.coeurvpn.killswitch.plist
sudo pfctl -a com.apple/coeur -F rules
Включить снова:
sudo launchctl bootstrap system /Library/LaunchDaemons/net.coeurvpn.killswitch.plist
Удалить защиту полностью:
сначала две команды «Временно выключить», затем:
sudo rm /Library/LaunchDaemons/net.coeurvpn.killswitch.plist /etc/pf.anchors/coeur
sudo sed -i '' '/coeur-vpn-killswitch/d' /etc/hosts
Linux
Подходит для NetworkManager и для OpenVPN из командной строки. Работает на nftables: наружу разрешены только туннель VPN (интерфейсы `tun`), подключение к серверам VPN и получение адреса от роутера. Правила живут в отдельной таблице и не мешают ufw и firewalld.
Как подключаться к VPN.
Проще всего через NetworkManager: он сам направляет запросы имён (DNS) в туннель. Импортируйте файл `.ovpn` в настройках сети (раздел VPN, импорт из файла) или одной командой, указав путь к своему файлу:
sudo nmcli connection import type openvpn file ~/coeur.ovpn
Если импорта VPN в настройках нет, установите модуль: `sudo apt install network-manager-openvpn-gnome` (Ubuntu, Debian) или `sudo dnf install NetworkManager-openvpn-gnome` (Fedora).
OpenVPN, запущенный из командной строки, DNS туннеля в системе сам не прописывает. С защитой сайты по именам тогда не откроются, а без защиты запросы имён уходят провайдеру. На Ubuntu и Debian установите пакет `sudo apt install openvpn-systemd-resolved` и добавьте в конец файла `.ovpn` эти строки:
script-security 2
up /etc/openvpn/update-systemd-resolved
down /etc/openvpn/update-systemd-resolved
down-pre
dhcp-option DOMAIN-ROUTE .
На других системах подключайтесь через NetworkManager.
Как включить защиту:
1. Установите nftables, если его нет: `sudo apt install nftables` (Ubuntu, Debian) или `sudo dnf install nftables` (Fedora).
2. Скопируйте блок ниже в текстовый редактор и замените домены в строке `SERVERS` своими (через пробел, внутри кавычек). Если нужен доступ к устройствам домашней сети (принтер, сетевое хранилище), в строке `ALLOW_LAN` поставьте `yes` вместо `no`: домашняя сеть станет доступна, а DNS роутера останется закрытым. Первая строка блока должна быть `sudo sh <<'EOF'`, последняя `EOF`.
3. Вставьте блок в терминал, нажмите Enter и, когда попросит, введите свой пароль.
sudo sh <<'EOF'
# Coeur VPN: интернет только через VPN (Linux)
SERVERS="vpn1.example.com vpn2.example.com" # <- домены из строк remote, через пробел
ALLOW_LAN=no # yes: оставить доступ к принтеру, NAS и устройствам дома
# Дальше ничего менять не нужно
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MARK="# coeur-vpn-killswitch"
NFT=$(command -v nft)
TMP=$(mktemp /tmp/coeur.XXXXXX); OLD=$(mktemp /tmp/coeur.XXXXXX)
# Прежние правила остаются, пока узнаются адреса серверов: открывается только DNS
WAS=""
[ -n "$NFT" ] && "$NFT" list table inet coeur_killswitch >/dev/null 2>&1 && WAS=yes
if [ "$WAS" = yes ]; then
"$NFT" insert rule inet coeur_killswitch output udp dport 53 accept comment "coeur-dns-lookup"
"$NFT" insert rule inet coeur_killswitch output tcp dport 53 accept comment "coeur-dns-lookup"
fi
grep "coeur-vpn-killswitch" /etc/hosts > "$OLD"
sed -i '/coeur-vpn-killswitch/d' /etc/hosts
IPS=""; ERR=""
for d in $SERVERS; do
case "$d" in
*[!0-9.]*) A=$(getent ahostsv4 "$d" | awk '{print $1}' | sort -u)
for ip in $A; do echo "$ip $d $MARK" >> "$TMP"; done ;;
*) A="$d" ;;
esac
if [ -z "$A" ]; then ERR="Не удалось узнать адрес $d. Проверьте интернет и домен."; break; fi
IPS="$IPS $A"
done
[ -z "$ERR" ] && [ -z "$IPS" ] && ERR="Не указан ни один сервер VPN (строка SERVERS)."
if [ -z "$NFT" ]; then
echo "nftables не установлен (шаг 1). Защита не включена."
elif [ -n "$ERR" ]; then
if [ "$WAS" = yes ]; then
[ -n "$(tail -c1 /etc/hosts)" ] && echo >> /etc/hosts
cat "$OLD" >> /etc/hosts
for h in $("$NFT" -a list chain inet coeur_killswitch output | sed -n 's/.*coeur-dns-lookup.* handle \([0-9]*\)$/\1/p'); do
"$NFT" delete rule inet coeur_killswitch output handle "$h"
done
echo "$ERR Новые правила не созданы, прежняя защита осталась."
else
echo "$ERR Защита не включена."
fi
else
IPS=$(printf '%s\n' $IPS | sort -u | paste -sd, -)
[ -n "$(tail -c1 /etc/hosts)" ] && echo >> /etc/hosts
cat "$TMP" >> /etc/hosts
LANS="10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 224.0.0.0/4"
LAN=""
# В режиме домашней сети DNS к её адресам всё равно закрыт: имена только через VPN
[ "$ALLOW_LAN" = yes ] && LAN="ip daddr { $LANS } udp dport 53 drop
ip daddr { $LANS } tcp dport 53 drop
ip daddr { $LANS } accept"
cat > /etc/coeur-killswitch.nft <<RULES
# Coeur VPN: интернет только через VPN
table inet coeur_killswitch
delete table inet coeur_killswitch
table inet coeur_killswitch {
set vpn_servers {
type ipv4_addr
elements = { $IPS }
}
chain output {
type filter hook output priority 0; policy drop;
oifname "lo" accept
oifname "tun*" accept
ip daddr @vpn_servers accept
udp sport 68 udp dport 67 accept
$LAN
}
}
RULES
cat > /etc/systemd/system/coeur-killswitch.service <<UNIT
[Unit]
Description=Coeur VPN: internet only through VPN
Before=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=$NFT -f /etc/coeur-killswitch.nft
ExecStop=$NFT delete table inet coeur_killswitch
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable coeur-killswitch 2>/dev/null
"$NFT" -f /etc/coeur-killswitch.nft
systemctl start coeur-killswitch
if "$NFT" list table inet coeur_killswitch >/dev/null 2>&1; then
echo "Защита включена. Серверы VPN: $(echo "$IPS" | sed 's/,/, /g')"
else
echo "Правила не загрузились, защита не включена. Проверьте SERVERS и повторите."
fi
fi
rm -f "$TMP" "$OLD"
EOF
В конце появится строка «Защита включена» со списком адресов серверов. Если вместо неё появилась строка с ошибкой, новые правила не созданы, а если защита уже была включена, она остаётся прежней. Блок создаёт системную службу: она включает правила при каждой загрузке, до того как поднимается сеть. Проверить: `sudo nft list table inet coeur_killswitch`.
Важно:
- Если VPN перестал подключаться или пришёл конфиг с другими доменами, выполните блок ещё раз. Старые правила и записи в hosts он заменит сам.
- Правила пускают любой интерфейс `tun`. Если на компьютере есть другой VPN на `tun`, он тоже будет разрешён.
Временно выключить:
sudo systemctl stop coeur-killswitch
Включить снова:
sudo systemctl start coeur-killswitch
Удалить защиту полностью:
sudo systemctl disable --now coeur-killswitch
sudo rm /etc/systemd/system/coeur-killswitch.service /etc/coeur-killswitch.nft
sudo systemctl daemon-reload
sudo sed -i '/coeur-vpn-killswitch/d' /etc/hosts
Android
Своего файрвола в Android нет, но есть системная настройка с тем же действием: система не пускает приложения в интернет, пока VPN не подключён. Нужен Android 8 или новее.
Как включить:
1. Импортируйте конфиг в OpenVPN Connect и один раз подключитесь.
2. Откройте «Настройки» → «Сеть и интернет» → «VPN». На Samsung: «Настройки» → «Подключения» → «Другие настройки» → «VPN». На других телефонах проще ввести «VPN» в поиске по настройкам.
3. Нажмите шестерёнку рядом с OpenVPN Connect.
4. Включите «Постоянная VPN» (Always-on VPN) и «Блокировать подключения без VPN» (Block connections without VPN). Точные названия у разных производителей немного отличаются.
5. В OpenVPN Connect откройте «Меню» → «Settings» и включите «Seamless Tunnel»: трафик не уйдёт мимо VPN во время переподключений и смены сети.
Теперь телефон подключает VPN сам, в том числе после перезагрузки, а без VPN интернета у приложений нет.
Чего Android не закрывает даже с этой настройкой.
При каждом подключении к сети Wi-Fi система сама проверяет, есть ли интернет, и делает это мимо VPN: несколько служебных запросов к серверам Google. Это не трафик ваших приложений, но в нём виден ваш настоящий адрес. Обычными настройками это не отключается, только через инструменты разработчика с компьютера.
Временно выключить (например, чтобы войти в Wi-Fi отеля): там же, в настройках VPN, выключите «Блокировать подключения без VPN», а потом включите снова.
iPhone и iPad
Файрвола для пользователя в iOS нет. Вместо него в OpenVPN Connect (версия 3.3 и новее) есть аварийный выключатель для профиля.
Как включить:
1. В OpenVPN Connect нажмите значок редактирования (карандаш) у своего профиля.
2. На экране «Edit Profile» включите «Kill Switch» и сохраните.
3. В «Меню» → «Settings» включите «Seamless Tunnel».
С таким профилем телефон не выпускает трафик, пока не установлено защищённое соединение, в том числе после перезагрузки. OpenVPN Connect подключается к этому профилю сам.
Чего iOS не закрывает.
По своим правилам система пропускает мимо любого VPN служебный трафик: подключение к сети, вход на странице авторизации Wi-Fi, сотовые службы вроде VoLTE и связь с Apple Watch ([документация Apple](developer.apple.com/documentation/networkextension/routing-your-vpn-network-traffic)). Это ограничение самой iOS, приложение VPN его не обойдёт. Трафик ваших приложений в этот список не входит.
Временно выключить: в том же профиле снимите «Kill Switch», а потом включите снова.
Как проверить, что защита работает
Хватит трёх проверок, на каждую уходит минута.
1. *VPN выключен.* Отключитесь в приложении VPN и откройте любой сайт. Он не должен открыться. Если открылся, защита не включена.
2. *Момент подключения и обрыв* (на компьютере). Запустите непрерывный ping: на Windows `ping -t 1.1.1.1` в командной строке, на Mac и Linux `ping 1.1.1.1` в терминале.
- Пока VPN не подключён, ответов нет.
- Подключите VPN: ответы появятся только после того, как туннель встанет.
- Отключите VPN или выключите и снова включите Wi-Fi: ответы прекратятся и не вернутся, пока VPN не подключится снова.
Остановить ping: Ctrl+C.
3. *VPN подключён.* Откройте (
Чтобы видеть ссылки, необходимо зарегистрироваться.
) и (
Чтобы видеть ссылки, необходимо зарегистрироваться.
). Везде должен быть виден только адрес VPN. Ваш настоящий адрес и адреса DNS вашего провайдера не должны появиться ни среди IPv4, ни среди IPv6.
Если интернета нет и с VPN.
Чаще всего у серверов сменились адреса или в домене опечатка. Сверьте домены в строке из своего раздела со строками `remote` в конфиге и выполните блок ещё раз: он заново узнает адреса. Если не помогло, напишите в поддержку.