From cc45fac342cadbbada4f7cd3769f2f24c6988d32 Mon Sep 17 00:00:00 2001 From: Niranjan Date: Tue, 7 Apr 2026 10:47:27 +0530 Subject: [PATCH] new changes --- .../app/api/__pycache__/ssl.cpython-314.pyc | Bin 14833 -> 17355 bytes YakPanel-server/backend/app/api/ssl.py | 86 ++++++++++++------ YakPanel-server/backend/requirements.txt | 1 + 3 files changed, 61 insertions(+), 26 deletions(-) diff --git a/YakPanel-server/backend/app/api/__pycache__/ssl.cpython-314.pyc b/YakPanel-server/backend/app/api/__pycache__/ssl.cpython-314.pyc index 228b2b2529106d9e274a7ed383d6d53ab5617349..c69cef0f230ed82b3da67aa6499c08900f5a680b 100644 GIT binary patch delta 5738 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 RsVKPxKQR5xK)af1`XBO1NvZ$< delta 3328 zcma)8Yit|G5x(V-6e)@?i4-M~B2N!XqAgMHhizH5q@L8W?UDvx|zPj*Hf@!={U? z@0q?!EY+~A>ech+2NqJyUMdcfD7#X8kuJgH$vs$!&(y;ODZkR1$lUX?Z4ihiQlG(VqJ zGPD`nZ3w%ulb2E{-9}|8os+a#>Vdv!k z>~y4YNiUL5%gF^zrdD+2MsOjF0_c2BNznvKwk%D_G%dC9J1R5}l%FRq4!YC=}no|;P} zz;4!rY)?Zsd}5IOqo&GQICAEytp}aX+L+Zi(7Y_eg6_*Vbf@N<(dULsbAm10%gDhA^gF(g)tVYU_x^hQKMtcf}C zDsGjWb9~>Hyu~3oSCAVY*yZ3iHnZAnWq_++u~zC*wwnf{_!44xDHd!Bkss~$MMLFBD&+A zxOe;F#Rm3#b1+gGFKv9NlUl%8#xd@U5;w(5nXjqEQWh^8Z9xKLe{BxC8^8zAe3y#t7Ps>+S;nZwa z)r2M$C`JILIweyg<9R_0w}W_J}L#bI@eA?xy2<5u8f`7*n@Xc}C78a#Yrxa5U+(oSBgmFt<)*T83Wv zK4NTy$Ic^pS{~1;DHy}W_P*cr_l9n?0%nLE$#U` zS$eP7led-TZDn~|N#5o&(2=(V^EQ9p=7r{^;#Z6FHX(0wnVe%S)GQ z?i2;{?y^f~Up;%*-8#7DZoQm(eeTK}G(f&8-gWh?yLxWB`tG`vHTVr2T{|{@_t?z( zv6;1_vupjzMSjCfoWrIKp0hje+t|yye+4nMdr#ler}qyKuB7@tM{qf=;8p%k*Wue; zNA9?uUHj7UHQVuf4*y+8{ko%mo66d@@eLj=|Fv1gd3QfhufaZ@uNTAQsu$qR;VQuE z0iacKy?dw`s<&#$=q~eHwLz2zDwd$SQBFo<<{JUjz0omP0o6@M|hRpADppSQ*y?|?$Fk-z85z3&`uGejWza{heYZ{|CDVvC<50z<2hJHRD@TxO62TJt13)$aX0OJ>N)R2!%+b8nD08h&Fp9=Ryd`8oMa)V?i- zEsBcwxG1eaTqu;jg8nhj*p?(&RbUo56}1=()@^C!RN?kpkn&U`kd2%}wSXa{6g#$Z z3P0u)=sJO#%CwS{G+Cu@g4)O2YT1E065Od5B_QocCK82J_2S${^v2lO;PIVaDVK{S zvs8|yBu$! str: ) +async def _le_hostnames_for_domain_row(db: AsyncSession, dom_row: Optional[Domain], primary: str) -> list[str]: + """All distinct hostnames for the site (for -d flags). Falls back to primary.""" + if not dom_row: + return [primary] if primary else [] + result = await db.execute(select(Domain).where(Domain.pid == dom_row.pid).order_by(Domain.id)) + rows = result.scalars().all() + seen: set[str] = set() + out: list[str] = [] + for d in rows: + n = (d.name or "").strip() + if not n: + continue + key = n.lower() + if key not in seen: + seen.add(key) + out.append(n) + if primary and primary.lower() not in seen: + out.insert(0, primary) + return out if out else ([primary] if primary else []) + + def _reload_panel_and_common_nginx() -> None: """Reload nginx so new vhost (ACME path) is live before certbot HTTP-01.""" cfg = get_runtime_config() @@ -189,41 +211,53 @@ async def ssl_request_cert( if not prefix: raise HTTPException(status_code=500, detail=_certbot_missing_message()) - cmd = prefix + [ - "certonly", - "--webroot", - "-w", - webroot_norm, - "-d", - dom, + hostnames = await _le_hostnames_for_domain_row(db, dom_row, dom) + + base_flags = [ "--non-interactive", "--agree-tos", "--email", body.email, - "--preferred-challenges", - "http", "--no-eff-email", ] - env = environment_with_system_path() - try: - proc = subprocess.run( - cmd, - capture_output=True, - text=True, - timeout=180, - env=env, - ) - except FileNotFoundError: - raise HTTPException(status_code=500, detail=_certbot_missing_message()) from None - except subprocess.TimeoutExpired: - raise HTTPException(status_code=500, detail="certbot timed out (180s)") from None + cmd_webroot = prefix + ["certonly", "--webroot", "-w", webroot_norm, *base_flags] + for h in hostnames: + cmd_webroot.extend(["-d", h]) + cmd_webroot.extend(["--preferred-challenges", "http"]) - if proc.returncode != 0: - msg = (proc.stderr or proc.stdout or "").strip() or f"certbot exited with code {proc.returncode}" + cmd_nginx = prefix + ["certonly", "--nginx", *base_flags] + for h in hostnames: + cmd_nginx.extend(["-d", h]) + + env = environment_with_system_path() + proc: subprocess.CompletedProcess[str] | None = None + last_err = "" + for cmd, label in ((cmd_webroot, "webroot"), (cmd_nginx, "nginx")): + try: + proc = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=300, + env=env, + ) + except FileNotFoundError: + raise HTTPException(status_code=500, detail=_certbot_missing_message()) from None + except subprocess.TimeoutExpired: + raise HTTPException(status_code=500, detail="certbot timed out (300s)") from None + if proc.returncode == 0: + break + chunk = (proc.stderr or proc.stdout or "").strip() or f"exit {proc.returncode}" + last_err = f"[{label}] {chunk}" + + if proc is None or proc.returncode != 0: + msg = last_err or "certbot failed" hint = ( - " Check: DNS A/AAAA for this domain points to this server; port 80 is reachable; " - "the website is enabled in YakPanel; nginx on port 80 loads this site’s vhost (same server as panel nginx if used)." + " Webroot and nginx plugins both failed. Check: " + "DNS A/AAAA for every -d name points to this server; port 80 reaches the nginx that serves these hosts; " + "site is enabled; install python3-certbot-nginx if the nginx method reports a missing plugin. " + "If you use a CDN proxy, pause it or use DNS validation instead." ) raise HTTPException(status_code=500, detail=(msg + hint)[:8000]) diff --git a/YakPanel-server/backend/requirements.txt b/YakPanel-server/backend/requirements.txt index 0dee7631..6b52c6c1 100644 --- a/YakPanel-server/backend/requirements.txt +++ b/YakPanel-server/backend/requirements.txt @@ -22,6 +22,7 @@ celery>=5.3.0 # Let's Encrypt (optional if system certbot/snap not used; enables python -m certbot from panel venv) certbot>=3.0.0 +certbot-nginx>=3.0.0 # Utils psutil>=5.9.0