[pbs-devel] superseded: [PATCH proxmox{-backup, } v4 0/8] fix #6939: acme: support servers returning 204 for nonce requests
Samuel Rufinatscha
s.rufinatscha at proxmox.com
Thu Jan 8 12:48:43 CET 2026
https://lore.proxmox.com/pbs-devel/20260108112629.189670-1-s.rufinatscha@proxmox.com/T/#t
On 12/3/25 11:21 AM, Samuel Rufinatscha wrote:
> 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 [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, create .deb package from latest
> PBS (master) containing the refactor, install created .deb package
> (2) install Pebble 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, download and trust 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
>
> Start the Pebble server in the background:
>
> ./pebble -config ./test/config/pebble-config.json &
>
> Create 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 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 the 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 rejects as expected.
>
> ## Patch summary
>
> 0001 – acme: include proxmox-acme-api dependency
> Adds proxmox-acme-api as a new dependency for the ACME code. This
> prepares the codebase to use the shared ACME API instead of local
> implementations.
>
> 0002 – acme: drop local AcmeClient
> Removes the local AcmeClient implementation. Minimal changes
> required to support the removal.
>
> 0003 – acme: change API impls to use proxmox-acme-api handler
> Updates existing ACME API implementations to use the handlers provided
> by proxmox-acme-api.
>
> 0004 – acme: certificate ordering through proxmox-acme-api
> Perform certificate ordering through proxmox-acme-api instead of local
> logic.
>
> 0005 – acme api: add helper to load client for an account
> Introduces a helper function to load an ACME client instance for a
> given account. Required for the PBS refactor.
>
> 0006 – acme: reduce visibility of Request type
> Restricts the visibility of the internal Request type.
>
> 0007 – acme: introduce http_status module
> Adds a dedicated http_status module for handling common HTTP status
> codes.
>
> 0008 – fix #6939: acme: support servers returning 204 for nonce
> Adjusts nonce handling to support ACME servers that return HTTP 204
> (No Content) for new-nonce requests.
>
> Thanks for considering this patch series, I look forward to your
> feedback.
>
> Best,
> Samuel Rufinatscha
>
> ## Changelog
>
> Changes from v3 to v4:
>
> 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-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.
>
> [PATCH proxmox v4 2/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 3/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.
>
> 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.
>
> Changes from v2 to v3:
>
> [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 v1 to v2:
>
> [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.
>
> [1] Bugzilla report #6939:
> [https://bugzilla.proxmox.com/show_bug.cgi?id=6939](https://bugzilla.proxmox.com/show_bug.cgi?id=6939)
> [2] RFC 8555 (ACME):
> [https://datatracker.ietf.org/doc/html/rfc8555/#section-7.2](https://datatracker.ietf.org/doc/html/rfc8555/#section-7.2)
> [3] PVE’s Perl ACME client (allow 2xx codes for nonce requests):
> [https://git.proxmox.com/?p=proxmox-acme.git;a=blob;f=src/PVE/ACME.pm;h=f1e9bb7d316e3cea1e376c610b0479119217aecc;hb=HEAD#l597](https://git.proxmox.com/?p=proxmox-acme.git;a=blob;f=src/PVE/ACME.pm;h=f1e9bb7d316e3cea1e376c610b0479119217aecc;hb=HEAD#l597)
> [4] Pebble ACME server:
> [https://github.com/letsencrypt/pebble](https://github.com/letsencrypt/pebble)
> [5] Pebble ACME server (perform GET request:
> [https://git.proxmox.com/?p=proxmox-acme.git;a=blob;f=src/PVE/ACME.pm;h=f1e9bb7d316e3cea1e376c610b0479119217aecc;hb=HEAD#l219](https://git.proxmox.com/?p=proxmox-acme.git;a=blob;f=src/PVE/ACME.pm;h=f1e9bb7d316e3cea1e376c610b0479119217aecc;hb=HEAD#l219)
>
> 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-api: add helper to load client for an account
> acme: reduce visibility of Request type
> acme: introduce http_status module
> 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(-)
>
More information about the pbs-devel
mailing list