docs: html.in: Convert aclpolkit to rst

Signed-off-by: Erik Skultety <eskultet@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
This commit is contained in:
Erik Skultety 2021-03-12 11:12:47 +01:00
parent 10bf55c99f
commit d91482807e
3 changed files with 311 additions and 524 deletions

View File

@ -1,523 +0,0 @@
<?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" &amp;&amp;
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" &amp;&amp;
subject.user == "berrange") {
if (action.lookup("connect_driver") == 'LXC' &amp;&amp;
action.lookup("domain_name") == 'demo') {
return polkit.Result.YES;
} else {
return polkit.Result.NO;
}
}
});
</pre>
</body>
</html>

310
docs/aclpolkit.rst Normal file
View File

@ -0,0 +1,310 @@
.. role:: since
=====================
Polkit access control
=====================
Libvirt's client `access control framework <acl.html>`__ 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.
.. contents::
Introduction
------------
A default install of libvirt will typically use
`polkit <https://www.freedesktop.org/wiki/Software/polkit/>`__ 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.
Permission names
----------------
The libvirt `object names and permission names <acl.html#perms>`__ are
mapped onto polkit action names using the simple pattern:
::
org.libvirt.api.$object.$permission
The only caveat is that any underscore characters in the object or
permission names are converted to hyphens. So, for example, the
``search_storage_vols`` permission on the ``storage_pool`` object maps
to the polkit action:
::
org.libvirt.api.storage-pool.search-storage-vols
The default policy for any permission which corresponds to a "read only"
operation, is to allow access. All other permissions default to deny
access.
Object identity attributes
--------------------------
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
virConnectPtr
~~~~~~~~~~~~~
============== =====================================
Attribute Description
============== =====================================
connect_driver Name of the libvirt connection driver
============== =====================================
virDomainPtr
~~~~~~~~~~~~
============== ============================================
Attribute Description
============== ============================================
connect_driver Name of the libvirt connection driver
domain_name Name of the domain, unique to the local host
domain_uuid UUID of the domain, globally unique
============== ============================================
virInterfacePtr
~~~~~~~~~~~~~~~
+-------------------+---------------------------------------------------------+
| Attribute | Description |
+===================+=========================================================+
| connect_driver | Name of the libvirt connection driver |
+-------------------+---------------------------------------------------------+
| interface_name | Name of the network interface, unique to the local host |
+-------------------+---------------------------------------------------------+
| interface_macaddr | MAC address of the network interface, not unique |
+-------------------+---------------------------------------------------------+
virNetworkPtr
~~~~~~~~~~~~~
============== =============================================
Attribute Description
============== =============================================
connect_driver Name of the libvirt connection driver
network_name Name of the network, unique to the local host
network_uuid UUID of the network, globally unique
============== =============================================
virNodeDevicePtr
~~~~~~~~~~~~~~~~
================ =================================================
Attribute Description
================ =================================================
connect_driver Name of the libvirt connection driver
node_device_name Name of the node device, unique to the local host
================ =================================================
virNWFilterPtr
~~~~~~~~~~~~~~
============== ====================================================
Attribute Description
============== ====================================================
connect_driver Name of the libvirt connection driver
nwfilter_name Name of the network filter, unique to the local host
nwfilter_uuid UUID of the network filter, globally unique
============== ====================================================
virSecretPtr
~~~~~~~~~~~~
=================== ===========================================
Attribute Description
=================== ===========================================
connect_driver Name of the libvirt connection driver
secret_uuid UUID of the secret, globally unique
secret_usage_volume Name of the associated volume, if any
secret_usage_ceph Name of the associated Ceph server, if any
secret_usage_target Name of the associated iSCSI target, if any
secret_usage_name Name of the associated TLS secret, if any
=================== ===========================================
virStoragePoolPtr
~~~~~~~~~~~~~~~~~
============== ==================================================
Attribute Description
============== ==================================================
connect_driver Name of the libvirt connection driver
pool_name Name of the storage pool, unique to the local host
pool_uuid UUID of the storage pool, globally unique
============== ==================================================
virStorageVolPtr
~~~~~~~~~~~~~~~~
============== ==================================================
Attribute Description
============== ==================================================
connect_driver Name of the libvirt connection driver
pool_name Name of the storage pool, unique to the local host
pool_uuid UUID of the storage pool, globally unique
vol_name Name of the storage volume, unique to the pool
vol_key Key of the storage volume, globally unique
============== ==================================================
Hypervisor Driver connect_driver
--------------------------------
The ``connect_driver`` parameter describes the client's `remote
Connection Driver <remote.html>`__ name based on the `URI <uri.html>`__
used for the connection.
:since:`Since 4.1.0`, 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
``connect_driver`` context of the secondary connection driver rather
than in the context of the primary driver. This affects the
``connect_driver`` returned from rule generation from the
``action.loookup`` function. The following table provides a list of the
various connection drivers and the ``connect_driver`` 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.
Connection Driver Name
~~~~~~~~~~~~~~~~~~~~~~
================= =======================
Connection Driver ``connect_driver`` name
================= =======================
bhyve bhyve
esx ESX
hyperv Hyper-V
interface interface
xen Xen
lxc LXC
network network
nodedev nodedev
nwfilter NWFilter
openvz OPENVZ
qemu QEMU
secret secret
storage storage
vbox VBOX
vmware VMWARE
vz vz
================= =======================
User identity attributes
------------------------
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.
Writing access control policies
-------------------------------
If using versions of polkit prior to 0.106 then it is only possible to
validate (user, permission) pairs via the ``.pkla`` files. Fully
validation of the (user, permission, object) triple requires the new
JavaScript ``.rules`` support that was introduced in version 0.106. The
latter is what will be described here.
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 ``/etc/polkit-1/rules.d`` directory with a name chosen by the
administrator (``100-libvirt-acl.rules`` would be a reasonable choice).
See the ``polkit(8)`` manual page for a description of how to write
these files in general. The key idea is to create a file containing
something like
::
polkit.addRule(function(action, subject) {
....logic to check 'action' and 'subject'...
});
In this code snippet above, the ``action`` object instance will
represent the libvirt permission being checked along with identifying
attributes for the object it is being applied to. The ``subject``
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 ``action`` object, the permission name is accessible via the
``id`` attribute, while the object identifying attributes are exposed
via the ``lookup`` method.
See `source
code <https://gitlab.com/libvirt/libvirt/-/tree/master/examples/polkit>`__
for a more complex example.
Example: restricting ability to connect to drivers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider a local user ``berrange`` who has been granted permission to
connect to libvirt in full read-write mode. The goal is to only allow
them to use the ``QEMU`` 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 ``connect_driver`` attribute is ``QEMU``, and
match on an action name of ``org.libvirt.api.connect.getattr``. Using
the javascript rules format, this ends up written as
::
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;
}
}
});
Example: restricting access to a single domain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider a local user ``berrange`` 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 ``demo`` on the LXC driver. To achieve
this we need to write a rule which checks whether the ``connect_driver``
attribute is ``LXC`` and the ``domain_name`` attribute is ``demo``, and
match on an action name of ``org.libvirt.api.domain.getattr``. Using the
javascript rules format, this ends up written as
::
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;
}
}
});

View File

@ -32,7 +32,6 @@ docs_assets = [
docs_html_in_files = [
'404',
'aclpolkit',
'api_extension',
'api',
'apps',
@ -106,6 +105,7 @@ docs_html_in_files = [
]
docs_rst_files = [
'aclpolkit',
'advanced-tests',
'best-practices',
'ci',