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:
240
internal/security/dns.go
Normal file
240
internal/security/dns.go
Normal file
@@ -0,0 +1,240 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user