[libvirt] [PATCH] Add documentation for access control system

Eric Blake eblake at redhat.com
Fri Aug 9 15:47:46 UTC 2013


On 08/08/2013 05:27 AM, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at redhat.com>
> 
> This adds two new pages to the website, acl.html describing
> the general access control framework and permissions models,
> and aclpolkit.html describing the use of polkit as an
> access control driver.
> 
> page.xsl is modified to support a new syntax
> 
>   <div id="include" filename="somefile.htmlinc"/>
> 
> which will cause the XSL transform to replace that <div>
> with the contents of 'somefile.htmlinc'. We use this in
> the acl.html.in file, to pull the table of permissions
> for each libvirt object. This table is autogenerated
> from the enums in src/access/viraccessperms.h by the
> genaclperms.pl script.
> 
> newapi.xsl is modified so that the list of permissions
> checks shown against each API will link to the description
> of the permissions in acl.html
> 
> Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
> ---
>  .gitignore             |   1 +
>  docs/Makefile.am       |  12 +-
>  docs/acl.html.in       | 100 ++++++++++++
>  docs/aclpolkit.html.in | 414 +++++++++++++++++++++++++++++++++++++++++++++++++
>  docs/auth.html.in      |   6 +-
>  docs/genaclperms.pl    | 124 +++++++++++++++
>  docs/newapi.xsl        |   4 +-
>  docs/page.xsl          |  11 ++
>  docs/sitemap.html.in   |  10 ++
>  9 files changed, 677 insertions(+), 5 deletions(-)
>  create mode 100644 docs/acl.html.in
>  create mode 100644 docs/aclpolkit.html.in
>  create mode 100644 docs/genaclperms.pl

[I still need to look into why, in a clean checkout, 'make distcheck'
fails when 'make all distcheck' passes, but that shoudln't hold up this
patch, whether or not this patch aggravates the issue]


> +aclperms.htmlinc: $(top_srcdir)/src/access/viraccessperm.h \
> +        genaclperms.pl Makefile.am
> +	$(PERL) genaclperms.pl $< > $@

Did you test a VPATH build?


> +    <p>
> +      In a default configuration, the libvirtd daemon have three levels

s/have/has/

> +      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 into an API call, some methods

s/into/in to/

> +      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 not access controll checks at

s/not/no/; s/controll/control/

> +      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.

Isn't sending SIGHUP sufficient, or does it have to be a full restart?

> +    </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>
> diff --git a/docs/aclpolkit.html.in b/docs/aclpolkit.html.in
> new file mode 100644
> index 0000000..d7be0bd
> --- /dev/null
> +++ b/docs/aclpolkit.html.in
> @@ -0,0 +1,414 @@
> +<?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

s/$/./

> +    </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

s/though/though,/

> +      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
> +      an "read only" operation, is to allow access. All other

s/an/a/

> +      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">

> +
> +    <h3><a name="object_secret">virSecretPtr</a></h3>
> +    <table class="acl">

> +        <tr>
> +          <td></td>
> +          <td></td>
> +        </tr>

Drop the empty row.

> +
> +    <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 & access will be denied.

s/&/and/

> +      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
> +      SASAL / Kerberos username and/or x509 distinguished

s/SASAL/SASL/

> +      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. That latter is what will be

s/That/The/

> +      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 a 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;
> +          }
> +    }

This function has no return statement when the initial 'if' is not
satisfied; is that valid?

> +});
> +    </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

s/a /an /

> +      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;
> +          }
> +    }

Again, what happens when you fall off the end of a rule without a return?

> +});
> +    </pre>
> +  </body>
> +</html>
> diff --git a/docs/auth.html.in b/docs/auth.html.in
> index e5703c7..37f2978 100644
> --- a/docs/auth.html.in
> +++ b/docs/auth.html.in
> @@ -2,12 +2,14 @@
>  <!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 >Authentication & access control</h1>
> +    <h1>Connection authentication</h1>
>      <p>
>        When connecting to libvirt, some connections may require client
>        authentication before allowing use of the APIs. The set of possible
>        authentication mechanisms is administrator controlled, independent
> -      of applications using libvirt.
> +      of applications using libvirt. Once authenticated, libvirt can apply
> +      fine grained <a href="acl.html">access control</a> to the operations
> +      performed by a client.
>      </p>
>  
>      <ul id="toc"></ul>
> diff --git a/docs/genaclperms.pl b/docs/genaclperms.pl
> new file mode 100644
> index 0000000..244a68e
> --- /dev/null
> +++ b/docs/genaclperms.pl

'chmod +x' before pushing, to match the permissions of all our other .pl
files.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 621 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20130809/44774a2c/attachment-0001.sig>


More information about the libvir-list mailing list