feat: Add optional DNS hook mode for certonly

Co-authored-by: mathieumoran1 <[email protected]>
This commit is contained in:
Cursor Agent
2025-12-22 08:40:25 +00:00
co-authored by mathieumoran1
parent 02dc41d63f
commit 98f40488aa
3 changed files with 42 additions and 10 deletions
+11 -1
View File
@@ -52,13 +52,23 @@ python3 -m python_video_server.letsencrypt certonly \
--domains example.com,www.example.com --domains example.com,www.example.com
``` ```
During issuance, you'll be prompted to create a TXT record like: During issuance, certbot will prompt you to create a TXT record like:
- Name: `_acme-challenge.example.com` - Name: `_acme-challenge.example.com`
- Type: `TXT` - Type: `TXT`
- Value: (a token certbot prints) - Value: (a token certbot prints)
After it propagates, press Enter and certbot will continue. After it propagates, press Enter and certbot will continue.
If you run into a situation where the prompt/token only shows up in logs, you can try the hook-based mode:
```bash
python3 -m python_video_server.letsencrypt certonly \
--challenge dns \
--dns-hooks \
--email [email protected] \
--domains example.com,www.example.com
```
Certificates will be written under: Certificates will be written under:
- `python_video_server/certs/config/live/<primary-domain>/fullchain.pem` - `python_video_server/certs/config/live/<primary-domain>/fullchain.pem`
- `python_video_server/certs/config/live/<primary-domain>/privkey.pem` - `python_video_server/certs/config/live/<primary-domain>/privkey.pem`
+27 -5
View File
@@ -257,26 +257,42 @@ def cmd_certonly(args: argparse.Namespace) -> int:
if challenge == "dns": if challenge == "dns":
# DNS-01 works without binding any local ports; suitable for shared hosting / no root. # DNS-01 works without binding any local ports; suitable for shared hosting / no root.
_, work_dir, _ = _ensure_dirs(base_dir) #
auth_hook, cleanup_hook = _ensure_dns_hook_scripts(work_dir) # Default: use certbot's built-in interactive manual prompt (most reliable).
# Optional: --dns-hooks to use our hook scripts (useful for automation).
certbot_args = [ certbot_args = [
"certonly", "certonly",
"--manual", "--manual",
"--preferred-challenges", "--preferred-challenges",
"dns", "dns",
"--manual-public-ip-logging-ok", "--manual-public-ip-logging-ok",
]
if bool(args.dns_hooks):
_, work_dir, _ = _ensure_dirs(base_dir)
auth_hook, cleanup_hook = _ensure_dns_hook_scripts(work_dir)
certbot_args += [
"--manual-auth-hook", "--manual-auth-hook",
str(auth_hook), str(auth_hook),
"--manual-cleanup-hook", "--manual-cleanup-hook",
str(cleanup_hook), str(cleanup_hook),
*_common_certbot_flags( ]
certbot_args += _common_certbot_flags(
email=args.email, email=args.email,
domains=domains, domains=domains,
base_dir=base_dir, base_dir=base_dir,
production=production, production=production,
non_interactive=True, non_interactive=True,
), )
] else:
# IMPORTANT: manual DNS prompt requires interactive mode.
certbot_args += _common_certbot_flags(
email=args.email,
domains=domains,
base_dir=base_dir,
production=production,
non_interactive=False,
)
else: else:
certbot_args = [ certbot_args = [
"certonly", "certonly",
@@ -371,6 +387,12 @@ def _build_parser() -> argparse.ArgumentParser:
choices=["http", "dns"], choices=["http", "dns"],
help="ACME challenge type: http (standalone) or dns (manual TXT record). Default: http.", help="ACME challenge type: http (standalone) or dns (manual TXT record). Default: http.",
) )
c.add_argument(
"--dns-hooks",
action="store_true",
default=bool(int(os.environ.get("LE_DNS_HOOKS", "0"))),
help="(DNS challenge only) Use hook scripts to print token and wait. Default uses certbot's interactive manual prompt.",
)
c.add_argument("--force-renewal", action="store_true", help="Force issuance even if not close to expiry.") c.add_argument("--force-renewal", action="store_true", help="Force issuance even if not close to expiry.")
c.add_argument( c.add_argument(
"--test-cert", "--test-cert",