From da13f2c70c06c38c4f8feba1a5725652d2f1efc7 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 8 Aug 2013 11:51:01 +0100 Subject: [PATCH] Add documentation for access control system 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
which will cause the XSL transform to replace that
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 --- .gitignore | 1 + docs/Makefile.am | 12 +- docs/acl.html.in | 100 ++++++++++ docs/aclpolkit.html.in | 410 +++++++++++++++++++++++++++++++++++++++++ docs/auth.html.in | 6 +- docs/genaclperms.pl | 124 +++++++++++++ docs/newapi.xsl | 4 +- docs/page.xsl | 11 ++ docs/sitemap.html.in | 10 + src/Makefile.am | 2 +- 10 files changed, 674 insertions(+), 6 deletions(-) create mode 100644 docs/acl.html.in create mode 100644 docs/aclpolkit.html.in create mode 100755 docs/genaclperms.pl diff --git a/.gitignore b/.gitignore index bc1c2378e3..8a94748e09 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ /daemon/libvirtd.policy /daemon/libvirtd.service /daemon/test_libvirtd.aug +/docs/aclperms.htmlinc /docs/apibuild.py.stamp /docs/devhelp/libvirt.devhelp /docs/hvsupport.html.in diff --git a/docs/Makefile.am b/docs/Makefile.am index aabee442a0..0b0d2d454c 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -128,7 +128,7 @@ fig = \ migration-unmanaged-direct.fig EXTRA_DIST= \ - apibuild.py \ + apibuild.py genaclperms.pl \ site.xsl newapi.xsl news.xsl page.xsl \ hacking1.xsl hacking2.xsl wrapstring.xsl \ $(dot_html) $(dot_html_in) $(gif) $(apihtml) $(apipng) \ @@ -139,6 +139,16 @@ EXTRA_DIST= \ sitemap.html.in \ todo.pl hvsupport.pl todo.cfg-example +BUILT_SOURCES += aclperms.htmlinc + +CLEANFILES = $(srcdir)/aclperms.htmlinc + +acl.html:: $(srcdir)/aclperms.htmlinc + +$(srcdir)/aclperms.htmlinc: $(top_srcdir)/src/access/viraccessperm.h \ + $(srcdir)/genaclperms.pl Makefile.am + $(PERL) $(srcdir)/genaclperms.pl $< > $@ + MAINTAINERCLEANFILES = \ $(addprefix $(srcdir)/,$(dot_html)) \ $(addprefix $(srcdir)/,$(apihtml)) \ diff --git a/docs/acl.html.in b/docs/acl.html.in new file mode 100644 index 0000000000..2d228e2ecf --- /dev/null +++ b/docs/acl.html.in @@ -0,0 +1,100 @@ + + + + +

Client access control

+

+ Libvirt's client access control framework 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. +

+ +
    + +

    Access control introduction

    + +

    + In a default configuration, the libvirtd daemon has three levels + 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. +

    + +

    + 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 + virDomainSetSchedulerParametersFlags method will + check whether the client user has the write + permission on the domain 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 in to an API call, some methods + will filter their results. For example the virConnectListAllDomains + method will check the search_domains on the connect + object, but will also filter the returned domain + objects to only those on which the client user has the + getattr permission. +

    + +

    Access control drivers

    + +

    + The access control framework is designed as a pluggable + system to enable future integration with arbitrary access + control technologies. By default, the none + driver is used, which does no access control checks at + all. At this time, libvirt ships with support for using + polkit as a real access + control driver. To learn how to use the polkit access + driver consult the configuration + docs. +

    + +

    + The access driver is configured in the libvirtd.conf + configuration file, using the access_drivers + 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: +

    + +
    +# augtool -s set '/files/etc/libvirt/libvirtd.conf/access_drivers[1]' polkit
    +    
    + +

    + And to reset back to the default (no-op) driver +

    + + +
    +# augtool -s rm /files/etc/libvirt/libvirtd.conf/access_drivers
    +    
    + +

    + Note: changes to libvirtd.conf require that + the libvirtd daemon be restarted. +

    + +

    Objects and permissions

    + +

    + 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 + API reference manual + documentation for the API in question. +

    + +
    + + + diff --git a/docs/aclpolkit.html.in b/docs/aclpolkit.html.in new file mode 100644 index 0000000000..3b0d81a87b --- /dev/null +++ b/docs/aclpolkit.html.in @@ -0,0 +1,410 @@ + + + + +

    Polkit access control

    + +

    + Libvirt's client access control framework 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. +

    + +
      + +

      Introduction

      + +

      + A default install of libvirt will typically use + 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 + 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

      + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      + +

      virDomainPtr

      + + + + + + + + + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      domain_nameName of the domain, unique to the local host
      domain_uuidUUID of the domain, globally unique
      + +

      virInterfacePtr

      + + + + + + + + + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      interface_nameName of the network interface, unique to the local host
      interface_macMAC address of the network interface, not unique
      + +

      virNetworkPtr

      + + + + + + + + + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      network_nameName of the network, unique to the local host
      network_uuidUUID of the network, globally unique
      + +

      virNodeDevicePtr

      + + + + + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      node_device_nameName of the node device, unique to the local host
      + +

      virNWFilterPtr

      + + + + + + + + + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      nwfilter_nameName of the network filter, unique to the local host
      nwfilter_uuidUUID of the network filter, globally unique
      + +

      virSecretPtr

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      secret_uuidUUID of the secret, globally unique
      secret_usage_volumeName of the associated volume, if any
      secret_usage_cephName of the associated Ceph server, if any
      secret_usage_targetName of the associated iSCSI target, if any
      + +

      virStoragePoolPtr

      + + + + + + + + + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      pool_nameName of the storage pool, unique to the local host
      pool_uuidUUID of the storage pool, globally unique
      + +

      virStorageVolPtr

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      AttributeDescription
      connect_driverName of the libvirt connection driver
      pool_nameName of the storage pool, unique to the local host
      pool_uuidUUID of the storage pool, globally unique
      vol_nameName of the storage volume, unique to the pool
      vol_keyKey of the storage volume, globally unique
      + + +

      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 acces 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 a set of + attributes with the naming convention _detail_[attrname]. + For example, the 'domain_name' attribute would be exposed via + a property _detail_domain_name. +

      + +

      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 _detail_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._detail_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 _detail_connect_driver attribute + is LXC and the _detail_domain_name + attribute is demo, and match on a 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._detail_connect_driver == 'LXC' &&
      +              action._detail_domain_name == 'busy') {
      +            return polkit.Result.YES;
      +          } else {
      +            return polkit.Result.NO;
      +          }
      +    }
      +});
      +    
      + + diff --git a/docs/auth.html.in b/docs/auth.html.in index e5703c7f6d..37f2978707 100644 --- a/docs/auth.html.in +++ b/docs/auth.html.in @@ -2,12 +2,14 @@ -

      Authentication & access control

      +

      Connection authentication

      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 access control to the operations + performed by a client.

        diff --git a/docs/genaclperms.pl b/docs/genaclperms.pl new file mode 100755 index 0000000000..244a68e600 --- /dev/null +++ b/docs/genaclperms.pl @@ -0,0 +1,124 @@ +#!/usr/bin/perl +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# . +# + +use strict; +use warnings; + +my @objects = ( + "CONNECT", "DOMAIN", "INTERFACE", + "NETWORK","NODE_DEVICE", "NWFILTER", + "SECRET", "STORAGE_POOL", "STORAGE_VOL", + ); + +my %class; + +foreach my $object (@objects) { + my $class = lc $object; + + $class =~ s/(^\w|_\w)/uc $1/eg; + $class =~ s/_//g; + $class =~ s/Nwfilter/NWFilter/; + $class = "vir" . $class . "Ptr"; + + $class{$object} = $class; +} + +my $objects = join ("|", @objects); + +my %opts; +my $in_opts = 0; + +my %perms; + +while (<>) { + if ($in_opts) { + if (m,\*/,) { + $in_opts = 0; + } elsif (/\*\s*\@(\w+):\s*(.*?)\s*$/) { + $opts{$1} = $2; + } + } elsif (m,/\*\*,) { + $in_opts = 1; + } elsif (/VIR_ACCESS_PERM_($objects)_((?:\w|_)+),/) { + my $object = $1; + my $perm = lc $2; + next if $perm eq "last"; + + $perm =~ s/_/-/g; + + $perms{$object} = {} unless exists $perms{$object}; + $perms{$object}->{$perm} = { + desc => $opts{desc}, + message => $opts{message}, + anonymous => $opts{anonymous} + }; + %opts = (); + } +} + +print < + + + +EOF + +foreach my $object (sort { $a cmp $b } keys %perms) { + my $class = $class{$object}; + my $olink = lc "object_" . $object; + print <$class + + + + + + + + +EOF + + foreach my $perm (sort { $a cmp $b } keys %{$perms{$object}}) { + my $description = $perms{$object}->{$perm}->{desc}; + + die "missing description for $object.$perm" unless + defined $description; + + my $plink = lc "perm_" . $object . "_" . $perm; + $plink =~ s/-/_/g; + + print < + + + +EOF + + } + + print < +
        PermissionDescription
        $perm$description
        +EOF +} + +print < + +EOF diff --git a/docs/newapi.xsl b/docs/newapi.xsl index 58f12ebcff..606d24436b 100644 --- a/docs/newapi.xsl +++ b/docs/newapi.xsl @@ -71,8 +71,8 @@ - - + + diff --git a/docs/page.xsl b/docs/page.xsl index 7da34ff5a1..a2da85416a 100644 --- a/docs/page.xsl +++ b/docs/page.xsl @@ -26,6 +26,10 @@ + + + + @@ -174,4 +178,11 @@ + + + + + + + diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in index fd10caff46..a8d2177983 100644 --- a/docs/sitemap.html.in +++ b/docs/sitemap.html.in @@ -68,6 +68,16 @@ Authentication Configure authentication for the libvirt daemon +
      • + Access control + Configure access control libvirt APIs + +
      • Migration Migrating guests between machines diff --git a/src/Makefile.am b/src/Makefile.am index 6c13d805df..7e24c1f2d8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1658,7 +1658,7 @@ test_libvirt_lockd.aug: locking/test_libvirt_lockd.aug.in \ test_virtlockd.aug: locking/test_virtlockd.aug.in \ locking/virtlockd.conf $(AUG_GENTEST) - $(AM_V_GEN)$(AUG_GENTEST) locking/virtlockd.conf $< $@ + $(AM_V_GEN)$(AUG_GENTEST) $(srcdir)/locking/virtlockd.conf $< $@ check-augeas-lockd: test_libvirt_lockd.aug $(AM_V_GEN)if test -x '$(AUGPARSE)'; then \ -- GitLab