Files
duffyduckandClaude Opus 5 fd76c18eb0 Wildcard-Zertifikate ueber Plesk-DNS und Let's Encrypt
Legt den A/AAAA-Record fuer einen Namen in Plesk an bzw. aktualisiert ihn und
holt anschliessend per DNS-01-Challenge ein Wildcard-Zertifikat von Let's
Encrypt. Alle Bestandteile werden einzeln abgelegt, zusaetzlich als
kombinierte bundle.pem und als passwortgeschuetzte cert.pfx.

- Plesk-Anbindung ueber die XML-API (Zone finden, Records lesen/anlegen/loeschen)
- ACME-Order mit dns-01, Account-Key wird wiederverwendet
- Propagations-Check gegen die autoritativen Nameserver der Zone, bricht vor
  der Validierung ab statt einen Fehlversuch bei Let's Encrypt zu verbrennen
- Renewal-Check: laeuft idempotent, taugt so direkt fuer den Cron
- Docker-Container, Konfiguration ueber .env, Ausgabe im Projektverzeichnis
- Tests ohne echten Plesk-Server bzw. ohne Zertifikatsausstellung

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 09:41:26 +02:00

55 lines
2.0 KiB
Python

"""Check the acme-library API usage against the real Let's Encrypt staging directory.
Fetches the directory and builds the client; account registration is stubbed out so
nothing is created on the CA side.
"""
import os
import pathlib
import sys
import tempfile
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))
os.environ.update(
PLESK_HOST="p", PLESK_API_KEY="k", ACME_EMAIL="a@b.de", CERT_PASSWORD="x",
ACME_ACCOUNT_DIR=tempfile.mkdtemp(), ACME_STAGING="true",
)
import acme, josepy
from app.config import load_config
from app import acme_client
from acme import challenges, messages
print("acme:", getattr(acme, "__version__", "?"), "josepy:", getattr(josepy, "__version__", "?"))
registered = []
acme_client.AcmeManager._register = lambda self, c: registered.append(c)
cfg = load_config(env_file="/dev/null")
m = acme_client.AcmeManager(cfg)
print("directory newOrder:", m.client.directory["newOrder"])
assert registered, "register hook not called"
assert m.account_key_file.is_file() and oct(m.account_key_file.stat().st_mode)[-3:] == "600"
# reload must reuse the very same account key
m2 = acme_client.AcmeManager(cfg)
assert m2.account_key.thumbprint() == m.account_key.thumbprint()
print("account key reused ok")
# dns-01 challenge helpers used in obtain_certificate()
chall = challenges.DNS01(token=b"0123456789abcdef0123456789abcdef")
validation = chall.validation(m.account_key)
name = chall.validation_domain_name("example.com")
assert name == "_acme-challenge.example.com", name
assert isinstance(validation, str) and len(validation) == 43, validation
resp = chall.response(m.account_key)
assert isinstance(resp, challenges.DNS01Response)
print("dns-01 helpers ok:", name, validation)
# the API surface obtain_certificate() relies on
for attr in ("new_order", "answer_challenge", "poll_and_finalize"):
assert hasattr(m.client, attr), attr
assert hasattr(messages, "STATUS_VALID")
assert hasattr(messages.NewRegistration, "from_data")
print("client API surface ok")
print("\nACME API CHECK PASSED")