svg

Firewalld and Docker

linux Firewalld docker podman

Currently, I’ve transitioned from Docker to Podman to explore its capabilities and learn more about container management. What tasks and setups did I migrate from Docker to Podman? Here’s the list:

  • Distrobox.
  • Development dependencies: Postgres, MySQL, Redis, and other databases or tools.
  • Containers needed for testing and running various applications.

What I Love About Podman

Daemonless

Podman eliminates the need for a central, always-running daemon. Each Podman command directly interacts with the container runtime (e.g., runc) and underlying system APIs, making it lightweight and efficient.

Rootless by Default

When executed by a non-root user, Podman operates in rootless mode. This ensures that Podman does not require root privileges to manage containers. Instead, it leverages user namespaces to provide isolation and security. This is a significant advantage over other container engines like Docker.

No Automatic iptables Rules

One of my biggest issues with Docker is how it silently adds iptables rules without my knowledge. Podman, especially in rootless mode, doesn’t modify your iptables by default. This transparency and control make it my preferred choice.

{% alert(caution=true) %} Real-World Example: Security Risks with Public Wi-Fi

Imagine you run the following command while working in your office:

docker run -d -p 8181:80 docker.io/sensitive-container

This starts a container exposing port 8181 mapped to port 80 inside the container. Everything seems fine while you’re on a trusted office network. However, the next day, you connect to public Wi-Fi at an airport or coffee shop. Unless you’ve properly configured your firewall, your sensitive container is now accessible to anyone on the same public network.

Do you think something like sudo ufw deny 8181 is enough? NO!. Because docker will silently override that rule. This is why I dislike docker silently mess with the firewall.

The risks here are immense. A malicious actor could discover your exposed port and potentially exploit the container or gain access to its data. This highlights the importance of controlling Docker’s network behavior and understanding how your firewall manages traffic. {% end %}

Why I Still Need Docker

Despite my preference for Podman, there are still tools and workflows tightly coupled with Docker. For instance, a fantastic deployment tool like Kamal relies on Docker. As a result, I keep Docker installed on my system but restrict its capabilities.

Here’s an example of how I disable Docker’s firewall modifications by adding the following to daemon.json:

{
  "iptables": false,
  "ip6tables": false
}

With this configuration, Docker no longer manages iptables rules. However, this means I need to manually handle network configurations. My firewall is managed by firewalld, and one immediate issue is that containers lose internet access. For example, building a container image fails when commands like apk add or npm install can’t run due to lack of internet connectivity.

Solving Internet Connectivity for Docker Containers with Firewalld

While this issue might seem inconvenient, I appreciate the control it offers. Now, I can explicitly define firewall rules, ensuring I’m aware of what’s happening on my system. Here’s how to allow internet access for Docker containers step by step:

1. Determine the Docker Bridge Network Subnet

By default, Docker uses the docker0 bridge. First, identify the IP subnet assigned to it:

ip addr show docker0

Look for an output similar to this:

inet 172.17.0.1/16

The subnet in this example is 172.17.0.0/16.

2. Allow Masquerading for Docker Bridge Subnet

To enable internet access, firewalld needs to perform NAT (masquerading) for the containers. Run the following commands:

# Add the docker0 interface to the public firewall zone
# The public zone is restrictive yet usable for daily network connections.
sudo firewall-cmd --zone=public --add-interface=docker0 --permanent

# Enable masquerade on public zone
sudo firewall-cmd --zone=public --add-masquerade --permanent

# Allow the Docker bridge network for masquerading
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="172.17.0.0/16" masquerade' --permanent

Note: Replace 172.17.0.0/16 with your actual Docker bridge subnet if it differs.

3. Reload Firewalld

Apply the changes by reloading firewalld:

sudo firewall-cmd --reload

4. Verify the Configuration

Check if masquerading is enabled and confirm that your rules are in place:

sudo firewall-cmd --zone=public --query-masquerade
sudo firewall-cmd --zone=public --list-rich-rules

5. Test Container Connectivity

Restart your Docker containers and verify that they now have internet access. You can test this by running commands like curl or ping inside a container.

Conclusion

By disabling Docker’s automatic iptables management and configuring firewalld manually, I’ve gained greater control and transparency over my system’s network rules. While Podman has become my primary container engine due to its rootless and daemonless nature, Docker remains essential for certain workflows and tools. Managing both tools in harmony has enhanced my container management experience and improved my understanding of firewall configurations.