feat: add security system with system-wide proxy, DNS protection and encryption
- System-wide proxy: automatic Windows proxy configuration for all apps - DNS leak protection: force all DNS queries through VPN - Config encryption: AES-256-GCM encryption for all config files - File protection: strict access permissions for config directory - Leak detection: built-in security check system - Kill Switch: temporarily disabled (will be improved in next version) Security features: ✓ Automatic system proxy setup ✓ DNS leak protection (optional) ✓ AES-256-GCM config encryption ✓ File and directory protection ✓ Security leak checker ⚠ Kill Switch disabled (caused internet blocking issues) Emergency recovery scripts included: - ОТКЛЮЧИТЬ_KILLSWITCH.bat - EMERGENCY_FIX_INTERNET.bat - ЕСЛИ_СЛОМАЛСЯ_ИНТЕРНЕТ.txt Documentation: - Markdown/SECURITY_GUIDE.md - full security guide - БЕЗОПАСНОСТЬ_БЫСТРЫЙ_СТАРТ.md - quick start guide - CHANGELOG_SECURITY.md - detailed changelog
This commit is contained in:
190
internal/security/killswitch.go
Normal file
190
internal/security/killswitch.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// KillSwitch управляет блокировкой интернета при разрыве VPN
|
||||
type KillSwitch struct {
|
||||
enabled bool
|
||||
originalRules []string
|
||||
}
|
||||
|
||||
// NewKillSwitch создает новый Kill Switch
|
||||
func NewKillSwitch() *KillSwitch {
|
||||
return &KillSwitch{
|
||||
enabled: false,
|
||||
originalRules: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
// Enable включает Kill Switch (блокирует весь трафик кроме VPN)
|
||||
func (ks *KillSwitch) Enable(vpnInterface string, allowedIPs []string) error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return ks.enableUnix(vpnInterface, allowedIPs)
|
||||
}
|
||||
return ks.enableWindows(vpnInterface, allowedIPs)
|
||||
}
|
||||
|
||||
// Disable отключает Kill Switch (восстанавливает нормальный трафик)
|
||||
func (ks *KillSwitch) Disable() error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return ks.disableUnix()
|
||||
}
|
||||
return ks.disableWindows()
|
||||
}
|
||||
|
||||
// enableWindows включает Kill Switch на Windows через Windows Firewall
|
||||
func (ks *KillSwitch) enableWindows(vpnInterface string, allowedIPs []string) error {
|
||||
// Создаем правило блокировки всего исходящего трафика
|
||||
cmd := exec.Command("netsh", "advfirewall", "firewall", "add", "rule",
|
||||
"name=VPN_KillSwitch_Block_Out",
|
||||
"dir=out",
|
||||
"action=block",
|
||||
"enable=yes")
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("ошибка создания правила блокировки: %w", err)
|
||||
}
|
||||
|
||||
// Создаем правило блокировки всего входящего трафика
|
||||
cmd = exec.Command("netsh", "advfirewall", "firewall", "add", "rule",
|
||||
"name=VPN_KillSwitch_Block_In",
|
||||
"dir=in",
|
||||
"action=block",
|
||||
"enable=yes")
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("ошибка создания правила блокировки: %w", err)
|
||||
}
|
||||
|
||||
// Разрешаем локальный трафик (127.0.0.1)
|
||||
cmd = exec.Command("netsh", "advfirewall", "firewall", "add", "rule",
|
||||
"name=VPN_KillSwitch_Allow_Localhost",
|
||||
"dir=out",
|
||||
"action=allow",
|
||||
"remoteip=127.0.0.1",
|
||||
"enable=yes")
|
||||
|
||||
cmd.Run() // Игнорируем ошибку
|
||||
|
||||
// Разрешаем трафик к VPN серверам
|
||||
for _, ip := range allowedIPs {
|
||||
cmd = exec.Command("netsh", "advfirewall", "firewall", "add", "rule",
|
||||
fmt.Sprintf("name=VPN_KillSwitch_Allow_%s", strings.ReplaceAll(ip, ".", "_")),
|
||||
"dir=out",
|
||||
"action=allow",
|
||||
fmt.Sprintf("remoteip=%s", ip),
|
||||
"enable=yes")
|
||||
|
||||
cmd.Run() // Игнорируем ошибки для отдельных IP
|
||||
}
|
||||
|
||||
ks.enabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// disableWindows отключает Kill Switch на Windows
|
||||
func (ks *KillSwitch) disableWindows() error {
|
||||
if !ks.enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Удаляем все правила Kill Switch
|
||||
rules := []string{
|
||||
"VPN_KillSwitch_Block_Out",
|
||||
"VPN_KillSwitch_Block_In",
|
||||
"VPN_KillSwitch_Allow_Localhost",
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
cmd := exec.Command("netsh", "advfirewall", "firewall", "delete", "rule",
|
||||
fmt.Sprintf("name=%s", rule))
|
||||
cmd.Run() // Игнорируем ошибки
|
||||
}
|
||||
|
||||
// Удаляем правила для разрешенных IP (пытаемся удалить все возможные)
|
||||
// Получаем список всех правил с префиксом VPN_KillSwitch_Allow_
|
||||
listCmd := exec.Command("netsh", "advfirewall", "firewall", "show", "rule", "name=all")
|
||||
output, _ := listCmd.Output()
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "VPN_KillSwitch_Allow_") {
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) >= 2 {
|
||||
ruleName := strings.TrimSpace(parts[1])
|
||||
deleteCmd := exec.Command("netsh", "advfirewall", "firewall", "delete", "rule",
|
||||
fmt.Sprintf("name=%s", ruleName))
|
||||
deleteCmd.Run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ks.enabled = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// enableUnix включает Kill Switch на Unix системах через iptables
|
||||
func (ks *KillSwitch) enableUnix(vpnInterface string, allowedIPs []string) error {
|
||||
// Сохраняем текущие правила
|
||||
cmd := exec.Command("iptables-save")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка сохранения правил iptables: %w", err)
|
||||
}
|
||||
ks.originalRules = strings.Split(string(output), "\n")
|
||||
|
||||
// Блокируем весь исходящий трафик по умолчанию
|
||||
cmd = exec.Command("iptables", "-P", "OUTPUT", "DROP")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("ошибка установки политики OUTPUT: %w", err)
|
||||
}
|
||||
|
||||
// Разрешаем локальный трафик
|
||||
cmd = exec.Command("iptables", "-A", "OUTPUT", "-o", "lo", "-j", "ACCEPT")
|
||||
cmd.Run()
|
||||
|
||||
// Разрешаем трафик через VPN интерфейс
|
||||
if vpnInterface != "" {
|
||||
cmd = exec.Command("iptables", "-A", "OUTPUT", "-o", vpnInterface, "-j", "ACCEPT")
|
||||
cmd.Run()
|
||||
}
|
||||
|
||||
// Разрешаем трафик к VPN серверам
|
||||
for _, ip := range allowedIPs {
|
||||
cmd = exec.Command("iptables", "-A", "OUTPUT", "-d", ip, "-j", "ACCEPT")
|
||||
cmd.Run()
|
||||
}
|
||||
|
||||
ks.enabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// disableUnix отключает Kill Switch на Unix системах
|
||||
func (ks *KillSwitch) disableUnix() error {
|
||||
if !ks.enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Восстанавливаем политику по умолчанию
|
||||
cmd := exec.Command("iptables", "-P", "OUTPUT", "ACCEPT")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("ошибка восстановления политики: %w", err)
|
||||
}
|
||||
|
||||
// Очищаем цепочку OUTPUT
|
||||
cmd = exec.Command("iptables", "-F", "OUTPUT")
|
||||
cmd.Run()
|
||||
|
||||
ks.enabled = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsEnabled проверяет, включен ли Kill Switch
|
||||
func (ks *KillSwitch) IsEnabled() bool {
|
||||
return ks.enabled
|
||||
}
|
||||
Reference in New Issue
Block a user