[libvirt] ACLs for libvirt

Hi all, I'd like to implement this new feature for libvirt. However, I think we should settle down on design first. My biggest concern is choosing the right level on on which ACLs will be implemented. Should be interested only in (user, API), or with more granularity (user, API, API's parameters)? Or should we take the RBAC path? How should we even identify and authorize users? My initial though is to create framework which can be used then to implement ACLs on any level we want. What's our opinion? Michal

On Wed, Nov 23, 2011 at 06:17:46PM +0100, Michal Privoznik wrote:
Hi all,
I'd like to implement this new feature for libvirt. However, I think we should settle down on design first. My biggest concern is choosing the right level on on which ACLs will be implemented. Should be interested only in (user, API), or with more granularity (user, API, API's parameters)? Or should we take the RBAC path? How should we even identify and authorize users?
My initial though is to create framework which can be used then to implement ACLs on any level we want.
My $.02 is that this has the potential to be such a huge project that we need to come up with a design that lets us bite off as much as we want at one time, leaving the rest of libvirt's functionality operating as it always has. So, for example, we could start by creating an ACL that would let a particular user boot and shutdown but not edit a VM; later when we wanted to let particular users connect to the VM's console, we could implement that, and so on. It needs to be done in a consistent way so that future additions are clearly slotted into the existing design. Dave
What's our opinion?
Michal
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Wed, Nov 23, 2011 at 12:27:27PM -0500, Dave Allan wrote:
On Wed, Nov 23, 2011 at 06:17:46PM +0100, Michal Privoznik wrote:
Hi all,
I'd like to implement this new feature for libvirt. However, I think we should settle down on design first. My biggest concern is choosing the right level on on which ACLs will be implemented. Should be interested only in (user, API), or with more granularity (user, API, API's parameters)? Or should we take the RBAC path? How should we even identify and authorize users?
My initial though is to create framework which can be used then to implement ACLs on any level we want.
My $.02 is that this has the potential to be such a huge project that we need to come up with a design that lets us bite off as much as we want at one time, leaving the rest of libvirt's functionality operating as it always has. So, for example, we could start by creating an ACL that would let a particular user boot and shutdown but not edit a VM; later when we wanted to let particular users connect to the VM's console, we could implement that, and so on. It needs to be done in a consistent way so that future additions are clearly slotted into the existing design.
I tend to agree, I think like for the security module, we should provide an access control driver API with exhaustive parameters, but as a first step implement a simple user based driver. We just need to make sure that it would allow to implement a full RBAC kind of driver. It seems to me that the driver just need to plug in the API entry points, i.e. mostly libvirt.c. I would probably not pass all APIs parameters at the API level but some function ID, and main arguments: - connection - domain - network - interface - pool - vol - device - snapshot - secret - stream - ... - and some kind of string (could be used for xml, uuid, device path, or migration URI for example) The first use of that driver could be to implemenent the read-only mode of connections internally. Might indicate that being able to stack multiple access control driver as an interesting feature (i.e. how to finer restrict read-only mode with extra rules). I think a lot of the code in the driver front-end should be fairly mechanical ... dunno if we should generate it from the API description though, just an idea. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Thu, Nov 24, 2011 at 02:08:31PM +0800, Daniel Veillard wrote:
On Wed, Nov 23, 2011 at 12:27:27PM -0500, Dave Allan wrote:
On Wed, Nov 23, 2011 at 06:17:46PM +0100, Michal Privoznik wrote:
Hi all,
I'd like to implement this new feature for libvirt. However, I think we should settle down on design first. My biggest concern is choosing the right level on on which ACLs will be implemented. Should be interested only in (user, API), or with more granularity (user, API, API's parameters)? Or should we take the RBAC path? How should we even identify and authorize users?
My initial though is to create framework which can be used then to implement ACLs on any level we want.
My $.02 is that this has the potential to be such a huge project that we need to come up with a design that lets us bite off as much as we want at one time, leaving the rest of libvirt's functionality operating as it always has. So, for example, we could start by creating an ACL that would let a particular user boot and shutdown but not edit a VM; later when we wanted to let particular users connect to the VM's console, we could implement that, and so on. It needs to be done in a consistent way so that future additions are clearly slotted into the existing design.
I tend to agree, I think like for the security module, we should provide an access control driver API with exhaustive parameters, but as a first step implement a simple user based driver. We just need to make sure that it would allow to implement a full RBAC kind of driver. It seems to me that the driver just need to plug in the API entry points, i.e. mostly libvirt.c. I would probably not pass all APIs parameters at the API level but some function ID, and main arguments: - connection - domain - network - interface - pool - vol - device - snapshot - secret - stream - ... - and some kind of string (could be used for xml, uuid, device path, or migration URI for example)
The first use of that driver could be to implemenent the read-only mode of connections internally. Might indicate that being able to stack multiple access control driver as an interesting feature (i.e. how to finer restrict read-only mode with extra rules).
I think a lot of the code in the driver front-end should be fairly mechanical ... dunno if we should generate it from the API description though, just an idea.
I'm afraid that kind of design will not work. You can't do access control based on the data in the libvirt API entry point frontend. It has not been validated by the driver, so it is not secure to trust it. Access controls checks have to be done at time of use, or as close as is possible, which means they have to be put into the internals of each driver. See my first mail for more details. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Wed, Nov 23, 2011 at 06:17:46PM +0100, Michal Privoznik wrote:
Hi all,
I'd like to implement this new feature for libvirt. However, I think we should settle down on design first. My biggest concern is choosing the right level on on which ACLs will be implemented. Should be interested only in (user, API), or with more granularity (user, API, API's parameters)? Or should we take the RBAC path? How should we even identify and authorize users?
My initial though is to create framework which can be used then to implement ACLs on any level we want.
What's our opinion?
I have been working on the plan & investigating various implementation details of this myself over the past few months. Here is the initial design mail I had on the subject: https://www.redhat.com/archives/libvir-list/2011-June/msg00244.html In terms of level of ACLs, this isn't something that can be done at the "API" entry point level, since it is only secure to perform checks after the internal object has been obtained by the driver: eg, consider a virDomainPtr instance containing name=foo, uuid=c7b3edbd-edaf-9455-926a-d65c16db1800 Now, the QMEU driver does virDomainObjPtr vm = virDomainObjLookupByUUID(c7b3edbd-edaf-9455-926a-d65c16db1800) And could in theory get back a virDomainObjPtr instance containing name=bar, uuid=c7b3edbd-edaf-9455-926a-d65c16db1800 If our access control checks specify "allow { name=foo }", then we should *deny* access, because the actual object we're about to use has name=bar. If we did the checks at the API entry point level, we would mistakenly allow access. In addition "API" is really the wrong level of granularity for expressing the checks. Several separate APIs will need to be covered under the same check, while at the same time, one API might require multiple checks. So what we actually need to do is perform checks, at time of use (ie inside each driver), on the triple (subject, object, operation). As per the design mail above, we need to consider two implementations of the access controllers. One is SELinux based and integrates with sVirt where, and here 'subject' will be the SELinux context of the client application which we obtain over the UNIX socket, or over the TCP socket using IPSec. The other general purpose RBAC system will be based around the authenticated client identity. If SASL was in use, then the SASL username (eg Kerberos principle). If SSL was in use then the identity is the x509 client DName from the certificate. If the socket is a UNIX socket, we also get th UNIX userid/groupid, regardless of auth mechanism. For SELinux, the object will be identified by the SELinux context of the object being operated upon, or the context of the libvirtd daemon if there is no context for the object itself. For non-SELinux controller, the object will be identified by the corresponding unique attributes, eg UUID, Name, path. For SELinux, authorization will be done through the SELinux policy. For non-SELinux controller, we will invent a configuration file format that can express RBAC. eg, something like role engineeringadmins { user jim; user fred; user ralph; }; objectset engineeringvms { vm name=vmapache1; vm name=vmapache2; vm name=vmbrew1; }; objectset engineeringstorage { pool name=engnfs1; pool name=engiscsi2; } actionset manage { actions start stop read read_secure ...more...; } permission { role engineeringadmins; objectset engineeringvms; actionset manage; } permission { role engineeringadmins; objectset engineeringstorage; actionset manage; } NB this is just a rough idea of a config format - ideally we will actually use the existing virConfPtr classes, so the data format will look a bit different. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Thu, Nov 24, 2011 at 09:11:20AM +0000, Daniel P. Berrange wrote:
On Wed, Nov 23, 2011 at 06:17:46PM +0100, Michal Privoznik wrote:
Hi all,
I'd like to implement this new feature for libvirt. However, I think we should settle down on design first. My biggest concern is choosing the right level on on which ACLs will be implemented. Should be interested only in (user, API), or with more granularity (user, API, API's parameters)? Or should we take the RBAC path? How should we even identify and authorize users?
My initial though is to create framework which can be used then to implement ACLs on any level we want.
What's our opinion?
I have been working on the plan & investigating various implementation details of this myself over the past few months. Here is the initial design mail I had on the subject:
https://www.redhat.com/archives/libvir-list/2011-June/msg00244.html
In terms of level of ACLs, this isn't something that can be done at the "API" entry point level, since it is only secure to perform checks after the internal object has been obtained by the driver:
eg, consider a virDomainPtr instance containing
name=foo, uuid=c7b3edbd-edaf-9455-926a-d65c16db1800
Now, the QMEU driver does
virDomainObjPtr vm = virDomainObjLookupByUUID(c7b3edbd-edaf-9455-926a-d65c16db1800)
And could in theory get back a virDomainObjPtr instance containing
name=bar, uuid=c7b3edbd-edaf-9455-926a-d65c16db1800
If our access control checks specify "allow { name=foo }", then we should *deny* access, because the actual object we're about to use has name=bar. If we did the checks at the API entry point level, we would mistakenly allow access.
In addition "API" is really the wrong level of granularity for expressing the checks. Several separate APIs will need to be covered under the same check, while at the same time, one API might require multiple checks.
For clarity here are some examples of why you can't do checks at the API level, and instead must do it inside each driver * The virDomainDefineXML API - you need todo two separate checks, one if the config does not already exist (eg defining a new guest), and another if the config does exist (updating an existing guest). * virDomainListDomains API - you need to filter the returned list of IDs to only those the user is allowed to view. You can't do the filtering if you only have the IDs, so it needs to be done inthe driver * virDomainListDomains, virDomainNumOfDomains, virDomainListDefinedDomains, virDomainNumOfDefinedDomains - these will all collapse to a single "list" operation, filtering for the user * All APIs - with SELinux you need the context from the domain XML which is not available at the API level. * virDomainCreateXML API - as with DefineXML there are two separate checks required - one if a config does not exist (starting an brand new transient guest) vs one if a config does exist (starting a persistent guest, with a temporarily different config). * virDomainAttachDevice API - need to todo different checks for the different types of device being attached & potentially multiple checks if the attached device references other host resources. eg a PCI device attach with managed=yes, must check that the user is allowed to attach a device, and also check that the user is allowed to detach & reset host PCI devices. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Thu, Nov 24, 2011 at 09:45:44AM +0000, Daniel P. Berrange wrote:
On Thu, Nov 24, 2011 at 09:11:20AM +0000, Daniel P. Berrange wrote: [...]
In terms of level of ACLs, this isn't something that can be done at the "API" entry point level, since it is only secure to perform checks after the internal object has been obtained by the driver:
eg, consider a virDomainPtr instance containing
name=foo, uuid=c7b3edbd-edaf-9455-926a-d65c16db1800
Now, the QMEU driver does
virDomainObjPtr vm = virDomainObjLookupByUUID(c7b3edbd-edaf-9455-926a-d65c16db1800)
And could in theory get back a virDomainObjPtr instance containing
name=bar, uuid=c7b3edbd-edaf-9455-926a-d65c16db1800
If our access control checks specify "allow { name=foo }", then we should *deny* access, because the actual object we're about to use has name=bar. If we did the checks at the API entry point level, we would mistakenly allow access.
So basically you say we can't check at the API level here because we can't trust the virDomainPtr and can only base the checks on the API internal data structures like virDomainObjPtr. I tend to agree then, and that means that the check is to be done in the final driver as a result (e.g. the qemu one, not at the remote level).
In addition "API" is really the wrong level of granularity for expressing the checks. Several separate APIs will need to be covered under the same check, while at the same time, one API might require multiple checks.
For clarity here are some examples of why you can't do checks at the API level, and instead must do it inside each driver
* The virDomainDefineXML API - you need todo two separate checks, one if the config does not already exist (eg defining a new guest), and another if the config does exist (updating an existing guest).
Okay
* virDomainListDomains API - you need to filter the returned list of IDs to only those the user is allowed to view. You can't do the filtering if you only have the IDs, so it needs to be done inthe driver
Here I'm far less convinced. To me the role system influence the API in saying if you can run the API or not. In that case you want to change the API output. I'm not sure it's a good idea. If I take the unix analogy, it's okay to forbid listing directories (even if some files may be accessible to the user), but changing the directory listing based on whom does the operation makes an awful lot of things more complex. I'm not sure we want to do this !
* virDomainListDomains, virDomainNumOfDomains, virDomainListDefinedDomains, virDomainNumOfDefinedDomains - these will all collapse to a single "list" operation, filtering for the user
I'm not sure I agree on changing the API semantic.
* All APIs - with SELinux you need the context from the domain XML which is not available at the API level.
okay
* virDomainCreateXML API - as with DefineXML there are two separate checks required - one if a config does not exist (starting an brand new transient guest) vs one if a config does exist (starting a persistent guest, with a temporarily different config).
okay all create operations are overloaded by allowing override.
* virDomainAttachDevice API - need to todo different checks for the different types of device being attached & potentially multiple checks if the attached device references other host resources. eg a PCI device attach with managed=yes, must check that the user is allowed to attach a device, and also check that the user is allowed to detach & reset host PCI devices.
If we try to go too fine grained we are likely to make it too hard to use, I see a risk here. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Nov 25, 2011 at 11:35:54AM +0800, Daniel Veillard wrote:
On Thu, Nov 24, 2011 at 09:45:44AM +0000, Daniel P. Berrange wrote:
In addition "API" is really the wrong level of granularity for expressing the checks. Several separate APIs will need to be covered under the same check, while at the same time, one API might require multiple checks.
For clarity here are some examples of why you can't do checks at the API level, and instead must do it inside each driver
* The virDomainDefineXML API - you need todo two separate checks, one if the config does not already exist (eg defining a new guest), and another if the config does exist (updating an existing guest).
Okay
* virDomainListDomains API - you need to filter the returned list of IDs to only those the user is allowed to view. You can't do the filtering if you only have the IDs, so it needs to be done inthe driver
Here I'm far less convinced. To me the role system influence the API in saying if you can run the API or not. In that case you want to change the API output. I'm not sure it's a good idea. If I take the unix analogy, it's okay to forbid listing directories (even if some files may be accessible to the user), but changing the directory listing based on whom does the operation makes an awful lot of things more complex. I'm not sure we want to do this !
This is actually something that is done in SELinux for directories. If the file is not labelled with a suitable context, it does not apppear when you do 'readdir()'. This filtering is essential if we are able to support the "ISP use case" where multiple users connect to the same libvirtd and must not see each other's domains.
* virDomainAttachDevice API - need to todo different checks for the different types of device being attached & potentially multiple checks if the attached device references other host resources. eg a PCI device attach with managed=yes, must check that the user is allowed to attach a device, and also check that the user is allowed to detach & reset host PCI devices.
If we try to go too fine grained we are likely to make it too hard to use, I see a risk here.
Ok, here's another example. The admin has created a guest for a user and wants to allow the user to change the VNC password in the fly, but not hotplug or unplug disks or network devices. Because the virDomainAttachDevice API is a generic entry point for performing many different actions, we will inevitably need lots of different security checks in this API. Daniel. -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Thu, Nov 24, 2011 at 09:11:20AM +0000, Daniel P. Berrange wrote:
On Wed, Nov 23, 2011 at 06:17:46PM +0100, Michal Privoznik wrote:
Hi all,
I'd like to implement this new feature for libvirt. However, I think we should settle down on design first. My biggest concern is choosing the right level on on which ACLs will be implemented. Should be interested only in (user, API), or with more granularity (user, API, API's parameters)? Or should we take the RBAC path? How should we even identify and authorize users?
My initial though is to create framework which can be used then to implement ACLs on any level we want.
What's our opinion?
I have been working on the plan & investigating various implementation details of this myself over the past few months. Here is the initial design mail I had on the subject:
https://www.redhat.com/archives/libvir-list/2011-June/msg00244.html
Here is a more detailed technical plan of what I intended to implement. Technical plan for implementing RBAC with integration to sVirt ============================================================== What follows is my current design for implementing fine grained access control in libvirt drivers. The intent is to provide RBAC, with an implementation going via sVirt, and another native libvirt implementation based on plain config files. It will also be able to replicate the existing VIR_CONNECT_RO access control checks. The intent of the design is to be comprehensive enough to enable SELinux to be used as the exclusive access controller across libvirt, without need of other control mechanism. eg, so you can write a policy for 'virt-top' which strictly confines what it can do with libvirt. I expect that the primary users of the SELinux capability will be application designers, or a few end user administrators with very high security needs. I expect that most end user adminsitrators will just use the simple access controller with its plain config files. Identity representation ======================= We need to be able to store a minimum of: - UNIX user name; - UNIX group name; - SASL username - x509 distinguished name - SELinux context potentially extended in the future Example structure enum { VIR_IDENTITY_ATTR_UNIX_USER_NAME, VIR_IDENTITY_ATTR_UNIX_GROUP_NAME, VIR_IDENTITY_ATTR_SASL_USER_NAME, VIR_IDENTITY_ATTR_X509_DISTINGUISHED_NAME, VIR_IDENTITY_ATTR_SELINUX_CONTEXT, }; typedef struct _virIdentity virIdentityAttr; struct _virIdentityAttr { int type; char *value; }; typedef struct _virIdentity virIdentity; struct _virIdentity { int nattrs; virIdentityAttr *attrs; }; Identity association ==================== Need to be able to associate an identity with a virConnectPtr instance. The initial identity is determined during the initial connection handshake and authentication process, by libvirtd. The client may provide an alternative identity in addition to the initial identity, which is what is used for actual operation checks. eg, there is identity to "authorize as" and an identity to "act as". initially both are the same identity. For a client to be able to set an alternative "act as" identity requires a permission check against the "authorize as" identity. Need a new API for opening a connection which includes an identity virConnectOpenIdentity(const char *name, virConnectAuthPtr auth, virIdentityPtr identity, unsigned int flags); When this is invoked on the libvirtd side, 'identity' is providing the initial "authorize as" identity. In terms of RPC API calls from the client, this is just a shorthand for doing conn = virConnectOpenIdentity(name, auth, NULL, flags); virConnectSetIdentity(conn, identity); (see next descrition) When this is invoked on the client side, 'identity' is providing the alternative "act as" identity. The "authorize as" identity is determined by libvirtd. Typically clients will not use this. Only clients which are brokers for other clients (eg libvirt-CIM, libvirt-SNMP, libvirt-QMF) would use this capability. There is also a new API for changing the identity on the fly virConnectSetIdentity(virConnectPtr conn, virIdentityPtr identity, unsigned int flags); This always updates the "act as" identity. Again this is rarely used unless the client is a broker for other clients. There is a new API for retrieving the current identity enum { VIR_CONNECT_GET_IDENTITY_AUTH_AS = 0, VIR_CONNECT_GET_IDENTITY_ACT_AS = (1 << 0), }; virIdentityPtr virConnectGetIdentity(virConnectPtr conn, unsigned int flags); Access controllers ================== The access controllers will live in src/access/. As with security drivers it will be possible to stack access controllers. The main internal API will be provided by viraccessmanager.c - Entry point for driver calls viraccessdriver.h - Defines internal driver impl contract viraccessvectors.h - Defines the list of permissions that are checkable There will then be several internal driver implementations viraccessselinux.c - SELinux access controller viraccesssimple.c - Simply config file based access controller viraccessreadonly.c - Simulate current VIR_CONNECT_RO flag viraccessnop.c - Do not perform any access control checks viraccessstack.c - Stack a ordered list of access controllers The access manager API will have the following methods for creating instances of the managers: virAccessManagerPtr virAccessManagerNewStack(virAccessManagerPtr first, virAccessManagerPtr second) virAccessManagerPtr virAccessManagerNewByName(const char *name) (name=selinux|simple|readonly|nop) There will be methods for checking permissions (access vector) against object instances: bool virAccessManagerCheckDomainDef(virAccessManagerPtr mgr, virIdentityPtr identity, virDomainDefPtr domain, virAccessVectorDomain vector); bool virAccessManagerCheckNetworkDef(virAccessManagerPtr mgr, virIdentityPtr identity, virNetworkDefPtr network, virAccessVectorNetwork vector); bool virAccessManagerCheckStoragePoolDef(virAccessManagerPtr mgr, virIdentityPtr identity, virStoragePoolDefPtr pool, virAccessVectorStoragePool vector); ....etc for each object type... There will also need to be a catch all for cases where we need todo a permission check for which there is no corresponding object bool virAccessMangerCheckGeneric(virAccessManagerPtr mgr, virIdentityPtr identity, virAccessVectorGeneric vector); We might add other types of check later too, as we do increasingly fine grained checking, eg on paths, net devices, whatever. NB, we only return bool (accept,deny), not a tri-state (accept,deny,error) because as far as the clients are concerned, the deny+error states must be indistinguishable. The access vectors file will contain enums specifying all possible logical operations for each type of object that need controlling, eg typedef enum { VIR_ACCESS_VECTOR_DOMAIN_DEFINE, VIR_ACCESS_VECTOR_DOMAIN_REDEFINE, VIR_ACCESS_VECTOR_DOMAIN_START, VIR_ACCESS_VECTOR_DOMAIN_STOP, VIR_ACCESS_VECTOR_DOMAIN_VIEW, (eg can check existance vir virConnectListDomains) VIR_ACCESS_VECTOR_DOMAIN_READ, (eg can read XML & other properties) VIR_ACCESS_VECTOR_DOMAIN_READ_SECURE, (eg can request VIR_DOMAIN_XML_SECURE flag) ....many more... } virAccessVectorDomain; typedef enum { VIR_ACCESS_VECTOR_STORAGE_POOL_DEFINE, VIR_ACCESS_VECTOR_STORAGE_POOL_REDEFINE, ....many more... } virAccessVectorStoragePool; ...more for each other object type... Mapping to SELinux ================== The SELinux driver will work as follows - Object -> context mapping - if there is an SELinux context available in the object in question, that will be used. Otherwise the context of libvirtd itself will be used - Identity: The identity will be populated with the clients' SELinux context, as obtained from getpeercon(). This works trivially for UNIX sockets, or non-trivially requiring IPSec configuration, for TCP sockets - Object types: each libvirt object type will have a corresponding SELinux security class. - Access vector enums in libvirt will be converted to a string format via a traditional libvirt VIR_ENUM_IMPL. The SELinux policy will define access vectors with these matching names. The string will then be converted to an SELinux access_vector_t value using XXXXX (can't remember API name right now) - The actual permission check is done using avc_has_perm(subject, object, object class, access vector) The SELinux policy will determine whether any single check results in an audit log Mapping to Simple Driver ======================== The simple access controller will work from traditional user names in the identiy (unix user, unix group, sasl, x509 dname, etc). In terms of objects, it will either use the UUID or the name primarily, though might also use other attributes from the XML as required (paths, network device names, whatever) There will be some simple configuration file format for defining - roles - this groups together users who do the same job - objectsets - this groups together objects which are managed together - permissionsets - ths groups together access vectors which are used together - grants - list of triples (role,objectset, permissionset) which actually grant permissions Each grant entry will optionally have an 'audit' flag. If this is set, then any time the access is granted or denied will result in libvirt sending an audit log message. Checking in drivers =================== Only stateful drivers running inside libvirtd will be access controlled. It is meaningless to attempt to access control drivers running outside libvirtd, because any checks are trivially bypassed simply by not using libvirtd and connecting directly to VMWare VirtualCenter / whatever. All stateful drivers will have all APIs modified to include permission checks in all relevant places. Most of the time this will be upfront accept/deny type checks before any processing takes place, but in some APIs there will be filtering where the access control check is actually used to filter what data is returned to the client. eg virDomainListDomains will filter out guests the client is not allowed to view. This is the unbounded, ill-defined part of the work where the fun issues will be discovered.... As we go through each API, we need to figure out what access vectors need to be checked. There may be multiple vectors needing checks per API call. The return data may also need to be filtered. Proof of suitability ==================== In the course of implementing all this it will be desirable to have some applications and/or scenarios to use to proof that the resulting system is actually suitable for its purpose. I would suggest the following applications - virt-viewer (control console access) - virt-top (control stats access) And for a scenario - virsh, in a shared user ISP like model. eg multiple users connecting to same libvirtd (qemu:///system), but each only able to access their own VMs. -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (4)
-
Daniel P. Berrange
-
Daniel Veillard
-
Dave Allan
-
Michal Privoznik