--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <body>
+ <h1>Client access control</h1>
+ <p>
+ Libvirt's client access control framework allows administrators
+ to setup fine grained permission rules across client users,
+ managed objects and API operations. This allows client connections
+ to be locked down to a minimal set of privileges.
+ </p>
+
+ <ul id="toc"></ul>
+
+ <h2><a name="intro">Access control introduction</a></h2>
+
+ <p>
+ In a default configuration, the libvirtd daemon has three levels
+ of access control. All connections start off in an unauthenticated
+ state, where the only API operations allowed are those required
+ to complete authentication. After successful authentication, a
+ connection either has full, unrestricted access to all libvirt
+ API calls, or is locked down to only "read only" operations,
+ according to what socket a client connection originated on.
+ </p>
+
+ <p>
+ The access control framework allows authenticated connections to
+ have fine grained permission rules to be defined by the administrator.
+ Every API call in libvirt has a set of permissions that will
+ be validated against the object being used. For example, the
+ <code>virDomainSetSchedulerParametersFlags</code> method will
+ check whether the client user has the <code>write</code>
+ permission on the <code>domain</code> object instance passed
+ in as a parameter. Further permissions will also be checked
+ if certain flags are set in the API call. In addition to
+ checks on the object passed in to an API call, some methods
+ will filter their results. For example the <code>virConnectListAllDomains</code>
+ method will check the <code>search_domains</code> on the <code>connect</code>
+ object, but will also filter the returned <code>domain</code>
+ objects to only those on which the client user has the
+ <code>getattr</code> permission.
+ </p>
+
+ <h2><a name="drivers">Access control drivers</a></h2>
+
+ <p>
+ The access control framework is designed as a pluggable
+ system to enable future integration with arbitrary access
+ control technologies. By default, the <code>none</code>
+ driver is used, which does no access control checks at
+ all. At this time, libvirt ships with support for using
+ <a href="http://www.freedesktop.org/wiki/Software/polkit/">polkit</a> as a real access
+ control driver. To learn how to use the polkit access
+ driver consult <a href="aclpolkit.html">the configuration
+ docs</a>.
+ </p>
+
+ <p>
+ The access driver is configured in the <code>libvirtd.conf</code>
+ configuration file, using the <code>access_drivers</code>
+ parameter. This parameter accepts an array of access control
+ driver names. If more than one access driver is requested,
+ then all must succeed in order for access to be granted.
+ To enable 'polkit' as the driver:
+ </p>
+
+ <pre>
+# augtool -s set '/files/etc/libvirt/libvirtd.conf/access_drivers[1]' polkit
+ </pre>
+
+ <p>
+ And to reset back to the default (no-op) driver
+ </p>
+
+
+ <pre>
+# augtool -s rm /files/etc/libvirt/libvirtd.conf/access_drivers
+ </pre>
+
+ <p>
+ <strong>Note:</strong> changes to libvirtd.conf require that
+ the libvirtd daemon be restarted.
+ </p>
+
+ <h2><a name="perms">Objects and permissions</a></h2>
+
+ <p>
+ Libvirt applies access control to all the main object
+ types in its API. Each object type, in turn, has a set
+ of permissions defined. To determine what permissions
+ are checked for specific API call, consult the
+ <a href="html/libvirt-libvirt.html">API reference manual</a>
+ documentation for the API in question.
+ </p>
+
+ <div id="include" filename="aclperms.htmlinc"/>
+
+ </body>
+</html>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <body>
+ <h1>Polkit access control</h1>
+
+ <p>
+ Libvirt's client <a href="acl.html">access control framework</a> allows
+ administrators to setup fine grained permission rules across client users,
+ managed objects and API operations. This allows client connections
+ to be locked down to a minimal set of privileges. The polkit driver
+ provides a simple implementation of the access control framework.
+ </p>
+
+ <ul id="toc"></ul>
+
+ <h2><a name="intro">Introduction</a></h2>
+
+ <p>
+ A default install of libvirt will typically use
+ <a href="http://www.freedesktop.org/wiki/Software/polkit/">polkit</a>
+ to authenticate the initial user connection to libvirtd. This is a
+ very coarse grained check though, either allowing full read-write
+ access to all APIs, or just read-only access. The polkit access
+ control driver in libvirt builds on this capability to allow for
+ fine grained control over the operations a user may perform on an
+ object.
+ </p>
+
+ <h2><a name="perms">Permission names</a></h2>
+
+ <p>
+ The libvirt <a href="acl.html#perms">object names and permission names</a>
+ are mapped onto polkit action names using the simple pattern:
+ </p>
+
+ <pre>org.libvirt.api.$object.$permission
+</pre>
+
+ <p>
+ The only caveat is that any underscore characters in the
+ object or permission names are converted to hyphens. So,
+ for example, the <code>search_storage_vols</code> permission
+ on the <code>storage_pool</code> object maps to the polkit
+ action:
+ </p>
+ <pre>org.libvirt.api.storage-pool.search-storage-vols
+</pre>
+
+ <p>
+ The default policy for any permission which corresponds to
+ a "read only" operation, is to allow access. All other
+ permissions default to deny access.
+ </p>
+
+ <h2><a name="attrs">Object identity attributes</a></h2>
+
+ <p>
+ To allow polkit authorization rules to be written to match
+ against individual object instances, libvirt provides a number
+ of authorization detail attributes when performing a permission
+ check. The set of attributes varies according to the type
+ of object being checked
+ </p>
+
+ <h3><a name="object_connect">virConnectPtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3><a name="object_domain">virDomainPtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ <tr>
+ <td>domain_name</td>
+ <td>Name of the domain, unique to the local host</td>
+ </tr>
+ <tr>
+ <td>domain_uuid</td>
+ <td>UUID of the domain, globally unique</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3><a name="object_interface">virInterfacePtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ <tr>
+ <td>interface_name</td>
+ <td>Name of the network interface, unique to the local host</td>
+ </tr>
+ <tr>
+ <td>interface_mac</td>
+ <td>MAC address of the network interface, not unique</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3><a name="object_network">virNetworkPtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ <tr>
+ <td>network_name</td>
+ <td>Name of the network, unique to the local host</td>
+ </tr>
+ <tr>
+ <td>network_uuid</td>
+ <td>UUID of the network, globally unique</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3><a name="object_node_device">virNodeDevicePtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ <tr>
+ <td>node_device_name</td>
+ <td>Name of the node device, unique to the local host</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3><a name="object_nwfilter">virNWFilterPtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ <tr>
+ <td>nwfilter_name</td>
+ <td>Name of the network filter, unique to the local host</td>
+ </tr>
+ <tr>
+ <td>nwfilter_uuid</td>
+ <td>UUID of the network filter, globally unique</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3><a name="object_secret">virSecretPtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ <tr>
+ <td>secret_uuid</td>
+ <td>UUID of the secret, globally unique</td>
+ </tr>
+ <tr>
+ <td>secret_usage_volume</td>
+ <td>Name of the associated volume, if any</td>
+ </tr>
+ <tr>
+ <td>secret_usage_ceph</td>
+ <td>Name of the associated Ceph server, if any</td>
+ </tr>
+ <tr>
+ <td>secret_usage_target</td>
+ <td>Name of the associated iSCSI target, if any</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3><a name="object_storage_pool">virStoragePoolPtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ <tr>
+ <td>pool_name</td>
+ <td>Name of the storage pool, unique to the local host</td>
+ </tr>
+ <tr>
+ <td>pool_uuid</td>
+ <td>UUID of the storage pool, globally unique</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3><a name="object_storage_vol">virStorageVolPtr</a></h3>
+ <table class="acl">
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>connect_driver</td>
+ <td>Name of the libvirt connection driver</td>
+ </tr>
+ <tr>
+ <td>pool_name</td>
+ <td>Name of the storage pool, unique to the local host</td>
+ </tr>
+ <tr>
+ <td>pool_uuid</td>
+ <td>UUID of the storage pool, globally unique</td>
+ </tr>
+ <tr>
+ <td>vol_name</td>
+ <td>Name of the storage volume, unique to the pool</td>
+ </tr>
+ <tr>
+ <td>vol_key</td>
+ <td>Key of the storage volume, globally unique</td>
+ </tr>
+ </tbody>
+ </table>
+
+
+ <h2><a name="user">User identity attributes</a></h2>
+
+ <p>
+ At this point in time, the only attribute provided by
+ libvirt to identify the user invoking the operation
+ is the PID of the client program. This means that the
+ polkit access control driver is only useful if connections
+ to libvirt are restricted to its UNIX domain socket. If
+ connections are being made to a TCP socket, no identifying
+ information is available and access will be denied.
+ Also note that if the client is connecting via an SSH
+ tunnel, it is the local SSH user that will be identified.
+ In future versions, it is expected that more information
+ about the client user will be provided, including the
+ SASL / Kerberos username and/or x509 distinguished
+ name obtained from the authentication provider in use.
+ </p>
+
+
+ <h2><a name="checks">Writing acces control policies</a></h2>
+
+ <p>
+ If using versions of polkit prior to 0.106 then it is only
+ possible to validate (user, permission) pairs via the <code>.pkla</code>
+ files. Fully validation of the (user, permission, object) triple
+ requires the new JavaScript <code>.rules</code> support that
+ was introduced in version 0.106. The latter is what will be
+ described here.
+ </p>
+
+ <p>
+ Libvirt does not ship any rules files by default. It merely
+ provides a definition of the default behaviour for each
+ action (permission). As noted earlier, permissions which
+ correspond to read-only operations in libvirt will be allowed
+ to all users by default; everything else is denied by default.
+ Defining custom rules requires creation of a file in the
+ <code>/etc/polkit-1/rules.d</code> directory with a name
+ chosen by the administrator (<code>100-libvirt-acl.rules</code>
+ would be a reasonable choice). See the <code>polkit(8)</code>
+ manual page for a description of how to write these files
+ in general. The key idea is to create a file containing
+ something like
+ </p>
+
+ <pre>
+ polkit.addRule(function(action, subject) {
+ ....logic to check 'action' and 'subject'...
+ });
+ </pre>
+
+ <p>
+ In this code snippet above, the <code>action</code> object
+ instance will represent the libvirt permission being checked
+ along with identifying attributes for the object it is being
+ applied to. The <code>subject</code> meanwhile will identify
+ the libvirt client app (with the caveat above about it only
+ dealing with local clients connected via the UNIX socket).
+ On the <code>action</code> object, the permission name is
+ accessible via the <code>id</code> attribute, while the
+ object identifying attributes are exposed via a set of
+ attributes with the naming convention <code>_detail_[attrname]</code>.
+ For example, the 'domain_name' attribute would be exposed via
+ a property <code>_detail_domain_name</code>.
+ </p>
+
+ <h3><a name="exconnect">Example: restricting ability to connect to drivers</a></h3>
+
+ <p>
+ Consider a local user <code>berrange</code>
+ who has been granted permission to connect to libvirt in
+ full read-write mode. The goal is to only allow them to
+ use the <code>QEMU</code> driver and not the Xen or LXC
+ drivers which are also available in libvirtd.
+ To achieve this we need to write a rule which checks
+ whether the <code>_detail_connect_driver</code> attribute
+ is <code>QEMU</code>, and match on an action
+ name of <code>org.libvirt.api.connect.getattr</code>. Using
+ the javascript rules format, this ends up written as
+ </p>
+
+ <pre>
+polkit.addRule(function(action, subject) {
+ if (action.id == "org.libvirt.api.connect.getattr" &&
+ subject.user == "berrange") {
+ if (action._detail_connect_driver == 'QEMU') {
+ return polkit.Result.YES;
+ } else {
+ return polkit.Result.NO;
+ }
+ }
+});
+ </pre>
+
+ <h3><a name="exdomain">Example: restricting access to a single domain</a></h3>
+
+ <p>
+ Consider a local user <code>berrange</code>
+ who has been granted permission to connect to libvirt in
+ full read-write mode. The goal is to only allow them to
+ see the domain called <code>demo</code> on the LXC driver.
+ To achieve this we need to write a rule which checks
+ whether the <code>_detail_connect_driver</code> attribute
+ is <code>LXC</code> and the <code>_detail_domain_name</code>
+ attribute is <code>demo</code>, and match on a action
+ name of <code>org.libvirt.api.domain.getattr</code>. Using
+ the javascript rules format, this ends up written as
+ </p>
+
+ <pre>
+polkit.addRule(function(action, subject) {
+ if (action.id == "org.libvirt.api.domain.getattr" &&
+ subject.user == "berrange") {
+ if (action._detail_connect_driver == 'LXC' &&
+ action._detail_domain_name == 'busy') {
+ return polkit.Result.YES;
+ } else {
+ return polkit.Result.NO;
+ }
+ }
+});
+ </pre>
+ </body>
+</html>
--- /dev/null
+#!/usr/bin/perl
+#
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+use warnings;
+
+my @objects = (
+ "CONNECT", "DOMAIN", "INTERFACE",
+ "NETWORK","NODE_DEVICE", "NWFILTER",
+ "SECRET", "STORAGE_POOL", "STORAGE_VOL",
+ );
+
+my %class;
+
+foreach my $object (@objects) {
+ my $class = lc $object;
+
+ $class =~ s/(^\w|_\w)/uc $1/eg;
+ $class =~ s/_//g;
+ $class =~ s/Nwfilter/NWFilter/;
+ $class = "vir" . $class . "Ptr";
+
+ $class{$object} = $class;
+}
+
+my $objects = join ("|", @objects);
+
+my %opts;
+my $in_opts = 0;
+
+my %perms;
+
+while (<>) {
+ if ($in_opts) {
+ if (m,\*/,) {
+ $in_opts = 0;
+ } elsif (/\*\s*\@(\w+):\s*(.*?)\s*$/) {
+ $opts{$1} = $2;
+ }
+ } elsif (m,/\*\*,) {
+ $in_opts = 1;
+ } elsif (/VIR_ACCESS_PERM_($objects)_((?:\w|_)+),/) {
+ my $object = $1;
+ my $perm = lc $2;
+ next if $perm eq "last";
+
+ $perm =~ s/_/-/g;
+
+ $perms{$object} = {} unless exists $perms{$object};
+ $perms{$object}->{$perm} = {
+ desc => $opts{desc},
+ message => $opts{message},
+ anonymous => $opts{anonymous}
+ };
+ %opts = ();
+ }
+}
+
+print <<EOF;
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <body>
+EOF
+
+foreach my $object (sort { $a cmp $b } keys %perms) {
+ my $class = $class{$object};
+ my $olink = lc "object_" . $object;
+ print <<EOF;
+<h3><a name="$olink">$class</a></h3>
+<table class="acl">
+ <thead>
+ <tr>
+ <th>Permission</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+EOF
+
+ foreach my $perm (sort { $a cmp $b } keys %{$perms{$object}}) {
+ my $description = $perms{$object}->{$perm}->{desc};
+
+ die "missing description for $object.$perm" unless
+ defined $description;
+
+ my $plink = lc "perm_" . $object . "_" . $perm;
+ $plink =~ s/-/_/g;
+
+ print <<EOF;
+ <tr>
+ <td><a name="$plink">$perm</a></td>
+ <td>$description</td>
+ </tr>
+EOF
+
+ }
+
+ print <<EOF;
+ </tbody>
+</table>
+EOF
+}
+
+print <<EOF;
+ </body>
+</html>
+EOF