- 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
241 lines
6.9 KiB
Go
241 lines
6.9 KiB
Go
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
|
||
}
|