From 5a1ee389dc1ee949744530c48e842f4a019e933f Mon Sep 17 00:00:00 2001 From: dev_agent Date: Mon, 5 Oct 2026 17:47:36 -0400 Subject: [PATCH] Keep MealQuest firewall rules first and off docker.service 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 --- .../docker.service.d/mealquest-firewall.conf | 3 -- deploy/systemd/mealquest-firewall.service | 12 +++++- deploy/systemd/mealquest-firewall.sh | 38 +++++++++++-------- 3 files changed, 34 insertions(+), 19 deletions(-) delete mode 100644 deploy/systemd/docker.service.d/mealquest-firewall.conf diff --git a/deploy/systemd/docker.service.d/mealquest-firewall.conf b/deploy/systemd/docker.service.d/mealquest-firewall.conf deleted file mode 100644 index afe3355..0000000 --- a/deploy/systemd/docker.service.d/mealquest-firewall.conf +++ /dev/null @@ -1,3 +0,0 @@ -# Re-apply the MealQuest DOCKER-USER rules after every docker (re)start. -[Service] -ExecStartPost=/usr/local/sbin/mealquest-firewall diff --git a/deploy/systemd/mealquest-firewall.service b/deploy/systemd/mealquest-firewall.service index fd98254..c7c5f8e 100644 --- a/deploy/systemd/mealquest-firewall.service +++ b/deploy/systemd/mealquest-firewall.service @@ -1,7 +1,17 @@ +# Applies the MealQuest DOCKER-USER rules (/usr/local/sbin/mealquest-firewall). +# +# PartOf= re-runs this unit whenever docker.service is stopped or restarted, +# and WantedBy=docker.service starts it whenever docker.service starts. Both +# are one-way: a failure here fails this unit visibly and never fails +# docker.service. +# +# Start MealQuest only after this unit has succeeded: +# systemctl start mealquest-firewall && docker compose up -d [Unit] Description=Restrict MealQuest port 8090 to Traefik (DOCKER-USER) After=docker.service Requires=docker.service +PartOf=docker.service [Service] Type=oneshot @@ -9,4 +19,4 @@ RemainAfterExit=yes ExecStart=/usr/local/sbin/mealquest-firewall [Install] -WantedBy=multi-user.target +WantedBy=multi-user.target docker.service diff --git a/deploy/systemd/mealquest-firewall.sh b/deploy/systemd/mealquest-firewall.sh index ca0ff66..361668c 100755 --- a/deploy/systemd/mealquest-firewall.sh +++ b/deploy/systemd/mealquest-firewall.sh @@ -1,21 +1,29 @@ #!/bin/sh -# Restrict the MealQuest published host port to Traefik (10.60.1.33) and the -# Docker host itself (10.60.1.14); drop every other source. +# Restrict the MealQuest published host port (10.60.1.14:8090) to Traefik +# (10.60.1.33) and the Docker host itself (10.60.1.14); drop every other +# source. # -# DOCKER-USER runs after Docker's DNAT, so the destination port there is the -# container port (8080), not the published one. Match the original -# destination port with conntrack instead of --dport. +# DOCKER-USER runs after Docker's DNAT, so the destination there is the +# container address and port, not the published ones. Match the original +# destination address and port with conntrack instead of -d/--dport. # -# Idempotent: each rule is checked (-C) before it is added, so re-runs add -# nothing. Installed as /usr/local/sbin/mealquest-firewall. +# The rules must sit at the top of DOCKER-USER, ahead of any other project's +# rules (an earlier RETURN or DROP would decide MealQuest traffic first). +# Every run deletes all rules tagged with the mealquest-firewall comment and +# re-inserts them at positions 1-3, so re-runs leave the same ordered rules +# and no duplicates. Installed as /usr/local/sbin/mealquest-firewall. set -eu -port=8090 -match="-p tcp -m conntrack --ctorigdstport $port --ctdir ORIGINAL" +tag="-m comment --comment mealquest-firewall" +match="-m conntrack --ctorigdst 10.60.1.14 --ctorigdstport 8090 --ctdir ORIGINAL" -iptables -C DOCKER-USER -s 10.60.1.33/32 $match -j RETURN 2>/dev/null || - iptables -I DOCKER-USER 1 -s 10.60.1.33/32 $match -j RETURN -iptables -C DOCKER-USER -s 10.60.1.14/32 $match -j RETURN 2>/dev/null || - iptables -I DOCKER-USER 2 -s 10.60.1.14/32 $match -j RETURN -iptables -C DOCKER-USER $match -j DROP 2>/dev/null || - iptables -A DOCKER-USER $match -j DROP +rules=$(iptables -w -S DOCKER-USER) +printf '%s\n' "$rules" | while read -r _ chain spec; do + case " $spec " in + *" --comment mealquest-firewall "*) iptables -w -D "$chain" $spec ;; + esac +done + +iptables -w -I DOCKER-USER 1 -s 10.60.1.33/32 $match $tag -j RETURN +iptables -w -I DOCKER-USER 2 -s 10.60.1.14/32 $match $tag -j RETURN +iptables -w -I DOCKER-USER 3 $match $tag -j DROP