Skip to content

Commit

Permalink
windows changes
Browse files Browse the repository at this point in the history
Signed-off-by: Harrison Affel <harrisonaffel@gmail.com>
  • Loading branch information
HarrisonWAffel authored and brandond committed May 16, 2024
1 parent 1cd7986 commit 1d22b69
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 2 deletions.
11 changes: 10 additions & 1 deletion pkg/agent/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,16 @@ func ensureNodePassword(nodePasswordFile string) (string, error) {
return "", err
}
nodePassword := hex.EncodeToString(password)
return nodePassword, os.WriteFile(nodePasswordFile, []byte(nodePassword+"\n"), 0600)

if err = os.WriteFile(nodePasswordFile, []byte(nodePassword+"\n"), 0600); err != nil {
return nodePassword, err
}

if err = configureACL(nodePassword); err != nil {
return nodePassword, err
}

return nodePassword, nil
}

func upgradeOldNodePasswordPath(oldNodePasswordFile, newNodePasswordFile string) {
Expand Down
8 changes: 7 additions & 1 deletion pkg/agent/config/config_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func applyCRIDockerdAddress(nodeConfig *config.Node) {
}

func applyContainerdQoSClassConfigFileIfPresent(envInfo *cmds.Agent, containerdConfig *config.Containerd) {
containerdConfigDir := filepath.Join(envInfo.DataDir, "agent", "etc", "containerd")
containerdConfigDir := filepath.Join(envInfo.DataDir, "agent", "etc", "containerd")

blockioPath := filepath.Join(containerdConfigDir, "blockio_config.yaml")

Expand All @@ -45,3 +45,9 @@ func applyContainerdQoSClassConfigFileIfPresent(envInfo *cmds.Agent, containerdC
}
}
}

// configureACL will configure an Access Control List for the specified file.
// On Linux, this function is a no-op
func configureACL(file string) error {
return nil
}
19 changes: 19 additions & 0 deletions pkg/agent/config/config_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ package config
import (
"path/filepath"

"github.com/k3s-io/k3s/pkg/agent/util/acl"
"github.com/k3s-io/k3s/pkg/cli/cmds"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
)

func applyContainerdStateAndAddress(nodeConfig *config.Node) {
Expand All @@ -22,3 +25,19 @@ func applyCRIDockerdAddress(nodeConfig *config.Node) {
func applyContainerdQoSClassConfigFileIfPresent(envInfo *cmds.Agent, containerdConfig *config.Containerd) {
// QoS-class resource management not supported on windows.
}

// configureACL will configure an Access Control List for the specified file,
// ensuring that only the LocalSystem and Administrators Group have access to the file contents
func configureACL(file string) error {
// by default Apply will use the current user (LocalSystem in the case of a Windows service)
// as the owner and current user group as the allowed group
// additionally, we define a DACL to permit access to the file to the local system and all administrators
if err := acl.Apply(file, nil, nil, []windows.EXPLICIT_ACCESS{
acl.GrantSid(windows.GENERIC_ALL, acl.LocalSystemSID()),
acl.GrantSid(windows.GENERIC_ALL, acl.BuiltinAdministratorsSID()),
}...); err != nil {
return errors.Wrapf(err, "failed to configure Access Control List For %s", file)
}

return nil
}
166 changes: 166 additions & 0 deletions pkg/agent/util/acl/acl_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//go:build windows
// +build windows

package acl

import (
"fmt"
"golang.org/x/sys/windows"
"unsafe"
)

// TODO: Remove in favor of the rancher/permissions repository once that is setup

func BuiltinAdministratorsSID() *windows.SID {
return mustGetSid(windows.WinBuiltinAdministratorsSid)
}

func LocalSystemSID() *windows.SID {
return mustGetSid(windows.WinLocalSystemSid)
}

func mustGetSid(sidType windows.WELL_KNOWN_SID_TYPE) *windows.SID {
sid, err := windows.CreateWellKnownSid(sidType)
if err != nil {
panic(err)
}
return sid
}

// GrantSid creates an EXPLICIT_ACCESS instance granting permissions to the provided SID.
func GrantSid(accessPermissions windows.ACCESS_MASK, sid *windows.SID) windows.EXPLICIT_ACCESS {
return windows.EXPLICIT_ACCESS{
AccessPermissions: accessPermissions,
AccessMode: windows.GRANT_ACCESS,
Inheritance: windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
Trustee: windows.TRUSTEE{
TrusteeForm: windows.TRUSTEE_IS_SID,
TrusteeValue: windows.TrusteeValueFromSID(sid),
},
}
}

// Apply performs both Chmod and Chown at the same time, where the filemode's owner and group will correspond to
// the provided owner and group (or the current owner and group, if they are set to nil)
func Apply(path string, owner *windows.SID, group *windows.SID, access ...windows.EXPLICIT_ACCESS) error {
if path == "" {
return fmt.Errorf("path cannot be empty")
}
return apply(path, owner, group, access...)
}

// apply performs a Chmod (if owner and group are provided) and sets a custom ACL based on the provided EXPLICIT_ACCESS rules
// To create EXPLICIT_ACCESS rules, see the helper functions in pkg/access
func apply(path string, owner *windows.SID, group *windows.SID, access ...windows.EXPLICIT_ACCESS) error {
// assemble arguments
args := securityArgs{
path: path,
owner: owner,
group: group,
access: access,
}

securityInfo := args.ToSecurityInfo()
if securityInfo == 0 {
// nothing to change
return nil
}
dacl, err := args.ToDACL()
if err != nil {
return err
}
return windows.SetNamedSecurityInfo(
path,
windows.SE_FILE_OBJECT,
securityInfo,
owner,
group,
dacl,
nil,
)
}

type securityArgs struct {
path string

owner *windows.SID
group *windows.SID

access []windows.EXPLICIT_ACCESS
}

func (a *securityArgs) ToSecurityInfo() windows.SECURITY_INFORMATION {
var securityInfo windows.SECURITY_INFORMATION

if a.owner != nil {
// override owner
securityInfo |= windows.OWNER_SECURITY_INFORMATION
}

if a.group != nil {
// override group
securityInfo |= windows.GROUP_SECURITY_INFORMATION
}

if len(a.access) != 0 {
// override DACL
securityInfo |= windows.DACL_SECURITY_INFORMATION
securityInfo |= windows.PROTECTED_DACL_SECURITY_INFORMATION
}

return securityInfo
}

func (a *securityArgs) ToSecurityAttributes() (*windows.SecurityAttributes, error) {
// define empty security descriptor
sd, err := windows.NewSecurityDescriptor()
if err != nil {
return nil, err
}
err = sd.SetOwner(a.owner, false)
if err != nil {
return nil, err
}
err = sd.SetGroup(a.group, false)
if err != nil {
return nil, err
}

// define security attributes using descriptor
var sa windows.SecurityAttributes
sa.Length = uint32(unsafe.Sizeof(sa))
sa.SecurityDescriptor = sd

if len(a.access) == 0 {
// security attribute should simply inherit parent rules
sa.InheritHandle = 1
return &sa, nil
}

// apply provided access rules to the DACL
dacl, err := a.ToDACL()
if err != nil {
return nil, err
}
err = sd.SetDACL(dacl, true, false)
if err != nil {
return nil, err
}

// set the protected DACL flag to prevent the DACL of the security descriptor from being modified by inheritable ACEs
// (i.e. prevent parent folders from modifying this ACL)
err = sd.SetControl(windows.SE_DACL_PROTECTED, windows.SE_DACL_PROTECTED)
if err != nil {
return nil, err
}

return &sa, nil
}

func (a *securityArgs) ToDACL() (*windows.ACL, error) {
if len(a.access) == 0 {
// No rules were specified
return nil, nil
}
return windows.ACLFromEntries(a.access, nil)
}

0 comments on commit 1d22b69

Please sign in to comment.