SSL certs: Include .local hostname and local IPs in SANs

The auto-generated SSL certificate now includes:
- hostname.local for mDNS browser access
- All detected local network IPs

This fixes browser access via stegasoo.local when HTTPS is enabled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee
2026-01-06 22:15:24 -05:00
parent 90ba8543a7
commit 70da348bce

View File

@@ -7,6 +7,7 @@ Uses cryptography library (already a dependency).
import datetime import datetime
import ipaddress import ipaddress
import socket
from pathlib import Path from pathlib import Path
from cryptography import x509 from cryptography import x509
@@ -15,6 +16,33 @@ from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID from cryptography.x509.oid import NameOID
def _get_local_ips() -> list[str]:
"""Get local IP addresses for this machine."""
ips = []
try:
# Get hostname and resolve to IP
hostname = socket.gethostname()
for addr_info in socket.getaddrinfo(hostname, None, socket.AF_INET):
ip = addr_info[4][0]
if ip not in ips and not ip.startswith("127."):
ips.append(ip)
except Exception:
pass
# Also try connecting to external to get primary interface IP
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
if ip not in ips:
ips.append(ip)
s.close()
except Exception:
pass
return ips
def get_cert_paths(base_dir: Path) -> tuple[Path, Path]: def get_cert_paths(base_dir: Path) -> tuple[Path, Path]:
"""Get paths for cert and key files.""" """Get paths for cert and key files."""
cert_dir = base_dir / "certs" cert_dir = base_dir / "certs"
@@ -64,12 +92,26 @@ def generate_self_signed_cert(
x509.DNSName("localhost"), x509.DNSName("localhost"),
x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")), x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")),
] ]
# Add hostname.local for mDNS access
if not hostname.endswith(".local"):
san_list.append(x509.DNSName(f"{hostname}.local"))
# Add the hostname as IP if it looks like one # Add the hostname as IP if it looks like one
try: try:
san_list.append(x509.IPAddress(ipaddress.IPv4Address(hostname))) san_list.append(x509.IPAddress(ipaddress.IPv4Address(hostname)))
except ipaddress.AddressValueError: except ipaddress.AddressValueError:
pass pass
# Add local network IPs
for local_ip in _get_local_ips():
try:
ip_addr = ipaddress.IPv4Address(local_ip)
if x509.IPAddress(ip_addr) not in san_list:
san_list.append(x509.IPAddress(ip_addr))
except (ipaddress.AddressValueError, ValueError):
pass
now = datetime.datetime.now(datetime.timezone.utc) now = datetime.datetime.now(datetime.timezone.utc)
cert = ( cert = (
x509.CertificateBuilder() x509.CertificateBuilder()