274 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
 | 
						|
=============================================
 | 
						|
Using Swift as Backing Store for Service Data
 | 
						|
=============================================
 | 
						|
 | 
						|
----------
 | 
						|
Background
 | 
						|
----------
 | 
						|
 | 
						|
This section provides guidance to OpenStack Service developers for how to
 | 
						|
store your users' data in Swift. An example of this is that a user requests
 | 
						|
that Nova save a snapshot of a VM. Nova passes the request to Glance,
 | 
						|
Glance writes the image to a Swift container as a set of objects.
 | 
						|
 | 
						|
Throughout this section, the following terminology and concepts are used:
 | 
						|
 | 
						|
* User or end-user. This is a person making a request that will result in
 | 
						|
  an OpenStack Service making a request to Swift.
 | 
						|
 | 
						|
* Project (also known as Tenant). This is the unit of resource ownership.
 | 
						|
  While data such as snapshot images or block volume backups may be
 | 
						|
  stored as a result of an end-user's request, the reality is that these
 | 
						|
  are project data.
 | 
						|
 | 
						|
* Service. This is a program or system used by end-users. Specifically, it
 | 
						|
  is any program or system that is capable of receiving end-user's tokens and
 | 
						|
  validating the token with the Keystone Service and has a need to store
 | 
						|
  data in Swift. Glance and Cinder are examples of such Services.
 | 
						|
 | 
						|
* Service User. This is a Keystone user that has been assigned to a Service.
 | 
						|
  This allows the Service to generate and use its own tokens so that it
 | 
						|
  can interact with other Services as itself.
 | 
						|
 | 
						|
* Service Project. This is a project (tenant) that is associated with a
 | 
						|
  Service. There may be a single project shared by many Services or there
 | 
						|
  may be a project dedicated to each Service. In this document, the
 | 
						|
  main purpose of the Service Project is to allow the system operator
 | 
						|
  to configure specific roles for each Service User.
 | 
						|
 | 
						|
-------------------------------
 | 
						|
Alternate Backing Store Schemes
 | 
						|
-------------------------------
 | 
						|
 | 
						|
There are three schemes described here:
 | 
						|
 | 
						|
* Dedicated Service Account (Single Tenant)
 | 
						|
 | 
						|
  Your Service has a dedicated Service Project (hence a single dedicated
 | 
						|
  Swift account). Data for all users and projects are stored in this
 | 
						|
  account. Your Service must have a user assigned to it (the Service User).
 | 
						|
  When you have data to store on behalf of one of your users, you use the
 | 
						|
  Service User credentials to get a token for the Service Project and
 | 
						|
  request Swift to store the data in the Service Project.
 | 
						|
 | 
						|
  With this scheme, data for all users is stored in a single account. This
 | 
						|
  is transparent to your users and since the credentials for the Service User
 | 
						|
  are typically not shared with anyone, your users' cannot access their
 | 
						|
  data by making a request directly to Swift. However, since data belonging
 | 
						|
  to all users is stored in one account, it presents a single point of
 | 
						|
  vulnerably to accidental deletion or a leak of the service-user
 | 
						|
  credentials.
 | 
						|
 | 
						|
* Multi Project (Multi Tenant)
 | 
						|
 | 
						|
  Data belonging to a project is stored in the Swift account
 | 
						|
  associated with the project. Users make requests to your Service using
 | 
						|
  a token scoped to a project in the normal way. You can then use this
 | 
						|
  same token to store the user data in the project's Swift account.
 | 
						|
 | 
						|
  The effect is that data is stored in multiple projects (aka tenants).
 | 
						|
  Hence this scheme has been known as the "multi tenant" scheme.
 | 
						|
 | 
						|
  With this scheme, access is controlled by Keystone. The users must
 | 
						|
  have a role that allows them to perform the request to your Service. In
 | 
						|
  addition, they must have a role that also allows them to store data in
 | 
						|
  the Swift account. By default, the admin or swiftoperator roles are
 | 
						|
  used for this purpose (specific systems may use other role names). If the
 | 
						|
  user does not have the appropriate roles, when your Service attempts
 | 
						|
  to access Swift, the operation will fail.
 | 
						|
 | 
						|
  Since you are using the user's token to access the data, it follows that
 | 
						|
  the user can use the same token to access Swift directly -- bypassing your
 | 
						|
  Service. When end-users are browsing containers, they will also see
 | 
						|
  your Service's containers and objects -- and may potentially delete
 | 
						|
  the data. Conversely, there is no single account where all data so leakage
 | 
						|
  of credentials will only affect a single project/tenant.
 | 
						|
 | 
						|
* Service Prefix Account
 | 
						|
 | 
						|
  Data belonging to a project is stored in a Swift account associated
 | 
						|
  with the project. This is similar to the Multi Project scheme described
 | 
						|
  above. However, the Swift account is different than the account that
 | 
						|
  users access. Specifically, it has a different account prefix. For example,
 | 
						|
  for the project 1234, the user account is named AUTH_1234. Your Service uses
 | 
						|
  a different account, for example, SERVICE_1234.
 | 
						|
 | 
						|
  To access the SERVICE_1234 account, you must present two tokens: the user's
 | 
						|
  token is put in the X-Auth-Token header. You present your Service's token
 | 
						|
  in the X-Service-Token header. Swift is configured such that only when both
 | 
						|
  tokens are presented will it allow access. Specifically, the user cannot
 | 
						|
  bypass your Service because they only have their own token. Conversely, your
 | 
						|
  Service can only access the data while it has a copy of the user's token --
 | 
						|
  the Service's token by itself will not grant access.
 | 
						|
 | 
						|
  The data stored in the Service Prefix Account cannot be seen by end-users.
 | 
						|
  So they cannot delete this data -- they can only access the data if they
 | 
						|
  make a request through your Service. The data is also more secure. To make
 | 
						|
  an unauthorized access, someone would need to compromise both an end-user's
 | 
						|
  and your Service User credentials. Even then, this would only expose one
 | 
						|
  project -- not other projects.
 | 
						|
 | 
						|
The Service Prefix Account scheme combines features of the Dedicated Service
 | 
						|
Account and Multi Project schemes. It has the private, dedicated,
 | 
						|
characteristics of the Dedicated Service Account scheme but does not present
 | 
						|
a single point of attack. Using the Service Prefix Account scheme is a little
 | 
						|
more involved than the other schemes, so the rest of this document describes
 | 
						|
it more detail.
 | 
						|
 | 
						|
-------------------------------
 | 
						|
Service Prefix Account Overview
 | 
						|
-------------------------------
 | 
						|
 | 
						|
The following diagram shows the flow through the system from the end-user,
 | 
						|
to your Service and then onto Swift::
 | 
						|
 | 
						|
      client
 | 
						|
         \
 | 
						|
          \   <request>: <path-specific-to-the-service>
 | 
						|
           \  x-auth-token: <user-token>
 | 
						|
            \
 | 
						|
          SERVICE
 | 
						|
             \
 | 
						|
              \    PUT: /v1/SERVICE_1234/<container>/<object>
 | 
						|
               \   x-auth-token: <user-token>
 | 
						|
                \  x-service-token: <service-token>
 | 
						|
                 \
 | 
						|
                Swift
 | 
						|
 | 
						|
The sequence of events and actions are as follows:
 | 
						|
 | 
						|
* Request arrives at your Service
 | 
						|
 | 
						|
* The <user-token> is validated by the keystonemiddleware.auth_token
 | 
						|
  middleware. The user's role(s) are used to determine if the user
 | 
						|
  can perform the request. See :doc:`overview_auth` for technical
 | 
						|
  information on the authentication system.
 | 
						|
 | 
						|
* As part of this request, your Service needs to access Swift (either to
 | 
						|
  write or read a container or object). In this example, you want to perform
 | 
						|
  a PUT on <container>/<object>.
 | 
						|
 | 
						|
* In the wsgi environment, the auth_token module will have populated the
 | 
						|
  HTTP_X_SERVICE_CATALOG item. This lists the Swift endpoint and account.
 | 
						|
  This is something such as https://<netloc>/v1/AUTH_1234 where ``AUTH_``
 | 
						|
  is a prefix and ``1234`` is the project id.
 | 
						|
 | 
						|
* The ``AUTH_`` prefix is the default value. However, your system may use a
 | 
						|
  different prefix. To determine the actual prefix, search for the first
 | 
						|
  underscore ('_') character in the account name. If there is no underscore
 | 
						|
  character in the account name, this means there is no prefix.
 | 
						|
 | 
						|
* Your Service should have a configuration parameter that provides the
 | 
						|
  appropriate prefix to use for storing data in Swift. There is more
 | 
						|
  discussion of this below, but for now assume the prefix is ``SERVICE_``.
 | 
						|
 | 
						|
* Replace the prefix (``AUTH_`` in above examples) in the path with
 | 
						|
  ``SERVICE_``, so the full URL to access the object becomes
 | 
						|
  https://<netloc>/v1/SERVICE_1234/<container>/<object>.
 | 
						|
 | 
						|
* Make the request to Swift, using this URL. In the X-Auth-Token header place
 | 
						|
  a copy of the <user-token>. In the X-Service-Token header, place your
 | 
						|
  Service's token. If you use python-swiftclient you can achieve this
 | 
						|
  by:
 | 
						|
 | 
						|
      * Putting the URL in the ``preauthurl`` parameter
 | 
						|
      * Putting the <user-token> in ``preauthtoken`` paramater
 | 
						|
      * Adding the X-Service-Token to the ``headers`` parameter
 | 
						|
 | 
						|
 | 
						|
Using the HTTP_X_SERVICE_CATALOG to get Swift Account Name
 | 
						|
----------------------------------------------------------
 | 
						|
 | 
						|
The auth_token middleware populates the wsgi environment with information when
 | 
						|
it validates the user's token. The HTTP_X_SERVICE_CATALOG item is a JSON
 | 
						|
string containing  details of the OpenStack endpoints. For Swift, this also
 | 
						|
contains the project's Swift account name. Here is an example of a catalog
 | 
						|
entry for Swift::
 | 
						|
 | 
						|
    "serviceCatalog": [
 | 
						|
        ...
 | 
						|
        {
 | 
						|
            ....
 | 
						|
            "type": "object-store",
 | 
						|
            "endpoints": [
 | 
						|
               ...
 | 
						|
               {
 | 
						|
                   ...
 | 
						|
                   "publicURL": "https://<netloc>/v1/AUTH_1234",
 | 
						|
                   "region": "<region-name>"
 | 
						|
                   ...
 | 
						|
               }
 | 
						|
               ...
 | 
						|
         ...
 | 
						|
         }
 | 
						|
    }
 | 
						|
 | 
						|
To get the End-user's account:
 | 
						|
 | 
						|
* Look for an entry with ``type`` of ``object-store``
 | 
						|
 | 
						|
* If there are several regions, there will be several endpoints. Use the
 | 
						|
  appropriate region name and select the ``publicURL`` item.
 | 
						|
 | 
						|
* The Swift account name is the final item in the path ("AUTH_1234" in this
 | 
						|
  example).
 | 
						|
 | 
						|
Getting a Service Token
 | 
						|
-----------------------
 | 
						|
 | 
						|
A Service Token is no different than any other token and is requested
 | 
						|
from Keystone using user credentials and project in the usual way. The core
 | 
						|
requirement is that your Service User has the appropriate role. In practice:
 | 
						|
 | 
						|
* Your Service must have a user assigned to it (the Service User).
 | 
						|
 | 
						|
* Your Service has a project assigned to it (the Service Project).
 | 
						|
 | 
						|
* The Service User must have a role on the Service Project. This role is
 | 
						|
  distinct from any of the normal end-user roles.
 | 
						|
 | 
						|
* The role used must the role configured in the /etc/swift/proxy-server.conf.
 | 
						|
  This is the ``<prefix>_service_roles`` option. In this example, the role
 | 
						|
  is the ``service`` role::
 | 
						|
 | 
						|
    [keystoneauth]
 | 
						|
    reseller_prefix = AUTH_, SERVICE_
 | 
						|
    SERVICE_service_role = service
 | 
						|
 | 
						|
The ``service`` role should only be granted to OpenStack Services. It should
 | 
						|
not be granted to users.
 | 
						|
 | 
						|
Single or multiple Service Prefixes?
 | 
						|
------------------------------------
 | 
						|
 | 
						|
Most of the examples used in this document used a single prefix. The
 | 
						|
prefix, ``SERVICE`` was used. By using a single prefix, an operator is
 | 
						|
allowing all OpenStack Services to share the same account for data
 | 
						|
associated with a given project. For test systems or deployments well protected
 | 
						|
on private firewalled networks, this is appropriate.
 | 
						|
 | 
						|
However, if one Service is compromised, that Service can access
 | 
						|
data created by another Service. To prevent this, multiple Service Prefixes may
 | 
						|
be used. This also requires that the operator configure multiple service
 | 
						|
roles. For example, in a system that has Glance and Cinder, the following
 | 
						|
Swift configuration could be used::
 | 
						|
 | 
						|
    [keystoneauth]
 | 
						|
    reseller_prefix = AUTH_, IMAGE_, BLOCK_
 | 
						|
    IMAGE_service_roles = image_service
 | 
						|
    BLOCK_service_roles = block_service
 | 
						|
 | 
						|
The Service User for Glance would be granted the ``image_service`` role on its
 | 
						|
Service Project and the Cinder Service user is granted the ``block_service``
 | 
						|
role on its project. In this scheme, if the Cinder Service was compromised,
 | 
						|
it would not be able to access any Glance data.
 | 
						|
 | 
						|
Container Naming
 | 
						|
----------------
 | 
						|
 | 
						|
Since a single Service Prefix is possible, container names should be prefixed
 | 
						|
with a unique string to prevent name clashes. We suggest you use the service
 | 
						|
type field (as used in the service catalog). For example, The Glance Service
 | 
						|
would use "image" as a prefix.
 |