Files
Go-VPN-Client/internal/vpn/vpn.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

167 lines
5.1 KiB
Go
Raw 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 vpn
import (
"fmt"
"os"
"runtime"
"syscall"
"time"
"vpn-client/internal/config"
"vpn-client/internal/logger"
"vpn-client/internal/proxy"
"vpn-client/internal/security"
"vpn-client/internal/wireguard"
)
// Disconnect отключает VPN
func Disconnect(logsDir string) error {
// Загружаем состояние
state, err := config.LoadState()
if err != nil {
return fmt.Errorf("ошибка загрузки состояния: %w", err)
}
if !state.Connected {
return fmt.Errorf("VPN не подключен")
}
fmt.Printf("Отключение от '%s'...\n", state.ConfigName)
// Отключаем защитные механизмы (только DNS, Kill Switch не используется)
fmt.Println("\nОтключение защитных механизмов...")
dnsProtection := security.NewDNSProtection()
if dnsProtection.IsEnabled() {
if err := dnsProtection.Disable(); err != nil {
fmt.Printf("%s Предупреждение при отключении DNS: %v\n", "⚠", err)
} else {
fmt.Println("✓ Защита DNS отключена")
}
}
// Логируем отключение
var logFile string
if state.ConfigType == "wireguard" {
logFile = logger.GetLogPath(logsDir, "wireguard")
} else if state.ConfigType == "vless" {
logFile = logger.GetLogPath(logsDir, "vless")
}
if logFile != "" {
logger.LogMessage(logFile, fmt.Sprintf("Начало отключения от '%s'", state.ConfigName))
}
// Останавливаем процесс в зависимости от типа
if state.ConfigType == "wireguard" {
// Отключаем WireGuard
if err := wireguard.Disconnect(state.Interface, logsDir); err != nil {
fmt.Printf("%s Ошибка отключения WireGuard: %v\n", "⚠", err)
}
} else if state.ProcessPID > 0 {
// Останавливаем процесс VLESS
process, err := os.FindProcess(state.ProcessPID)
if err == nil {
if runtime.GOOS == "windows" {
// На Windows используем taskkill
process.Kill()
} else {
// На Unix используем SIGTERM
process.Signal(syscall.SIGTERM)
}
// Ждем завершения процесса
time.Sleep(1 * time.Second)
if logFile != "" {
logger.LogMessage(logFile, fmt.Sprintf("Отключено от '%s' (PID: %d)", state.ConfigName, state.ProcessPID))
}
if state.LogFile != "" && logFile != "" {
logger.LogMessage(logFile, fmt.Sprintf("Лог трафика сохранен: %s", state.LogFile))
}
}
}
// Сбрасываем состояние
newState := &config.ConnectionState{
Connected: false,
ConfigName: "",
ConfigType: "",
StartTime: "",
Interface: "",
ProcessPID: 0,
LogFile: "",
}
if err := config.SaveState(newState); err != nil {
return fmt.Errorf("ошибка сохранения состояния: %w", err)
}
// Отключаем системный прокси если был VLESS
if state.ConfigType == "vless" {
if err := proxy.DisableSystemProxy(); err != nil {
fmt.Printf("%s Не удалось отключить системный прокси: %v\n", "⚠", err)
fmt.Println("Отключите его вручную в настройках Windows")
} else {
fmt.Println("✓ Системный прокси отключен")
}
}
fmt.Println("✓ Отключено от VPN")
return nil
}
// GetStatus возвращает текущий статус подключения
func GetStatus() (*config.ConnectionState, error) {
return config.LoadState()
}
// ShowStatus отображает детальный статус подключения
func ShowStatus() error {
state, err := GetStatus()
if err != nil {
return fmt.Errorf("ошибка получения статуса: %w", err)
}
if !state.Connected {
fmt.Println("\n❌ VPN не подключен")
return nil
}
fmt.Println("\n" + "==================================================")
fmt.Println("📊 Статус VPN")
fmt.Println("==================================================")
fmt.Printf("Статус: ✓ Подключено\n")
fmt.Printf("Конфиг: %s\n", state.ConfigName)
fmt.Printf("Тип: %s\n", state.ConfigType)
if state.StartTime != "" {
startTime, err := time.Parse(time.RFC3339, state.StartTime)
if err == nil {
duration := time.Since(startTime)
hours := int(duration.Hours())
minutes := int(duration.Minutes()) % 60
seconds := int(duration.Seconds()) % 60
fmt.Printf("Время подключения: %02d:%02d:%02d\n", hours, minutes, seconds)
}
}
if state.ConfigType == "vless" {
fmt.Printf("Прокси: 127.0.0.1:10808\n")
if state.LogFile != "" {
fmt.Printf("Лог трафика: %s\n", state.LogFile)
}
} else if state.ConfigType == "wireguard" {
// Получаем статистику WireGuard
stats, err := wireguard.GetStats(state.Interface)
if err == nil {
fmt.Printf("\nСтатистика трафика:\n")
fmt.Printf(" Получено: %s\n", stats["rx"])
fmt.Printf(" Отправлено: %s\n", stats["tx"])
}
}
fmt.Println("==================================================")
return nil
}