Currently our authentication model for local connections is using the basic
UNIX file permissions, possibly with a setuid helper (in Xen case only). It
can be summarized as
- If app using libvirt is running as root
=> full access
- Else
=> read only access
The latter is enforced by fact that in Xen case libvirt_proxy only has impl
for a handful of read only APIs, or in non-Xen case that the UNIX domain
socket for the daemon /var/run/libvirt/libvirt-sock is mode 0700, while
/var/run/libvirt/libvirt-sock-ro is 0777 & the daemon enforces based on which
socket the client connects to.
This is good because it allows non-root to at least monitor guest state
while requiring root authentication for actually changing state.
This is bad because it requires any app which wants to change state to run
as root. ie we are required to launch virt-manager as root to gain ability
to manage local guests. Problem with this include:
- running the entire PyGTK & GTK & X codebase as root is undesirable
- no integration with the DBus desktop session (gnome-vfs integartion)
- no integration with the GNOME keyring (for VNC server passwords)
- redundant (&dangerous) if all you want to do is manage remote libvirt hosts
In summary what I really need for virt-manager is
- Always run as non-root
- Authenticate for local guest management (ie read+write)
UNIX domain sockets already provide a way for each end to identify the PID
and UID of the other end. This enables the libvirt daemon to determine the
identity of the application on the other end. With this information the
daemon merely needs to check this identity against some access control policy
rules. Where to get/define these rules though ?
Enter PolicyKit.
http://lists.freedesktop.org/archives/hal/2006-March/004770.html
http://lists.freedesktop.org/archives/hal/2007-June/008815.html
PolicyKit is a
freedesktop.org project to replace all the distro specific
'do admin stuff as root' hacks like consolehelper[1], sudo, gksudo with
a standard mechanism. Most importantly this mechanism does not require
that the application in question run as root, or ever gain root privileges.
In addition this is completely configurable by the administrator in a
application independant manner.
How would the daemon use PolicyKit for authenticating clients on UNIX
domain sockets
- libvirtd defines two actions it can check called 'libvirt-local-monitor
(read only monitoring of state), and 'libvirt-local-manage' (full
read-write management).
- Both libvirt-sock and libvirt-sock-ro are mode 0777 (ie all users)
- A connection arrives on the UNIX domain socket either libvirt-sock-ro
or libvirt-sock
- libvirtd use SO_PEERCRED to get the PID of the client
- If the connection is on libvirt-sock-ro 'action' is
'libvirt-local-monitor'
Else if connection is on libvirt-sock 'action' is
'libvirt-local-manage'
- Libvirt asks policy kit if PID is allowed to perform the 'action'
-> Yes, connection proceeds
-> No, connection is dropped
NB, having two separate UNIX domain sockets is strictly speaking redundant
now - the daemon could simply check each 'action' in turn - but keeping the
separate sockets simplifies the code, because it minimises the diffs when
PolicyKit is not enabled by the 'configure' script.
How is policy determined by PolicyKit ?
- libvirt RPM provides /usr/share/PolicyKit/policy/libvirt.policy
- This file defines the two actions it supports 'libvirt-local-monitor'
and 'libvirt-local-manage'.
- For each action we define a default policy for an active desktop
session, and an inactive desktop session. The sample policy is to
allow 'libvirt-local-monitor' for any local desktop session, but
only allow 'libvirt-local-manage' for the active session with the
additional requirement that the user authenticate with PolicyKit
using the root password.
This basically replicates the existing security semantics, without requiring
that we run virt-manager itself as root. This accomplishes the core goal.
There are other benefits to PolicyKit though - the administrator can override
the application provided default Policy. For example, could add a rule that
says
"if the user fred is logged into the local X session, allow them to do
'libvirt-local-manage' without any further prompts for root password"
Since PolicyKit is a general framework, that rule could be generalized
beyond just virt-manager
"if the user fred is logged into the local X session, allow them to do
any defined action without any further prompts for root password"
Or going the other way the administrator can lock things down requiring a
root password even for read only monitoring of VMs. Or a halfway house of
requiring the user's own password (sudo equiv, instead of su equiva).
If course this doesn't just apply to virt-manager either. This transparently
'just works' for virsh too & any other apps using libvirt - eg if policy
grants 'libvirt-local-manage' then you can do full management whether virsh
is root or not.
This all talks about enforcement of policy from the daemon side. There is
one outstanding question - if the policy so requires, where/how do we ask
the user to enter their credentials.
PolicyKit provides two mechanisms for this:
- A command line tool polkit-grant which uses pam to authenticate you
as root, or using your own user password. If successful you will have
been granted the permission to perform that action for the remainder
of the session.
- A DBus service for triggering display of some UI for authenticating.
There is a GNOME impl of this service & likely a KDE impl too.
eg with virsh start off without any priveleges
$ virsh --connect qemu:///system net-list
libvir: Remote error : Connection reset by peer
error: failed to connect to the hypervisor
error: no valid connection
$ polkit-grant --action libvirtd-local-manage2
Authentication is required.
Password:
Keep this privilege for the session? [no/session]?
$ ./src/virsh --connect qemu:///system net-list --all
Name State Autostart
-----------------------------------------
default active yes
One option is to have the libvirt library spawn polkit-grant if not finding
any $DISPLAY, and call the DBus service (for UI prompt) if finding any. The
other option is to punt this code upto the application - eg virt-manager
always call the DBus service, while virsh always call polkit-grant.
Finally, PolicyKit is new in Fedora 8 if you wish to try this out. The
configure script is setup so that it automagically detects presence of
PolicyKit and only turns it on at compile time if its available. The
fallback is to continue current behaviour of restricting based on the
socket permissions.
http://fedoraproject.org/wiki/Releases/FeaturePolicyKit
Finally, finally, if we enable PolicyKit support, then we should disable
the Xen specific libvirt_proxy setuid binary, since it would subvert the
policy for libvirt-local-monitor. IMHO we should just kill the setuid
binary altogether, since we already require that the libvirt daemon be
running for the networking APIs & this provides read-only access already
which works for non-Xen drivers too.
Attaching the patch implementing the proof of concept
$ diffstat ~/libvirt-policykit.patch
configure.in | 60 ++++++++++++++++++--
qemud/Makefile.am | 12 +++-
qemud/internal.h | 23 ++++++-
qemud/libvirtd.policy | 41 ++++++++++++++
qemud/qemud.c | 145 ++++++++++++++++++++++++++++++++++++++++++++------
qemud/remote.c | 6 +-
6 files changed, 258 insertions(+), 29 deletions(-)
Oh, finally, finally, finally. PolicyKit is pretty new so I'd understand
if there's some resistance to including it in an official libvirt release
at this point. I think the patch is small enough that we could carry it
as add-on in the Fedora RPMs only to start with untill its proved to be
maintainable long term - I really need this for Fedora 8 to make virt-manager
work sanely - ie not need to run as root.
Regards,
Dan.
--
|=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=|
|=- Perl modules:
http://search.cpan.org/~danberr/ -=|
|=- Projects:
http://freshmeat.net/~danielpb/ -=|
|=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|