[pbs-devel] [PATCH proxmox{-backup, } 0/8] fix #6939: acme: support servers returning 204 for nonce requests
Samuel Rufinatscha
s.rufinatscha at proxmox.com
Tue Dec 2 16:56:51 CET 2025
Hi,
this series fixes account registration for ACME providers that return
HTTP 204 No Content to the newNonce request. Currently, both the PBS
ACME client and the shared ACME client in proxmox-acme only accept
HTTP 200 OK for this request. The issue was observed in PBS against a
custom ACME deployment and reported as bug #6939 [1].
## Problem
During ACME account registration, PBS first fetches an anti-replay
nonce by sending a HEAD request to the CA’s newNonce URL.
RFC 8555 §7.2 [2] states that:
* the server MUST include a Replay-Nonce header with a fresh nonce,
* the server SHOULD use status 200 OK for the HEAD request,
* the server MUST also handle GET on the same resource and may return
204 No Content with an empty body.
The reporter observed the following error message:
*ACME server responded with unexpected status code: 204*
and mentioned that the issue did not appear with PVE 9 [1]. Looking at
PVE’s Perl ACME client [3], it uses a GET request instead of HEAD and
accepts any 2xx success code when retrieving the nonce. This difference
in behavior does not affect functionality but is worth noting for
consistency across implementations.
## Approach
To support ACME providers which return 204 No Content, the Rust ACME
clients in proxmox-backup and proxmox need to treat both 200 OK and 204
No Content as valid responses for the nonce request, as long as a
Replay-Nonce header is present.
This series changes the expected field of the internal Request type
from a single u16 to a list of allowed status codes
(e.g. &'static [u16]), so one request can explicitly accept multiple
success codes.
To avoid fixing the issue twice (once in PBS’ own ACME client and once
in the shared Rust client), this series first refactors PBS to use the
shared AcmeClient from proxmox-acme / proxmox-acme-api, similar to PDM,
and then applies the bug fix in that shared implementation so that all
consumers benefit from the more tolerant behavior.
## Testing
*Testing the refactor*
To test the refactor, I
(1) installed latest stable PBS on a VM
(2) created .deb package from latest PBS (master), containing the
refactor
(3) installed created .deb package
(4) installed Pebble from Let's Encrypt from Let's Encrypt [5] on the
same VM
(5) created an ACME account and ordered the new certificate for the
host domain.
Steps to reproduce:
(1) install latest stable PBS on a VM, created .deb package from latest
PBS (master) containing the refactor, install created .deb package
(2) install Pebble from Let's Encrypt from Let's Encrypt [5] on the
same VM:
cd
apt update
apt install -y golang git
git clone https://github.com/letsencrypt/pebble
cd pebble
go build ./cmd/pebble
then, downloaded and trusted the Pebble cert:
wget https://raw.githubusercontent.com/letsencrypt/pebble/main/test/certs/pebble.minica.pem
cp pebble.minica.pem /usr/local/share/ca-certificates/pebble.minica.crt
update-ca-certificates
We want Pebble to perform HTTP-01 validation against port 80, because
PBS’s standalone plugin will bind port 80. Set httpPort to 80.
nano ./test/config/pebble-config.json
Started the Pebble server in the background:
./pebble -config ./test/config/pebble-config.json &
Created a Pebble ACME account:
proxmox-backup-manager acme account register default admin at example.com \
--directory 'https://127.0.0.1:14000/dir'
To verify persistence of the account I checked
ls /etc/proxmox-backup/acme/accounts
Verified if update-account works
proxmox-backup-manager acme account update default --contact "a at example.com,b at example.com"
proxmox-backup-manager acme account info default
In the PBS GUI, you can create a new domain. You can use your host
domain name (see /etc/hosts). Select the created account and order the
certificate.
After a page reload, you might need to accept the new certificate in the browser.
In the PBS dashboard, you should then see the new Pebble certificate.
*Note: on reboot, the created Pebble ACME account will be gone and you
will need to create a new one. Pebble does not persist account info.
In that case remove your previously created account in
/etc/proxmox-backup/acme/accounts.
*Testing the newNonce fix*
To prove the ACME newNonce fix, I put nginx in front of Pebble, to
intercept the newNonce request in order to return 204 No Content
instead of 200 OK, all other requests are unchanged and forwarded to
Pebble. Requires trusting the nginx CAs via
/usr/local/share/ca-certificates + update-ca-certificates on the VM.
Then I ran following command against nginx:
proxmox-backup-manager acme account register proxytest root at backup.local --directory 'https://nginx-address/dir
The account could be created successfully. When adjusting the nginx
configuration to return any other non-expected success status code,
PBS expectely rejects.
## Patch summary
[PATCH proxmox-backup v4 1/4] acme: include proxmox-acme-api dependency
[PATCH proxmox-backup v4 2/4] acme: drop local AcmeClient
[PATCH proxmox-backup v4 3/4] acme: change API impls to use proxmox-acme-api handlers
[PATCH proxmox-backup v4 4/4] acme: certificate ordering through proxmox-acme-api
[PATCH proxmox v4 1/4] acme: reduce visibility of Request type
[PATCH proxmox v4 2/4] acme: introduce http_status module
[PATCH proxmox v4 3/4] acme-api: add helper to load client for an account
[PATCH proxmox v4 4/4] fix #6939: support servers returning 204 for newNonce
Thanks for considering this patch series, I look forward to your
feedback.
Best,
Samuel Rufinatscha
## Changes from v1:
[PATCH proxmox v2 1/1] fix #6939: support providers returning 204 for nonce
requests
* Introduced `http_success` module to contain the http success codes
* Replaced `Vec<u16>` with `&[u16]` for expected codes to avoid
allocations.
* Clarified the PVEs Perl ACME client behaviour in the commit message.
[PATCH proxmox-backup v2 1/1] acme: accept HTTP 204 from newNonce endpoint
* Integrated the `http_success` module, replacing `Vec<u16>` with `&[u16]`
* Clarified the PVEs Perl ACME client behaviour in the commit message.
## Changes from v2:
[PATCH proxmox v3 1/1] fix #6939: support providers returning 204 for nonce
requests
* Rename `http_success` module to `http_status`
[PATCH proxmox-backup v3 1/1] acme: accept HTTP 204 from newNonce endpoint
* Replace `http_success` usage
## Changes from v3:
Removed: [PATCH proxmox-backup v3 1/1].
Added:
[PATCH proxmox-backup v4 1/4] acme: include proxmox-acme-api dependency
* New: add proxmox-acme-api as a dependency and initialize it in
PBS so PBS can use the shared ACME API instead.
[PATCH proxmox-backup v4 2/4] acme: drop local AcmeClient
* New: remove the PBS-local AcmeClient implementation and switch PBS
over to the shared proxmox-acme async client.
[PATCH proxmox-backup v4 3/4] acme: change API impls to use proxmox-acme-api
handlers
* New: rework PBS’ ACME API endpoints to delegate to
proxmox-acme-api handlers instead of duplicating logic locally.
[PATCH proxmox-backup v4 4/4] acme: certificate ordering through
proxmox-acme-api
* New: move PBS’ ACME certificate ordering logic over to
proxmox-acme-api, keeping only certificate installation/reload in
PBS.
[PATCH proxmox v4 1/4] acme: reduce visibility of Request type
* New: hide the low-level Request type and its fields behind
constructors / reduced visibility so changes to “expected” no longer
affect the public API as they did in v3.
[PATCH proxmox v4 2/4] acme: introduce http_status module
* New: split out the HTTP status constants into an internal
http_status module as a separate preparatory cleanup before the bug
fix, instead of doing this inline like in v3.
[PATCH proxmox v4 3/4] acme-api: add helper to load client for an account
* New: add a load_client_with_account helper in proxmox-acme-api so
PBS (and others) can construct an AcmeClient for a configured account
without duplicating boilerplate.
Changed:
[PATCH proxmox v3 1/1] -> [PATCH proxmox v4 4/4]
fix #6939: acme: support server returning 204 for nonce requests
* Rebased on top of the refactor: keep the same behavioural fix as in v3
(accept 204 for newNonce with Replay-Nonce present), but implement it
on top of the http_status module that is part of the refactor.
proxmox-backup:
Samuel Rufinatscha (4):
acme: include proxmox-acme-api dependency
acme: drop local AcmeClient
acme: change API impls to use proxmox-acme-api handlers
acme: certificate ordering through proxmox-acme-api
Cargo.toml | 3 +
src/acme/client.rs | 691 -------------------------
src/acme/mod.rs | 5 -
src/acme/plugin.rs | 336 ------------
src/api2/config/acme.rs | 407 ++-------------
src/api2/node/certificates.rs | 240 ++-------
src/api2/types/acme.rs | 98 ----
src/api2/types/mod.rs | 3 -
src/bin/proxmox-backup-api.rs | 2 +
src/bin/proxmox-backup-manager.rs | 2 +
src/bin/proxmox-backup-proxy.rs | 1 +
src/bin/proxmox_backup_manager/acme.rs | 21 +-
src/config/acme/mod.rs | 51 +-
src/config/acme/plugin.rs | 99 +---
src/config/node.rs | 29 +-
src/lib.rs | 2 -
16 files changed, 103 insertions(+), 1887 deletions(-)
delete mode 100644 src/acme/client.rs
delete mode 100644 src/acme/mod.rs
delete mode 100644 src/acme/plugin.rs
delete mode 100644 src/api2/types/acme.rs
proxmox:
Samuel Rufinatscha (4):
acme: reduce visibility of Request type
acme: introduce http_status module
acme-api: add helper to load client for an account
fix #6939: acme: support servers returning 204 for nonce requests
proxmox-acme-api/src/account_api_impl.rs | 5 +++++
proxmox-acme-api/src/lib.rs | 3 ++-
proxmox-acme/src/account.rs | 27 +++++++++++++-----------
proxmox-acme/src/async_client.rs | 8 +++----
proxmox-acme/src/authorization.rs | 2 +-
proxmox-acme/src/client.rs | 8 +++----
proxmox-acme/src/lib.rs | 6 ++----
proxmox-acme/src/order.rs | 2 +-
proxmox-acme/src/request.rs | 25 +++++++++++++++-------
9 files changed, 51 insertions(+), 35 deletions(-)
Summary over all repositories:
25 files changed, 154 insertions(+), 1922 deletions(-)
--
Generated by git-murpp 0.8.1
More information about the pbs-devel
mailing list