PKISharp / ACME-PS

PowerShell module supporting ACME v2 certificate management
MIT License
104 stars 31 forks source link

Best way to re-use an account across requests? #110

Closed GuyPaddock closed 3 years ago

GuyPaddock commented 3 years ago

Apologies if this should be an obvious use case to implement, but I looked through the samples and didn't see any that re-use the state for multiple requests. We're running into this error when trying to renew multiple certificates because out script generates a new account key and account for each one:

AcmeHttpException: Server returned Problem (Status: 429).
Type: urn:ietf:params:acme:error:rateLimited
Error creating new account :: too many registrations for this IP: see https://letsencrypt.org/docs/rate-limits/

I tried writing a script that skips creation of the New-ACMEAccountKey and New-ACMEAccount steps if the state directory already exists, but that yields the following error:

AcmeHttpException: Server returned Problem (Status: 400).
Type: urn:ietf:params:acme:error:malformed
JWS verification error

Here's the code block that executes if we see that the state directory already exists:

    $acmeStateDir = "C:\Temp\AcmeState";

    New-ACMEState -Path $acmeStateDir;

    # Load URLs from service directory
    Get-ACMEServiceDirectory -State $acmeStateDir -ServiceName $AcmeServiceName;

    # Retrieve the first anti-replay nonce
    New-ACMENonce -State $acmeStateDir;
GuyPaddock commented 3 years ago

I was able to answer my own question with a little more fiddling... I had to eliminate the New-ACMEState call when the folder exists.

Full code snipper that works:

    If (Test-Path $acmeStateDir -PathType Container) {
        "*** Re-using existing ACME account..."

        # Load URLs from service directory
        Get-ACMEServiceDirectory -State $acmeStateDir -ServiceName $AcmeServiceName;

        # Retrieve the first anti-replay nonce
        New-ACMENonce -State $acmeStateDir;
    }
    Else {
        "*** Creating ACME account..."

        # Create the state object - will be saved to disk
        New-ACMEState -Path $acmeStateDir;

        # Load URLs from service directory
        Get-ACMEServiceDirectory -State $acmeStateDir -ServiceName $AcmeServiceName;

        # Retrieve the first anti-replay nonce
        New-ACMENonce -State $acmeStateDir;

        # Create an account key and store it to the state
        New-ACMEAccountKey -State $acmeStateDir -Force;

        # Register account key with acme service
        New-ACMEAccount -State $acmeStateDir -EmailAddresses $contactMailAddresses -AcceptTOS;
    }
glatzert commented 3 years ago

Hey. While your script is essentially correct, it does "too much".

If there's no account, you need to create one (or copy one from another machine) - take a look into the ACME-State dir after creation and you'll see it's simple structure. Since -AcceptTOS is something a human can, but a machine can't, you are supposed to do this manually once and then have some setup steps to move your account to the correct locations (but that's only how it's meant to be, if that's practical in you setup depends).

The state object (and path) saves everything you need to be able to create orders, so you can remove the IF block from your code - just do nothing in that case. E.g if you are running the first time, do the else-block, and do nothing all other times - just use the state-path or object.