Skip to content

Add pesto support for dynamic port forwarding via pasta control socket#755

Open
Honny1 wants to merge 1 commit intocontainers:mainfrom
Honny1:pesto-support
Open

Add pesto support for dynamic port forwarding via pasta control socket#755
Honny1 wants to merge 1 commit intocontainers:mainfrom
Honny1:pesto-support

Conversation

@Honny1
Copy link
Copy Markdown
Member

@Honny1 Honny1 commented Apr 9, 2026

  • Add a pesto client (pesto_linux.go) that invokes the pesto binary to dynamically update pasta's port forwarding table via a UNIX domain socket, enabling rootless bridge containers to add/remove port mappings without restarting pasta.
  • Start pasta with -c <socketPath> to enable the pesto control channel and expose PestoSocketPath in RootlessNetnsInfo.

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

This requires the pesto binary, so it requires a version of passt that includes the "Read-only dynamic update implementation" and "Dynamic configuration update implementation" patch series, which has not yet been merged or released. The next passt release after that series lands will be the first to ship pesto.

@github-actions github-actions Bot added the common Related to "common" package label Apr 9, 2026
Comment thread common/libnetwork/pasta/pesto_linux.go
@Honny1 Honny1 force-pushed the pesto-support branch 3 times, most recently from 83c12a5 to 36bdb53 Compare April 10, 2026 12:30
@Honny1 Honny1 marked this pull request as ready for review April 10, 2026 12:31
}

args := portMappingsToPestoArgs(ports)
args = append(args, socketPath)
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.

can socketPath == ""

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No, it cannot be. It should be checked by the caller. Should I add a check to be sure?

// does NOT perform DNAT to the container. Netavark handles that inside the
// netns. Therefore each mapping uses HostPort as both source and destination
// (e.g. "-t 0.0.0.0/8080") so traffic arrives at the port netavark expects.
func portMappingsToPestoArgs(ports []types.PortMapping) []string {
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.

does this func make sense if ports is empty? i think you will get something in args that is not expected?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think this makes sense. If no ports are specified, it will set -t none -u none, meaning no ports will be forwarded.

@Luap99 Luap99 added the podman 6 breaking changes that should go only into podman 6 only label Apr 22, 2026
@packit-as-a-service
Copy link
Copy Markdown

Packit jobs failed. @containers/packit-build please check.

@Honny1
Copy link
Copy Markdown
Member Author

Honny1 commented Apr 23, 2026

/packit rebuild-failed

1 similar comment
@Honny1
Copy link
Copy Markdown
Member Author

Honny1 commented Apr 23, 2026

/packit rebuild-failed

@Luap99
Copy link
Copy Markdown
Member

Luap99 commented May 6, 2026

Since GH PR reviews are down posted here in the hope it works

Not supporting ipv6 here is a hard blocker which we cannot ship with as this would be a major regression.

ipv6 dnat work for all addresses except ::1 and I would expect the incoming packet address to be from the tap and not lo? Or I guess that depend on the local splice bypass when the host connection comes from ::1. Maybe adding the --no-splice option to pasta helps with that part cc @sbrivio-rh

Testing locally with the --no-splice option even the real problem is not packets are send via link local address with is never routed

fe80::8880:e9ff:fead:a5cc is the link local inside the rootless netns

18:47:02.596861 enp9s0u2u1u2 In  IP6 fe80::20bc:e1f1:6719:b4ce.35144 > fe80::8880:e9ff:fead:a5cc.8080: Flags [S], seq 625554470, win 65535, options [mss 61440,nop,wscale 8], length 0
18:47:02.596926 enp9s0u2u1u2 Out IP6 fe80::8880:e9ff:fead:a5cc.8080 > fe80::20bc:e1f1:6719:b4ce.35144: Flags [R.], seq 0, ack 625554471, win 0, length 0

But I do have an actual global address assigned as well
inet6 2620:52:0:2c20::1290/64 scope global nodad noprefixroute

So why is pasta not forwarding to that address? If it would do that then it should work I think


Then there is the larger design issue that comes due stripping off the host ip on the podman side in the rootless netns. All packets arrive only on the tap interface in pasta so the netns has no longer any way to route them to the right container ip as the real host ip info is lost at that point, i.e. try something like this all, request will land in one container which is bad and I Am not sure how we could even avoid this here.

podman run --rm  --network bridge -p 127.0.0.1:8080:80  -d nginx
podman run --rm  --network bridge -p 127.0.0.2:8080:80  -d nginx

It is essentially this machine problem containers/podman#14928, we would need to remap ports in the rootless netns dynamically but this is ugly as we need to keep state and it also means we can run out of ports if users forward a lot of ports on many ips on the host but the pasta tap0 interface would only have one set so we get capped at 65535 I guess

@Luap99
Copy link
Copy Markdown
Member

Luap99 commented May 6, 2026

It is essentially this machine problem containers/podman#14928, we would need to remap ports in the rootless netns dynamically but this is ugly as we need to keep state and it also means we can run out of ports if users forward a lot of ports on many ips on the host but the pasta tap0 interface would only have one set so we get capped at 65535 I guess

@sbrivio-rh @dgibson I think you talked about the flow table possibly having a destination ip. Is that something that would work today already? I guess it could used to avoid the issue if podman passes the final container ip right with the port mappings and then skip the dnat firewall rules completely because pasta handles it directly in one go

@Luap99
Copy link
Copy Markdown
Member

Luap99 commented May 6, 2026

Testing locally with the --no-splice option even the real problem is not packets are send via link local address with is never routed

I just noticed I tested with the wrong network without ipv6 enabled, we must test with a network created with podman network create --ipv6. Still does not work due the link local but now the tcpdump makes more sense and curl times out instead of the connection reset.

19:32:18.855995 enp9s0u2u1u2 In  IP6 fe80::20bc:e1f1:6719:b4ce.51690 > fe80::f469:6cff:fe96:3065.8080: Flags [S], seq 1712433705, win 65535, options [mss 61440,nop,wscale 8], length 0
19:32:18.856044 enp9s0u2u1u2 Out IP6 fe80::f469:6cff:fe96:3065 > fe80::20bc:e1f1:6719:b4ce: ICMP6, destination unreachable, beyond scope fe80::f469:6cff:fe96:3065, source address fe80::20bc:e1f1:6719:b4ce, length 76
19:32:19.856120 enp9s0u2u1u2 In  IP6 fe80::20bc:e1f1:6719:b4ce.51690 > fe80::f469:6cff:fe96:3065.8080: Flags [S], seq 1712433706, win 65535, options [mss 61440,nop,wscale 8], length 0
19:32:19.856209 enp9s0u2u1u2 Out IP6 fe80::f469:6cff:fe96:3065 > fe80::20bc:e1f1:6719:b4ce: ICMP6, destination unreachable, beyond scope fe80::f469:6cff:fe96:3065, source address fe80::20bc:e1f1:6719:b4ce, length 76

@sbrivio-rh
Copy link
Copy Markdown

But I do have an actual global address assigned as well inet6 2620:52:0:2c20::1290/64 scope global nodad noprefixroute

So why is pasta not forwarding to that address? If it would do that then it should work I think

I just realised that this might be due to Podman passing --map-guest-addr, which is taken into account in the inbound NAT logic:

        } else if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.map_guest_addr) &&
                   inany_equals6(addr, &c->ip6.addr)) {
                translated->a6 = c->ip6.map_guest_addr;

I guess we might want to maintain the original scope instead, that is, if the original destination address wasn't a link-local address, we shouldn't convert that. I didn't really think it through, but this definitely looks like something we can fix in pasta.

@sbrivio-rh @dgibson I think you talked about the flow table possibly having a destination ip. Is that something that would work today already? I guess it could used to avoid the issue if podman passes the final container ip right with the port mappings and then skip the dnat firewall rules completely because pasta handles it directly in one go

Ah, yes, that sounds like a good idea! We don't have the interface for that yet, and the destination address is not represented yet in struct fwd_rule, but at this point it should be straightforward to add (I didn't try yet).

We happened to discuss the command line and pesto syntax for that, the seemingly obvious choice would be the same syntax as we use to match on the original destination address, but after the :, that is, say, while:

-t 192.0.2.1/8022:22

forwards traffic originally directed to 192.0.2.1 and port 8022 to port 22 in the container, with the inbound NAT logic I referenced above, we would allow a syntax like:

-t 192.0.2.1/8022:192.0.2.3/22

to also specify a mapped destination IP address. I can give that a try tomorrow, it doesn't really look complicated at this stage.

@sbrivio-rh
Copy link
Copy Markdown

But I do have an actual global address assigned as well inet6 2620:52:0:2c20::1290/64 scope global nodad noprefixroute
So why is pasta not forwarding to that address? If it would do that then it should work I think

I just realised that this might be due to Podman passing --map-guest-addr, which is taken into account in the inbound NAT logic

If that's the case, the patch at https://archives.passt.top/passt-dev/20260507043149.1989693-1-sbrivio@redhat.com/ (b4 shazam https://archives.passt.top/passt-dev/20260507043149.1989693-1-sbrivio@redhat.com/ ) should probably fix the issue. But I don't know with which parameters pasta is started in your test, and where your connection attempt actually comes from.

I'm looking into the second point meanwhile (which might supersede this one anyway if I understood correctly).

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

Labels

common Related to "common" package podman 6 breaking changes that should go only into podman 6 only

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

4 participants