Eine Tutorialreihe zum Einsatz von Podman: Anwendung, Vorteile und Funktionsweise.
Inhaltsverzeichnis
- Teil: Einleitung und Installation
- Teil: Podman CLI
- Teil: Wie funktioniert die Restart-Policy? Oder: Podman hat keinen Daemon
- Teil: Quadlets
- Teil: Updates
- Teil: Healthchecks
- Teil: Ports publishing – rootful & rootless
- Teil: Volumes & Mounts
- Teil: Netzwerk: Kommunikation zwischen Containern
- Teil: Capabilities
Einleitung
Bisher haben wir IT Tools eingerichtet, um die Konzepte rund um Podman zu verstehen. Der Vorteil von IT Tools ist dessen Einfachheit und basale Anforderungen an Podman. So konnten wir uns auf die einfacheren Konzepte fokussieren.
Für die weitere Betrachtung der Podman-Konzepte reicht IT Tools nicht mehr. Stattdessen werden wir den Reverse Proxy Caddy einrichten und ihn dazu nutzen, IT Tools über https erreichbar zu machen. Aber Achtung: Dies ist kein Caddy-Tutorial! Wir nutzen Caddy beispielhaft, um Podman-Funktionalität zu verstehen, nicht um Caddy zu lernen.
In diesem Artikel werden wir uns nochmals mit Ports beschäftigen. Bei IT Tools haben wir das Thema kurz gestreift, aber wichtige Aspekte ausgelassen. Das holen wir jetzt nach.
Ports Publishing
Eines der Hauptmerkmale von Containern ist deren Isolation. Innerhalb des Containers ist alles erreichbar, doch der Weg vom und zum Host ist blockiert. Nichts kann rein oder raus. Meist wollen wir aber keine vollständige Isolation: IT Tools ist eine Web-Applikation, die wir über unseren Browser erreichbar machen wollen. Eine vollständige Isolation würde das verunmöglichen.
Innerhalb des Containers hört IT Tools auf dem Port 8080. Wir müssen also den Container-internen Port, auf dem die Web-Applikation erreichbar ist, von aussen erreichbar machen. Das tun wir, indem wir den Port veröffentlichen (publish) und ihn einem Port des Hosts zuordnen.
Wenn wir uns den Podman-Befehl nochmals anschauen, sehen wir, dass die Option -p 8080:8080 (oder --publish 8080:8080) genau das tut:
podman run --pull always --restart unless-stopped -p 8080:8080 -d docker.io/sharevb/it-tools:latest
Der Port des Hosts 8080 wird dem Port des Containers 8080 zugeordnet. Es muss dabei nicht der gleiche Port sein, der Einfachheit halber wird es aber meist so gemacht.
Ein Blick in die Dokumentation zeigt, dass noch mehr möglich ist. Für die meisten Situationen reicht jedoch diese 1:1-Zuteilung.
# podman-run -- Podman Documentation
--publish, -p=[[ip:][hostPort]:]containerPort[/protocol]
Doppelbelegung von Ports
Wichtig ist noch, dass du einen Port nicht doppelt belegen kannst. Wenn ich also IT Tools bei mir schon mit -p 8080:8080 am Laufen habe, kann keine andere App auf diesen Host-Port zugeordnet werden. Eine andere App (nennen wir sie My-App) müsste ich dann beispielsweise mit -p 8081:8080 einrichten.
IT Tools ist jetzt mittels Browser über und My-App über erreichbar.
Privileged Ports
und sind zwar nett für einen kurzen Test, für eine Produktions-Umgebung wollen wir aber IT Tools über und die imaginäre My-App über erreichbar machen.
Dafür brauchen wir vier Dinge:
- Wir benötigen eine eigene Domain: Wir nutzen hier die Beispiel-Domain (Achtung: Verwende sie nur zu Testzwecken!). Für den produktiven Einsatz brauchst du eine eigene Domain.
- Beide Apps müssen auf den Host-Port 443 lauschen: Wenn wir einer URL keinen Port anhängen, bedeutet das, dass der Standardport verwendet wird. Bei https ist der Standardport 443. Wir müssen also den Container-Port von IT Tools und von My-App auf Port 443 mappen. Dann bekommen wir allerdings das Problem der Doppelbelegung (siehe Doppelbelegung von Ports).
- Wir benötigen ein SSL Zertifikat: Damit unsere Kommunikation mit den Apps sicher ist (dafür steht das "s" in https), wird die Kommunikation verschlüsselt. Für diese Art der Verschlüsselung benötigen wir ein SSL-Zertifikat.
- Wir benötigen DNS-Einträge, die auf die IP-Adresse unseres Container-Hosts zeigen: und müssen in die IP des Container-Hosts übersetzt werden.
DNS-Einträge (Nummer 4)
In einer Produktiv-Umgebung erreichst du Nummer 4 mittels DNS-Einträgen in deinem DNS-Server. Das ist aber kein DNS-Tutorial und würde den Rahmen dieses Artikels sprengen. Wenn du weisst, wie du DNS-Einträge erstellst und was das für Auswirkungen hat, darfst du das gerne so machen.
Wir nutzen hier eine Lösung, die für einfache Tests absolut ausreichend ist. Wir schreiben unsere Einträge in /etc/hosts. Das hat den Nachteil, dass nur die Maschine mit der bearbeiteten hosts-Datei auf die Apps zugreifen kann. Wenn du mit den Tests fertig bist, solltest du diese Einträge wieder rückgängig machen.
-
Sichere die originale hosts-Datei.
sudo cp /etc/hosts /etc/hosts.backup -
Schreibe einen Eintrag für it-tools in
/etc/hosts. (Denke daran, durch deine eigene Domain zu ersetzen, falls du eine hast.)myip=$(ip route get 1.1.1.1 | awk '{print $7; exit}') echo "$myip it-tools.example.com" | sudo tee -a /etc/hosts -
Wenn du mit den Tests fertig bist, solltest du unbedingt die ursprüngliche
hosts-Datei wiederherstellen.sudo mv /etc/hosts.backup /etc/hosts
URL, Port 443 und SSL-Zertifikat (Nummern 1-3)
Für Nummer 1-3 brauchen wir einen Reverse Proxy: Caddy.
Achtung: An dieser Stelle möchte ich nochmals klarstellen, dass dies kein Caddy-Tutorial ist. Ich werde in einem späteren Artikel eine Caddy-Konfiguration bereitstellen, die für dieses simple Szenario gerade so funktioniert. Nicht mehr und nicht weniger.
Das Doppelbelegungsproblem mit Port 443 lösen wir, indem nur noch Caddy auf Port 443 hört und intern alle Anfragen an die jeweilige App weiterleitet. Die Apps ihrerseits können wieder auf ihrem internen Port 8080 lauschen. Die korrekte Zuteilung zu den Apps macht Caddy anhand der eingegebenen URL. Und das SSL-Zertifikat für unsere Domain erledigt Caddy auch gerade.
Auf Docker-Hub finden wir wieder den Docker-Befehl, den wir an Podman anpassen müssen.
docker run -d -p 80:80 -p 443:443 -p 443:443/udp \
-v /site:/srv \
-v caddy_data:/data \
-v caddy_config:/config \
caddy caddy file-server --domain example.com
Wir ersetzen docker mit podman und setzen die vollständige URL. Für den Moment löschen wir die Zeilen mit der Option -v und alles, was nach dem Image steht. Darum werden wir uns in den nächsten Artikeln kümmern. Zu guter Letzt entfernen wir die Option -d, damit alles im Vordergrund läuft und wir sehen können, was unser Caddy gerade macht. Wenn wir jetzt alles auf eine Zeile schreiben, erhalten wir einen simplen Befehl:
podman run -p 80:80 -p 443:443 -p 443:443/udp docker.io/caddy
Wir sehen, dass wir die Option -p drei Mal aufführen. Das liegt daran, dass Caddy auf allen drei Ports (80/tcp, 443/tcp und 443/udp) hört. Folglich müssen alle drei entsprechend einem Host-Port zugeordnet werden.
Permission denied
Wenn wir unseren Podman-Befehl laufen lassen, erhalten wir eine Fehlermeldung:
Error: pasta failed with exit code 1:
Listen failed for HOST TCP port */80: Permission denied
Couldn't listen on requested ports
Ports kleiner als 1024 gelten in Linux als privilegiert, was bedeutet, dass nur der Root-User Prozesse an sie binden (also darauf lauschen lassen) kann. Dies ist eine Sicherheitseinstellung des Linux-Kernels und hat mit Podman direkt nichts zu tun. (Wir werden das Thema nochmals in einem späteren Artikel über Capabilities antreffen.)
Im folgenden Diagramm läuft App1 als rootful Container und kann daher den internen Port sowohl an Port 80, als auch an Port 8080 binden. App2 läuft als rootless Container und kann daher nur an Port 8080 binden.
flowchart LR
subgraph Host
subgraph Unprivileged["Andere Ports (1024-65535)"]
P8080("Port 8080")
end
subgraph Privileged["🔒 Privilegierte Ports (0-1023)"]
P80("Port 80")
end
subgraph "Rootful Container 🔑"
App1["App 1
(Port 8080)"]
end
subgraph "Rootless Container"
App2["App 2
(Port 8080)"]
end
end
App1 -->|"✅"| P80
App1 -->|"✅"| P8080
App2 -->|"✅"| P8080
App2 -->|"❌"| P80
Wenn du Caddy als Root (also mit sudo) startest, klappt alles wie am Schnürchen. Teste es selber:
sudo podman run -p 80:80 -p 443:443 -p 443:443/udp docker.io/caddy
Okay, "wie am Schnürchen" ist anders. Caddy gibt zwei Warnungen. Aber an dieser Stelle geht es darum, dass Podman den Container überhaupt erstellt. Das hat geklappt.
Die zwei warn-Zeilen sind erwartet, weil wir das nackte docker.io/caddy-Image ohne eigentlichen Befehl (caddy file-server --domain example.com) laufen lassen.
Doch ein Problem nach dem anderen. Zuerst kümmern wir uns um das Problem der privilegierten Ports, wenn wir Caddy rootless laufen lassen wollen.
Wie gesagt, betrifft das den Linux-Kernel, nicht Podman. Da es nicht direkt mit Podman zusammenhängt, müssen wir das Problem auch ausserhalb von Podman lösen. Mir sind folgende fünf Lösungsansätze bekannt:
- Wechsel zu rootful Containern: IT Tools, My-App und Caddy werden alle mit Root-Rechten gestartet.
- Nur Caddy wird rootful laufen gelassen, alles andere läuft rootless.
- Wir installieren Caddy traditionell auf dem Host (nicht als Container) und alles andere weiterhin als rootless Container.
- Wir definieren um, welche Ports privilegiert sind: Nur Ports kleiner als 80.
- Wir nutzen die Firewall, um eine Port-Weiterleitung zu machen.
Schauen wir uns die Lösungswege genauer an:
Wechsel zu rootful Containern
Wenn wir alle Container rootful laufen lassen, ist das Problem gelöst. Dafür läuft wieder alles mit Root-Rechten, was wiederum ein Sicherheitsproblem darstellt. Wir entscheiden uns also einfach dafür, ein Sicherheitsrisiko durch ein anderes zu ersetzen.
Fazit: Davon würde ich abraten!
Caddy rootful, alles andere rootless
Das funktioniert leider nicht. Rootful Container und rootless Container können nicht miteinander kommunizieren. Weshalb das so ist, werden wir im Artikel [Netzwerk: Kommunikation zwischen Containern]() sehen.
Fazit: Funktioniert nicht.
Caddy direkt auf dem Host, alles andere als rootless Container
Caddy nicht containerisiert auf dem Host zu installieren und die anderen Apps weiterhin als rootless Container laufen zu lassen, nimmt uns zwar alle Vorteile von Containern für Caddy, dafür behalten wir die Vorteile von rootless Containern bei den anderen Apps.
Fazit: Eine Überlegung wert.
Privilegierte Ports neu definieren
Wir definieren, dass nur Ports kleiner als 80 privilegiert sind. Jetzt kann Caddy auch als rootless Container auf die Host-Ports 80 und 443 publishen. Aber eben auch alle anderen User und Apps, die sonst keine Root-Rechte hätten. Vertraust du denen? Kannst du das neue Problem anderweitig einschränken?
Fazit: Mein Favorit Nr. 1.
Im Folgenden findest du die Befehle, die du dafür nutzen kannst:
# Temporär setzen (bis zum nächsten Reboot)
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
# Dauerhaft setzen
echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/99-unprivileged-ports.conf
sudo sysctl --system
Port-Weiterleitung mittels der Firewall
Nutze die Firewall-Funktionen des Hosts, um den Datenverkehr von privilegierten Ports auf nicht-privilegierte umzuleiten. Alles, was das Host-System auf dem Port 80 erreicht, wird auf den Port 8000 umgeleitet. Alles auf Port 443 auf Port 8443. Dies bedeutet nur minimale Konfigurationsänderungen am Host-Betriebssystem und wir behalten alle Features von rootless Containern. Den Caddy-Container lassen wir die privilegierten Ports auf die unprivilegierten Ports publishen: -p 8000:80 -p 8443:443 -p 8443:443/udp. Das einzige Problem, das ich mit dieser Lösung sehe, ist dass ein weiteres Tool zum Einsatz kommt, das du kennen musst und eventuell vergessen gehen könnte, wenn nicht genau dokumentiert.
Fazit: Mein Favorit Nr. 2.
Im Folgenden möchte ich die Konfigurationen der gängigsten Firewalls auflisten:
UFW
Zuerst wird der Traffic umgeleitet:
# In /etc/ufw/before.rules, vor den *filter-Regeln einfügen:
*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000
-A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
COMMIT
Danach wird der umgeleitete Traffic erlaubt.
sudo ufw allow 8000/tcp
sudo ufw allow 8443/tcp
sudo ufw enable
Danach UFW neu laden: sudo ufw disable && sudo ufw enable.
iptables
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
nftables
sudo vi /etc/nftables.conf
table ip nat {
chain prerouting {
type nat hook prerouting priority 0; policy accept;
# Redirect HTTP (port 80) to port 8000
tcp dport 80 redirect to 8000
# Redirect HTTPS (port 443) to port 8443
tcp dport 443 redirect to 8443
}
}
Welche Lösung wählen?
Ich habe bisher keine perfekte Lösung für dieses Problem. Wenn du eine bessere Lösung kennst, schreibe sie bitte in die Kommentare.
Ich favorisiere daher zwei Lösungen: Die Neudefinition der privilegierten Ports und die Port-Weiterleitung.
Mein Favorit Nr. 1 ist die Neudefinition der privilegierten Ports. Sie ist simpel und setzt direkt am eigentlichen Problem an. Über die Firewall kann ich dafür sorgen, dass wirklich nur diejenigen Ports von aussen zugänglich sind, die ich brauche.
Mein Favorit Nr. 2 ist die Port-Weiterleitung, da alle Sicherheitsfunktionen weiterhin intakt sind. Sie ist simpel, vor allem da meist sowieso nur die Ports 80 und 443 betroffen sind.
Aber letzten Endes musst du selber entscheiden, was für dich und deine Umstände am besten passt.
Was soll das Ganze und was macht Docker?
Wovor sollen uns privilegierte Ports überhaupt schützen? Das gängige Argument ist, dass die Ports unter 1024 alle Ports sind, die gut bekannten, kritischen Diensten zugeordnet sind. Wenn alle User alle Ports belegen dürften, könnte beispielsweise ein böswilliger User das ausnutzen und einen bösen ssh-Server (Port 22) erstellen, der alles aufzeichnet.
Mir scheint dieses Argument etwas fadenscheinig. Erstens gibt es auch kritische Dienste, die einen Standardport ab 1024 haben. Ein Beispiel ist der SIP-Server, der für Telefonie genutzt wird. Er hat den Port 5060. Zweitens gibt es Dienste, die einen Port kleiner als 1024 haben, bei denen es ziemlich nützlich wäre, wenn sie nicht mehr mit Root-Rechten laufen müssten, wie unser Beispiel ja gerade zeigt.
Andere Stimmen meinen, dass es sich hierbei lediglich um ein historisches Überbleibsel aus der alten Unix-Zeit ist. Es sei auch schon damals ein Hack gewesen und heute mehr denn je überholt. Diese Meinung scheint für mich wesentlich mehr Sinn zu ergeben.
Dieser Meinung scheint auch Docker zu sein. Denn Docker setzt automatisch net.ipv4.ip_unprivileged_port_start=0, macht also Lösung 4.
Weshalb ist also das Neudefinieren des Ports nicht mein Favorit? Weil ich kein Sicherheits-Experte bin und ich mir nicht anmasse, alle Gründe und Auswirkungen von privilegierten Ports zu kennen. Daher gehe ich den sicheren Weg mit der Lösung 5.
Fazit und Ausblick
Du kennst jetzt den ganzen Weg von einem simplen -p-Port-Mapping bis zu einer produktionsnahen Umgebung: die vier Zutaten (Domain, DNS-Eintrag, Port 443, SSL-Zertifikat), einen provisorischen /etc/hosts-Workaround für lokale Tests, und vor allem das grundlegende Problem rootless mit privilegierten Ports (
Quellen:
- «caddy – Official Image | Docker Hub». Zugriff am 4. August 2026. https://hub.docker.com/_/caddy#docker-compose-example.
- Aral Balkan. «Dear Linux, Privileged Ports Must Die». 30. August 2022. https://ar.al/2022/08/30/dear-linux-privileged-ports-must-die/.
- Carroarmato0. «Exposing Podman Containers Fully on the Network». 8. Mai 2020. https://blog.carroarmato0.be/2020/05/08/exposing-podman-container-on-the-network/.
- «Chris’s Wiki :: blog/unix/BSDRcmdsAndPrivPorts». Zugriff am 22. August 2026. https://utcc.utoronto.ca/~cks/space/blog/unix/BSDRcmdsAndPrivPorts.
- Exposing Privileged Ports with Podman – Joshua D. Boyd. o. J. Zugriff am 7. August 2026. https://blog.jdboyd.net/2024/05/exposing-privileged-ports-with-podman/.
- «ip_unprivileged_port_start | sysctl-explorer.net». Zugriff am 10. August 2026. https://sysctl-explorer.net/net/ipv4/ip_unprivileged_port_start/.
- «MacOS Mojave 10.14 no longer enforces privileged ports | Hacker News». Zugriff am 22. August 2026. https://news.ycombinator.com/item?id=18302380.
- moby. «Add Default Sysctls to Allow Ping Sockets and Privileged Ports with No Capabilities by Justincormack · Pull Request #41030 · Moby/Moby». GitHub. Zugriff am 22. August 2026. https://github.com/moby/moby/pull/41030.
- OneUptime | One Complete Observability Platform. «How to Bind Privileged Ports with Rootless Podman». 18. März 2026. https://oneuptime.com/blog/post/2026-03-18-bind-privileged-ports-rootless-podman/view.
- «podman-run — Podman documentation». Zugriff am 4. August 2026. https://docs.podman.io/en/latest/markdown/podman-run.1.html.
- Red Hat Customer Portal. «Rootless Podman Is Unable to Use Host Ports Less than 1024». 17. Mai 2024. https://access.redhat.com/solutions/7044059.
- «the persistent idiocy of ‹privileged ports› on Unix». the persistent idiocy of «privileged ports» on Unix, o. J. Zugriff am 22. August 2026. https://wibblement.blogspot.com/2021/11/the-persistent-idiocy-of-privileged.html.
