My previous article demonstrated that a socket-activated container can use an activated socket to serve the internet even when the network is disabled through the option --network=none
for podman run
. This article takes this idea one step further by also restricting internet access for Podman and its helper programs such as conmon and the OCI runtime.
To follow along with the examples, you must have:
- Podman 3.4.0 or above
- runc 1.1.3 or crun 1.5 or above
- container-selinux 2.183.0 or above (if you're using SELinux)
When using Podman in a systemd service, the systemd directive RestrictAddressFamilies can be used to restrict Podman's access to sockets. This restriction concerns only the use of the system call socket()
, meaning that socket-activated sockets are unaffected by the directive.
[ Download now: Podman basics cheat sheet ]
Podman can still run containers that need internet access only through socket-activated sockets when systemd is configured to restrict Podman's ability to use the system call socket()
for the AF_INET and AF_INET6 socket families. Podman would then be blocked from pulling any container images, so the container image must be present beforehand.
Restrict a socket-activated echo server
You can probably see how RestrictAddressFamilies
with a simple echo server container supports socket activation.
If the --pull=never
option is added to podman run
, the echo server container continues to work even with the very restricted setting:
RestrictAddressFamilies=AF_UNIX AF_NETLINK
All use of the system call socket()
is then disallowed except for the AF_UNIX sockets and AF_NETLINK sockets.
If there were a security vulnerability in Podman, conmon, or the OCI runtime, this configuration limits the avenues an intruder has to attempt to launch attacks on other devices on the network.
Create the systemd unit files
Create the container:
$ podman pull \
-q ghcr.io/eriksjolund/socket-activate-echo
6f68cb020b4b04e7d124df6c1bc60547e44b987223ac93d00515cc1412a7bc9a
$ podman create --rm --name restricted-echo \
--network=none --pull=never ghcr.io/eriksjolund/socket-activate-echo
f1f8ecd129c33012dac0be81dfdbd2a269295d322fd1ffd818fe8672ee0238ee
Generate the systemd service unit:
$ mkdir -p ~/.config/systemd/user
$ podman generate systemd --name \
--new restricted-echo > ~/.config/systemd/user/restricted-echo.service
Add the following two lines in the [Service]
section:
$ sed -i '/\[Service\]/a \
RestrictAddressFamilies=AF_UNIX AF_NETLINK\
NoNewPrivileges=yes' ~/.config/systemd/user/restricted-echo.service
Add the following two lines in the [Unit]
section:
$ sed -i '/\[Unit\]/a \
After=podman-usernamespace.service\
Requires=podman-usernamespace.service' ~/.config/systemd/user/restricted-echo.service
Create the file ~/.config/systemd/user/restricted-echo.socket
and paste this into it:
[Unit]
Description=restricted echo server
[Socket]
ListenStream=127.0.0.1:9000
[Install]
WantedBy=sockets.target
Create the file ~/.config/systemd/user/podman-usernamespace.service
with these contents:
[Unit]
Description=podman-usernamespace.service
[Service]
Type=oneshot
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman unshare /bin/true
RemainAfterExit=yes
Test the echo server
After editing the unit files, reload systemd:
$ systemctl --user daemon-reload
Next, start the socket unit:
$ systemctl --user start restricted-echo.socket
Finally, test the echo server with the program socat
:
$ echo hello | socat - tcp4:127.0.0.1:9000
hello
The echo server works as expected! It replies with hello after receiving the text hello.
Podman is blocked from establishing new connections to the internet, but everything works as expected because Podman is configured to not attempt to pull the container image.
[ Thinking about security? Check out this guide to boosting hybrid cloud security and protecting your business. ]
Now modify the service unit so that Podman always pulls the container image:
$ grep -- --pull= ~/.config/systemd/user/restricted-echo.service
--pull=never ghcr.io/eriksjolund/socket-activate-echo
$ sed -i s/pull=never/pull=always/ ~/.config/systemd/user/restricted-echo.service
$ grep -- --pull= ~/.config/systemd/user/restricted-echo.service
--pull=always ghcr.io/eriksjolund/socket-activate-echo
After editing the unit file, systemd needs to reload its configuration:
$ systemctl --user daemon-reload
Stop the service:
$ systemctl --user stop restricted-echo.service
Test the echo server with the program socat
:
$ echo hello | socat - tcp4:127.0.0.1:9000
As expected, the service fails because Podman is blocked from establishing a connection to the container registry.
Use journalctl
to see the related error message:
$ journalctl --user -xe -u restricted-echo.service | grep -A2 "Trying to pull" | tail -3
Jul 16 08:26:10 asus podman[28272]: Trying to pull ghcr.io/eriksjolund/socket-activate-echo:latest...
Jul 16 08:26:10 asus podman[28272]: Error: initializing source docker://ghcr.io/eriksjolund/socket-activate-echo:latest: pinging container registry ghcr.io: Get "https://ghcr.io/v2/": dial tcp 140.82.121.34:443: socket: address family not supported by protocol
Jul 16 08:26:10 asus systemd[10686]: test.service: Main process exited, code=exited, status=125/n/a
The service and socket are marked as failed
:
$ systemctl --user is-failed restricted-echo.service
failed
$ systemctl --user is-failed restricted-echo.socket
failed
Revert the change and use --pull=never
instead:
$ sed -i s/pull=always/pull=never/ \
~/.config/systemd/user/restricted-echo.service
$ systemctl --user daemon-reload
$ systemctl --user reset-failed restricted-echo.service
$ systemctl --user reset-failed restricted-echo.socket
$ systemctl --user start restricted-echo.socket
Use a separate service for creating the user namespace
Consider a situation where systemd starts the systemd user services for a user directly after a reboot. Assume that lingering has been enabled for the user with loginctl enable-linger USERNAME
and that the user is not logged in. The Podman systemd user service that starts first detects that the Podman user namespace is missing and tries to create it. This normally succeeds, but when RestrictAddressFamilies
is used together with rootless Podman, it fails.
The reason is that using RestrictAddressFamilies
in an unprivileged systemd user service implies NoNewPrivileges=yes. This prevents /usr/bin/newuidmap
and /usr/bin/newgidmap
from running with elevated privileges. Podman executes newuidmap
and newgidmap
to set up user namespace. Both executables normally run with elevated privileges, as they need to perform operations not available to an unprivileged user. These capabilities are:
$ getcap /usr/bin/newuidmap
/usr/bin/newuidmap cap_setuid=ep
$ getcap /usr/bin/newgidmap
/usr/bin/newgidmap cap_setgid=ep
You just need to set up the user namespace once because the created user namespace is reused for all other invocations of Podman. You can make services using RestrictAddressFamilies
or NoNewPrivileges=yes
work by configuring them to start after a systemd user service that is responsible for setting up the user namespace.
For instance, the unit restricted-echo.service
depends on podman-usernamespace.service
:
$ grep podman-usernamespace.service ~/.config/systemd/user/restricted-echo.service
After=podman-usernamespace.service
Requires=podman-usernamespace.service
The service podman-usernamespace.service
is a Type=oneshot
service that executes podman unshare /bin/true
. This command is normally used for other things, but a side effect of the command is that it sets up the user namespace.
[ Improve your skills managing and using SELinux with this helpful guide. ]
Enable the socket unit and reboot:
$ systemctl --user enable restricted-echo.socket
$ sudo reboot
After the reboot, test the echo server with the program socat
:
$ echo hello | socat - tcp4:127.0.0.1:9000
hello
The echo server works as expected even after a reboot!
Note that using the systemd directive RestrictAddressFamilies
to restrict Podman is probably not strictly supported in Podman, as it's not mentioned in Podman documentation.
Wrap up
The systemd directive RestrictAddressFamilies
provides a way to restrict network access for Podman and its helper programs, while a socket-activated echo server container can still serve the internet. One use case for this is running a socket-activated web server container so that Podman and its helper programs run with few privileges. If they are compromised due to a security vulnerability, the intruder would gain few privileges, having fewer opportunities to use the compromise as a starting point for attacks on other devices.
About the author
Erik Sjölund enjoys learning and discovering new things, especially within container technologies. He holds a master's degree in Engineering Physics and has worked as a Linux sysadmin and software developer, especially in the field of life sciences.
Browse by channel
Automation
The latest on IT automation for tech, teams, and environments
Artificial intelligence
Updates on the platforms that free customers to run AI workloads anywhere
Open hybrid cloud
Explore how we build a more flexible future with hybrid cloud
Security
The latest on how we reduce risks across environments and technologies
Edge computing
Updates on the platforms that simplify operations at the edge
Infrastructure
The latest on the world’s leading enterprise Linux platform
Applications
Inside our solutions to the toughest application challenges
Original shows
Entertaining stories from the makers and leaders in enterprise tech
Products
- Red Hat Enterprise Linux
- Red Hat OpenShift
- Red Hat Ansible Automation Platform
- Cloud services
- See all products
Tools
- Training and certification
- My account
- Customer support
- Developer resources
- Find a partner
- Red Hat Ecosystem Catalog
- Red Hat value calculator
- Documentation
Try, buy, & sell
Communicate
About Red Hat
We’re the world’s leading provider of enterprise open source solutions—including Linux, cloud, container, and Kubernetes. We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.
Select a language
Red Hat legal and privacy links
- About Red Hat
- Jobs
- Events
- Locations
- Contact Red Hat
- Red Hat Blog
- Diversity, equity, and inclusion
- Cool Stuff Store
- Red Hat Summit