puppetlabs / puppet

Server automation framework and application
https://puppet.com/open-source/#osp
Apache License 2.0
7.44k stars 2.19k forks source link

Windows Service Provider Doesn't Support gMSAs #9328

Open holysoles opened 6 months ago

holysoles commented 6 months ago

Use Case

gMSAs are domain accounts used to run services, and are a more secure alternative than traditional managed service accounts for running services or other automation.

When attempting to provide a gMSA as the logonaccount for a service resource, an error is encountered:

> puppet resource service someservice logonaccount='DOMAIN\msvc_automation$'
Error: The given password is invalid for user 'DOMAIN\msvc_automation$'.
Error: /Service[someservice]/logonaccount: change from 'DOMAIN\svc_user' to 'DOMAIN\msvc_automation$' failed" The given password is invalid for user 'DOMAIN\msvc_automation$'.

This continues to occur event with alternative attempts, such as with the ~ used as a password placeholder in PsExec.

> puppet resource service someservice logonaccount='DOMAIN\msvc_automation$' logonpassword=undef
> puppet resource service someservice logonaccount='DOMAIN\msvc_automation$' logonpassword='~'

Describe the Solution You Would Like

Support configuring services as gMSAs with the oob service resource on Windows.

Describe Alternatives You've Considered

Alternatives include using an exec resource that reconfigures the service to run as a gMSA:

exec { "Configure someservice logon account":
  command  => "${facts['os']['windows']['system32']}\\sc.exe config someservice obj= 'DOMAIN\\msvc_automation$'",
  unless   => "${facts['os']['windows']['system32']}\\sc.exe qc someservice | ${facts['os']['windows']['system32']}\\findstr.exe -li 'DOMAIN\\msvc_automation$'",
  provider => powershell,
}

Additional Context

In the windows service provider, there is a check done to validate the logon credentials here

That eventually takes us here where we call the Windows API to get an impersonation token. The logon type LOGON32_LOGON_NETWORK is attempted, and if that fails, LOGON32_LOGON_INTERACTIVE is used.

gMSA cleartext password retrieval is fairly complex, but can be done with assistance from the PS Module DSInternals in the ConvertFrom-ADManagedPasswordBlob function , and the general process is described in this blog post. Forcing a Puppet user to provide this value is not user friendly though.

Alternatively, if user context is SYSTEM (or another account with the "Act as part of the operating system" privledge) during the LogonUserW call, and if the logontype is specified as LOGON32_LOGON_SERVICE, the cleartext password can be provided as _SA_{262E99C9-6160-4871-ACEC-4E61736B6F21}, which is defined in lmaccess.h as the constant SERVICE_ACCOUNT_PASSWORD . This is detailed in this MS discussion and the constant is noted in this Rust winapi wrapper (among other places like the windows sdk).

I see a few possible solutions when credentials are being validated, or the password is being set for the resource:

for the scheduled_task resource here, testing if a user is a gMSA is done simply by checking if the last character is $ which seems reasonable. However it seems like proper testing is doable via NetIsServiceAccount

github-actions[bot] commented 6 months ago

Migrated issue to PUP-12036

cthorn42 commented 6 months ago

@holysoles thanks for the great and in depth issue. This will be looked into.