On 10/31/2012 05:10 AM, Ishimoto, Ryu wrote:
Hi Everyone,
I wanted to ask a question about the 'generic ethernet' NIC type. If
I understand its security concerns correctly, because it expects QEMU
to execute scripts to bring up/down the interface, it requires that
the host security level is lowered to allow QEMU to perform privileged
operations.
It's not just that. It's also problematic that we are passing control
over to some random external script about which we know nothing. This
not only has security implications, but also supportability
implications. That's why we set the "tainted" flag in the domain's
status whenever there is an external script.
As I recall, the "lowered host security level" is at least some extra
cgroups acls (cgroup_device_acl, set in /etc/libvirt/qemu.conf) due to
the fact that the tap device *name* is sent to qemu rather than libvirt
opening the device and sending the open fd.
While this is definitely not desirable, I'm have a situation
where I
want to use libvirt to work with a custom networking solution that is
neither Linux bridge or OpenVSwitch. Currently, to make this happen,
I would create a tap interface myself, configure it(like adding it to
the datapath), and inform libvirt of this interface as a generic
ethernet type with script attribute set to ''.
Yes, that's exactly the kind of situation the generic ethernet device
exists for.
In order to make this work without the security issue mentioned, I
would like to suggest a new device type(or just a modification of
generic ethernet type) in which libvirt accepts an interface name,
opens it and gets its fd, and passes the fd to QEMU. QEMU does not
run any scripts and expects the tap interface to be already open.
So the difference from standard type='network' or type='bridge' is that
it expects the device to be already created, but not yet opened? And
will it also already be attached to its bridge (or whatever device)?
(One thing to point out is that the existing type='bridge' and
type='network' interfaces already allow for the tap device to be
already-created - if the interface definition has a <target> element
with a "dev" attribute that doesn't start with "vnet", then
libvirt
doesn't try to auto-generate a name, instead using the name provided.
Because ioctl(TUNSETIFF) (which libvirt uses to create/open the tap
device) properly handles the case where the tap device already exists,
in this case the existing tap device will be used.)
Another solution might be to allow custom scripts to be plugged
into
libvirt that allows you to add an interface to a bridge instead of
running brctl or ovs-vsctl.
Both of these are what the generic ethernet type is intended for, and as
a matter of fact before explicit support was added for Open vSwitch,
that's how people connected their tap devices to an Open vSwitch bridge.
The latter solution lets libvirt create the tap interface, whereas
the former requires that the tap is 'plugged' into the network before
libvirt takes control for the VM to have connectivity when it
launches. I prefer the former because it seems less disruptive while
providing similar features.
I've only used type='ethernet' to verify that it worked, so don't have a
very good database of potential usage scenarios and whether or not what
I suggest below would work for all of them, so my advice here is suspect
:-) However, this is what I notice:
On the qemu commandline, both type='bridge' (I'll leave out
type='network' from now on, as it's essentially identical to bridge for
this discussion) and type='ethernet' add a "tap" argument to the qemu
command line, but in one case we create/open a tap device and sent the
fd to qemu with
-netdev tap,fd=nn,...
and in the other, we just blindly send the name of the tap device, as
well as script parameter (if requested) and allow qemu to open it:
-netdev tap,ifname=/dev/mytapX,script=/etc/libvirt/myscript,...
It *looks* like (from the "qemu-kvm -help" output) we could just as
easily open that tap device in libvirt, and send "-netdev
tap,fd=nn,script=/etc/libvirt/myscript,..." to qemu (or, when no script
is specified, just "-netdev tap,fd=nn,...".
If this is true (any qemu people who can tell me for sure?), simply
changing the code for type='ethernet' in this manner would solve your
problem - you would make sure that:
1) your <interface> is "type='ethernet'"
2) your tap device has a name ("<target dev='xxx'/>") that
doesn't start
with "vnet", *and*
3) that device is created and attached to the proper place beforehand
libvirt would then open the provided tap device and pass it to qemu,
with no other action performed. Because it would be passing an open fd
to qemu (instead of a tap device name) and because there would be no
script involved, no decreased security level would be required for qemu.
As far as running a user-specified script, currently we leave it up to
qemu to do this; perhaps we should have an attribute for type='ethernet'
that indicates libvirt should execute the script instead? (maybe the
choice of sending tap device name vs fd to qemu should be controlled by
the same attribute that controls whether libvirt or qemu executes the up
script.
(and btw, there's an open request to support the "down script" that qemu
supports).
I would be more than happy to send a patch for this, and I apologize
that this lacks details, but I just wanted to first make sure that
what I'm suggesting here makes sense and that I'm not missing
something critical, as I am fairly new to libvirt.
Would what I outlined above work? (modify type='ethernet' to open the
given tap device and send its fd rather than name + optionally executing
the script in libvirt rather than passing it to qemu)