Hi Chaser, Thanks for your report! See my answer inline, below. On Thu, 9 May 2024 23:13:09 -0400 Kangjing Huang <huangkangjing(a)gmail.com> wrote:Hi there, I was tweaking around pasta and its usage with podman, and I realized that from pasta guest namespaces it is possible to access host ports through the address of secondary interfaces on the host. Say I have two interfaces on host, with eth0 connecting to a gateway and eth1 connected to another LAN:This binds your server to "any" address, so this is expected, more below.$ # On host $ ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.2 netmask 255.255.255.0 broadcast 192.168.1.255 ... $ ifconfig eth1 eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.110.1 netmask 255.255.255.0 broadcast 192.168.110.255 ... $ ip route default via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.2 metric 1024 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.2 metric 1024 192.168.1.1 dev eth0 proto dhcp scope link src 192.168.1.2 metric 1024 192.168.110.0/24 dev eth1 proto kernel scope link src 192.168.110.1If there is some service started on host: > $ python -m http.server > Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...This fails simply because your container also has address 192.168.1.2, by default, see: https://passt.top/#addresses You can use the address of the default gateway from the container to use this connection path. By the way, with default options (implying '--tcp-ns auto'), you can actually connect to a loopback address to reach your server on the host: $ python3 -m http.server 8082 Serving HTTP on 0.0.0.0 port 8082 (http://0.0.0.0:8082/) ... [...] and from the container: # curl 127.0.0.1:8082 <!DOCTYPE html> <html lang='en'> [...] this is also reflected in the man page: auto Dynamically forward ports bound in the namespace. The list of ports is periodically derived (every second) from listening sockets reported by /proc/net/tcp and /proc/net/tcp6, see proc(5). and later, for --tcp-ns: -T, --tcp-ns spec Configure TCP port forwarding from target namespace to init namespace. spec is as described above for TCP. Default is auto. note that this was instead considered an unexpected (hence insecure) behaviour for Podman, which passes the '--tcp-ns none' option by default, as well as '--no-map-gw'. See also this discussion, referring to Podman integration, at: https://lists.podman.io/archives/list/podman@lists.podman.io/message/OG5U6Y…From a pasta namespace, it is impossible to access the host ports bythe address of the main interface: > $ pasta --config-net > $ # Now in pasta namespace > $ curl 192.168.1.2:8000 > curl: (7) Failed to connect to 192.168.1.2 port 8000 after 0 ms: Couldn't connect to serverHowever I found that it is possible to do so by the address of the secondary interface:Yes, it is, so much that we even test for it: https://passt.top/passt/tree/test/pasta/tcp?id=1ba76c9e8c14b21d8c2c7cb71abd… in those test cases, "via tap" means using the tap interface pasta creates in the namespace, which is the same path you use by specifying the address of a secondary interface such as in your example. Sure, the host is in a different namespace from pasta's namespace, but the host can route traffic between them, just like it can route traffic from external hosts. What difference would it make if the client in your namespace could connect to an external proxy, that connects back to the host using your secondary interface? The only relevant bit for pasta's job here is to maintain the correct source address: $ python3 -m http.server 8082 Serving HTTP on 0.0.0.0 port 8082 (http://0.0.0.0:8082/) ... 1.2.3.1 - - [10/May/2024 12:13:17] "GET / HTTP/1.1" 200 - and not make that connection appearing as if it comes from a loopback device, because that would trick access control performed by services running on the host, or even a specific address binding. If I do this: $ python3 -m http.server -b 127.0.0.1 8082 Serving HTTP on 127.0.0.1 port 8082 (http://127.0.0.1:8082/) ... I won't be able to connect from the container using the local address of a non-remote interface, as expected: # curl 1.2.3.1:8082 curl: (28) Failed to connect to 1.2.3.1 port 8082 after 129895 ms: Couldn't connect to server See CVE-2021-20199 for why this matters (even though that's the reverse case).$ # In same pasta environment as above $ curl 192.168.110.1:8000 <!DOCTYPE HTML> <html lang="en"> ...Is this an expected behavior?I believe this is a security escape in the container context, since containerized services can gain access to unintended resources.Note that pasta doesn't implement containerisation, it just detaches a user and network namespace (again, by default) for convenience, but the resulting shell will even have access to the host filesystem. Podman implements containers, though, so it needs to care about this, and it passes non-default options, such as '--no-map-gw'. If it didn't, we would actually have what you describe, a container being able to access unintended resources, such as a localhost-bound server. By the way, for the future, if you believe you found a security issue, even if it's just a suspicion, please use the passt-sec(a)passt.top list instead, so that we can implement responsible disclosure if needed: https://passt.top/#security-and-vulnerability-reports but in this case, I really don't see the need. -- Stefano