public inbox for passt-user@passt.top
 help / color / mirror / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: baleti <baleti3266@gmail.com>
Cc: passt-user@passt.top
Subject: Re: Feature request: option to prevent guest from reaching host external addresses
Date: Mon, 4 May 2026 15:41:24 +1000	[thread overview]
Message-ID: <afgxhMK_fsHS5rMG@zatzit> (raw)
In-Reply-To: <500da332-7eb2-4523-8d63-ebc1000f81a8@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 7888 bytes --]

On Sat, May 02, 2026 at 03:25:17PM +0100, baleti wrote:
> Hi,
> 
> I'd like to propose a new option for passt, working title --no-map-host, as
> a complement to the existing --no-map-gw.

So, I can see that might be useful, and I'm not necessarily against
it, but there are some important differences between the proposed
option and --no-map-gw, that make it trickier to work out exactly what
it should do and how.

Further details noted below, but the key difference is that
--no-map-gw behaviour arises naturally: the guest cannot reach the
host's loopback unless we specifically implement translation.  We do
that translation by default, but --no-map-gw turns it off.  That's not
true for the host's external address(es) - from the guest's point of
view (and even passt's, to an extent) there's no inherent difference
between a host external address and the address of any random machine
on the internet.  To block this case we'd instead need to explicitly
filter out such connection attempts.

> The gap
> 
> --no-map-gw prevents the guest from reaching host loopback services via the
> gateway address mapping, which is useful. However, there is currently no
> mechanism to prevent the guest from reaching services bound to the host's
> real external address (e.g. 0.0.0.0:22).
> 
> Because passt proxies outbound guest connections as the host user, a
> connection from the guest to the host's own external IP is transparently
> forwarded — passt opens the socket on the host side and the connection
> succeeds. From the perspective of the service being connected to (e.g.
> sshd), it appears as a local connection.

That depends on what you mean by "local" here (unfortunately there are
several possible meanings, even within kernel networking interfaces).
The connection will *not* appear to come from a local scope address
(127.0.0.0/8 or ::1) - it will come from one of the host's external IP
addresses - typically the same one that you're connecting to.

It will, obviously, come from an IP that the host owns, and will
usually be routed over the host's 'lo' interface.

> A concrete example with a typical setup:
> 
>   passt -t 2222 --no-map-gw --vhost-user --socket /tmp/passt-vm
> 
>   ss -tulpn shows:
>     tcp LISTEN 0.0.0.0:22   sshd
> 
> From inside the guest, a compromised or untrusted workload can reach sshd
> directly:
> 
>   ssh user@192.168.1.x   # host's external IP, connection succeeds


Yes, although this is also true for an untrusted external machine on
the host's LAN.

> This also enables VM-to-VM lateral movement when multiple guests share the
> same host: each guest can reach the others' forwarded ports via the host's
> external IP.

Right, the usual assumption here is that guest's forwarded ports are
intended to be exposed to the surrounding network - that does include
other VMs or containers, as well as the host and whatever's on the
host's LAN.

> The operator has no indication this is happening. Services bound to 0.0.0.0
> are generally considered "LAN-exposed" rather than "VM-guest-exposed", and
> this assumption is silently violated.

As far as I can tell something bound to 0.0.0.0 would be
VM-guest-exposed with most VM network setups, not just passt, unless
you explicitly introduce a firewall to catch it.

> Proposed option
> 
> --no-map-host, which would cause passt to drop or reject TCP/UDP connections
> from the guest whose destination matches any of the host's own configured
> addresses (the same addresses passt already knows about for DHCP/NDP
> assignment purposes).

So, when the guest is sharing the host's IP (default behaviour) the
host's address is already implicitly blocked unless -map-guest-addr is
also specified, simply because if the guest tries to send to the
host's external address it will instead address itself.  However, the
guest does have the ability to ignore the suggested IP, so that's not
really enforced.

Enforcing it raises several edge cases that need consideration though:

 * If the host has several external addresses, do we want to block all
   of them?  One of them?  Some of them?
 * If the host changes external address, how do we update the
   filtering to cover the new address?

     We're looking (for other reasons) to introduce a netlink monitor
     to track host address changes, which could be used for this.
     However the update would not be instant.  Is a short window where
     the old/new address could be accessed acceptable?

These semantics would need to be pinned down in order to implement
this.

> An alternative spelling --map-host-addr none modelled on --map-host-loopback
> none would also be consistent with the existing option naming.

Right, I don't like that naming because although the effects are
analagous from the point of view of your usecase, they're very
different in terms of what they mean internally (an explicit filter
versus disabling a translation).

> This would stay entirely within passt's existing socket-layer design and
> require no new privileges.

No, but it does need new filtering logic to be implemented (but we're
kind of working on that already, see below).

> Why this matters for rootless setups
> 
> The primary audience for passt is rootless VM deployments where TAP+bridge
> (the traditional isolation mechanism) is not available without privilege. In
> these setups, passt is the only network layer, so operators rely on it to
> provide whatever isolation is possible. --no-map-gw is a good step in that
> direction; --no-map-host would close the remaining obvious gap.

This doesn't fully make sense to me - AFAICT, TAP-bridges would also
leave the host's external address exposed to the VMs, unless filtered
by an additional mechanism (e.g. iptables / nftables).

> Happy to discuss or test patches. Thanks for the project — it's been very
> useful.


Thoughts on possible ways to achieve what you're looking for.

# Existing approaches

## --outbound-if4 and --outbound-if6

These options let you route VM traffic explicitly to specific host
interface.  That won't directly make host addresses inaccessible, but
might be enough to stop it appearing as "local".  You'd have to check
for the details of your use case.

## Isolate VMs within a netns

You can create a network namespace using unshare -Un, and run your VM
along with the associated passt within it.  That lets the namespace
owner configure routing between it and the host as you like, including
explicit filters, if you want.

# Upcoming approaches

## Forwarding tables

We're in the process of implementing much more flexible forwarding /
filtering rules, which should make this a lot more feasible.  We hope
to merge the first draft pretty soon, but that will only over incoming
and "spliced" (for containers) connections.  We do plan to extend that
to filtering outbound connections as well though.  That would
certainly allow you to block specific addresses from the VMs.  This is
just waiting on the time to implement it.

## Multiple guest address handling

Only indirectly related, but we're also working on better handling of
multiple guest addresses, allowing (amongst other things) all the
host's addresses to be reflected in the guest, not just one.  We
hadn't discussed it but we could potentially add to that enforcement
that the guest only use the addresses it's told do, which would also
effectively block host addresses from the guest (without
--map-guest-addr).  There would likely be unavoidable races in
updating that for host address changes, however.

-- 
David Gibson (he or they)	| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you, not the other way
				| around.
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

      reply	other threads:[~2026-05-04  6:56 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-02 14:25 baleti
2026-05-04  5:41 ` David Gibson [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=afgxhMK_fsHS5rMG@zatzit \
    --to=david@gibson.dropbear.id.au \
    --cc=baleti3266@gmail.com \
    --cc=passt-user@passt.top \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).