diff --git a/go.mod b/go.mod index 766b48c..ce83620 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module vpn-client -go 1.21 +go 1.25.0 require github.com/fatih/color v1.16.0 require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.42.0 // indirect ) diff --git a/go.sum b/go.sum index c9fe800..2a30e11 100644 --- a/go.sum +++ b/go.sum @@ -9,3 +9,5 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= diff --git a/internal/admin/admin_unix.go b/internal/admin/admin_unix.go new file mode 100644 index 0000000..d425a74 --- /dev/null +++ b/internal/admin/admin_unix.go @@ -0,0 +1,29 @@ +// +build !windows + +package admin + +import ( + "fmt" + "os" +) + +// IsAdmin проверяет, запущено ли приложение с правами root +func IsAdmin() bool { + return os.Geteuid() == 0 +} + +// RequireAdmin проверяет права root и завершает программу, если их нет +func RequireAdmin() { + if !IsAdmin() { + fmt.Println("╔════════════════════════════════════════════════════════════╗") + fmt.Println("║ ⚠ ТРЕБУЮТСЯ ПРАВА ROOT ║") + fmt.Println("╚════════════════════════════════════════════════════════════╝") + fmt.Println() + fmt.Println("Это приложение требует прав root для работы с VPN.") + fmt.Println() + fmt.Println("Пожалуйста, запустите приложение с sudo:") + fmt.Println(" sudo ./vpn-client-cli") + fmt.Println() + os.Exit(1) + } +} diff --git a/internal/admin/admin_windows.go b/internal/admin/admin_windows.go new file mode 100644 index 0000000..3eb413a --- /dev/null +++ b/internal/admin/admin_windows.go @@ -0,0 +1,57 @@ +// +build windows + +package admin + +import ( + "fmt" + "os" + "golang.org/x/sys/windows" +) + +// IsAdmin проверяет, запущено ли приложение с правами администратора +func IsAdmin() bool { + var sid *windows.SID + + // Получаем SID группы администраторов + err := windows.AllocateAndInitializeSid( + &windows.SECURITY_NT_AUTHORITY, + 2, + windows.SECURITY_BUILTIN_DOMAIN_RID, + windows.DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &sid) + if err != nil { + return false + } + defer windows.FreeSid(sid) + + // Получаем текущий токен процесса + token := windows.Token(0) + + // Проверяем членство в группе + member, err := token.IsMember(sid) + if err != nil { + return false + } + + return member +} + +// RequireAdmin проверяет права администратора и завершает программу, если их нет +func RequireAdmin() { + if !IsAdmin() { + fmt.Println("╔════════════════════════════════════════════════════════════╗") + fmt.Println("║ ⚠ ТРЕБУЮТСЯ ПРАВА АДМИНИСТРАТОРА ║") + fmt.Println("╚════════════════════════════════════════════════════════════╝") + fmt.Println() + fmt.Println("Это приложение требует прав администратора для работы с VPN.") + fmt.Println() + fmt.Println("Пожалуйста, запустите приложение от имени администратора:") + fmt.Println(" 1. Щелкните правой кнопкой мыши на vpn-client-cli.exe") + fmt.Println(" 2. Выберите 'Запуск от имени администратора'") + fmt.Println() + fmt.Println("Нажмите Enter для выхода...") + fmt.Scanln() + os.Exit(1) + } +} diff --git a/internal/cli/monitor.go b/internal/cli/monitor.go index 67d59de..570d4c2 100644 --- a/internal/cli/monitor.go +++ b/internal/cli/monitor.go @@ -3,7 +3,9 @@ package cli import ( "fmt" "os" + "os/signal" "strings" + "syscall" "time" "vpn-client/internal/config" @@ -26,6 +28,19 @@ func MonitorConnection() error { fmt.Println("Нажмите Ctrl+C для выхода из мониторинга\n") time.Sleep(1 * time.Second) + // Создаем канал для обработки сигналов + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + + // Создаем канал для остановки мониторинга + stopChan := make(chan bool, 1) + + // Запускаем горутину для обработки сигналов + go func() { + <-sigChan + stopChan <- true + }() + // Запускаем мониторинг ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() @@ -35,6 +50,12 @@ func MonitorConnection() error { case <-ticker.C: clearScreen() displayRealtimeStatus(state) + case <-stopChan: + // Восстанавливаем обработку сигналов по умолчанию + signal.Reset(os.Interrupt, syscall.SIGTERM) + fmt.Println("\n\nВыход из мониторинга...") + time.Sleep(500 * time.Millisecond) + return nil } } } diff --git a/main_cli.go b/main_cli.go index 028bb54..5baa8ea 100644 --- a/main_cli.go +++ b/main_cli.go @@ -4,11 +4,15 @@ import ( "fmt" "os" + "vpn-client/internal/admin" "vpn-client/internal/cli" "vpn-client/internal/config" ) func main() { + // Проверка прав администратора + admin.RequireAdmin() + // Инициализация конфигурации if err := config.Init(); err != nil { fmt.Fprintf(os.Stderr, "Ошибка инициализации: %v\n", err)