Using a Let's Encrypt Certificate on iDRAC (WIP)

Trust is important for a CA, and a CA that makes a mistake should be permanently untrusted. That should have been the case for StartCom, but the Mozilla Foundation was too lenient. Anyways, I cannot bring myself to keep using them and have been slowly transitioning to Let's Encrypt which got much more usable lately.

For one, the Let's Encrypt client doesn't run anywhere; there is simply no way you could run it on the iDRAC server, or at least no easy way. With ACME support though that's another story. You can generate certs on another machine and validate yourself via a special DNS TXT record, then upload the cert to the target machine.

This time around I decided to play with the ACMESharp, which is a PowerShell module. I went the PowerShell route because I was too lazy to install racadm on a Linux server and that gave me an excuse to try it out. Turns out the library is still kind of a work in progress and the DNS challenge has to be done manually. Or can be scripted.

First, we install ACMESharp: Install-Module -Name ACMESharp -AllowClobber. Next, create a new messy script (depending on experience or lack of thereof).

Import-Module -Name ACMESharp

$domain = ""
$alias = "idrac$(Get-Date -Format yyy-MM-dd--HH-mm)"

$certname = "idrac$(Get-Date -Format yyy-MM-dd--HH-mm)"
$keyfile = "C:\Admin\Certs\$certname.key"
$crtfile = "C:\Admin\Certs\$certname.crt"
$pfxfile = "C:\Admin\Certs\$certname.pfx"

$racIp = ""
$racUser = "root"
$racPwd = "calvin"

# initialization
if((Get-ACMEVault) -eq $null) {  
    New-ACMERegistration -Contacts mailto:[email protected] -AcceptTos
if((Get-ACMEIdentifier | Where-Object {$_.Alias -eq $alias}).Count -eq 0) {  
    New-ACMEIdentifier -Dns $domain -Alias $alias

# challenge
$challenge = Complete-ACMEChallenge $alias -ChallengeType dns-01 -Handler manual
$recordName = ($challenge.Challenges | Where-Object {$_.Type -eq "dns-01"}).Challenge.RecordName
$recordValue = ($challenge.Challenges | Where-Object {$_.Type -eq "dns-01"}).Challenge.RecordValue

# update cloudflare
$CFHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$CFHeaders.Add("X-Auth-Email", "[email protected]")
$CFHeaders.Add("X-Auth-Key", "<cf-auth-key>")

$zone = Invoke-RestMethod -Uri<zone-id>/dns_records?per_page=100 -Method Get -ContentType "application/json" -Headers $CFHeaders
$CFRecord = $zone.result | where {$ -eq $recordName}

function UpdateDNSRecord($record, $content) {  
    if($record.content -eq $content) { return }
    $Data = @{
    $JsonData = $Data | ConvertTo-Json
    $Uri = '<zone-id>/dns_records/' + $
    Invoke-RestMethod -Uri $Uri -Method Put -ContentType "application/json" -Headers $CFHeaders -Body $JsonData
function CreateDNSRecord($name, $content) {  
    $Data = @{
    $JsonData = $Data | ConvertTo-Json
    $Uri = '<zone-id>/dns_records'
    Invoke-RestMethod -Uri $Uri -Method Post -ContentType "application/json" -Headers $CFHeaders -Body $JsonData

if($CFRecord.Count -eq 0) {  
    CreateDNSRecord $recordName $recordValue
else {  
    UpdateDNSRecord $CFRecord $recordValue

# submit challenge response
Submit-ACMEChallenge $alias -ChallengeType dns-01

# status
(Update-ACMEIdentifier $alias -ChallengeType dns-01).Challenges | Where-Object {$_.Type -eq "dns-01"}
Update-ACMEIdentifier $alias

# request certificate
New-ACMECertificate $alias -Generate -Alias $certname  
Submit-ACMECertificate $certname  
Update-ACMECertificate $certname

# export key
Get-ACMECertificate $certname -ExportKeyPEM $keyfile

# export cert
Get-ACMECertificate $certname -ExportCertificatePem $crtfile

# upload to idrac
racadm -r $racIp -u $racUser -p $racPwd sslkeyupload -t 1 -f $keyfile  
racadm -r $racIp -u $racUser -p $racPwd sslcertupload -t 1 -f $crtfile  

Now, automated renewal seems to still be a work in progress for ACMESharp, so I have resorted to using unique names for the aliases as they recommend doing for now, and having this script run every 89 days since LE certs expire after 90 days. So I'm calling my script a WIP for now too since I'll have to wait 90 days to see if it actually works fine...

Also I really should consider learning how to write PowerShell modules, with the number of scripts in which I'm using the CloudFlare API...