The most surprising thing about Podman’s security capabilities is that by default, it actually doesn’t drop privileges as much as you might expect, and understanding why is key to securing your containers.
Let’s see this in action. Imagine you have a simple container that needs to bind to a privileged port, like 80. Normally, only root can do this.
# On the host, create a dummy file
touch /tmp/hello.txt
# Build a simple container image (e.g., using a Dockerfile)
# Dockerfile:
# FROM fedora:latest
# RUN dnf install -y httpd
# COPY hello.txt /var/www/html/
# EXPOSE 80
# CMD ["httpd", "-DFOREGROUND"]
podman build -t my-httpd .
# Now, try to run it, binding to port 80
podman run -d -p 80:80 my-httpd
If you run this, you’ll likely see an error related to bind: permission denied. This is because the httpd process inside the container, even though it’s not running as root on the host, is still subject to the host’s network namespace rules.
Podman, by default, runs containers with a User Namespace. This means that the root user inside your container is mapped to an unprivileged user on the host. However, certain operations, like binding to ports below 1024, are restricted even for unprivileged users on the host unless specific capabilities are granted.
Here’s the mental model:
- User Namespaces: Podman uses user namespaces to isolate the UIDs and GIDs within the container from the host. The
rootuser inside the container (UID 0) is mapped to a non-root user on the host (oftenrootless_uid_Nfrom/etc/subuidand/etc/subgid). This is a fundamental security boundary. - Capabilities: Linux capabilities are a way to break down the all-or-nothing power of
rootinto smaller, distinct privileges. Instead of giving a process full root access, you can grant it just the specific capability it needs. For example,CAP_NET_BIND_SERVICEallows a process to bind to privileged ports (0-1023). - The Default Behavior: When you run a container with
podman run, Podman carefully selects a set of capabilities to grant to the container process. It removes most capabilities that a full root user would have. The goal is to grant only what’s necessary. - The Problem with Privileged Ports: The
httpdprocess inside our container needs to bind to port 80. This operation requires theCAP_NET_BIND_SERVICEcapability. By default, Podman does not grant this capability to containers unless explicitly told to. This is why thepermission deniederror occurs.
To fix this, you need to explicitly grant the CAP_NET_BIND_SERVICE capability to your container.
# Clean up previous attempt
podman stop $(podman ps -q)
podman rm $(podman ps -aq)
# Run the container again, granting the capability
podman run -d --cap-add NET_BIND_SERVICE -p 80:80 my-httpd
Now, if you check curl localhost:80 or curl 127.0.0.1:80, you should see the content of your hello.txt file. The --cap-add NET_BIND_SERVICE flag tells Podman to include this specific capability when setting up the container’s security environment.
This demonstrates a crucial point: Podman’s security model is about least privilege. It starts with a very restricted set of capabilities and allows you to add back only what’s absolutely necessary. You can also use --cap-drop to remove capabilities that are granted by default but not needed for your specific workload, further hardening the container.
The full list of capabilities can be found in man capabilities. Podman’s default set is usually a good starting point, but understanding the specific needs of your application is key to fine-tuning these settings. For instance, if your container needs to manipulate network interfaces, you might need CAP_NET_ADMIN. If it needs to change system time, CAP_SYS_TIME.
When you use --cap-add, Podman modifies the seccomp filter and the Linux capabilities set for the container process. It essentially tells the kernel, "This process, even though it’s running under a user namespace and is not truly root, is allowed to perform this specific privileged operation." This is a much safer approach than running the container with --privileged, which essentially disables most security restrictions.
The next logical step after securing individual capabilities is understanding how to manage Podman’s security profiles more broadly, such as with Seccomp or AppArmor.