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:
2026-04-12 19:01:24 +06:00
parent 20d24a3639
commit b809e84220
18 changed files with 2063 additions and 31 deletions

View 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
}