Signed-off-by: Erik Skultety <eskultet@xxxxxxxxxx> --- docs/aclpolkit.html.in | 523 ----------------------------------------- docs/aclpolkit.rst | 310 ++++++++++++++++++++++++ docs/meson.build | 2 +- 3 files changed, 311 insertions(+), 524 deletions(-) delete mode 100644 docs/aclpolkit.html.in create mode 100644 docs/aclpolkit.rst diff --git a/docs/aclpolkit.html.in b/docs/aclpolkit.html.in deleted file mode 100644 index a82001187e..0000000000 --- a/docs/aclpolkit.html.in +++ /dev/null @@ -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" && - 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> diff --git a/docs/aclpolkit.rst b/docs/aclpolkit.rst new file mode 100644 index 0000000000..09e5d9ea27 --- /dev/null +++ b/docs/aclpolkit.rst @@ -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; + } + } + }); diff --git a/docs/meson.build b/docs/meson.build index 5536005125..0f402bbf6a 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -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', -- 2.29.2