Greetings,
I run acme scripts outside of OpnSense to get and update a wildcard certificate for my domain. I use this certificate for various services and I automate their installation via the acme post installation hooks.
I have a tool that lets me import the certificate to OpnSense using the API but, I cannot find the endpoint and required parameters to update the Administration settings to use the newly imported certificate in the Web UI. Is there an API endpoint to do this? I'm running OPNsense 25.1.12
thanks
John
Hi
I am looking for that too. Unfortunately I still have no answer.
According to the API reference(here: OPNsense API Reference (https://docs.opnsense.org/development/api/core/trust.html)), there should be the action "set", which my intuition says that it's "setting a certificate to a certain status/value". AI suggested that, in order to update an existing certificate, I should send POST request identical as the "add" one, but simply send it to https://<my_opnsense>/api/trust/cert/set/<uuid_of_the_existing_cert> . I've tried that, and many other combination like uuid field in the body or in the URL in a PHP fashion(?uuid=<uuid_of_the_existing_cert>), but nothing worked.
I am out of clues after exhausting all I could get from google and AI
What I do is just replace the existing one via the api and restart the webgui. I think it matches on description.
I have scripts to do exactly this, but I can't share them until I'm back at work in January.
Hi, thanks. I'll remind you about it :)
may I ask, are your scripts using API only, or you're doing it the PHP way?
Thx.
McCasian
Powershell scripts. Let's see how this looks
param($result)
# ----- EDIT THESE -----
$FullchainPath = '/usr/share/certify/alanplum.crt'
$PrivKeyPath = '/usr/share/certify/alanplum.key'
$DescrCommon = 'CertifyTheWeb Wildcard'
$Targets = @(
@{ hostName='hide.me.net'; key='hidden'; secret='hidden' }
)
# --------------------------------
# Ensure PowerShell 7+ for -SkipCertificateCheck
if (-not ($PSVersionTable.PSVersion.Major -ge 7)) {
throw "This script requires PowerShell 7+ for -SkipCertificateCheck. Current: $($PSVersionTable.PSVersion)"
}
# Read PEMs once; use LEAF cert
if (!(Test-Path $FullchainPath)) { throw "Fullchain not found: $FullchainPath" }
if (!(Test-Path $PrivKeyPath)) { throw "Private key not found: $PrivKeyPath" }
$allPem = (Get-Content -Raw $FullchainPath) -replace "`r`n","`n"
$LeafPem = [regex]::Match(
$allPem,
'-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----'
).Value
$KeyPem = (Get-Content -Raw $PrivKeyPath) -replace "`r`n","`n"
function Invoke-OpnAddOrUpdate {
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$HostName,
[Parameter(Mandatory)][string]$Key,
[Parameter(Mandatory)][string]$Secret,
[Parameter(Mandatory)][string]$Descr,
[Parameter(Mandatory)][string]$Leaf,
[Parameter(Mandatory)][string]$Prv
)
$base = "https://$HostName"
$pair = "${Key}:${Secret}"
$basic = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($pair))
$headers = @{
Authorization = $basic
Accept = 'application/json'
}
# 1) Search existing by description
$uuid = $null
try {
$search = Invoke-RestMethod -Method Get -Uri "$base/api/trust/cert/search" `
-Headers $headers -SkipCertificateCheck
if ($search -and $search.rows) {
$row = $search.rows | Where-Object { $_.descr -eq $Descr } | Select-Object -First 1
if ($row) { $uuid = $row.uuid }
}
} catch {
$status = $_.Exception.Response.StatusCode.value__ 2>$null
$msg = if ($_.ErrorDetails.Message) { $_.ErrorDetails.Message } else { $_.Exception.Message }
Write-Warning ("[FAIL] {0} search (HTTP {1}) {2}" -f $HostName,$status,$msg)
return
}
# 2) Build payload (PEMs via *_payload)
$payload = @{
cert = @{
action = 'import'
descr = $Descr
cert_type = 'usr_cert'
private_key_location = 'firewall'
crt_payload = $Leaf
prv_payload = $Prv
csr_payload = ''
}
} | ConvertTo-Json -Depth 8
# 3) Update if found, else add
if ($uuid) { $url = "$base/api/trust/cert/set/$uuid"; $action='UPDATED' }
else { $url = "$base/api/trust/cert/add" ; $action='ADDED' }
try {
$null = Invoke-RestMethod -Method Post -Uri $url -Headers $headers `
-ContentType 'application/json' -Body $payload -SkipCertificateCheck
Write-Host ("[OK] {0} {1}" -f $HostName,$action)
} catch {
$status = $_.Exception.Response.StatusCode.value__ 2>$null
$msg = if ($_.ErrorDetails.Message) { $_.ErrorDetails.Message } else { $_.Exception.Message }
Write-Warning ("[FAIL] {0} (HTTP {1}) {2}" -f $HostName,$status,$msg)
}
}
foreach ($t in $Targets) {
try {
$descr = if ($t.ContainsKey('descr') -and $t.descr) { $t.descr } else { $DescrCommon }
Invoke-OpnAddOrUpdate -HostName $t.hostName -Key $t.key -Secret $t.secret `
-Descr $descr -Leaf $LeafPem -Prv $KeyPem
} catch {
Write-Warning ("[FAIL] {0} unexpected error: {1}" -f $t.hostName, $_.Exception.Message)
}
}
That's was hard to get from my iPad at home :)
Hi ProximusAl
You made my day, thank you very much. I can now update the firewall certificate in place. It still does not solve changing the Web UI certificate via the API, but once the correct certificate is selected in the UI, being able to renew it in place through the API is sufficient for me.
For anyone looking for the cURL way of updating the certificate in-place, here it is:
###
POST https://opnsense.example.com/api/trust/cert/set/<cert_uuid> HTTP/1.1
Authorization: Basic {{key}}:{{secret}}
Content-Type: application/json
{"cert":
{
"action":"import",
"descr":"dummy_description",
"cert_type":"usr_cert",
"private_key_location":"firewall",
"crt_payload":"-----BEGIN CERTIFICATE-----\n[...]\n-----END CERTIFICATE-----",
"prv_payload":"-----BEGIN PRIVATE KEY-----\n[...]\n-----END PRIVATE KEY-----",
"csr_payload":""
}
}
Best regards
Casian