GTI terminates TLS in its Nginx Proxy Manager (10.0.10.100), not on the Hub host, so the Hub must be reachable on the LAN — by the proxy only. - docker-compose.prod.yml passes FORWARDED_ALLOW_IPS (uvicorn then trusts X-Forwarded-For/-Proto from the proxy only: real client IPs in audit and session records, https scheme) and WEB_COOKIE_SECURE. Defaults keep the previous behaviour. - restrict_hub_port.sh + gti-fiscal-hub-port.service: Docker-published ports bypass UFW, so the source restriction lives in DOCKER-USER, matched on the conntrack original destination with --ctdir ORIGINAL, and is re-applied whenever docker.service starts (the chain is not persisted across reboots). - *.service forced to LF like *.sh; tests cover both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
37 lines
1.8 KiB
Python
37 lines
1.8 KiB
Python
"""The Hub backup/restore scripts must actually run on the Linux host.
|
|
|
|
Both failures below were found by running the scripts on the production VM,
|
|
not by reading them.
|
|
"""
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).parents[1]
|
|
HUB = ROOT / "deploy" / "hub"
|
|
|
|
|
|
def test_shell_scripts_are_forced_to_lf():
|
|
# With core.autocrlf=true on the Windows release machine, `git archive`
|
|
# shipped CRLF and `sh` failed on the first line ("set: Illegal option -").
|
|
rules = [line.split() for line in (ROOT / ".gitattributes").read_text(encoding="utf-8").splitlines() if line.strip() and not line.startswith("#")]
|
|
assert ["*.sh", "text", "eol=lf"] in rules
|
|
assert ["*.service", "text", "eol=lf"] in rules
|
|
for script in [*HUB.glob("*.sh"), *HUB.glob("*.service")]:
|
|
assert b"\r" not in script.read_bytes(), script.name
|
|
|
|
|
|
def test_hub_port_restriction_uses_docker_user_chain():
|
|
# Docker-published ports bypass UFW/INPUT; only DOCKER-USER filters them.
|
|
# --ctdir ORIGINAL keeps the container's replies out of the DROP rule.
|
|
script = (HUB / "restrict_hub_port.sh").read_text(encoding="utf-8")
|
|
assert "DOCKER-USER" in script and "--ctdir ORIGINAL" in script and "--ctorigdstport" in script
|
|
unit = (HUB / "gti-fiscal-hub-port.service").read_text(encoding="utf-8")
|
|
assert "After=docker.service" in unit and "PartOf=docker.service" in unit
|
|
|
|
|
|
def test_backup_retention_does_not_delete_on_the_host():
|
|
# Docker creates the bind-mounted backup directory as root, so a host-side
|
|
# rm by the deploy user fails once retention kicks in.
|
|
lines = [line.strip() for line in (HUB / "backup.sh").read_text(encoding="utf-8").splitlines() if not line.strip().startswith("#")]
|
|
retention = [line for line in lines if "rm -f" in line]
|
|
assert retention and all(line.startswith("$COMPOSE exec -T postgres") for line in retention)
|