VYPR
Moderate severityNVD Advisory· Published Jul 30, 2025· Updated Jul 30, 2025

Moby's Firewalld reload makes published container ports accessible from remote hosts

CVE-2025-54388

Description

Moby is an open source container framework developed by Docker Inc. that is distributed as Docker Engine, Mirantis Container Runtime, and various other downstream projects/products. In versions 28.2.0 through 28.3.2, when the firewalld service is reloaded it removes all iptables rules including those created by Docker. While Docker should automatically recreate these rules, versions before 28.3.3 fail to recreate the specific rules that block external access to containers. This means that after a firewalld reload, containers with ports published to localhost (like 127.0.0.1:8080) become accessible from remote machines that have network routing to the Docker bridge, even though they should only be accessible from the host itself. The vulnerability only affects explicitly published ports - unpublished ports remain protected. This issue is fixed in version 28.3.3.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Docker/Moby fails to restore iptables rules after firewalld reload, exposing localhost-only container ports to remote networks.

In Moby versions 28.2.0 through 28.3.2, when the firewalld service is reloaded, it removes all iptables rules, including those created by Docker. While Docker is designed to recreate these rules automatically, versions before 28.3.3 fail to restore the specific rules that block external access to containers with ports published to localhost (e.g., 127.0.0.1:8080) [1].

This vulnerability can be exploited without authentication if an attacker has network routing to the Docker bridge. After a firewalld reload, containers that should only be accessible from the host become reachable from remote machines. The attack surface is limited to explicitly published ports; unpublished ports remain protected [1].

An attacker who successfully exploits this flaw can gain unauthorized network access to services running in containers that were intended to be host-only, potentially leading to data exposure or further compromise. The impact is contingent on the attacker's network position and the services exposed [1].

The issue has been fixed in Moby version 28.3.3. The fix ensures that iptables rules for per-endpoint rules are properly restored after a firewalld reload, as demonstrated in the pull request [3] and associated commit [4]. Users are advised to upgrade to the patched version.

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/docker/dockerGo
>= 28.2.0, < 28.3.328.3.3

Affected products

2

Patches

1
bea959c7b793

Merge pull request #50506 from robmry/backport-28.x/fix_firewalld_reload

https://github.com/moby/mobyRob MurrayJul 25, 2025via ghsa
2 files changed · +31 3
  • integration/networking/bridge_linux_test.go+22 3 modified
    @@ -185,6 +185,8 @@ func TestBridgeICC(t *testing.T) {
     				Force: true,
     			})
     
    +			networking.FirewalldReload(t, d)
    +
     			pingHost := tc.pingHost
     			if pingHost == "" {
     				if tc.isLinkLocal {
    @@ -319,6 +321,7 @@ func TestBridgeINC(t *testing.T) {
     			defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{
     				Force: true,
     			})
    +			networking.FirewalldReload(t, d)
     
     			ctr1Info := container.Inspect(ctx, t, c, id1)
     			targetAddr := ctr1Info.NetworkSettings.Networks[bridge1].IPAddress
    @@ -457,6 +460,7 @@ func TestBridgeINCRouted(t *testing.T) {
     
     	for _, fwdPolicy := range []string{"ACCEPT", "DROP"} {
     		networking.SetFilterForwardPolicies(t, firewallBackend, fwdPolicy)
    +		networking.FirewalldReload(t, d)
     		t.Run(fwdPolicy, func(t *testing.T) {
     			for _, tc := range testcases {
     				t.Run(tc.name+"/v4/ping", func(t *testing.T) {
    @@ -574,6 +578,8 @@ func TestRoutedAccessToPublishedPort(t *testing.T) {
     			)
     			defer network.RemoveNoError(ctx, t, c, routedNetName)
     
    +			networking.FirewalldReload(t, d)
    +
     			// With docker-proxy disabled, a container can't normally access a port published
     			// from a container in a different bridge network. But, users can add rules to
     			// the DOCKER-USER chain to get around that limitation of docker's iptables rules.
    @@ -823,6 +829,7 @@ func TestInternalNwConnectivity(t *testing.T) {
     		container.WithNetworkMode(bridgeName),
     	)
     	defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true})
    +	networking.FirewalldReload(t, d)
     
     	execCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
     	defer cancel()
    @@ -1000,9 +1007,10 @@ func TestNoIP6Tables(t *testing.T) {
     	ctx := setupTest(t)
     
     	testcases := []struct {
    -		name        string
    -		option      string
    -		expIPTables bool
    +		name            string
    +		option          string
    +		reloadFirewalld bool
    +		expIPTables     bool
     	}{
     		{
     			name:        "ip6tables on",
    @@ -1013,10 +1021,18 @@ func TestNoIP6Tables(t *testing.T) {
     			name:   "ip6tables off",
     			option: "--ip6tables=false",
     		},
    +		{
    +			name:            "ip6tables off with firewalld reload",
    +			option:          "--ip6tables=false",
    +			reloadFirewalld: true,
    +		},
     	}
     
     	for _, tc := range testcases {
     		t.Run(tc.name, func(t *testing.T) {
    +			if tc.reloadFirewalld {
    +				skip.If(t, !networking.FirewalldRunning(), "firewalld is not running")
    +			}
     			ctx := testutil.StartSpan(ctx, t)
     
     			d := daemon.New(t)
    @@ -1039,6 +1055,9 @@ func TestNoIP6Tables(t *testing.T) {
     			id := container.Run(ctx, t, c, container.WithNetworkMode(netName))
     			defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true})
     
    +			if tc.reloadFirewalld {
    +				networking.FirewalldReload(t, d)
    +			}
     			var cmd *exec.Cmd
     			if d.FirewallBackendDriver(t) == "nftables" {
     				cmd = exec.Command("nft", "list", "table", "ip6", "docker-bridges")
    
  • libnetwork/drivers/bridge/port_mapping_linux.go+9 0 modified
    @@ -792,11 +792,20 @@ func releasePortBindings(pbs []portBinding, fwn firewaller.Network) error {
     func (n *bridgeNetwork) reapplyPerPortIptables() {
     	n.Lock()
     	var allPBs []portBinding
    +	var allEPs []*bridgeEndpoint
     	for _, ep := range n.endpoints {
     		allPBs = append(allPBs, ep.portMapping...)
    +		allEPs = append(allEPs, ep)
     	}
     	n.Unlock()
     
    +	for _, ep := range allEPs {
    +		netip4, netip6 := ep.netipAddrs()
    +		if err := n.firewallerNetwork.AddEndpoint(context.TODO(), netip4, netip6); err != nil {
    +			log.G(context.TODO()).Warnf("Failed to reconfigure Endpoint: %s", err)
    +		}
    +	}
    +
     	if err := n.firewallerNetwork.AddPorts(context.Background(), mergeChildHostIPs(allPBs)); err != nil {
     		log.G(context.TODO()).Warnf("Failed to reconfigure NAT: %s", err)
     	}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.