diff --git a/python_video_server/README.md b/python_video_server/README.md index f8311a5..54d0eba 100644 --- a/python_video_server/README.md +++ b/python_video_server/README.md @@ -52,13 +52,23 @@ python3 -m python_video_server.letsencrypt certonly \ --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` - Type: `TXT` - Value: (a token certbot prints) 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 you@example.com \ + --domains example.com,www.example.com +``` + Certificates will be written under: - `python_video_server/certs/config/live//fullchain.pem` - `python_video_server/certs/config/live//privkey.pem` diff --git a/python_video_server/__pycache__/letsencrypt.cpython-312.pyc b/python_video_server/__pycache__/letsencrypt.cpython-312.pyc index 7d125db..709b682 100644 Binary files a/python_video_server/__pycache__/letsencrypt.cpython-312.pyc and b/python_video_server/__pycache__/letsencrypt.cpython-312.pyc differ diff --git a/python_video_server/letsencrypt.py b/python_video_server/letsencrypt.py index 0277f4f..92d0489 100644 --- a/python_video_server/letsencrypt.py +++ b/python_video_server/letsencrypt.py @@ -257,26 +257,42 @@ def cmd_certonly(args: argparse.Namespace) -> int: if challenge == "dns": # 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 = [ "certonly", "--manual", "--preferred-challenges", "dns", "--manual-public-ip-logging-ok", - "--manual-auth-hook", - str(auth_hook), - "--manual-cleanup-hook", - str(cleanup_hook), - *_common_certbot_flags( + ] + + 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", + str(auth_hook), + "--manual-cleanup-hook", + str(cleanup_hook), + ] + certbot_args += _common_certbot_flags( email=args.email, domains=domains, base_dir=base_dir, production=production, 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: certbot_args = [ "certonly", @@ -371,6 +387,12 @@ def _build_parser() -> argparse.ArgumentParser: choices=["http", "dns"], 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( "--test-cert",