Maestro task: 180f2479-7937-425a-a6ed-450c4793cf04 (Feature 07 - Deployment, Identity and Backups).
Done-condition
deploy/systemd/mealquest-firewall.service (plus its script) installs DOCKER-USER iptables rules allowing the MealQuest host port only from 10.60.1.33 (Traefik) and the host itself and dropping all other sources, idempotently on every start, without touching NestQuest's rules. Enabled on 10.60.1.14. Evidence: curl from another LAN host fails, from the Traefik host returns 200, systemctl is-enabled reports enabled, rules survive a reboot or a systemctl restart docker.
What this adds
deploy/systemd/mealquest-firewall.sh - idempotent DOCKER-USER rules for the published host port 8090.
deploy/systemd/mealquest-firewall.service - oneshot unit, enabled at boot.
deploy/systemd/docker.service.d/mealquest-firewall.conf - docker.service drop-in re-applying the rules after a docker restart.
Why conntrack --ctorigdstport 8090
DOCKER-USER runs after Docker DNATs the destination, so by then the port is the container's 8080, not the published 8090. Matching --dport 8090 would never fire. The script matches the ORIGINAL destination port (conntrack --ctorigdstport 8090 --ctdir ORIGINAL), Docker's documented method, so the allow/deny is scoped to MealQuest only and does not disturb NestQuest's 8080/8081 rules.
Evidence (on 10.60.1.14)
iptables -S DOCKER-USER shows RETURN for 10.60.1.33/32 and 10.60.1.14/32 on original-port 8090, then DROP for all other sources on 8090; NestQuest's six 8080/8081 rules are unchanged.
From 10.60.1.33: http://10.60.1.14:8090/health -> 200. From another LAN host (10.60.1.85): timed out (curl exit 28).
A live systemctl restart docker/reboot was NOT run (this host runs other production services); docker-restart survival is covered by the drop-in, not exercised live.
Known trade-off: the drop-in ExecStartPost is fatal, so a failing script during a docker start would mark docker.service failed. A non-fatal form (ExecStartPost=-...) is possible if preferred.
Maestro task: 180f2479-7937-425a-a6ed-450c4793cf04 (Feature 07 - Deployment, Identity and Backups).
## Done-condition
deploy/systemd/mealquest-firewall.service (plus its script) installs DOCKER-USER iptables rules allowing the MealQuest host port only from 10.60.1.33 (Traefik) and the host itself and dropping all other sources, idempotently on every start, without touching NestQuest's rules. Enabled on 10.60.1.14. Evidence: curl from another LAN host fails, from the Traefik host returns 200, systemctl is-enabled reports enabled, rules survive a reboot or a systemctl restart docker.
## What this adds
- deploy/systemd/mealquest-firewall.sh - idempotent DOCKER-USER rules for the published host port 8090.
- deploy/systemd/mealquest-firewall.service - oneshot unit, enabled at boot.
- deploy/systemd/docker.service.d/mealquest-firewall.conf - docker.service drop-in re-applying the rules after a docker restart.
## Why conntrack --ctorigdstport 8090
DOCKER-USER runs after Docker DNATs the destination, so by then the port is the container's 8080, not the published 8090. Matching --dport 8090 would never fire. The script matches the ORIGINAL destination port (conntrack --ctorigdstport 8090 --ctdir ORIGINAL), Docker's documented method, so the allow/deny is scoped to MealQuest only and does not disturb NestQuest's 8080/8081 rules.
## Evidence (on 10.60.1.14)
- iptables -S DOCKER-USER shows RETURN for 10.60.1.33/32 and 10.60.1.14/32 on original-port 8090, then DROP for all other sources on 8090; NestQuest's six 8080/8081 rules are unchanged.
- From 10.60.1.33: http://10.60.1.14:8090/health -> 200. From another LAN host (10.60.1.85): timed out (curl exit 28).
- systemctl is-enabled mealquest-firewall.service -> enabled; is-active -> active. docker and nestquest-api-firewall.service remain active.
- Re-running the script twice produced no duplicate rules (diff unchanged).
- https://mealquest.cubecraftlabs.com/health -> 200.
## Not tested
- A live `systemctl restart docker`/reboot was NOT run (this host runs other production services); docker-restart survival is covered by the drop-in, not exercised live.
- Known trade-off: the drop-in ExecStartPost is fatal, so a failing script during a docker start would mark docker.service failed. A non-fatal form (ExecStartPost=-...) is possible if preferred.
Restrict the published MealQuest port 8090 in DOCKER-USER to Traefik
(10.60.1.33) and the Docker host (10.60.1.14), dropping all other
sources. DOCKER-USER sees post-DNAT traffic, so rules match the original
destination port with conntrack rather than --dport. A docker.service
drop-in re-applies the rules after every docker restart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review of the port 8090 firewall:
- Insert the three MealQuest rules at the top of DOCKER-USER, ahead of
other projects' rules. Each run deletes every rule tagged
"mealquest-firewall" and re-inserts them at positions 1-3, so re-runs
restore the order and never duplicate.
- Scope the match to MealQuest's published address with
--ctorigdst 10.60.1.14, so outbound container connections to a remote
:8090 are not matched.
- Drop the docker.service ExecStartPost drop-in, which made a firewall
error fatal to the shared Docker daemon. The unit is now PartOf= and
WantedBy= docker.service: it re-runs on every Docker start or restart
and fails visibly on its own without failing docker.service.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Maestro task: 180f2479-7937-425a-a6ed-450c4793cf04 (Feature 07 - Deployment, Identity and Backups).
Done-condition
deploy/systemd/mealquest-firewall.service (plus its script) installs DOCKER-USER iptables rules allowing the MealQuest host port only from 10.60.1.33 (Traefik) and the host itself and dropping all other sources, idempotently on every start, without touching NestQuest's rules. Enabled on 10.60.1.14. Evidence: curl from another LAN host fails, from the Traefik host returns 200, systemctl is-enabled reports enabled, rules survive a reboot or a systemctl restart docker.
What this adds
Why conntrack --ctorigdstport 8090
DOCKER-USER runs after Docker DNATs the destination, so by then the port is the container's 8080, not the published 8090. Matching --dport 8090 would never fire. The script matches the ORIGINAL destination port (conntrack --ctorigdstport 8090 --ctdir ORIGINAL), Docker's documented method, so the allow/deny is scoped to MealQuest only and does not disturb NestQuest's 8080/8081 rules.
Evidence (on 10.60.1.14)
Not tested
systemctl restart docker/reboot was NOT run (this host runs other production services); docker-restart survival is covered by the drop-in, not exercised live.Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.
Independent Codex gpt-6-sol review: APPROVED for this exact commit.