rmbolger / Posh-ACME

PowerShell module and ACME client to create certificates from Let's Encrypt (or other ACME CA)
https://poshac.me/docs/latest/
MIT License
778 stars 190 forks source link

Pull cert into local certificate store using FQDN and Subsequent renew #541

Closed fthobe closed 6 months ago

fthobe commented 8 months ago

Hey,

first thank you so much for your help, I explored how to get the FQDN of the windows machine to make your script from the active domain LE certificate blog article work also on non DC hosts. First things first, I wanted to add the script below in case others might need it.

Two questions came up:

  1. I saw -Force does not delete the old cert despite being written in the documentation
  2. Is there any reason why not to use force and just throw the first script into the task manager?
  3. Your renewal code contains DC variables as well, I tried to modify them, could you take a look? I feel insecure about the process of obtaining the name.

thanks for helping on my experiment.

Fabian

Script to obtain SSL certificate from let's encrypt using posh-acme for AD joined computers for the personal cert store

 # Cloud Flare requires a simple API token, but we need to secure the string to keep it safe
$token = ConvertTo-SecureString 'thisisverysecret' -AsPlainText -Force
$pArgs = @{CFToken=$token}

# The default certificate password is "poshacme", but we prefer some extra security. At this point you could also just use the CF token. 
$CertPass = 'thisissomehowsecret'

# To get the correct hostname independent of features installed, use System.Net.Dns.
$certNames = @([System.Net.Dns]::GetHostEntry([string]"localhost").HostName)

# This notification email is contacted if the certificate is close to expiration date.
$notifyEmail = 'dev@domain.app'

$certParams = @{
    Domain = $certNames
    PfxPass = $CertPass
    DnsPlugin = 'Cloudflare'
    PluginArgs = $pArgs
    AcceptTOS = $true
    Install = $true
    Contact = $notifyEmail  # optional
    Verbose = $true         # optional
}

New-PACertificate @certParams

Renewal

$taskname = "Renew LE Certificate"
$taskdesc = "Renews the Let's Encrypt certificate installed on this domain controller."
$actionArg = '-C "Start-Transcript $env:LOCALAPPDATA\cert-renewal.log -Append; $name=([System.Net.Dns]::GetHostEntry([string]"localhost").HostName); $oldCert=gci Cert:\LocalMachine\My | ?{ $_.Subject -eq \"CN=$name\" } | sort -d NotAfter | select -f 1; if (Submit-Renewal -Verbose) { $oldCert | ri; Restart-Service ADWS } Stop-Transcript; exit $LASTEXITCODE"'
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $actionArg
$trigger =  New-ScheduledTaskTrigger -Daily -At 2am -RandomDelay (New-TimeSpan -Minutes 30)
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 30)
Register-ScheduledTask $taskname -Action $action -Trigger $trigger -User 'System' -Settings $settings -Desc $taskdesc
rmbolger commented 8 months ago

I saw -Force does not delete the old cert despite being written in the documentation

Where did you see in the documentation that -Force deletes the old cert? It does not and never has. That parameter is only related to the ACME order object. The function normally tries to re-use an existing pending order that if one exists that has not been completed. But -Force tells it to ignore any existing orders and create a new one from scratch. It shouldn't be necessary to use on a normal basis.

The solution you've got in your task for cleaning up old certs should work just fine.

Is there any reason why not to use force and just throw the first script into the task manager?

Don't use -Force. The reason you want to use Submit-Renewal in your scheduled task instead of your original script is that it will only take action when the previous order has reached the renewal window. If New-PACertificate is directly in your task, it will be renewing the cert every time the task runs which you don't want.

Your renewal code contains DC variables as well, I tried to modify them, could you take a look? I feel insecure about the process of obtaining the name.

Obtaining the DNS name for a machine is tricky because it's not guaranteed that the DNS name matches what the computer thinks its own name is. But for domain joined Windows systems, I think your code should generally work. On non-domain joined machines, it may only return a short hostname rather than an FQDN. It also doesn't work on Linux if that matters to you.

fthobe commented 8 months ago

Hey, thank you for the clarification. I think I misunderstood an error message referencing -force then :)

I am aware that the solution works only for domain joined windows devices, I figured that limitation was easier to handle than the DC one as all servers we used are domain joined, but not all are DCs.

rmbolger commented 8 months ago

Submit-Renewal has a -Force option that can be used to ignore the suggested renewal window. Just don't forget to take it out when you're done testing.

The cert doesn't technically need to be in the cert store to use the functions in Posh-ACME.Deploy. And they'll also add it for you if necessary. But yes, you can use those functions to deploy the resulting cert in the services they support.

fthobe commented 7 months ago

Hey, getting back to the -Force discussion on New-PACertificate:

PS C:\Windows\system32> certificatePulllocalhost.ps1
VERBOSE: Updating directory info from https://acme-v02.api.letsencrypt.org/directory
VERBOSE: Using ACME Server https://acme-v02.api.letsencrypt.org/directory
VERBOSE: Using account 1525528706
VERBOSE: Order name not specified, using 'mi-dc-pr-1.gmservice.app'
VERBOSE: Using existing order 'mi-dc-pr-1.gmservice.app' with status valid
VERBOSE: Updating plugin args for plugin(s) Cloudflare
WARNING: This certificate order has already been completed. Use -Force to overwrite the current certificate.

I traced it down to "New-PACertificate.ps1", can you help me to understand the warning better? For me overwrite means replace the old one.

rmbolger commented 7 months ago

New-PACertificate always tries to use an existing order if it can find one that matches the specified parameters instead of creating a new one from scratch every time. This reduces your chances of being rate limited. The warning is basically letting you know that it cancelled the operation you requested because the cert you wanted already exists. So if you actually did want to replace the current one, you need to specify -Force to bypass the warning. It's effectively the same thing that happens with Submit-Renewal when the renewal window on the previous cert hasn't been reached yet but without the warning in that case since that is the expected result.