Skip to content

Rootless bridge: preserve source IPs via pesto/pasta#28478

Open
Honny1 wants to merge 4 commits into
containers:mainfrom
Honny1:pesto-support
Open

Rootless bridge: preserve source IPs via pesto/pasta#28478
Honny1 wants to merge 4 commits into
containers:mainfrom
Honny1:pesto-support

Conversation

@Honny1
Copy link
Copy Markdown
Member

@Honny1 Honny1 commented Apr 9, 2026

Require passta version:

This requires the pesto binary, available since passt-0^20260507.g1afd4ed.

Local Passt Setup + testing

# Build pasta with pesto support
git clone git://passt.top/passt 
cd passt
git checkout ec96f0124282 
b4 shazam https://archives.passt.top/passt-dev/20260506092241.1607480-1-sbrivio@redhat.com/
make 
sudo make install

cd ../podman
make binaries
make localintegration FOCUS_FILE=run_networking_test.go

TODO:

  • Proper vendor container-libs
  • Rebase + drop commit after merging removal of slirp (Podman6: Remove slirp #27828)
  • Documentation
  • Deal with the strict pasta version question.

Add rootless_port_forwarder="pasta" option in containers.conf that switches rootless bridge port forwarding from rootlessport to pasta, preserving source IPs. The default remains rootlessport.

Problem

When running rootless containers on a bridge network (podman run -p 8080:80 --network mynet), the old rootlessport userspace TCP/UDP proxy destroyed source IP information. Every connection appeared to come from 127.0.0.1 inside the container, regardless of the actual client IP.

Solution

Pesto is a client tool for passta that dynamically updates port forwarding rules via a UNIX domain socket. Instead of proxying traffic in userspace (which loses source IPs), pesto configures pasta to forward at the kernel level using splice (localhost) or TAP (external traffic), preserving the original source IP.

How it works

Host -> pasta (splice/TAP) -> rootless netns -> netavark bridge DNAT -> container
  1. A shared pasta instance runs in the rootless network namespace with a control socket (-c pasta.sock)
  2. On container start: netavark sets up the bridge + nftables DNAT rules, then pesto --add registers ports in pasta's forwarding table
  3. On container stop: pesto --delete removes the container's ports, then netavark tears down bridge/DNAT
  4. On network connect/disconnect/reload: port forwarding state is updated accordingly

Key implementation details:

  • HostIP stripping in the netavark wrapper (convertNetOpts): when rootless_port_forwarder="pasta" is active, HostIP is stripped from port mappings before passing to netavark. Pesto handles host-side address binding; netavark's DNAT rules inside the rootless netns must not restrict on destination address since pasta's splice delivers traffic with a different destination than the user-specified HostIP
  • Dual-stack binding: when no explicit HostIP is set, pesto binds both 0.0.0.0 and [::] so IPv6 networks work out of the box
  • Config toggle: rootless_port_forwarder in [network] section of containers.conf selects between rootlessport (default) and pasta

Current limitations

  • Same port on different HostIPs (e.g. -p 127.0.0.1:8080:80 and -p 127.0.0.2:8080:80 on separate containers) does not work in pasta mode because HostIP stripping causes conflicting DNAT rules. This requires destination mapping support in pesto.

Fixes: https://redhat.atlassian.net/browse/RUN-2214
Fixes: #8193
Fixes: https://redhat.atlassian.net/browse/RUN-3587

Depends on: passt >= passt-0^20260507.g1afd4ed
Depends on: containers/container-libs#755
Depends on: #27828
Depends on: #28451

Checklist

Ensure you have completed the following checklist for your pull request to be reviewed:

  • Certify you wrote the patch or otherwise have the right to pass it on as an open-source patch by signing all
    commits. (git commit -s). (If needed, use git commit -s --amend). The author email must match
    the sign-off email address. See CONTRIBUTING.md
    for more information.
  • Referenced issues using Fixes: #00000 in commit message (if applicable)
  • Tests have been added/updated (or no tests are needed)
  • Documentation has been updated (or no documentation changes are needed)
  • All commits pass make validatepr (format/lint checks)
  • Release note entered in the section below (or None if no user-facing changes)

Does this PR introduce a user-facing change?

Add experimental rootless_port_forwarder="pasta" option in containers.conf. When enabled, rootless bridge network port forwarding uses pasta's kernel-level forwarding (via pesto) instead of rootlessport's userspace proxy, preserving the original client source IP inside containers. Requires a passt build with pesto support. The default remains rootlessport.

@Honny1 Honny1 changed the title Rootless bridge: preserve source IPs via pesto/pasta [WIP] Rootless bridge: preserve source IPs via pesto/pasta Apr 9, 2026
@packit-as-a-service
Copy link
Copy Markdown

[NON-BLOCKING] Packit jobs failed. @containers/packit-build please check. Everyone else, feel free to ignore.

1 similar comment
@packit-as-a-service
Copy link
Copy Markdown

[NON-BLOCKING] Packit jobs failed. @containers/packit-build please check. Everyone else, feel free to ignore.

@Honny1 Honny1 added rootless pasta pasta(1) bugs or features 6.0 Breaking changes for Podman 6.0 labels Apr 10, 2026
@github-actions github-actions Bot added the kind/api-change Change to remote API; merits scrutiny label Apr 10, 2026
@Honny1 Honny1 force-pushed the pesto-support branch 2 times, most recently from 6fc22c5 to 662445d Compare April 14, 2026 12:26
@Honny1 Honny1 added bloat_approved Approve a PR in which binary file size grows by over 50k labels Apr 14, 2026
@Honny1 Honny1 force-pushed the pesto-support branch 2 times, most recently from 292a7b8 to 9fc6f49 Compare April 23, 2026 16:33
@packit-as-a-service
Copy link
Copy Markdown

tmt tests failed for commit 9fc6f49. @lsm5, @psss, @thrix please check.

@Honny1 Honny1 force-pushed the pesto-support branch 2 times, most recently from 68bbedb to f511d44 Compare April 24, 2026 07:35
@packit-as-a-service
Copy link
Copy Markdown

[NON-BLOCKING] Packit jobs failed. @containers/packit-build please check. Everyone else, feel free to ignore.

@packit-as-a-service
Copy link
Copy Markdown

tmt tests failed for commit f511d44. @lsm5, @psss, @thrix please check.

TODO: vendor container-libs

Signed-off-by: Jan Rodák <hony.com@seznam.cz>
@Honny1 Honny1 changed the title [WIP] Rootless bridge: preserve source IPs via pesto/pasta Rootless bridge: preserve source IPs via pesto/pasta May 11, 2026
@Honny1 Honny1 marked this pull request as ready for review May 11, 2026 19:53
@Honny1 Honny1 requested a review from Luap99 May 11, 2026 19:55
Honny1 added 3 commits May 12, 2026 11:55
Add rootless_port_forwarder="pasta" option that uses pesto to update
pasta's forwarding table via UNIX socket, preserving source IPs that
rootlessport's userspace proxy masks.

HostIP is stripped from port mappings in the netavark wrapper when
pasta forwarding is active because pesto handles host-side binding
while pasta's splice changes the destination IP that netavark DNAT
expects. Pesto binds both 0.0.0.0 and [::] for dual-stack support.

Fixes: https://redhat.atlassian.net/browse/RUN-2214
Fixes: containers#8193
Fixes: https://redhat.atlassian.net/browse/RUN-3587

Signed-off-by: Jan Rodák <hony.com@seznam.cz>
Signed-off-by: Jan Rodák <hony.com@seznam.cz>
Signed-off-by: Jan Rodák <hony.com@seznam.cz>

Any other options will be passed through to netavark without validation. This can be useful to pass arguments to netavark plugins.

For rootless bridge networks, port forwarding uses `rootlessport` by default. Setting `rootless_port_forwarder="pasta"` in the `[network]` section of **[containers.conf(5)](https://github.com/containers/container-libs/blob/main/common/docs/containers.conf.5.md)** switches to pasta's kernel-level forwarding (via `pesto`), which preserves the original client source IP address inside the container.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which should ensure to document the exact pasta behavior is still experimental and subject to change

Comment on lines +64 to +67
if r.config.Network.RootlessPortForwarder == "pasta" {
err = r.setupRootlessPortMappingViaPesto(ctr)
} else {
err = r.setupRootlessPortMappingViaRLK(ctr, ctrNS, netStatus)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have the switch case over r.config.Network.RootlessPortForwarder here and error on invalid value instead of the main containers.conf validation in common, see my other comment there

// leak write end to conmon. Pasta forwarding mode does not use
// rootlessport, so no pipe is needed.
if rootless.IsRootless() && len(ctr.config.PortMappings) > 0 &&
ctr.runtime.config.Network.RootlessPortForwarder != "pasta" {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having these branched to be == "rootlessport" would seem more logic to readers.
Also overall it would be helpful to define both strings as content and use them consistently then to avoid typos and allow code readers easily check of where they are referenced

Comment thread test/e2e/common_test.go

// sendMessageToAddr sends a message to the given tcp address (host:port).
//
//nolint:unused,nolintlint // only called from linux-only test files, unused on freebsd
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well then put them into the file where they are used instead of this no lint thing

}
}

It("podman run bridge network preserves source IP", func() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about all of these tests

for source ip we have podman networking: port with --userns=keep-id for rootless or --uidmap=* for rootful system test

And in general we have a ton of other networking tests already, I rather not duplicate a bunch of slightly different cases.

If we want to test this then we likely want to create a loop over selected existing network tests that run twice with both rootlessport/pasta

// (source == bridge gateway) is gone.
//
// The exact mapped address is a pasta implementation detail.
Expect(output).ToNot(MatchRegexp(`connect to .* from .*` + subnetPrefix))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that does not seem to match this though..., it is the exact inverse from the other case below...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.0 Breaking changes for Podman 6.0 bloat_approved Approve a PR in which binary file size grows by over 50k kind/api-change Change to remote API; merits scrutiny pasta pasta(1) bugs or features rootless

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Alternate port_handler that keeps the source ip for user-defined rootless networks

2 participants