mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-29 03:51:17 +00:00
Use https: links for websites that support them. The URIs which are used as namespace identifiers are left alone. Signed-off-by: Ján Tomko <jtomko@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com> Reviewed-by: Neal Gompa <ngompa13@gmail.com>
524 lines
16 KiB
XML
524 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="https://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>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
<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>xen</td>
|
|
<td>Xen</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>qemu</td>
|
|
<td>QEMU</td>
|
|
</tr>
|
|
<tr>
|
|
<td>secret</td>
|
|
<td>secret</td>
|
|
</tr>
|
|
<tr>
|
|
<td>storage</td>
|
|
<td>storage</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>
|
|
</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://gitlab.com/libvirt/libvirt/-/tree/master/examples/polkit">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>
|