swift/doc/source/overview_backing_store.rst

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`` parameter
* 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.