Files
arkonsadter b809e84220 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
2026-04-12 19:01:24 +06:00

241 lines
6.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package security
import (
"fmt"
"os/exec"
"runtime"
"strings"
)
// DNSProtection управляет защитой от DNS утечек
type DNSProtection struct {
originalDNS []string
enabled bool
}
// NewDNSProtection создает новый объект защиты DNS
func NewDNSProtection() *DNSProtection {
return &DNSProtection{
originalDNS: []string{},
enabled: false,
}
}
// Enable включает защиту от DNS утечек
func (dp *DNSProtection) Enable(vpnDNS []string) error {
if runtime.GOOS != "windows" {
return dp.enableUnix(vpnDNS)
}
return dp.enableWindows(vpnDNS)
}
// Disable отключает защиту и восстанавливает оригинальные DNS
func (dp *DNSProtection) Disable() error {
if runtime.GOOS != "windows" {
return dp.disableUnix()
}
return dp.disableWindows()
}
// enableWindows включает защиту DNS на Windows
func (dp *DNSProtection) enableWindows(vpnDNS []string) error {
// Получаем список активных сетевых интерфейсов
cmd := exec.Command("netsh", "interface", "show", "interface")
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("ошибка получения списка интерфейсов: %w", err)
}
// Парсим вывод для получения имен интерфейсов
lines := strings.Split(string(output), "\n")
var activeInterfaces []string
for _, line := range lines {
if strings.Contains(line, "Connected") || strings.Contains(line, "Подключено") {
fields := strings.Fields(line)
if len(fields) >= 4 {
// Имя интерфейса обычно последнее поле
interfaceName := strings.Join(fields[3:], " ")
activeInterfaces = append(activeInterfaces, interfaceName)
}
}
}
// Сохраняем текущие DNS для каждого интерфейса
for _, iface := range activeInterfaces {
cmd = exec.Command("netsh", "interface", "ip", "show", "dns", iface)
output, err := cmd.Output()
if err == nil {
dp.originalDNS = append(dp.originalDNS, string(output))
}
}
// Устанавливаем VPN DNS для всех активных интерфейсов
for _, iface := range activeInterfaces {
// Устанавливаем первичный DNS
if len(vpnDNS) > 0 {
cmd = exec.Command("netsh", "interface", "ip", "set", "dns",
iface, "static", vpnDNS[0])
if err := cmd.Run(); err != nil {
fmt.Printf("Предупреждение: не удалось установить DNS для %s: %v\n", iface, err)
}
}
// Добавляем вторичные DNS
for i := 1; i < len(vpnDNS); i++ {
cmd = exec.Command("netsh", "interface", "ip", "add", "dns",
iface, vpnDNS[i], fmt.Sprintf("index=%d", i+1))
cmd.Run() // Игнорируем ошибки для дополнительных DNS
}
}
// Очищаем DNS кэш
exec.Command("ipconfig", "/flushdns").Run()
dp.enabled = true
return nil
}
// disableWindows отключает защиту DNS на Windows
func (dp *DNSProtection) disableWindows() error {
if !dp.enabled {
return nil
}
// Получаем список активных интерфейсов
cmd := exec.Command("netsh", "interface", "show", "interface")
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("ошибка получения списка интерфейсов: %w", err)
}
lines := strings.Split(string(output), "\n")
var activeInterfaces []string
for _, line := range lines {
if strings.Contains(line, "Connected") || strings.Contains(line, "Подключено") {
fields := strings.Fields(line)
if len(fields) >= 4 {
interfaceName := strings.Join(fields[3:], " ")
activeInterfaces = append(activeInterfaces, interfaceName)
}
}
}
// Восстанавливаем автоматическое получение DNS
for _, iface := range activeInterfaces {
cmd = exec.Command("netsh", "interface", "ip", "set", "dns",
iface, "dhcp")
cmd.Run() // Игнорируем ошибки
}
// Очищаем DNS кэш
exec.Command("ipconfig", "/flushdns").Run()
dp.enabled = false
dp.originalDNS = []string{}
return nil
}
// enableUnix включает защиту DNS на Unix системах
func (dp *DNSProtection) enableUnix(vpnDNS []string) error {
// Сохраняем оригинальный resolv.conf
cmd := exec.Command("cat", "/etc/resolv.conf")
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("ошибка чтения resolv.conf: %w", err)
}
dp.originalDNS = strings.Split(string(output), "\n")
// Создаем новый resolv.conf с VPN DNS
var newResolv strings.Builder
for _, dns := range vpnDNS {
newResolv.WriteString(fmt.Sprintf("nameserver %s\n", dns))
}
// Записываем новый resolv.conf
cmd = exec.Command("sh", "-c", fmt.Sprintf("echo '%s' > /etc/resolv.conf", newResolv.String()))
if err := cmd.Run(); err != nil {
return fmt.Errorf("ошибка записи resolv.conf: %w", err)
}
dp.enabled = true
return nil
}
// disableUnix отключает защиту DNS на Unix системах
func (dp *DNSProtection) disableUnix() error {
if !dp.enabled || len(dp.originalDNS) == 0 {
return nil
}
// Восстанавливаем оригинальный resolv.conf
content := strings.Join(dp.originalDNS, "\n")
cmd := exec.Command("sh", "-c", fmt.Sprintf("echo '%s' > /etc/resolv.conf", content))
if err := cmd.Run(); err != nil {
return fmt.Errorf("ошибка восстановления resolv.conf: %w", err)
}
dp.enabled = false
dp.originalDNS = []string{}
return nil
}
// IsEnabled проверяет, включена ли защита DNS
func (dp *DNSProtection) IsEnabled() bool {
return dp.enabled
}
// GetCurrentDNS возвращает текущие DNS серверы
func GetCurrentDNS() ([]string, error) {
if runtime.GOOS == "windows" {
return getCurrentDNSWindows()
}
return getCurrentDNSUnix()
}
func getCurrentDNSWindows() ([]string, error) {
cmd := exec.Command("nslookup", "localhost")
output, err := cmd.Output()
if err != nil {
return nil, err
}
var dnsServers []string
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "Address:") {
parts := strings.Split(line, ":")
if len(parts) >= 2 {
dns := strings.TrimSpace(parts[1])
if dns != "" && dns != "127.0.0.1" {
dnsServers = append(dnsServers, dns)
}
}
}
}
return dnsServers, nil
}
func getCurrentDNSUnix() ([]string, error) {
cmd := exec.Command("cat", "/etc/resolv.conf")
output, err := cmd.Output()
if err != nil {
return nil, err
}
var dnsServers []string
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.HasPrefix(strings.TrimSpace(line), "nameserver") {
parts := strings.Fields(line)
if len(parts) >= 2 {
dnsServers = append(dnsServers, parts[1])
}
}
}
return dnsServers, nil
}