From 46738b7f482fb663f12b0d15dbacb621c13762fd Mon Sep 17 00:00:00 2001 From: Peri Thompson Date: Wed, 10 Mar 2021 11:15:42 +0000 Subject: [PATCH] Added check for Windows Permissions --- cmd/kubelet/app/server.go | 19 ++----- cmd/kubelet/app/server_others.go | 33 ++++++++++++ cmd/kubelet/app/server_windows.go | 85 +++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 cmd/kubelet/app/server_others.go create mode 100644 cmd/kubelet/app/server_windows.go diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index 97bb522734d..8671db7f719 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -1,5 +1,5 @@ /* -Copyright 2015 The Kubernetes Authors. +Copyright 2021 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ import ( "math" "net" "net/http" - "os" "path" "path/filepath" "strconv" @@ -260,6 +259,9 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API // add the kubelet config controller to kubeletDeps kubeletDeps.KubeletConfigController = kubeletConfigController + if err := checkPermissions(); err != nil { + klog.ErrorS(err, "kubelet running with insufficient permissions") + } // set up signal context here in order to be reused by kubelet and docker shim ctx := genericapiserver.SetupSignalContext() @@ -430,15 +432,6 @@ func Run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Depend return nil } -func checkPermissions() error { - if uid := os.Getuid(); uid != 0 { - return fmt.Errorf("kubelet needs to run as uid `0`. It is being run as %d", uid) - } - // TODO: Check if kubelet is running in the `initial` user namespace. - // http://man7.org/linux/man-pages/man7/user_namespaces.7.html - return nil -} - func setConfigz(cz *configz.Config, kc *kubeletconfiginternal.KubeletConfiguration) error { scheme, _, err := kubeletscheme.NewSchemeAndCodecs() if err != nil { @@ -760,10 +753,6 @@ func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Depend } } - if err := checkPermissions(); err != nil { - klog.Error(err) - } - utilruntime.ReallyCrash = s.ReallyCrashForTesting // TODO(vmarmol): Do this through container config. diff --git a/cmd/kubelet/app/server_others.go b/cmd/kubelet/app/server_others.go new file mode 100644 index 00000000000..a6cced1a1a8 --- /dev/null +++ b/cmd/kubelet/app/server_others.go @@ -0,0 +1,33 @@ +// +build !windows + +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "fmt" + "os" +) + +func checkPermissions() error { + if uid := os.Getuid(); uid != 0 { + return fmt.Errorf("kubelet needs to run as uid `0`. It is being run as %d", uid) + } + // TODO: Check if kubelet is running in the `initial` user namespace. + // http://man7.org/linux/man-pages/man7/user_namespaces.7.html + return nil +} diff --git a/cmd/kubelet/app/server_windows.go b/cmd/kubelet/app/server_windows.go new file mode 100644 index 00000000000..cdb83e321f4 --- /dev/null +++ b/cmd/kubelet/app/server_windows.go @@ -0,0 +1,85 @@ +// +build windows + +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "fmt" + "os/user" + + "golang.org/x/sys/windows" +) + +func isAdmin() (bool, error) { + // Get current user + u, err := user.Current() + if err != nil { + return false, fmt.Errorf("Error retrieving current user: %s", err) + } + // Get IDs of group user is a member of + ids, err := u.GroupIds() + if err != nil { + return false, fmt.Errorf("Error retrieving group ids: %s", err) + } + + // Check for existence of BUILTIN\ADMINISTRATORS group id + for i := range ids { + // BUILTIN\ADMINISTRATORS + if "S-1-5-32-544" == ids[i] { + return true, nil + } + } + return false, nil +} + +func checkPermissions() error { + //https://github.com/golang/go/issues/28804#issuecomment-505326268 + var sid *windows.SID + var userIsAdmin bool + + // https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-checktokenmembership + 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 fmt.Errorf("Error while checking for elevated permissions: %s", err) + } + + //We must free the sid to prevent security token leaks + defer windows.FreeSid(sid) + token := windows.Token(0) + + userIsAdmin, err = isAdmin() + if err != nil { + return fmt.Errorf("Error while checking admin group membership: %s", err) + } + + member, err := token.IsMember(sid) + if err != nil { + return fmt.Errorf("Error while checking for elevated permissions: %s", err) + } + if !member { + return fmt.Errorf("kubelet needs to run with administrator permissions. Run as admin is: %t, User in admin group: %t", member, userIsAdmin) + } + + return nil +}