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>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""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")
|
||||
Reference in New Issue
Block a user