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 ce1ca25c..1943f038 100644 Binary files a/YakPanel-server/backend/app/api/__pycache__/ssl.cpython-314.pyc and b/YakPanel-server/backend/app/api/__pycache__/ssl.cpython-314.pyc differ diff --git a/YakPanel-server/backend/app/api/ssl.py b/YakPanel-server/backend/app/api/ssl.py index 178ecfea..6c866833 100644 --- a/YakPanel-server/backend/app/api/ssl.py +++ b/YakPanel-server/backend/app/api/ssl.py @@ -2,6 +2,7 @@ import os import re import shutil +import socket import subprocess import sys from fastapi import APIRouter, Depends, HTTPException @@ -12,7 +13,7 @@ from typing import Optional from app.core.database import get_db from app.core.config import get_runtime_config -from app.core.utils import environment_with_system_path, read_file, nginx_reload_all_known, nginx_binary_candidates +from app.core.utils import environment_with_system_path, exec_shell_sync, read_file, nginx_reload_all_known, nginx_binary_candidates from app.api.auth import get_current_user from app.models.user import User from app.models.site import Site, Domain @@ -125,6 +126,23 @@ def _reload_panel_and_common_nginx() -> tuple[bool, str]: return nginx_reload_all_known(timeout=60) +def _localhost_accepts_tcp(port: int, timeout: float = 2.0) -> bool: + """True if something accepts a TCP connection on this machine (checks IPv4 loopback).""" + try: + with socket.create_connection(("127.0.0.1", port), timeout=timeout): + return True + except OSError: + return False + + +def _ss_reports_listen_443() -> bool | None: + """Parse ss/netstat output; None if the probe could not run.""" + out, _ = exec_shell_sync("ss -tln 2>/dev/null || netstat -tln 2>/dev/null", timeout=5) + if not out or not out.strip(): + return None + return bool(re.search(r":443\b", out)) + + @router.get("/domains") async def ssl_domains( current_user: User = Depends(get_current_user), @@ -362,9 +380,27 @@ async def ssl_diagnostics(current_user: User = Depends(get_current_user)): "Add the include below (or symlink this directory into /etc/nginx/conf.d/)." ) - if effective_listen_443: + localhost_443_open = _localhost_accepts_tcp(443) + ss_443 = _ss_reports_listen_443() + + if not localhost_443_open and not effective_listen_443: hints.append( - "Loaded nginx configuration includes a 443 listener. If HTTPS still fails, open TCP port 443 on the OS firewall and cloud/VPS security group." + "This server is not accepting TCP on 127.0.0.1:443 — nothing is listening on 443 yet. " + "Fix nginx (listen 443 ssl + include panel vhosts) first; opening only the cloud firewall will not fix ERR_CONNECTION_REFUSED until nginx binds 443." + ) + elif effective_listen_443 and localhost_443_open: + hints.append( + "Nginx loads HTTPS and 127.0.0.1:443 accepts connections on this host. " + "If browsers off this machine still see connection refused, allow inbound TCP 443: " + "sudo ufw allow 443/tcp && sudo ufw reload (or firewalld), and your VPS Security Group / provider firewall." + ) + elif effective_listen_443 and not localhost_443_open: + hints.append( + "nginx -T reports listen 443, but connecting to 127.0.0.1:443 failed — check nginx error.log; nginx may have failed to bind (permission or address already in use)." + ) + elif localhost_443_open and not effective_listen_443: + hints.append( + "127.0.0.1:443 accepts TCP, but nginx -T from panel binaries did not show listen 443 — another process may own 443; check ss -tlnp and which nginx serves port 80." ) return { @@ -375,6 +411,8 @@ async def ssl_diagnostics(current_user: User = Depends(get_current_user)): "nginx_effective_listen_443": effective_listen_443, "panel_vhost_path_in_nginx_t": panel_include_in_effective_config, "nginx_t_probe_errors": nginx_t_errors, + "localhost_443_accepts_tcp": localhost_443_open, + "ss_reports_443_listen": ss_443, "hints": hints, } diff --git a/YakPanel-server/frontend/src/pages/DomainsPage.tsx b/YakPanel-server/frontend/src/pages/DomainsPage.tsx index 7dc1bc42..04d03294 100644 --- a/YakPanel-server/frontend/src/pages/DomainsPage.tsx +++ b/YakPanel-server/frontend/src/pages/DomainsPage.tsx @@ -25,6 +25,10 @@ interface SslDiagnostics { nginx_effective_listen_443: boolean panel_vhost_path_in_nginx_t: boolean nginx_t_probe_errors: string[] + /** Something accepted TCP when connecting to 127.0.0.1:443 from the panel process */ + localhost_443_accepts_tcp: boolean + /** ss/netstat reported :443 in listen table, or null if probe unavailable */ + ss_reports_443_listen: boolean | null hints: string[] } @@ -114,6 +118,32 @@ export function DomainsPage() { ) : null}
http block of the nginx process that serves your sites:{diag.include_snippet}
+ + The panel cannot see your cloud provider firewall. If localhost shows open but browsers off-network fail, open TCP 443 in the VPS control panel (security group) and OS firewall. +
+