Files
Go-VPN-Client/internal/cli/security.go
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

324 lines
13 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 cli
import (
"fmt"
"strings"
"vpn-client/internal/config"
"vpn-client/internal/security"
)
// securityMenu отображает меню безопасности
func securityMenu() {
for {
clearScreen()
fmt.Println(bold("╔════════════════════════════════════════════════════════════╗"))
fmt.Println(bold("║ 🔒 БЕЗОПАСНОСТЬ И ЗАЩИТА ║"))
fmt.Println(bold("╚════════════════════════════════════════════════════════════╝"))
fmt.Println()
secManager := security.NewSecurityManager(config.ConfigDir, "default-password")
status := secManager.GetSecurityStatus()
// Показываем текущий статус
fmt.Println(cyan("Текущий статус безопасности:"))
fmt.Println(strings.Repeat("─", 60))
if status["kill_switch_enabled"].(bool) {
fmt.Printf(" Kill Switch: %s (отключен в этой версии)\n", yellow("⚠"))
} else {
fmt.Printf(" Kill Switch: %s (отключен в этой версии)\n", yellow("⚠"))
}
if status["dns_protection_enabled"].(bool) {
fmt.Printf(" Защита DNS: %s (активна)\n", green("✓"))
} else {
fmt.Printf(" Защита DNS: %s (неактивна)\n", red("✗"))
}
if status["encryption_enabled"].(bool) {
fmt.Printf(" Шифрование конфигов: %s (включено)\n", green("✓"))
} else {
fmt.Printf(" Шифрование конфигов: %s (выключено)\n", yellow("⚠"))
}
if status["config_dir_protected"].(bool) {
fmt.Printf(" Защита директории: %s (активна)\n", green("✓"))
} else {
fmt.Printf(" Защита директории: %s (неактивна)\n", red("✗"))
}
fmt.Println()
fmt.Println(strings.Repeat("─", 60))
fmt.Println()
fmt.Println("1. Настроить Kill Switch")
fmt.Println("2. Настроить защиту DNS")
fmt.Println("3. Управление шифрованием конфигов")
fmt.Println("4. Проверить утечки")
fmt.Println("5. Защитить директорию конфигураций")
fmt.Println("6. Показать текущие DNS серверы")
fmt.Println("0. Назад")
fmt.Println()
choice := readInput("Выберите действие: ")
switch choice {
case "1":
configureKillSwitch(secManager)
case "2":
configureDNSProtection(secManager)
case "3":
configureEncryption(secManager)
case "4":
checkForLeaks(secManager)
case "5":
protectConfigDirectory(secManager)
case "6":
showCurrentDNS()
case "0":
return
default:
fmt.Printf("%s Неверный выбор\n", red("✗"))
pause()
}
}
}
func configureKillSwitch(secManager *security.SecurityManager) {
clearScreen()
fmt.Println(bold("╔════════════════════════════════════════════════════════════╗"))
fmt.Println(bold("║ НАСТРОЙКА KILL SWITCH ║"))
fmt.Println(bold("╚════════════════════════════════════════════════════════════╝"))
fmt.Println()
fmt.Println(red("⚠️ KILL SWITCH ВРЕМЕННО ОТКЛЮЧЕН В ЭТОЙ ВЕРСИИ"))
fmt.Println()
fmt.Println("Kill Switch был отключен из-за проблем с блокировкой интернета.")
fmt.Println("Функция будет доработана и добавлена в следующей версии.")
fmt.Println()
fmt.Println("Для защиты используйте:")
fmt.Println(" • Защиту DNS (предотвращает DNS утечки)")
fmt.Println(" • Системный прокси (работает автоматически)")
fmt.Println(" • Шифрование конфигураций")
fmt.Println()
pause()
}
func configureDNSProtection(secManager *security.SecurityManager) {
clearScreen()
fmt.Println(bold("╔════════════════════════════════════════════════════════════╗"))
fmt.Println(bold("║ НАСТРОЙКА ЗАЩИТЫ DNS ║"))
fmt.Println(bold("╚════════════════════════════════════════════════════════════╝"))
fmt.Println()
fmt.Println("Защита DNS предотвращает утечку DNS запросов за пределы VPN.")
fmt.Println()
secConfig, err := secManager.LoadSecurityConfig()
if err != nil {
fmt.Printf("%s Ошибка загрузки конфигурации: %v\n", red("✗"), err)
pause()
return
}
fmt.Printf("Текущий статус: ")
if secConfig.DNSProtectionEnabled {
fmt.Println(green("Включена"))
} else {
fmt.Println(red("Отключена"))
}
fmt.Printf("\nТекущие VPN DNS серверы:\n")
for i, dns := range secConfig.VPNDNSServers {
fmt.Printf(" %d. %s\n", i+1, dns)
}
fmt.Println()
fmt.Println("1. Включить/отключить защиту DNS")
fmt.Println("2. Изменить DNS серверы")
fmt.Println("0. Назад")
fmt.Println()
choice := readInput("Выберите действие: ")
switch choice {
case "1":
secConfig.DNSProtectionEnabled = !secConfig.DNSProtectionEnabled
if err := secManager.SaveSecurityConfig(secConfig); err != nil {
fmt.Printf("%s Ошибка сохранения: %v\n", red("✗"), err)
} else {
if secConfig.DNSProtectionEnabled {
fmt.Printf("%s Защита DNS включена\n", green("✓"))
} else {
fmt.Printf("%s Защита DNS отключена\n", yellow("⚠"))
}
}
pause()
case "2":
fmt.Println("\nВведите DNS серверы (через запятую):")
fmt.Println("Например: 1.1.1.1,8.8.8.8")
fmt.Print("> ")
dnsInput := readInput("")
dnsServers := strings.Split(dnsInput, ",")
var cleanDNS []string
for _, dns := range dnsServers {
dns = strings.TrimSpace(dns)
if dns != "" {
cleanDNS = append(cleanDNS, dns)
}
}
if len(cleanDNS) > 0 {
secConfig.VPNDNSServers = cleanDNS
if err := secManager.SaveSecurityConfig(secConfig); err != nil {
fmt.Printf("%s Ошибка сохранения: %v\n", red("✗"), err)
} else {
fmt.Printf("%s DNS серверы обновлены\n", green("✓"))
}
} else {
fmt.Printf("%s Не указаны DNS серверы\n", red("✗"))
}
pause()
}
}
func configureEncryption(secManager *security.SecurityManager) {
clearScreen()
fmt.Println(bold("╔════════════════════════════════════════════════════════════╗"))
fmt.Println(bold("║ ШИФРОВАНИЕ КОНФИГУРАЦИЙ ║"))
fmt.Println(bold("╚════════════════════════════════════════════════════════════╝"))
fmt.Println()
fmt.Println("Шифрование защищает ваши конфигурации VPN от несанкционированного")
fmt.Println("доступа. Файлы шифруются с использованием AES-256-GCM.")
fmt.Println()
secConfig, err := secManager.LoadSecurityConfig()
if err != nil {
fmt.Printf("%s Ошибка загрузки конфигурации: %v\n", red("✗"), err)
pause()
return
}
fmt.Printf("Текущий статус: ")
if secConfig.EncryptionEnabled {
fmt.Println(green("Включено"))
} else {
fmt.Println(red("Отключено"))
}
fmt.Println()
fmt.Println("1. Включить шифрование")
fmt.Println("2. Отключить шифрование")
fmt.Println("0. Назад")
fmt.Println()
choice := readInput("Выберите действие: ")
switch choice {
case "1":
secConfig.EncryptionEnabled = true
if err := secManager.SaveSecurityConfig(secConfig); err != nil {
fmt.Printf("%s Ошибка сохранения: %v\n", red("✗"), err)
} else {
fmt.Printf("%s Шифрование включено\n", green("✓"))
fmt.Println("\nВнимание: Конфигурации будут зашифрованы автоматически")
}
pause()
case "2":
fmt.Println(yellow("\n⚠ Предупреждение: Отключение шифрования снизит безопасность!"))
confirm := readInput("Вы уверены? (yes/no): ")
if strings.ToLower(confirm) == "yes" {
secConfig.EncryptionEnabled = false
if err := secManager.SaveSecurityConfig(secConfig); err != nil {
fmt.Printf("%s Ошибка сохранения: %v\n", red("✗"), err)
} else {
fmt.Printf("%s Шифрование отключено\n", yellow("⚠"))
}
}
pause()
}
}
func checkForLeaks(secManager *security.SecurityManager) {
clearScreen()
fmt.Println(bold("╔════════════════════════════════════════════════════════════╗"))
fmt.Println(bold("║ ПРОВЕРКА УТЕЧЕК ║"))
fmt.Println(bold("╚════════════════════════════════════════════════════════════╝"))
fmt.Println()
fmt.Println("Проверка безопасности соединения...")
fmt.Println()
leaks, err := secManager.CheckForLeaks()
if err != nil {
fmt.Printf("%s Ошибка проверки: %v\n", red("✗"), err)
pause()
return
}
if len(leaks) == 0 {
fmt.Printf("%s Утечек не обнаружено!\n", green("✓"))
fmt.Println("\nВаше соединение безопасно:")
fmt.Println(" • DNS запросы защищены")
fmt.Println(" • Kill Switch активен")
fmt.Println(" • Трафик идет через VPN")
} else {
fmt.Printf("%s Обнаружены потенциальные утечки:\n\n", red("✗"))
for i, leak := range leaks {
fmt.Printf(" %d. %s\n", i+1, leak)
}
fmt.Println("\nРекомендации:")
fmt.Println(" • Проверьте настройки безопасности")
fmt.Println(" • Убедитесь, что VPN подключен")
fmt.Println(" • Включите Kill Switch и защиту DNS")
}
pause()
}
func protectConfigDirectory(secManager *security.SecurityManager) {
clearScreen()
fmt.Println(bold("╔════════════════════════════════════════════════════════════╗"))
fmt.Println(bold("║ ЗАЩИТА ДИРЕКТОРИИ КОНФИГУРАЦИЙ ║"))
fmt.Println(bold("╚════════════════════════════════════════════════════════════╝"))
fmt.Println()
fmt.Println("Установка строгих прав доступа к файлам конфигураций...")
fmt.Println()
if err := secManager.ProtectConfigDirectory(); err != nil {
fmt.Printf("%s Ошибка: %v\n", red("✗"), err)
} else {
fmt.Printf("%s Директория конфигураций защищена\n", green("✓"))
fmt.Println("\nТеперь только владелец может читать и изменять конфигурации")
}
pause()
}
func showCurrentDNS() {
clearScreen()
fmt.Println(bold("╔════════════════════════════════════════════════════════════╗"))
fmt.Println(bold("║ ТЕКУЩИЕ DNS СЕРВЕРЫ ║"))
fmt.Println(bold("╚════════════════════════════════════════════════════════════╝"))
fmt.Println()
dnsServers, err := security.GetCurrentDNS()
if err != nil {
fmt.Printf("%s Ошибка получения DNS: %v\n", red("✗"), err)
pause()
return
}
if len(dnsServers) == 0 {
fmt.Println(yellow("DNS серверы не обнаружены"))
} else {
fmt.Println("Активные DNS серверы:")
for i, dns := range dnsServers {
fmt.Printf(" %d. %s\n", i+1, dns)
}
}
pause()
}