mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-23 14:15:28 +00:00
4f1107614d
https://bugzilla.redhat.com/show_bug.cgi?id=1631606
Since commit 8259255
usage of a primary connection driver for
a virConnect has been modified to open (virConnectOpen) and use
a connection to the specific driver in order to handle the API
calls to/for that driver. This causes some confusion and issues
for ACL polkit rule scripts to know exactly which driver by
name will be used.
Add some documentation describing the processing of the primary
and secondary connection as well as the list of the connect_driver
names used for each driver.
Signed-off-by: John Ferlan <jferlan@redhat.com>
ACKed-by: Michal Privoznik <mprivozn@redhat.com>
536 lines
16 KiB
XML
536 lines
16 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE html>
|
|
<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 id="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 id="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 id="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 id="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 id="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 id="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_macaddr</td>
|
|
<td>MAC address of the network interface, not unique</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3><a id="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 id="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 id="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 id="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>
|
|
<tr>
|
|
<td>secret_usage_name</td>
|
|
<td>Name of the associated TLS secret, if any</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3><a id="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 id="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 id="connect_driver">Hypervisor Driver connect_driver</a></h2>
|
|
<p>
|
|
The <code>connect_driver</code> parameter describes the
|
|
client's <a href="remote.html">remote Connection Driver</a>
|
|
name based on the <a href="uri.html">URI</a> used for the
|
|
connection.
|
|
</p>
|
|
<p>
|
|
<span class="since">Since 4.1.0</span>, when calling an API
|
|
outside the scope of the primary connection driver, the
|
|
primary driver will attempt to open a secondary connection
|
|
to the specific API driver in order to process the API. For
|
|
example, when hypervisor domain processing needs to make an
|
|
API call within the storage driver or the network filter driver
|
|
an attempt to open a connection to the "storage" or "nwfilter"
|
|
driver will be made. Similarly, a "storage" primary connection
|
|
may need to create a connection to the "secret" driver in order
|
|
to process secrets for the API. If successful, then calls to
|
|
those API's will occur in the <code>connect_driver</code> context
|
|
of the secondary connection driver rather than in the context of
|
|
the primary driver. This affects the <code>connect_driver</code>
|
|
returned from rule generation from the <code>action.loookup</code>
|
|
function. The following table provides a list of the various
|
|
connection drivers and the <code>connect_driver</code> name
|
|
used by each regardless of primary or secondary connection.
|
|
The access denied error message from libvirt will list the
|
|
connection driver by name that denied the access.
|
|
</p>
|
|
|
|
<h3><a id="object_connect_driver">Connection Driver Name</a></h3>
|
|
<table class="acl">
|
|
<thead>
|
|
<tr>
|
|
<th>Connection Driver</th>
|
|
<th><code>connect_driver</code> name</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>bhyve</td>
|
|
<td>bhyve</td>
|
|
</tr>
|
|
<tr>
|
|
<td>esx</td>
|
|
<td>ESX</td>
|
|
</tr>
|
|
<tr>
|
|
<td>hyperv</td>
|
|
<td>Hyper-V</td>
|
|
</tr>
|
|
<tr>
|
|
<td>interface</td>
|
|
<td>interface</td>
|
|
</tr>
|
|
<tr>
|
|
<td>libxl</td>
|
|
<td>xenlight</td>
|
|
</tr>
|
|
<tr>
|
|
<td>lxc</td>
|
|
<td>LXC</td>
|
|
</tr>
|
|
<tr>
|
|
<td>network</td>
|
|
<td>network</td>
|
|
</tr>
|
|
<tr>
|
|
<td>nodedev</td>
|
|
<td>nodedev</td>
|
|
</tr>
|
|
<tr>
|
|
<td>nwfilter</td>
|
|
<td>NWFilter</td>
|
|
</tr>
|
|
<tr>
|
|
<td>openvz</td>
|
|
<td>OPENVZ</td>
|
|
</tr>
|
|
<tr>
|
|
<td>phyp</td>
|
|
<td>PHYP</td>
|
|
</tr>
|
|
<tr>
|
|
<td>qemu</td>
|
|
<td>QEMU</td>
|
|
</tr>
|
|
<tr>
|
|
<td>secret</td>
|
|
<td>secret</td>
|
|
</tr>
|
|
<tr>
|
|
<td>storage</td>
|
|
<td>storage</td>
|
|
</tr>
|
|
<tr>
|
|
<td>uml</td>
|
|
<td>UML</td>
|
|
</tr>
|
|
<tr>
|
|
<td>vbox</td>
|
|
<td>VBOX</td>
|
|
</tr>
|
|
<tr>
|
|
<td>vmware</td>
|
|
<td>VMWARE</td>
|
|
</tr>
|
|
<tr>
|
|
<td>vz</td>
|
|
<td>vz</td>
|
|
</tr>
|
|
<tr>
|
|
<td>xenapi</td>
|
|
<td>XenAPI</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
|
|
<h2><a id="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 id="checks">Writing access 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 the
|
|
<code>lookup</code> method.
|
|
</p>
|
|
|
|
<p>
|
|
See
|
|
<a href="https://libvirt.org/git/?p=libvirt.git;a=tree;f=examples/polkit;hb=HEAD">source code</a>
|
|
for a more complex example.
|
|
</p>
|
|
|
|
<h3><a id="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>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.lookup("connect_driver") == 'QEMU') {
|
|
return polkit.Result.YES;
|
|
} else {
|
|
return polkit.Result.NO;
|
|
}
|
|
}
|
|
});
|
|
</pre>
|
|
|
|
<h3><a id="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>connect_driver</code> attribute
|
|
is <code>LXC</code> and the <code>domain_name</code>
|
|
attribute is <code>demo</code>, and match on an 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.lookup("connect_driver") == 'LXC' &&
|
|
action.lookup("domain_name") == 'demo') {
|
|
return polkit.Result.YES;
|
|
} else {
|
|
return polkit.Result.NO;
|
|
}
|
|
}
|
|
});
|
|
</pre>
|
|
</body>
|
|
</html>
|