
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 -=|