Skip to content

Add support for custom ACME servers#617

Open
Sausageroll2077 wants to merge 2 commits into
linuxserver:masterfrom
Sausageroll2077:custom-acme-server
Open

Add support for custom ACME servers#617
Sausageroll2077 wants to merge 2 commits into
linuxserver:masterfrom
Sausageroll2077:custom-acme-server

Conversation

@Sausageroll2077

Copy link
Copy Markdown

linuxserver.io


  • I have read the contributing guideline and understand that I have made the correct modifications

Description:

Adds a third cert provider option alongside Let's Encrypt and ZeroSSL: a user-defined ACME server. This lets SWAG obtain and renew certificates from an internal/private ACME CA such as step-ca.

New environment variables (all only used when CERTPROVIDER=custom):

Variable Required Description
CERTPROVIDERURL yes ACME directory URL of the custom server, e.g. https://ca.example.com/acme/acme/directory. If unset, the container logs an error and sleeps.
ACMECABUNDLE no Trust an internal CA so certbot can reach the ACME server over TLS. Either a path to a CA bundle PEM mounted into the container, or the base64-encoded contents of that PEM. Written to /config/cabundle.pem and exported as REQUESTS_CA_BUNDLE.
EAB_KID / EAB_HMAC_KEY no External Account Binding credentials for servers that require EAB. Both must be set together, or neither.

Implementation notes:

  • The custom server URL is written to certbot's cli.ini server key, the same mechanism used for Let's Encrypt/ZeroSSL.
  • REQUESTS_CA_BUNDLE is exported during both initial issuance (init-certbot-config) and renewals (le-renew.sh), since it is an env var and is not persisted in cli.ini.
  • A stale /config/cabundle.pem is removed when switching back to a non-custom provider.
  • The custom provider and URL are tracked in .donoteditthisfile.conf for change-detection and revocation, so switching servers revokes against, and re-issues from, the correct ACME server.
  • EAB is optional and only consumed at account registration, so it has no effect on later renewals (matching the existing ZeroSSL behaviour).
  • Docs added to readme-vars.yml under the CERTPROVIDER option (README is auto-generated), plus a changelog entry.

Benefits of this PR and context:

Resolves request #186 for custom ACME server support. Right now there's no way to point SWAG at an internal ACME CA (step-ca, etc.). This will let you set CERTPROVIDER=custom and go. It's fully backwards compatible; nothing changes unless you opt in.

It's a fresh take on the abandoned #204, ported to the current s6-overlay v3 / s6-rc.d layout, and also wires up CA-bundle trust on the renewal path plus optional EAB support.

How Has This Been Tested?

Built the image locally and ran an end-to-end integration suite against real ACME servers on a Docker network:

step-ca (smallstep/step-ca) — issuance, trust, renewal

  • Issuance with ACMECABUNDLE provided as a file path — cert issued by the custom CA; server= set in cli.ini; /config/cabundle.pem written; no stale eab-kid.
  • Issuance with ACMECABUNDLE provided as base64 — bundle decoded and cert issued.
  • Missing CERTPROVIDERURL — container warns and does not issue.
  • Renewal via le-renew.sh: without the CA bundle the renewal fails with CERTIFICATE_VERIFY_FAILED; with it, certbot establishes a trusted connection and reaches the renewing stage — confirming REQUESTS_CA_BUNDLE is correctly exported on the cron renewal path.

Pebble (letsencrypt/pebble) with EAB required — EAB happy path

  • Correct EAB_KID + EAB_HMAC_KEY — cert issued; eab-kid/eab-hmac-key written to cli.ini; log confirms EAB registration.
  • Wrong EAB_HMAC_KEY — registration rejected by the server, no cert.
  • Only one of EAB_KID/EAB_HMAC_KEY set — container warns and does not issue.

Switching provider back from custom

  • custom (step-ca) → Let's Encrypt — the old cert is revoked against the custom CA (no TLS verify error), /config/cabundle.pem is removed, and server= is repointed to Let's Encrypt.
  • custom + EAB (Pebble) → Let's Encrypt — stale eab-kid/eab-hmac-key are cleared from cli.ini and the cabundle is removed, so Let's Encrypt registers cleanly.

All scenarios passed.

Source / References:

Allow using a custom/internal ACME server (e.g. step-ca) via
CERTPROVIDER=custom and CERTPROVIDERURL=<acme directory url>.

- ACMECABUNDLE (file path or base64 PEM) is written to
  /config/cabundle.pem and trusted via REQUESTS_CA_BUNDLE for both
  initial issuance and renewals (le-renew.sh).
- Optional External Account Binding via EAB_KID/EAB_HMAC_KEY for
  servers that require it; both must be set or neither.
- Custom provider is tracked for change-detection and revocation, so
  switching servers re-issues against the correct ACME server.

Closes linuxserver#186

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

Support custom ACME servers

2 participants