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:
4
go.mod
4
go.mod
@@ -1,11 +1,11 @@
|
|||||||
module vpn-client
|
module vpn-client
|
||||||
|
|
||||||
go 1.21
|
go 1.25.0
|
||||||
|
|
||||||
require github.com/fatih/color v1.16.0
|
require github.com/fatih/color v1.16.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // 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
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
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.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=
|
||||||
|
|||||||
29
internal/admin/admin_unix.go
Normal file
29
internal/admin/admin_unix.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
57
internal/admin/admin_windows.go
Normal file
57
internal/admin/admin_windows.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"vpn-client/internal/config"
|
"vpn-client/internal/config"
|
||||||
@@ -26,6 +28,19 @@ func MonitorConnection() error {
|
|||||||
fmt.Println("Нажмите Ctrl+C для выхода из мониторинга\n")
|
fmt.Println("Нажмите Ctrl+C для выхода из мониторинга\n")
|
||||||
time.Sleep(1 * time.Second)
|
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)
|
ticker := time.NewTicker(1 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
@@ -35,6 +50,12 @@ func MonitorConnection() error {
|
|||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
clearScreen()
|
clearScreen()
|
||||||
displayRealtimeStatus(state)
|
displayRealtimeStatus(state)
|
||||||
|
case <-stopChan:
|
||||||
|
// Восстанавливаем обработку сигналов по умолчанию
|
||||||
|
signal.Reset(os.Interrupt, syscall.SIGTERM)
|
||||||
|
fmt.Println("\n\nВыход из мониторинга...")
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"vpn-client/internal/admin"
|
||||||
"vpn-client/internal/cli"
|
"vpn-client/internal/cli"
|
||||||
"vpn-client/internal/config"
|
"vpn-client/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Проверка прав администратора
|
||||||
|
admin.RequireAdmin()
|
||||||
|
|
||||||
// Инициализация конфигурации
|
// Инициализация конфигурации
|
||||||
if err := config.Init(); err != nil {
|
if err := config.Init(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Ошибка инициализации: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Ошибка инициализации: %v\n", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user