cancel
Showing results for 
Search instead for 
Did you mean: 
TudorRaduta
Community Manager
Community Manager
  • 220 Views

Friday Superboss: The "Rootless" Nightmare

Survive This One

Happy Friday! Today we are tackling the single most confusing topic in modern RHEL administration.

We are going to deploy a containerized service. Sounds easy, right? podman run and you're done?

Wrong.

Today, you must deploy a Rootless Container that integrates fully with the host system. This changes the rules of everything: networking, file permissions, and startup behavior. If you miss one flag, it fails silently.

The Scenario: The "Secure" Web Server

You need to deploy an Nginx web server container.

The Constraints (The Nightmare):

  1. Zero Privilege: The container must run as the user web_dev. You are NOT allowed to run Podman as root/sudo.
  2. The Data: It must serve files located on the host at /home/web_dev/html.
  3. The Persistence: The container must start automatically when the server boots, even if the user web_dev never logs in.

The Map:

Rootless containers break standard rules. Use these maps to find your way back:

  • man podman-generate-systemd (Or man podman-systemd.unit for Quadlets)
  • man loginctl (The secret to reboot survival)
  • man podman-run (Look for the :Z option)

Your Architect Challenge:

You need to navigate 4 specific traps to make this work. How do you solve them?

Trap 1: The Network Block

Nginx wants to listen on port 80 inside the container. You map it to host port 80 (-p 80:80). It fails immediately "Permission Denied."

  • Why? And what port must you use on the host side to fix this without becoming root?

Trap 2: The SELinux Wall

You map the volume: -v /home/web_dev/html:/usr/share/nginx/html. The container starts, but Nginx gives a 403 Forbidden because SELinux blocked access to the host files.

  • The Fix: What one letter must you add to the end of your volume string to tell SELinux to automatically relabel the content for the container? (e.g., :?)

Trap 3: The Ghost Reboot

You generated the Systemd unit file. You enabled it with systemctl --user enable container-nginx. You reboot the server. The container does not start. It only starts when you SSH in as web_dev.

  • The Fix: What loginctl command must you run to allow this user's services to start at boot without an active session?

Trap 4: The Firewall (Bonus)

The container is running on a high port (e.g., 8080). You opened port 8080 in firewall-cmd, but external users still can't connect.

  • The Fix: Since rootless containers use slirp4netns/pasta, they don't always hit the standard tables. How do you verify the container is actually bound to the public interface?

Good luck!

4 Replies
Ad_astra
Flight Engineer Flight Engineer
Flight Engineer
  • 189 Views

Hello 

To answer the questions:

Trap 1:

The option of -p 8080:80 would allow for a non-root user to map from port 8080 on the host to the port 80 on the container. 

Ports on the host numbered from 1-1023 are priveleged ports which require root level access to bind to a process. 

Trap 2:

Adding an uppercase Z to the end of the volume mount as follows will resolve the SE Linux permissions issue:

-v /home/web_dev/html:/usr/share/nginx/html:Z

This will allow the container exclusive acccess to the bind mount. Using a lowercase z would permit other containers to share access to the bind mount. 

Trap 3:

Using loginctl enable-linger will allow for the containerized service to run on system start or reboot, even if the user is not logged in. 

Trap 4:

Running the command podman port <container-id> will show the port mapping for a running container. If a host was not specified, the container is assigned the broadcast address (0.0.0.0). This means that the container is accessible from all networks on the host machine.

It is also possible to assign a specific network on the host machine to the port mapping on a container by prefixing the port mapping with an specific network on the host. For example:

-p 192.168.0.100:8080:80 <container-id>

This would allow you to fine tune your firewall rules. 

Chetan_Tiwary_
Community Manager
Community Manager
  • 133 Views

@Ad_astra good job covering all the traps !

Architect_005
Mission Specialist
Mission Specialist
  • 53 Views

Hi
Just trying my hands to untrap these traps.

Trap 1: The Network Block

Nginx wants to listen on port 80 inside the container. You map it to host port 80 (-p 80:80). It fails immediately "Permission Denied."
Why? And what port must you use on the host side to fix this without becoming root?

**On Linux, binding to privileged ports (<1024) requires root privileges. Rootless containers cannot bind host port 80 directly.Using a non-privileged port (≥1024) on the host side, e.g. 8080.

# podman run -d -p 8080:80 nginx

Nginx still listens on port 80 inside the container, but externally you connect via `http://host:8080`.


Trap 2: The SELinux Wall

You map the volume: -v /home/web_dev/html:/usr/share/nginx/html. The container starts, but Nginx gives a 403 Forbidden because SELinux blocked access to the host files.
The Fix: What one letter must you add to the end of your volume string to tell SELinux to automatically relabel the content for the container? (e.g., :?)

**This option ':Z' makes it private relabeling (exclusive to this container).

# podman run -d -p 8080:80 -v /home/web_dev/html:/usr/share/nginx/html:Z nginx


Trap 3: The Ghost Reboot

You generated the Systemd unit file. You enabled it with systemctl --user enable container-nginx. You reboot the server. The container does not start. It only starts when you SSH in as web_dev.
The Fix: What loginctl command must you run to allow this user's services to start at boot without an active session?

**Enable lingering for the user so their services can start at boot without an active login.

# loginctl enable-linger web_dev

Hence `systemd --user` for `web_dev` will run at boot, and the container auto-starts.

Trap 4: The Firewall (Bonus)

The container is running on a high port (e.g., 8080). You opened port 8080 in firewall-cmd, but external users still can't connect.
The Fix: Since rootless containers use slirp4netns/pasta, they don't always hit the standard tables. How do you verify the container is actually bound to the public interface?

**Using anyone of the below commands can shows whether the container is listening on `0.0.0.0:8080` (public) or only `127.0.0.1:8080` (local).
#ss -ltnp | grep 8080
#podman port <container_id>

0 Kudos
Asma-Alfayyad
Flight Engineer
Flight Engineer
  • 40 Views

Trap 1: The Network Block (Privileged Ports)

podman run -p 80:80 nginx ,, Fails with Permission denied because ports < 1024 are privileged and rootless Podman containers cannot acquire this capability.

So we need to use an unprivileged host port (≥1024), like 8080:

podman run -p 8080:80 nginx

Trap 2: The SELinux Wall

-v /home/web_dev/html:/usr/share/nginx/html

Container runs, but Nginx returns 403 Forbidden,, because SELinux labels /home/web_dev/html as user_home_t, which containers are not allowed to read.

We need to add  :Z to the volume mount:

-v /home/web_dev/html:/usr/share/nginx/html:Z

To relabels the content to container_file_t , and makes it accessible only to this container

Trap 3: The Ghost Reboot (User Systemd Units)

Container does not start until web_dev logs in,,, because user systemd instances only start when the user has a login session.

So we need to enable lingering for the user:

loginctl enable-linger web_dev  

to starts the user systemd instance at boot, runs containers without an interactive login

Trap 4: The Firewall

Container is running, and port 8080 is open in firewall-cmd ,, but external clients still cannot connect.

In this case we must confirm the port is actually bound to the public interface, not just localhost.

From the host: ss -tulnp | grep 8080

     0.0.0.0:8080 or :::8080 >>>> this indicate that public
     127.0.0.1:8080 → localhost only >>>external access fails

From Podman itself: podman port nginx

if the result  as 80/tcp -> 0.0.0.0:8080  ,, so its good

If it shows 127.0.0.1, recreate the container with explicit binding:

-p 0.0.0.0:8080:80

0 Kudos
Join the discussion
You must log in to join this conversation.