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.
執筆者紹介
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.
類似検索
チャンネル別に見る
自動化
テクノロジー、チームおよび環境に関する IT 自動化の最新情報
AI (人工知能)
お客様が AI ワークロードをどこでも自由に実行することを可能にするプラットフォームについてのアップデート
オープン・ハイブリッドクラウド
ハイブリッドクラウドで柔軟に未来を築く方法をご確認ください。
セキュリティ
環境やテクノロジー全体に及ぶリスクを軽減する方法に関する最新情報
エッジコンピューティング
エッジでの運用を単純化するプラットフォームのアップデート
インフラストラクチャ
世界有数のエンタープライズ向け Linux プラットフォームの最新情報
アプリケーション
アプリケーションの最も困難な課題に対する Red Hat ソリューションの詳細
オリジナル番組
エンタープライズ向けテクノロジーのメーカーやリーダーによるストーリー