feat(admin): add platform-specific admin privilege checks

- Upgrade Go version from 1.21 to 1.25.0
- Update golang.org/x/sys dependency to v0.42.0
- Add Unix/Linux admin check using os.Geteuid() with sudo requirement
- Add Windows admin check using windows.SID and token membership validation
- Integrate admin privilege validation into main CLI entry point
- Enhance monitor.go with graceful signal handling for Ctrl+C interrupts
- Add signal channels for clean shutdown of monitoring loop
- Ensures VPN client runs with required elevated privileges on both platforms
This commit is contained in:
2026-04-06 19:06:03 +06:00
parent 58ffb567ce
commit d88139af1b
6 changed files with 115 additions and 2 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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
}
}
}