Posh-ACME icon indicating copy to clipboard operation
Posh-ACME copied to clipboard

Export-PluginArgs : Access is denied

Open mshlmv opened this issue 2 years ago • 18 comments

Hi there.

I have Posh-ACME 4.14.0 and I tried to get certificate with powershell script automation. But I get an error when passing the API key (apparently).

$ApiToken = 'XXX'
$DnsName = 'xxx.xxx.com'
$AdminCertEmail = '[email protected]'

Set-PAServer LE_PROD

$pArgs = @{ SelectelAdminToken = (ConvertTo-SecureString $ApiToken -AsPlainText -Force) }
$newCert = New-PACertificate $DnsName -AcceptTOS -Install -Contact $AdminCertEmail -Plugin Selectel
 -PluginArgs $pArgs

At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\4.14.0\Public\Set-PAOrder.ps1:139 char:17
+ ...               Export-PluginArgs -Order $order -PluginArgs $PluginArgs
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Export-PluginArgs], CryptographicException
    + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicException,Export-PluginArgs

Submit-ChallengeValidation : Cannot bind argument to parameter 'SelectelAdminToken' because it is null.
+         Submit-ChallengeValidation
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Submit-ChallengeValidation], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Submit-ChallengeValidation

Connected via ssh, I run this script as a local administrator on a computer in the domain

mshlmv avatar Jun 27 '22 16:06 mshlmv

Hi @mshlmv, thanks for reaching out. I'm not seeing the Access Denied error in the output you posted. I do see a CryptographicException apparently thrown by Export-PluginArgs though. That error is likely the cause of the following error with Submit-ChallengeValidation. Can you try running your New-PACertificate call with Verbose and Debug output so I can hopefully get a better sense of where this is happening within Export-PluginArgs?

$ApiToken = 'XXX'
$DnsName = 'xxx.xxx.com'
$AdminCertEmail = '[email protected]'

Set-PAServer LE_PROD

$DebugPreference = 'Continue'
$newCert = New-PACertificate $DnsName -AcceptTOS -Install -Contact $AdminCertEmail -Plugin Selectel -PluginArgs $pArgs -Verbose

rmbolger avatar Jun 27 '22 19:06 rmbolger

I'm sorry, I missed this line when copying.

OK, now full verbose output:

VERBOSE: Updating directory info from https://acme-v02.api.letsencrypt.org/directory
DEBUG: Requesting nonce from https://acme-v02.api.letsencrypt.org/acme/new-nonce
DEBUG: Saving PAServer to disk
DEBUG: Loading PAServer list from disk
DEBUG: Enabling cert validation
DEBUG: Loading PAAccount 607526636 from disk
DEBUG: Loading PAOrder list from disk
VERBOSE: Using ACME Server https://acme-v02.api.letsencrypt.org/directory
DEBUG: Loading PAAccount list from disk
VERBOSE: Using account 607526636
VERBOSE: Order name not specified, using 'xxx.xxx.com'
DEBUG: Loading PAOrder list from disk
DEBUG: Refreshing order 'xxx.xxx.com'
DEBUG: ACME Header:
{
    "kid":  "https://acme-v02.api.letsencrypt.org/acme/acct/607526636",
    "alg":  "ES256",
    "nonce":  "0002kcXxCVNYtFe-Tay6WPlMtKVfhJMenU5l9rMF0pav-i8",
    "url":  "https://acme-v02.api.letsencrypt.org/acme/order/607526636/101537792046"
}
DEBUG: ACME Payload: (empty)
DEBUG: Signing message using EC with SHA256
DEBUG: POST https://acme-v02.api.letsencrypt.org/acme/order/607526636/101537792046
{"payload":"","protected":"eyJraWQiOiJodHRwczovL2FjbWUtdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9hY2N0LzYwNzUyNjYzNiIs
ImFsZyI6IkVTMjU2Iiwibm9uY2UiOiIwMDAya2NYeENWTll0RmUtVGF5NldQbE10S1ZmaEpNZW5VNWw5ck1GMHBhdi1pOCIsInVybCI6Imh0dHBzOi8
vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL29yZGVyLzYwNzUyNjYzNi8xMDE1Mzc3OTIwNDYifQ","signature":"6YJvjgPRAJEJJ
HyEPhJU8fv_7MyanspjKdHVIOkn5wU2XrLKVDLC0V6NeadXFatl8k1Gr1RAwz7A7nhWzIJ0RA"}
DEBUG: ACME Response:
{
  "status": "pending",
  "expires": "2022-07-04T12:39:48Z",
  "identifiers": [
    {
      "type": "dns",
      "value": "xxx.xxx.com"
    }
  ],
  "authorizations": [
    "https://acme-v02.api.letsencrypt.org/acme/authz-v3/124245243236"
  ],
  "finalize": "https://acme-v02.api.letsencrypt.org/acme/finalize/607526636/101537792046"
}
DEBUG: Updated nonce: 0002WUNBjNnrIohsm_Ve6cqWr-PNzzo5vPXX8BSsxlmonoY
DEBUG: Loading PAOrder list from disk
VERBOSE: Using existing order 'xxx.xxx.com' with status pending
DEBUG: Set order params:
{
    "Plugin":  [
                   "Selectel"
               ],
    "Install":  {
                    "IsPresent":  true
                },
    "PluginArgs":  {
                       "SelectelAdminToken":  {
                                                  "Length":  31
                                              }
                   }
}
DEBUG: Loading PAOrder list from disk
VERBOSE: Updating plugin args for plugin(s) Selectel
DEBUG: Exporting plugin args for order 'xxx.xxx.com' with plugins Selectel
DEBUG: Loading PAOrder list from disk
DEBUG: Attempting to load plugin Selectel
DEBUG: Removing old value for SelectelAdminToken
DEBUG: Adding new value for SelectelAdminToken
Export-PluginArgs : Access is denied.
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\4.14.0\Public\Set-PAOrder.ps1:139 char:17
+ ...               Export-PluginArgs -Order $order -PluginArgs $PluginArgs
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Export-PluginArgs], CryptographicException
    + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicException,Export-PluginArgs

DEBUG: ACME Header:
{
    "kid":  "https://acme-v02.api.letsencrypt.org/acme/acct/607526636",
    "alg":  "ES256",
    "nonce":  "0002WUNBjNnrIohsm_Ve6cqWr-PNzzo5vPXX8BSsxlmonoY",
    "url":  "https://acme-v02.api.letsencrypt.org/acme/authz-v3/124245243236"
}
DEBUG: ACME Payload: (empty)
DEBUG: Signing message using EC with SHA256
DEBUG: POST https://acme-v02.api.letsencrypt.org/acme/authz-v3/124245243236
{"payload":"","protected":"eyJraWQiOiJodHRwczovL2FjbWUtdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9hY2N0LzYwNzUyNjYzNiIs
ImFsZyI6IkVTMjU2Iiwibm9uY2UiOiIwMDAyV1VOQmpObnJJb2hzbV9WZTZjcVdyLVBOenpvNXZQWFg4QlNzeGxtb25vWSIsInVybCI6Imh0dHBzOi8
vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2F1dGh6LXYzLzEyNDI0NTI0MzIzNiJ9","signature":"zNLXgs7Jl3LWdZZoKHDdhoS
lGiOlaLVuWRtbrfPtCV7mx-YjF1pCx_CG6VGIV456KxiMKm89eAEjWMXEfH1z0Q"}
DEBUG: ACME Response:
{
  "identifier": {
    "type": "dns",
    "value": "xxx.xxx.com"
  },
  "status": "pending",
  "expires": "2022-07-04T12:39:48Z",
  "challenges": [
    {
      "type": "http-01",
      "status": "pending",
      "url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/124245243236/00RVaw",
      "token": "hXc83dLae99UfFuLEtcQM4jseoxVILrWkUV23EO2nzA"
    },
    {
      "type": "dns-01",
      "status": "pending",
      "url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/124245243236/FiEUww",
      "token": "hXc83dLae99UfFuLEtcQM4jseoxVILrWkUV23EO2nzA"
    },
    {
      "type": "tls-alpn-01",
      "status": "pending",
      "url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/124245243236/MBXugg",
      "token": "hXc83dLae99UfFuLEtcQM4jseoxVILrWkUV23EO2nzA"
    }
  ]
}
DEBUG: Updated nonce: 0002awnsXJl_BMFXhNp7GzCQpZyC06dpNPK-Ex8TLv5jODA
DEBUG: Plugin: Selectel
DEBUG: DnsAlias:
DEBUG: Loading PAOrder list from disk
VERBOSE: Publishing challenge for Domain xxx.xxx.com with Token hXc83dLae99UfFuLEtcQM4jseoxVILrWkUV23EO2nzA
using Plugin Selectel and DnsAlias ''.
DEBUG: Loading PAAccount list from disk
DEBUG: Calling Selectel plugin to add _acme-challenge.xxx.xxx.com TXT with value
ciTDUdz9i1BRxvJFI1T-38O5Tiq19776eZbdPVbDH8A
Submit-ChallengeValidation : Cannot bind argument to parameter 'SelectelAdminToken' because it is null.
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\4.14.0\Public\New-PACertificate.ps1:242 char:9
+         Submit-ChallengeValidation
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Submit-ChallengeValidation], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Submit-ChallengeValidation

DEBUG: Refreshing order 'xxx.xxx.com'
DEBUG: ACME Header:
{
    "kid":  "https://acme-v02.api.letsencrypt.org/acme/acct/607526636",
    "alg":  "ES256",
    "nonce":  "0002awnsXJl_BMFXhNp7GzCQpZyC06dpNPK-Ex8TLv5jODA",
    "url":  "https://acme-v02.api.letsencrypt.org/acme/order/607526636/101537792046"
}
DEBUG: ACME Payload: (empty)
DEBUG: Signing message using EC with SHA256
DEBUG: POST https://acme-v02.api.letsencrypt.org/acme/order/607526636/101537792046
{"payload":"","protected":"eyJraWQiOiJodHRwczovL2FjbWUtdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9hY2N0LzYwNzUyNjYzNiIs
ImFsZyI6IkVTMjU2Iiwibm9uY2UiOiIwMDAyYXduc1hKbF9CTUZYaE5wN0d6Q1FwWnlDMDZkcE5QSy1FeDhUTHY1ak9EQSIsInVybCI6Imh0dHBzOi8
vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL29yZGVyLzYwNzUyNjYzNi8xMDE1Mzc3OTIwNDYifQ","signature":"X9zDMs60B_leZ
lAUMR7dvSd1O5ETPU6k0DtKDw-dL1XalK5w0qR0_CcTyQfyrDjrWWAOXtlzFNYDkFnmu1DWNg"}
DEBUG: ACME Response:
{
  "status": "pending",
  "expires": "2022-07-04T12:39:48Z",
  "identifiers": [
    {
      "type": "dns",
      "value": "xxx.xxx.com"
    }
  ],
  "authorizations": [
    "https://acme-v02.api.letsencrypt.org/acme/authz-v3/124245243236"
  ],
  "finalize": "https://acme-v02.api.letsencrypt.org/acme/finalize/607526636/101537792046"
}
DEBUG: Updated nonce: 0002daI5Bpzc2wWcw8RwQ2DFWnQCimBok-trrLaopJjeDTY
DEBUG: Loading PAOrder list from disk

mshlmv avatar Jun 28 '22 07:06 mshlmv

Ok, there's the Access Denied still seeming to happen during an export of the plugin args. It seems I don't have enough debug logging enable to know exactly which line inside Export-PluginArgs is triggering the error. But my guess is it's happening when it tries to write the file to disk.

Are you using an alternate config location by any chance? Or is there any reason why the order's pluginargs.json file wouldn't be writable by the user running the module? You can find the exact folder location using (Get-PAOrder).Folder.

rmbolger avatar Jun 28 '22 18:06 rmbolger

No, the config is located by default in the user's home directory.

I guess the problem is with running the ConvertTo-SecureString module inside Posh-ACME.

If I pass an API token from stdin, like this

$pArgs = @{
    SelectelAdminToken = (Read-Host 'API key' -AsSecureString)
}

then everything works fine.

mshlmv avatar Jun 29 '22 11:06 mshlmv

No, the config is located by default in the user's home directory.

Now I run the script as a domain administrator and it works fine

Why local administrator rights are not enough for this is not clear :(

mshlmv avatar Jun 29 '22 11:06 mshlmv

The fact that it works with domain admin and not local admin is even more weird when using the default home directory location. The basic module functionality should work even with a non-admin account when writing to the home directory. Are home directories on this server mounted from a network location or synchronized with OneDrive? Something that would make them not as simple as just a folder structure on the local disk?

You could probably run some filesystem specific tests by like creating a folder in LOCALAPPDATA and trying to create a file in it.

$testdir = Join-Path $env:LOCALAPPDATA 'mytest'
if (-not (Test-Path $testdir -PathType Container)) {
    New-Item -ItemType Directory -Path $testdir -Force -EA Stop
}
'test content' | Out-File (Join-Path $testdir 'myfile.txt') -Force -EA Stop

rmbolger avatar Jun 29 '22 15:06 rmbolger

@mshlmv out of interest, what version of windows are you running and what malware/defender type protection is running? Any non-default settings you can think of?

webprofusion-chrisc avatar Jun 30 '22 03:06 webprofusion-chrisc

@rmbolger thank you for guess. I tested it and it didn't lead me to a sad result, everything works fine :)

@webprofusion-chrisc thank you for your interest. I have a version of Windows Server 2019 core, a fresh installation without additional settings, security features (firewall) are disabled. Just join in the domain, the GPO is also missing.

mshlmv avatar Jun 30 '22 10:06 mshlmv

I tested it and it didn't lead me to a sad result, everything works fine :)

By "everything", do you mean the cert issuance or just the test code I posted?

If the cert issuance still results in an error, you might try deleting the existing Posh-ACME config folder from LOCALAPPDATA and effectively starting over. If there are some non-standard ACLs on something in there that would be causing this issue, it's a quick way to reset everything. Just remember to force re-import the module if you're in the same PowerShell session after you delete the folder (Import-Module Posh-ACME -Force).

rmbolger avatar Jun 30 '22 15:06 rmbolger

@rmbolger following your last advice I get this

rm -r C:\Users\local-admin\AppData\Local\Posh-ACME\
Import-Module Posh-ACME -Force
Set-PAServer LE_STAGE
Please review the Terms of Service here: https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
$newCert = New-PACertificate $DnsName -AcceptTOS -Install -Contact $AdminCertEmail -Plugin Selectel
 -PluginArgs $pArgs
Export-PluginArgs : Access is denied.
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\4.14.0\Public\New-PAOrder.ps1:306 char:9
+         Export-PluginArgs -Order $order -PluginArgs $PluginArgs
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Export-PluginArgs], CryptographicException
    + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicException,Export-PluginArgs


cmdlet Add-DnsTxt at command pipeline position 1
Supply values for the following parameters:
SelectelAdminToken:

mshlmv avatar Jul 04 '22 10:07 mshlmv

Now I'm testing it on a non-domain server and I don't get an error, but I still get a value request for the token

cmdlet Add-DnsTxt at command pipeline position 1
Supply values for the following parameters:
SelectelAdminToken:

mshlmv avatar Jul 04 '22 12:07 mshlmv

On the non-domain server, had you setup your $pArgs variable with the proper value first? If there were no other errors, that prompt usually just means the hashtable passed to -PluginArgs is either null, empty, or has values that don't match the required parameter names for the plugin (or perhaps typo'd names).

rmbolger avatar Jul 04 '22 17:07 rmbolger

I get prompt only in LE_STAGE environment, if I enable LE_PROD, everything works fine

mshlmv avatar Jul 05 '22 06:07 mshlmv

After a series of experiments , I can draw several conclusions

On a non-domain computer, if you run Posh-ACME by connecting via SSH as a local administrator, then nothing works. If you connect via RDP by the same user, then everything works fine.

On a domain computer under a domain administrator, both SSH and RDP work. And it does not work under the local administrator in any way.

mshlmv avatar Jul 05 '22 09:07 mshlmv

I can't say I've tested using Posh-ACME via SSH against a Windows box. But I don't see why the protocol used to connect would make a difference. I don't doubt the results of your tests. I just don't have the expertise to explain why it might be happening. The differences between a domain-joined system and non-domain-joined are also a mystery.

At the end of the day, what the module is doing under the hood for the Export-PluginArgs function that is failing is just not that complicated. It's using native PowerShell functions to serialize some SecureString objects and write them to disk as JSON in the current user's LOCALAPPDATA. None of it requires UAC elevated permissions in the OS. It shouldn't require local or domain administrator permissions. Whatever is wrong feels very environment specific. Something about your OS deployment or security configuration is different than everyone else who seems to be working.

Out of curiosity, what OS version are we talking about here? What SSH server?

rmbolger avatar Jul 06 '22 16:07 rmbolger

I'm also getting this error. I'm running the script as part of an ansible playbook and it's connecting via winrm. The server is running windows 2019, domain joined, and running on AWS.

Tim-Co avatar Jul 26 '22 12:07 Tim-Co

I may have stumbled on the culprit after I started looking for other places you might get Access Denied during that function call. My current theory is that the Access Denied is actually happening on the calls to ConvertFrom-SecureString that use the Windows DPAPI libraries by default. This blog post explains a bit more about what might be going on.

https://blog.stangroome.com/2012/05/17/powershell-remoting-user-profiles-delegation-and-dpapi/

If I'm right, a workaround should be fairly simple. You'll need to enable UseAltPluginEncryption on the PAAccount object being used to request the cert. This can either be done at account creation time using New-PAAccount -UseAltPluginEncryption -AcceptTOS or after the fact using Set-PAAccount -UseAltPluginEncryption. Once that is done, you can test it by either re-writing the plugin args in your existing PAOrder (Set-PAOrder -Plugin blah -PluginArgs $pArgs) or just re-running your original New-PACertificate command.

The UseAltPluginEncryption flag basically changes how the SecureString and PSCredential variables are saved to disk that doesn't rely on DPAPI anymore.

rmbolger avatar Jul 27 '22 21:07 rmbolger

@Tim-Co or @mshlmv Were either of you able to confirm whether enabling the UseAltPluginEncryption flag fixed the access denied issues when using the module over a remote connection?

rmbolger avatar Aug 24 '22 17:08 rmbolger

I just ran into the same issue while setting up some new servers, and as above it was happening only when I was using PowerShell remoting (WinRM). -UseAltPluginEncryption fixed the issue.

CaiB avatar Mar 05 '23 22:03 CaiB

Thanks for the confirmation, @CaiB. I'm not sure if there's anything I can necessarily do in the module to avoid needing the Alt Plugin Encryption workaround. But I'll try to find a good place in the docs to mention it might be necessary when using PS remoting.

rmbolger avatar Mar 05 '23 23:03 rmbolger

Added a FAQ entry about this on the doc site.

rmbolger avatar Jun 28 '23 06:06 rmbolger