docs: add Docker/firewalld bypass diagram to firewall-and-network.md

Visualizes the documented gap: pfSense always applies, firewalld only
covers native services, Docker-published ports skip straight past it.
This commit is contained in:
thesabear committed 2026-08-26 13:02:55 +03:00
1 parent 033140f65d
commit 656da0e81c
2 files changed
+6

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="822px" height="705px" viewBox="0 0 822 705" id="ge-svg-45Po1F0wK5Lgy7woHj7E" content="&lt;mxfile host=&quot;embed.diagrams.net&quot; scale=&quot;1&quot; border=&quot;0&quot;&gt;&#10; &lt;diagram name=&quot;Docker Firewalld Bypass&quot; id=&quot;docker-bypass-1&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;1178&quot; dy=&quot;659&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;1100&quot; pageHeight=&quot;700&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;traffic&quot; parent=&quot;1&quot; style=&quot;shape=cloud;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;&quot; value=&quot;Incoming Traffic&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry height=&quot;90&quot; width=&quot;200&quot; x=&quot;440&quot; y=&quot;30&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;pfsense&quot; parent=&quot;1&quot; style=&quot;shape=mxgraph.basic.shield;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;&quot; value=&quot;pfSense&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:11px&amp;quot;&amp;gt;Layer 1 — always applies&amp;lt;/font&amp;gt;&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry height=&quot;120&quot; width=&quot;120&quot; x=&quot;480&quot; y=&quot;170&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;bgfill_host&quot; parent=&quot;1&quot; style=&quot;rounded=1;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=none;&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry height=&quot;300&quot; width=&quot;820&quot; x=&quot;140&quot; y=&quot;360&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;host&quot; parent=&quot;1&quot; style=&quot;swimlane;whiteSpace=wrap;html=1;startSize=30;fillColor=none;strokeColor=#666666;dashed=1;fontSize=12;fontStyle=1;fontColor=#333333;align=left;spacingLeft=8;verticalAlign=middle;&quot; value=&quot;TARGET HOST — everything below this line is decided by what&amp;#39;s running on it&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry height=&quot;300&quot; width=&quot;820&quot; x=&quot;140&quot; y=&quot;360&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;firewalld&quot; parent=&quot;host&quot; style=&quot;shape=mxgraph.basic.shield;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;&quot; value=&quot;firewalld&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:11px&amp;quot;&amp;gt;Layer 2 — native services only&amp;lt;/font&amp;gt;&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry height=&quot;110&quot; width=&quot;110&quot; x=&quot;80&quot; y=&quot;70&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;native&quot; parent=&quot;host&quot; style=&quot;rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;&quot; value=&quot;Native Service&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:11px&amp;quot;&amp;gt;e.g. Alloy, Wazuh agent&amp;lt;/font&amp;gt;&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry height=&quot;65&quot; width=&quot;190&quot; x=&quot;40&quot; y=&quot;210&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;docker&quot; parent=&quot;host&quot; style=&quot;rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;&quot; value=&quot;Docker-published port&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:11px&amp;quot;&amp;gt;dozens of containerized apps&amp;lt;/font&amp;gt;&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry height=&quot;65&quot; width=&quot;220&quot; x=&quot;550&quot; y=&quot;210&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;e3&quot; edge=&quot;1&quot; parent=&quot;host&quot; source=&quot;firewalld&quot; style=&quot;html=1;endArrow=block;strokeColor=#6c8ebf;strokeWidth=2;rounded=1;&quot; target=&quot;native&quot;&gt;&#10; &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;e1&quot; edge=&quot;1&quot; parent=&quot;1&quot; soLine truncated

After

Width:  |  Height:  |  Size: 386 KiB

+2
View File
@@ -62,6 +62,8 @@ rule family="ipv4" source address="[automation host]" port port="[this host's SS
> [!WARNING]
> **A real, documented gap**: on Docker hosts, container-published ports bypass firewalld entirely — Docker's own iptables rules are evaluated before firewalld's zone rules, so firewalld's local restrictions only actually protect the small number of *native*, non-containerized services on that host (things like the observability agent from [`docs/observability.md`](observability.md)), not the dozens of containerized apps in [`stacks/`](../stacks/). The network-level segmentation above still applies regardless — pfSense doesn't care whether a port is opened by firewalld or by Docker's own NAT rules — so this is a defense-in-depth gap specifically at the host layer, not an open-to-the-world one. Worth naming precisely rather than glossing over: firewalld protects less of a Docker host's actual attack surface than it might first appear to.
![Two enforcement layers per packet — pfSense always applies, firewalld only catches native services, Docker-published ports skip straight past it](../assets/firewall-docker-bypass.svg)
## Edge firewall: three jobs, one device
One device, three distinct responsibilities, not three separate boxes: