fix(config): secure-by-default loopback bind and startup-error logging
- Default `host: 127.0.0.1` in config.example.yaml; require explicit api_tokens or `allow_lan_without_auth: true` before binding LAN. - Mirror pre-uvicorn fatal errors to startup-errors.log in the config dir so silent boot failures via wscript/pythonw are diagnosable. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
+20
-11
@@ -320,44 +320,53 @@ def main():
|
||||
print("\nAuthentication is DISABLED (no tokens configured)")
|
||||
return
|
||||
|
||||
# Stderr is invisible when launched via wscript / pythonw (Start Menu shortcut,
|
||||
# autostart). Mirror pre-uvicorn failures to a file in the config dir so the
|
||||
# next silent boot failure is diagnosable.
|
||||
def _fatal(msg: str, exit_code: int = 1) -> None:
|
||||
print(msg, file=sys.stderr)
|
||||
try:
|
||||
log_path = get_config_dir() / "startup-errors.log"
|
||||
from datetime import datetime
|
||||
with open(log_path, "a", encoding="utf-8") as f:
|
||||
f.write(f"[{datetime.now().isoformat(timespec='seconds')}] {msg}\n")
|
||||
except OSError:
|
||||
pass
|
||||
sys.exit(exit_code)
|
||||
|
||||
# First-run bootstrap: if no config has ever been written, generate one
|
||||
# with a random token instead of starting in the insecure "no-auth" mode.
|
||||
config_path = get_config_dir() / "config.yaml"
|
||||
if not config_path.exists() and not settings.api_tokens:
|
||||
try:
|
||||
generate_default_config(config_path)
|
||||
print(
|
||||
_fatal(
|
||||
f"\nFirst run: generated default config at {config_path}.\n"
|
||||
"Run --show-token to retrieve the API token, then restart.",
|
||||
file=sys.stderr,
|
||||
exit_code=0,
|
||||
)
|
||||
sys.exit(0)
|
||||
except OSError as e:
|
||||
print(f"WARNING: could not bootstrap config: {e}", file=sys.stderr)
|
||||
|
||||
# Refuse to bind a non-loopback address with no tokens, unless explicitly opted in.
|
||||
non_loopback = args.host not in ("127.0.0.1", "localhost", "::1")
|
||||
if non_loopback and not settings.api_tokens and not settings.allow_lan_without_auth:
|
||||
print(
|
||||
_fatal(
|
||||
"ERROR: refusing to bind a non-loopback address with no api_tokens configured.\n"
|
||||
"Either set api_tokens in config.yaml, bind to 127.0.0.1,"
|
||||
" or set allow_lan_without_auth: true in config.yaml to override.",
|
||||
file=sys.stderr,
|
||||
" or set allow_lan_without_auth: true in config.yaml to override."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Check if port is available before starting
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
try:
|
||||
sock.bind((args.host if args.host != "0.0.0.0" else "127.0.0.1", args.port))
|
||||
except OSError:
|
||||
print(
|
||||
_fatal(
|
||||
f"ERROR: Port {args.port} is already in use. "
|
||||
f"Another instance of Media Server may be running.\n"
|
||||
f"Stop the other process or use --port to pick a different port.",
|
||||
file=sys.stderr,
|
||||
f"Stop the other process or use --port to pick a different port."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
from .tray import PYSTRAY_AVAILABLE, TrayManager
|
||||
|
||||
|
||||
Reference in New Issue
Block a user