diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..888fcdcb Binary files /dev/null and b/.DS_Store differ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b176ea0b..94cc4b92 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.18 + go-version: 1.19 check-latest: true cache: true - name: Checkout code diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef20f420..10dcf165 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.18 + go-version: 1.19 check-latest: true cache: true - name: Push image to docker hub @@ -31,7 +31,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.18 + go-version: 1.19 check-latest: true cache: true - name: Setup Minikube @@ -74,7 +74,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.18 + go-version: 1.19 check-latest: true cache: true - uses: docker-practice/actions-setup-docker@master @@ -130,7 +130,7 @@ jobs: # - name: Set up Go # uses: actions/setup-go@v2 # with: -# go-version: 1.18 +# go-version: 1.19 # # - run: | # # choco install docker-desktop # # docker version diff --git a/.github/workflows/upload_release.yml b/.github/workflows/upload_release.yml index b39d09c4..be68ded1 100644 --- a/.github/workflows/upload_release.yml +++ b/.github/workflows/upload_release.yml @@ -24,7 +24,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.18 + go-version: 1.19 check-latest: true cache: true - name: Checkout code diff --git a/build/Dockerfile b/build/Dockerfile index 44d99ced..9f90b57a 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,7 +1,7 @@ ARG BASE=github.com/wencaiwulue/kubevpn FROM envoyproxy/envoy:v1.21.1 AS envoy -FROM golang:1.18 AS builder +FROM golang:1.19 AS builder COPY . /go/src/$BASE diff --git a/pkg/handler/cleaner.go b/pkg/handler/cleaner.go index b7b8791c..516b92e6 100644 --- a/pkg/handler/cleaner.go +++ b/pkg/handler/cleaner.go @@ -70,7 +70,15 @@ func updateRefCount(configMapInterface v12.ConfigMapInterface, name string, incr err = retry.OnError( retry.DefaultRetry, func(err error) bool { - return !k8serrors.IsNotFound(err) + notFound := k8serrors.IsNotFound(err) + if notFound { + return false + } + conflict := k8serrors.IsConflict(err) + if conflict { + return true + } + return false }, func() (err error) { var cm *corev1.ConfigMap diff --git a/pkg/util/elevatecheck_windows.go b/pkg/util/elevatecheck_windows.go index 971fd4d7..499ba09e 100644 --- a/pkg/util/elevatecheck_windows.go +++ b/pkg/util/elevatecheck_windows.go @@ -4,34 +4,86 @@ package util import ( + "fmt" + log "github.com/sirupsen/logrus" + "github.com/wencaiwulue/kubevpn/pkg/util/wintoken" "os" - "strings" + "os/exec" + "os/signal" "syscall" - - "github.com/sirupsen/logrus" - "golang.org/x/sys/windows" ) -// ref https://stackoverflow.com/questions/31558066/how-to-ask-for-administer-privileges-on-windows-with-go +// ref https://stackoverflow.com/questions/31558066/how-to-ask-for-administer-privileges-on-windows-with-go func RunWithElevated() { - verb := "runas" exe, _ := os.Executable() - cwd, _ := os.Getwd() - args := strings.Join(os.Args[1:], " ") - - verbPtr, _ := windows.UTF16PtrFromString(verb) - exePtr, _ := syscall.UTF16PtrFromString(exe) - cwdPtr, _ := syscall.UTF16PtrFromString(cwd) - argPtr, _ := syscall.UTF16PtrFromString(args) - - var showCmd int32 = 1 //SW_NORMAL - - err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) + cmd := exec.Command(exe, "connect") + log.Debug(cmd.Args) + fmt.Println(cmd.Args) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + cmd.Env = append(os.Environ(), "KUBECONFIG="+os.Getenv("KUBECONFIG")) + //token, err := wintoken.GetInteractiveToken(wintoken.TokenLinked) + //if err != nil { + // panic(err) + //} + token, err := wintoken.OpenProcessToken(0, wintoken.TokenPrimary) if err != nil { - logrus.Warn(err) + panic(err) + } + err = token.EnableAllPrivileges() + if err != nil { + panic(err) + } + + defer token.Close() + cmd.SysProcAttr = &syscall.SysProcAttr{ + Token: syscall.Token(token.Token()), + } + // while send single CTRL+C, command will quit immediately, but output will cut off and print util quit final + // so, mute single CTRL+C, let inner command handle single only + go func() { + signals := make(chan os.Signal) + signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL) + <-signals + }() + err = cmd.Run() + if err != nil { + log.Warn(err) } } +//func RunWithElevated() { +// verb := "runas" +// //exe, _ := os.Executable() +// cwd, _ := os.Getwd() +// args := strings.Join(os.Args[:], " ") +// +// //verbPtr, _ := windows.UTF16PtrFromString(verb) +// //exePtr, _ := syscall.UTF16PtrFromString(exe) +// cwdPtr, _ := syscall.UTF16PtrFromString(cwd) +// argPtr, _ := syscall.UTF16PtrFromString(args) +// +// // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow +// //var showCmd int32 = 1 //SW_NORMAL +// //process := windows.CurrentProcess() +// //process := windows.GetDesktopWindow() +// //windows.Setenv("KUBECONFIG", os.Getenv("KUBECONFIG")) +// v, _ := syscall.UTF16PtrFromString("KUBECONFIG=" + os.Getenv("KUBECONFIG")) +// +// var si windows.StartupInfo +// var pi windows.ProcessInformation +////windows.TokenElevation +// // CreateProcess(NULL,"cleanmgr",NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi); //调用系统的清理磁盘程序 +// err := windows.CreateProcessAsUser(nil,nil, argPtr, nil, nil, true, windows.NORMAL_PRIORITY_CLASS, v, cwdPtr, &si, &pi) +// //r1, _, e1 := syscall.Syscall6(procShellExecuteW.Addr(), 6, uintptr(hwnd), uintptr(unsafe.Pointer(verb)), uintptr(unsafe.Pointer(file)), uintptr(unsafe.Pointer(args)), uintptr(unsafe.Pointer(cwd)), uintptr(showCmd)) +// //r1, _, e1 := syscall.Syscall12(procCreateProcessW.Addr(), 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0) +// //err := windows.ShellExecute(windows.Handle(process), verbPtr, exePtr, argPtr, cwdPtr, showCmd) +// if err != nil { +// logrus.Warn(err) +// } +//} + func IsAdmin() bool { _, err := os.Open("\\\\.\\PHYSICALDRIVE0") if err != nil { diff --git a/pkg/util/wintoken/gettoken.go b/pkg/util/wintoken/gettoken.go new file mode 100644 index 00000000..14133122 --- /dev/null +++ b/pkg/util/wintoken/gettoken.go @@ -0,0 +1,139 @@ +//go:build windows +// +build windows + +package wintoken + +import ( + "fmt" + "unsafe" + + "golang.org/x/sys/windows" +) + +const ( + WTS_CURRENT_SERVER_HANDLE windows.Handle = 0 +) + +// OpenProcessToken opens a process token using PID, pass 0 as PID for self token +func OpenProcessToken(pid int, tokenType tokenType) (*Token, error) { + var ( + t windows.Token + duplicatedToken windows.Token + procHandle windows.Handle + err error + ) + + if pid == 0 { + procHandle = windows.CurrentProcess() + } else { + procHandle, err = windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, uint32(pid)) + } + if err != nil { + return nil, err + } + + if err = windows.OpenProcessToken(procHandle, windows.TOKEN_ALL_ACCESS, &t); err != nil { + return nil, err + } + + defer windows.CloseHandle(windows.Handle(t)) + + switch tokenType { + case TokenPrimary: + if err := windows.DuplicateTokenEx(t, windows.MAXIMUM_ALLOWED, nil, windows.SecurityDelegation, windows.TokenPrimary, &duplicatedToken); err != nil { + return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err) + } + case TokenImpersonation: + if err := windows.DuplicateTokenEx(t, windows.MAXIMUM_ALLOWED, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken); err != nil { + return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err) + } + + case TokenLinked: + if err := windows.DuplicateTokenEx(t, windows.MAXIMUM_ALLOWED, nil, windows.SecurityDelegation, windows.TokenPrimary, &duplicatedToken); err != nil { + return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err) + } + dt, err := duplicatedToken.GetLinkedToken() + windows.CloseHandle(windows.Handle(duplicatedToken)) + if err != nil { + return nil, fmt.Errorf("error while getting LinkedToken: %w", err) + } + duplicatedToken = dt + } + + return &Token{token: duplicatedToken, typ: tokenType}, nil +} + +// GetInteractiveToken gets the interactive token associated with current logged in user +// It uses windows API WTSEnumerateSessions, WTSQueryUserToken and DuplicateTokenEx to return a valid wintoken +func GetInteractiveToken(tokenType tokenType) (*Token, error) { + + switch tokenType { + case TokenPrimary, TokenImpersonation, TokenLinked: + default: + return nil, ErrOnlyPrimaryImpersonationTokenAllowed + } + + var ( + sessionPointer uintptr + sessionCount uint32 + interactiveToken windows.Token + duplicatedToken windows.Token + sessionID uint32 + ) + + err := windows.WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, (**windows.WTS_SESSION_INFO)(unsafe.Pointer(&sessionPointer)), &sessionCount) + if err != nil { + return nil, fmt.Errorf("error while enumerating sessions: %v", err) + } + defer windows.WTSFreeMemory(sessionPointer) + + sessions := make([]*windows.WTS_SESSION_INFO, sessionCount) + size := unsafe.Sizeof(windows.WTS_SESSION_INFO{}) + + for i := range sessions { + sessions[i] = (*windows.WTS_SESSION_INFO)(unsafe.Pointer(sessionPointer + (size * uintptr(i)))) + } + + for i := range sessions { + if sessions[i].State == windows.WTSActive { + sessionID = sessions[i].SessionID + break + } + } + if sessionID == 0 { + return nil, ErrNoActiveSession + } + + if err := windows.WTSQueryUserToken(sessionID, &interactiveToken); err != nil { + return nil, fmt.Errorf("error while WTSQueryUserToken: %w", err) + } + + defer windows.CloseHandle(windows.Handle(interactiveToken)) + + switch tokenType { + case TokenPrimary: + if err := windows.DuplicateTokenEx(interactiveToken, windows.MAXIMUM_ALLOWED, nil, windows.SecurityDelegation, windows.TokenPrimary, &duplicatedToken); err != nil { + return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err) + } + case TokenImpersonation: + if err := windows.DuplicateTokenEx(interactiveToken, windows.MAXIMUM_ALLOWED, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken); err != nil { + return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err) + } + case TokenLinked: + if err := windows.DuplicateTokenEx(interactiveToken, windows.MAXIMUM_ALLOWED, nil, windows.SecurityDelegation, windows.TokenPrimary, &duplicatedToken); err != nil { + return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err) + } + dt, err := duplicatedToken.GetLinkedToken() + windows.CloseHandle(windows.Handle(duplicatedToken)) + if err != nil { + return nil, fmt.Errorf("error while getting LinkedToken: %w", err) + } + duplicatedToken = dt + } + + if windows.Handle(duplicatedToken) == windows.InvalidHandle { + return nil, ErrInvalidDuplicatedToken + } + + return &Token{typ: tokenType, token: duplicatedToken}, nil +} diff --git a/pkg/util/wintoken/vars.go b/pkg/util/wintoken/vars.go new file mode 100644 index 00000000..add57b9d --- /dev/null +++ b/pkg/util/wintoken/vars.go @@ -0,0 +1,11 @@ +package wintoken + +import "fmt" + +var ( + ErrNoActiveSession error = fmt.Errorf("no active session found") + ErrInvalidDuplicatedToken error = fmt.Errorf("invalid duplicated token") + ErrOnlyPrimaryImpersonationTokenAllowed error = fmt.Errorf("only primary or impersonation token types allowed") + ErrNoPrivilegesSpecified error = fmt.Errorf("no privileges specified") + ErrTokenClosed error = fmt.Errorf("token has been closed") +) diff --git a/pkg/util/wintoken/wintoken.go b/pkg/util/wintoken/wintoken.go new file mode 100644 index 00000000..f1d3e4cd --- /dev/null +++ b/pkg/util/wintoken/wintoken.go @@ -0,0 +1,410 @@ +//go:build windows +// +build windows + +package wintoken + +import ( + "bytes" + "encoding/binary" + "fmt" + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + procLookupPrivilegeName = modadvapi32.NewProc("LookupPrivilegeNameW") + procLookupPrivilegeDisplayName = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") +) + +type ( + tokenType int + privModType int +) + +const ( + PrivDisable privModType = iota + PrivEnable + PrivRemove +) + +type Token struct { + typ tokenType + token windows.Token +} + +// TokenUserDetail is the structure that exposes token details +// Details contain Username, Domain, Account Type, User Profile Directory, Environment +type TokenUserDetail struct { + Username string + Domain string + AccountType uint32 + UserProfileDir string + Environ []string +} + +func (t TokenUserDetail) String() string { + return fmt.Sprintf("Username: %s, Domain: %s, Account Type: %d, UserProfileDir: %s", t.Username, t.Domain, t.AccountType, t.UserProfileDir) +} + +// Privilege is the structure which exposes privilege details +// Details contain Name, Description, Enabled, EnabledByDefault, Removed, UsedForAccess +type Privilege struct { + Name string + Description string + Enabled bool + EnabledByDefault bool + Removed bool + UsedForAccess bool +} + +func (p Privilege) String() string { + status := "Disabled" + if p.Removed { + status = "Removed" + } else if p.Enabled { + status = "Enabled" + } + return fmt.Sprintf("%s: %s", p.Name, status) +} + +const ( + tokenUnknown tokenType = iota + TokenPrimary + TokenImpersonation + TokenLinked +) + +// NewToken can be used to supply your own token for the wintoken struct +// so you can use the same flexiblity provided by the package +func NewToken(token windows.Token, typ tokenType) *Token { + return &Token{ + token: token, + typ: typ, + } +} + +// Token returns the underlying token for use +func (t *Token) Token() windows.Token { + return t.token +} + +// Close closes the underlying token +func (t *Token) Close() { + windows.Close(windows.Handle(t.token)) + t.token = 0 +} + +func (t *Token) errIfTokenClosed() error { + if t.token == 0 { + return ErrTokenClosed + } + return nil +} + +func lookupPrivilegeNameByLUID(luid uint64) (string, string, error) { + nameBuffer := make([]uint16, 256) + nameBufferSize := uint32(len(nameBuffer)) + displayNameBuffer := make([]uint16, 256) + displayNameBufferSize := uint32(len(displayNameBuffer)) + + sysName, err := windows.UTF16PtrFromString("") + if err != nil { + return "", "", err + } + + if r1, _, err := procLookupPrivilegeName.Call(uintptr(unsafe.Pointer(sysName)), uintptr(unsafe.Pointer(&luid)), uintptr(unsafe.Pointer(&nameBuffer[0])), uintptr(unsafe.Pointer(&nameBufferSize))); r1 == 0 { + return "", "", err + } + + var langID uint32 + if r1, _, err := procLookupPrivilegeDisplayName.Call(uintptr(unsafe.Pointer(sysName)), uintptr(unsafe.Pointer(&nameBuffer[0])), uintptr(unsafe.Pointer(&displayNameBuffer[0])), uintptr(unsafe.Pointer(&displayNameBufferSize)), uintptr(unsafe.Pointer(&langID))); r1 == 0 { + return "", "", err + } + + return windows.UTF16ToString(nameBuffer), windows.UTF16ToString(displayNameBuffer), nil +} + +// UserDetails gets User details associated with token +func (t *Token) UserDetails() (TokenUserDetail, error) { + uSid, err := t.token.GetTokenUser() + if err != nil { + return TokenUserDetail{}, err + } + user, domain, typ, err := uSid.User.Sid.LookupAccount("") + if err != nil { + return TokenUserDetail{}, err + } + uProfDir, err := t.token.GetUserProfileDirectory() + if err != nil { + return TokenUserDetail{}, err + } + env, err := t.token.Environ(false) + if err != nil { + return TokenUserDetail{}, err + } + return TokenUserDetail{Username: user, Domain: domain, AccountType: typ, UserProfileDir: uProfDir, Environ: env}, nil +} + +// GetPrivileges lists all Privileges from the token +func (t *Token) GetPrivileges() ([]Privilege, error) { + if err := t.errIfTokenClosed(); err != nil { + return nil, err + } + + n := uint32(0) + windows.GetTokenInformation(t.token, windows.TokenPrivileges, nil, 0, &n) + + b := make([]byte, n) + if err := windows.GetTokenInformation(t.token, windows.TokenPrivileges, &b[0], uint32(len(b)), &n); err != nil { + return nil, err + } + + privBuff := bytes.NewBuffer(b) + + var nPrivs uint32 + if err := binary.Read(privBuff, binary.LittleEndian, &nPrivs); err != nil { + return nil, fmt.Errorf("cannot read number of privileges: %w", err) + } + + privDetails := make([]Privilege, int(nPrivs)) + + for i := 0; i < int(nPrivs); i++ { + + var ( + luid uint64 + attributes uint32 + currentPrivInfo Privilege + err error + ) + + if err := binary.Read(privBuff, binary.LittleEndian, &luid); err != nil { + return nil, fmt.Errorf("cannot read LUID from buffer: %w", err) + } + + if err := binary.Read(privBuff, binary.LittleEndian, &attributes); err != nil { + return nil, fmt.Errorf("cannot read attributes from buffer: %w", err) + } + + currentPrivInfo.Name, currentPrivInfo.Description, err = lookupPrivilegeNameByLUID(luid) + if err != nil { + return nil, fmt.Errorf("cannot get privilege info based on the LUID: %w", err) + } + + currentPrivInfo.EnabledByDefault = (attributes & windows.SE_PRIVILEGE_ENABLED_BY_DEFAULT) > 0 + currentPrivInfo.UsedForAccess = (attributes & windows.SE_PRIVILEGE_USED_FOR_ACCESS) > 0 + currentPrivInfo.Enabled = (attributes & windows.SE_PRIVILEGE_ENABLED) > 0 + currentPrivInfo.Removed = (attributes & windows.SE_PRIVILEGE_REMOVED) > 0 + + privDetails[i] = currentPrivInfo + } + + return privDetails, nil +} + +// EnableAllPrivileges enables all privileges in the token +func (t *Token) EnableAllPrivileges() error { + if err := t.errIfTokenClosed(); err != nil { + return err + } + + privs, err := t.GetPrivileges() + if err != nil { + return err + } + + var toBeEnabled []string + + for _, p := range privs { + if !p.Removed && !p.Enabled { + toBeEnabled = append(toBeEnabled, p.Name) + } + } + return t.modifyTokenPrivileges(toBeEnabled, PrivEnable) +} + +// DisableAllPrivileges disables all privileges in the token +func (t *Token) DisableAllPrivileges() error { + if err := t.errIfTokenClosed(); err != nil { + return err + } + + privs, err := t.GetPrivileges() + if err != nil { + return err + } + + var toBeDisabled []string + + for _, p := range privs { + if !p.Removed && p.Enabled { + toBeDisabled = append(toBeDisabled, p.Name) + } + } + return t.modifyTokenPrivileges(toBeDisabled, PrivDisable) +} + +// RemoveAllPrivileges removes all privileges from the token +func (t *Token) RemoveAllPrivileges() error { + if err := t.errIfTokenClosed(); err != nil { + return err + } + + privs, err := t.GetPrivileges() + if err != nil { + return err + } + + var toBeRemoved []string + + for _, p := range privs { + if !p.Removed { + toBeRemoved = append(toBeRemoved, p.Name) + } + } + return t.modifyTokenPrivileges(toBeRemoved, PrivRemove) +} + +// EnableTokenPrivileges enables token privileges by list of privilege names +func (t *Token) EnableTokenPrivileges(privs []string) error { + return t.modifyTokenPrivileges(privs, PrivEnable) +} + +// DisableTokenPrivileges disables token privileges by list of privilege names +func (t *Token) DisableTokenPrivileges(privs []string) error { + return t.modifyTokenPrivileges(privs, PrivDisable) +} + +// RemoveTokenPrivileges removes token privileges by list of privilege names +func (t *Token) RemoveTokenPrivileges(privs []string) error { + return t.modifyTokenPrivileges(privs, PrivRemove) +} + +// EnableTokenPrivileges enables token privileges by privilege name +func (t *Token) EnableTokenPrivilege(priv string) error { + return t.modifyTokenPrivilege(priv, PrivEnable) +} + +// DisableTokenPrivilege disables token privileges by privilege name +func (t *Token) DisableTokenPrivilege(priv string) error { + return t.modifyTokenPrivilege(priv, PrivDisable) +} + +// RemoveTokenPrivilege removes token privileges by privilege name +func (t *Token) RemoveTokenPrivilege(priv string) error { + return t.modifyTokenPrivilege(priv, PrivRemove) +} + +func (t *Token) modifyTokenPrivileges(privs []string, mode privModType) error { + if err := t.errIfTokenClosed(); err != nil { + return err + } + + if len(privs) == 0 { + return ErrNoPrivilegesSpecified + } + + errMsgConst := "" + switch mode { + case PrivDisable: + errMsgConst = "disabling" + case PrivEnable: + errMsgConst = "enabling" + case PrivRemove: + errMsgConst = "removing" + } + var errMsg string + for _, p := range privs { + err := t.modifyTokenPrivilege(p, mode) + if err != nil { + if len(errMsg) != 0 { + errMsg += "\n" + } + errMsg += fmt.Sprintf("%s privilege for %s failed: %s", errMsgConst, p, err) + } + } + + if len(errMsg) != 0 { + return fmt.Errorf(errMsg) + } + return nil +} + +func (t *Token) modifyTokenPrivilege(priv string, mode privModType) error { + if err := t.errIfTokenClosed(); err != nil { + return err + } + + var luid windows.LUID + + if err := windows.LookupPrivilegeValue(nil, windows.StringToUTF16Ptr(priv), &luid); err != nil { + return fmt.Errorf("LookupPrivilegeValueW failed: %w", err) + } + + ap := windows.Tokenprivileges{ + PrivilegeCount: 1, + } + ap.Privileges[0].Luid = luid + + switch mode { + case PrivEnable: + ap.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED + case PrivRemove: + ap.Privileges[0].Attributes = windows.SE_PRIVILEGE_REMOVED + } + + if err := windows.AdjustTokenPrivileges(t.token, false, &ap, 0, nil, nil); err != nil { + return fmt.Errorf("AdjustTokenPrivileges failed: %w", err) + } + + return nil +} + +// GetIntegrityLevel is used to get integrity level of the token +func (t *Token) GetIntegrityLevel() (string, error) { + if err := t.errIfTokenClosed(); err != nil { + return "", err + } + + n := uint32(0) + windows.GetTokenInformation(t.token, windows.TokenIntegrityLevel, nil, 0, &n) + + b := make([]byte, n) + if err := windows.GetTokenInformation(t.token, windows.TokenIntegrityLevel, &b[0], uint32(len(b)), &n); err != nil { + return "", err + } + + tml := (*windows.Tokenmandatorylabel)(unsafe.Pointer(&b[0])) + sid := (*windows.SID)(unsafe.Pointer(tml.Label.Sid)) + switch sid.String() { + case "S-1-16-4096": + return "Low", nil + + case "S-1-16-8192": + return "Medium", nil + + case "S-1-16-12288": + return "High", nil + + case "S-1-16-16384": + return "System", nil + + default: + return "Unknown", nil + } +} + +// GetLinkedToken is used to get the linked token if any +func (t *Token) GetLinkedToken() (*Token, error) { + + lt, err := t.token.GetLinkedToken() + if err != nil { + return nil, err + } + + return &Token{ + typ: TokenLinked, + token: lt, + }, nil +} diff --git a/pkg/webhook/convert.go b/pkg/webhook/convert.go index 51a9f8a8..c4b4cd7a 100644 --- a/pkg/webhook/convert.go +++ b/pkg/webhook/convert.go @@ -26,43 +26,6 @@ func convertAdmissionRequestToV1(r *v1beta1.AdmissionRequest) *v1.AdmissionReque } } -func convertAdmissionRequestToV1beta1(r *v1.AdmissionRequest) *v1beta1.AdmissionRequest { - return &v1beta1.AdmissionRequest{ - Kind: r.Kind, - Namespace: r.Namespace, - Name: r.Name, - Object: r.Object, - Resource: r.Resource, - Operation: v1beta1.Operation(r.Operation), - UID: r.UID, - DryRun: r.DryRun, - OldObject: r.OldObject, - Options: r.Options, - RequestKind: r.RequestKind, - RequestResource: r.RequestResource, - RequestSubResource: r.RequestSubResource, - SubResource: r.SubResource, - UserInfo: r.UserInfo, - } -} - -func convertAdmissionResponseToV1(r *v1beta1.AdmissionResponse) *v1.AdmissionResponse { - var pt *v1.PatchType - if r.PatchType != nil { - t := v1.PatchType(*r.PatchType) - pt = &t - } - return &v1.AdmissionResponse{ - UID: r.UID, - Allowed: r.Allowed, - AuditAnnotations: r.AuditAnnotations, - Patch: r.Patch, - PatchType: pt, - Result: r.Result, - Warnings: r.Warnings, - } -} - func convertAdmissionResponseToV1beta1(r *v1.AdmissionResponse) *v1beta1.AdmissionResponse { var pt *v1beta1.PatchType if r.PatchType != nil { diff --git a/pkg/webhook/pods.go b/pkg/webhook/pods.go index d18b5ff4..509a7360 100644 --- a/pkg/webhook/pods.go +++ b/pkg/webhook/pods.go @@ -40,7 +40,6 @@ func admitPods(ar v1.AdmissionReview) *v1.AdmissionResponse { switch ar.Request.Operation { case v1.Create: from, _ := json.Marshal(pod) - fmt.Println(ar.String()) var found bool for i := 0; i < len(pod.Spec.Containers); i++ { if pod.Spec.Containers[i].Name == config.ContainerSidecarVPN { @@ -151,41 +150,3 @@ func applyPodPatch(ar v1.AdmissionReview, shouldPatchPod func(*corev1.Pod) bool, } return &reviewResponse } - -// denySpecificAttachment denies `kubectl attach to-be-attached-pod -i -c=container1" -// or equivalent client requests. -func denySpecificAttachment(ar v1.AdmissionReview) *v1.AdmissionResponse { - klog.V(2).Info("handling attaching pods") - if ar.Request.Name != "to-be-attached-pod" { - return &v1.AdmissionResponse{Allowed: true} - } - podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} - if e, a := podResource, ar.Request.Resource; e != a { - err := fmt.Errorf("expect resource to be %s, got %s", e, a) - klog.Error(err) - return toV1AdmissionResponse(err) - } - if e, a := "attach", ar.Request.SubResource; e != a { - err := fmt.Errorf("expect subresource to be %s, got %s", e, a) - klog.Error(err) - return toV1AdmissionResponse(err) - } - - raw := ar.Request.Object.Raw - podAttachOptions := corev1.PodAttachOptions{} - deserializer := codecs.UniversalDeserializer() - if _, _, err := deserializer.Decode(raw, nil, &podAttachOptions); err != nil { - klog.Error(err) - return toV1AdmissionResponse(err) - } - klog.V(2).Info(fmt.Sprintf("podAttachOptions=%#v\n", podAttachOptions)) - if !podAttachOptions.Stdin || podAttachOptions.Container != "container1" { - return &v1.AdmissionResponse{Allowed: true} - } - return &v1.AdmissionResponse{ - Allowed: false, - Result: &metav1.Status{ - Message: "attaching to pod 'to-be-attached-pod' is not allowed", - }, - } -}