Detect subdomains structurally to skip wildcard entries without certs
Add CUSTOM_TLDS config (HAPROXY_CUSTOM_TLDS env, default: "it.com") and _get_base_domain() for eTLD+1 detection. _check_subdomain now uses three layers: registered domains, certificate domains, and structural analysis. This ensures nocodb.inouter.com never gets a *.nocodb wildcard entry even when inouter.com has no cert or registration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ from ..config import (
|
||||
MAX_SLOTS,
|
||||
SUBPROCESS_TIMEOUT,
|
||||
CERTS_DIR,
|
||||
CUSTOM_TLDS,
|
||||
REMOTE_MODE,
|
||||
logger,
|
||||
)
|
||||
@@ -37,13 +38,43 @@ from ..db import db_load_certs
|
||||
from ..utils import parse_servers_state, disable_server_slot
|
||||
|
||||
|
||||
def _check_subdomain(domain: str, registered_domains: set[str]) -> tuple[bool, Optional[str]]:
|
||||
"""Check if a domain is a subdomain of an existing registered domain or certificate domain.
|
||||
def _get_base_domain(domain: str) -> Optional[str]:
|
||||
"""Get the base domain (eTLD+1) considering custom multi-part TLDs.
|
||||
|
||||
For example, vault.anvil.it.com is a subdomain if anvil.it.com exists.
|
||||
nocodb.inouter.com is a subdomain if inouter.com has a certificate.
|
||||
Subdomains should not have wildcard entries added to avoid conflicts,
|
||||
because wildcard certs (*.example.com) only cover one level deep.
|
||||
Examples (with CUSTOM_TLDS={"it.com"}):
|
||||
inouter.com -> inouter.com (base domain itself)
|
||||
nocodb.inouter.com -> inouter.com
|
||||
anvil.it.com -> anvil.it.com (base domain, it.com is TLD)
|
||||
gitea.anvil.it.com -> anvil.it.com
|
||||
|
||||
Returns:
|
||||
The base domain, or None if the domain is a TLD itself.
|
||||
"""
|
||||
parts = domain.split(".")
|
||||
|
||||
# Check custom multi-part TLDs first (e.g., it.com)
|
||||
for tld in CUSTOM_TLDS:
|
||||
tld_parts = tld.split(".")
|
||||
if len(parts) > len(tld_parts) and domain.endswith("." + tld):
|
||||
return ".".join(parts[-(len(tld_parts) + 1):])
|
||||
|
||||
# Standard single-part TLD (e.g., .com, .net, .org)
|
||||
if len(parts) >= 2:
|
||||
return ".".join(parts[-2:])
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _check_subdomain(domain: str, registered_domains: set[str]) -> tuple[bool, Optional[str]]:
|
||||
"""Check if a domain is a subdomain using structural analysis and known domains.
|
||||
|
||||
Uses three layers of detection:
|
||||
1. Registered domains: vault.anvil.it.com is a subdomain if anvil.it.com is registered.
|
||||
2. Certificate domains: nocodb.inouter.com is a subdomain if inouter.com has a cert.
|
||||
3. Structural analysis: nocodb.inouter.com is deeper than eTLD+1 (inouter.com).
|
||||
|
||||
Wildcard entries are skipped for subdomains because wildcard certs
|
||||
(*.example.com) only cover one level deep.
|
||||
|
||||
Args:
|
||||
domain: Domain name to check (e.g., "api.example.com").
|
||||
@@ -52,7 +83,7 @@ def _check_subdomain(domain: str, registered_domains: set[str]) -> tuple[bool, O
|
||||
Returns:
|
||||
Tuple of (is_subdomain, parent_domain or None).
|
||||
"""
|
||||
# Combine registered domains and certificate domains as known base domains
|
||||
# Check against registered domains and certificate domains
|
||||
cert_domains = set(db_load_certs())
|
||||
known_domains = registered_domains | cert_domains
|
||||
|
||||
@@ -61,6 +92,12 @@ def _check_subdomain(domain: str, registered_domains: set[str]) -> tuple[bool, O
|
||||
candidate = ".".join(parts[i:])
|
||||
if candidate in known_domains:
|
||||
return True, candidate
|
||||
|
||||
# Structural analysis: if domain is deeper than its base domain, it's a subdomain
|
||||
base = _get_base_domain(domain)
|
||||
if base and domain != base:
|
||||
return True, base
|
||||
|
||||
return False, None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user