swift/doc/source/overview_encryption.rst
Tim Burke 13c0980e71 docs: Clarify that encryption should not be in reconciler pipeline
UpgradeImpact
=============
Operators should verify that encryption is not enabled in their
reconciler pipelines; having it enabled there may harm data durability.
For more information, see https://launchpad.net/bugs/1910804

Change-Id: I1a1d78ed91d940ef0b4eba186dcafd714b4fb808
Closes-Bug: #1910804
2021-01-21 15:39:35 -06:00

813 lines
33 KiB
ReStructuredText

=================
Object Encryption
=================
Swift supports the optional encryption of object data at rest on storage nodes.
The encryption of object data is intended to mitigate the risk of users' data
being read if an unauthorised party were to gain physical access to a disk.
.. note::
Swift's data-at-rest encryption accepts plaintext object data from the
client, encrypts it in the cluster, and stores the encrypted data. This
protects object data from inadvertently being exposed if a data drive
leaves the Swift cluster. If a user wishes to ensure that the plaintext
data is always encrypted while in transit and in storage, it is strongly
recommended that the data be encrypted before sending it to the Swift
cluster. Encrypting on the client side is the only way to ensure that the
data is fully encrypted for its entire lifecycle.
Encryption of data at rest is implemented by middleware that may be included in
the proxy server WSGI pipeline. The feature is internal to a Swift cluster and
not exposed through the API. Clients are unaware that data is encrypted by this
feature internally to the Swift service; internally encrypted data should never
be returned to clients via the Swift API.
The following data are encrypted while at rest in Swift:
* Object content i.e. the content of an object PUT request's body
* The entity tag (ETag) of objects that have non-zero content
* All custom user object metadata values i.e. metadata sent using
X-Object-Meta- prefixed headers with PUT or POST requests
Any data or metadata not included in the list above are not encrypted,
including:
* Account, container and object names
* Account and container custom user metadata values
* All custom user metadata names
* Object Content-Type values
* Object size
* System metadata
.. note::
This feature is intended to provide `confidentiality` of data that is at
rest i.e. to protect user data from being read by an attacker that gains
access to disks on which object data is stored.
This feature is not intended to prevent undetectable `modification`
of user data at rest.
This feature is not intended to protect against an attacker that gains
access to Swift's internal network connections, or gains access to key
material or is able to modify the Swift code running on Swift nodes.
.. _encryption_deployment:
------------------------
Deployment and operation
------------------------
Encryption is deployed by adding two middleware filters to the proxy
server WSGI pipeline and including their respective filter configuration
sections in the `proxy-server.conf` file. :ref:`Additional steps
<container_sync_client_config>` are required if the container sync feature is
being used.
The `keymaster` and `encryption` middleware filters must be to the right of all
other middleware in the pipeline apart from the final proxy-logging middleware,
and in the order shown in this example::
<other middleware> keymaster encryption proxy-logging proxy-server
[filter:keymaster]
use = egg:swift#keymaster
encryption_root_secret = your_secret
[filter:encryption]
use = egg:swift#encryption
# disable_encryption = False
See the `proxy-server.conf-sample` file for further details on the middleware
configuration options.
Keymaster middleware
--------------------
The `keymaster` middleware must be configured with a root secret before it is
used. By default the `keymaster` middleware will use the root secret configured
using the ``encryption_root_secret`` option in the middleware filter section of
the `proxy-server.conf` file, for example::
[filter:keymaster]
use = egg:swift#keymaster
encryption_root_secret = your_secret
Root secret values MUST be at least 44 valid base-64 characters and
should be consistent across all proxy servers. The minimum length of 44 has
been chosen because it is the length of a base-64 encoded 32 byte value.
.. note::
The ``encryption_root_secret`` option holds the master secret key used for
encryption. The security of all encrypted data critically depends on this
key and it should therefore be set to a high-entropy value. For example, a
suitable ``encryption_root_secret`` may be obtained by base-64 encoding a
32 byte (or longer) value generated by a cryptographically secure random
number generator.
The ``encryption_root_secret`` value is necessary to recover any encrypted
data from the storage system, and therefore, it must be guarded against
accidental loss. Its value (and consequently, the proxy-server.conf file)
should not be stored on any disk that is in any account, container or
object ring.
The ``encryption_root_secret`` value should not be changed once deployed.
Doing so would prevent Swift from properly decrypting data that was
encrypted using the former value, and would therefore result in the loss of
that data.
One method for generating a suitable value for ``encryption_root_secret`` is to
use the ``openssl`` command line tool::
openssl rand -base64 32
Separate keymaster configuration file
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``encryption_root_secret`` option may alternatively be specified in a
separate config file at a path specified by the ``keymaster_config_path``
option, for example::
[filter:keymaster]
use = egg:swift#keymaster
keymaster_config_path = /etc/swift/keymaster.conf
This has the advantage of allowing multiple processes which need to be
encryption-aware (for example, proxy-server and container-sync) to share the
same config file, ensuring that consistent encryption keys are used by those
processes. It also allows the keymaster configuration file to have different
permissions than the `proxy-server.conf` file.
A separate keymaster config file should have a ``[keymaster]`` section
containing the ``encryption_root_secret`` option::
[keymaster]
encryption_root_secret = your_secret
.. note::
Alternative keymaster middleware is available to retrieve encryption root
secrets from an :ref:`external key management system
<encryption_root_secret_in_external_kms>` such as `Barbican
<https://docs.openstack.org/barbican>`_ rather than storing root secrets in
configuration files.
Once deployed, the encryption filter will by default encrypt object data and
metadata when handling PUT and POST requests and decrypt object data and
metadata when handling GET and HEAD requests. COPY requests are transformed
into GET and PUT requests by the :ref:`copy` middleware before reaching the
encryption middleware and as a result object data and metadata is decrypted and
re-encrypted when copied.
.. _changing_the_root_secret:
Changing the encryption root secret
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
From time to time it may be desirable to change the root secret that is used to
derive encryption keys for new data written to the cluster. The `keymaster`
middleware allows alternative root secrets to be specified in its configuration
using options of the form::
encryption_root_secret_<secret_id> = <secret value>
where ``secret_id`` is a unique identifier for the root secret and ``secret
value`` is a value that meets the requirements for a root secret described
above.
Only one root secret is used to encrypt new data at any moment in time. This
root secret is specified using the ``active_root_secret_id`` option. If
specified, the value of this option should be one of the configured root secret
``secret_id`` values; otherwise the value of ``encryption_root_secret`` will be
taken as the default active root secret.
.. note::
The active root secret is only used to derive keys for new data written to
the cluster. Changing the active root secret does not cause any existing
data to be re-encrypted.
Existing encrypted data will be decrypted using the root secret that was active
when that data was written. All previous active root secrets must therefore
remain in the middleware configuration in order for decryption of existing data
to succeed. Existing encrypted data will reference previous root secret by
the ``secret_id`` so it must be kept consistent in the configuration.
.. note::
Do not remove or change any previously active ``<secret value>`` or ``<secret_id>``.
For example, the following keymaster configuration file specifies three root
secrets, with the value of ``encryption_root_secret_2`` being the current
active root secret::
[keymaster]
active_root_secret_id = 2
encryption_root_secret = your_secret
encryption_root_secret_1 = your_secret_1
encryption_root_secret_2 = your_secret_2
.. note::
To ensure there is no loss of data availability, deploying a new key to
your cluster requires a two-stage config change. First, add the new key
to the ``encryption_root_secret_<secret_id>`` option and restart the
proxy-server. Do this for all proxies. Next, set the
``active_root_secret_id`` option to the new secret id and restart the
proxy. Again, do this for all proxies. This process ensures that all
proxies will have the new key available for *decryption* before any proxy
uses it for *encryption*.
Encryption middleware
---------------------
Once deployed, the encryption filter will by default encrypt object data and
metadata when handling PUT and POST requests and decrypt object data and
metadata when handling GET and HEAD requests. COPY requests are transformed
into GET and PUT requests by the :ref:`copy` middleware before reaching the
encryption middleware and as a result object data and metadata is decrypted and
re-encrypted when copied.
.. _encryption_root_secret_in_external_kms:
Encryption Root Secret in External Key Management System
--------------------------------------------------------
The benefits of using a dedicated system for storing the encryption root secret
include the auditing and access control infrastructure that are already in
place in such a system, and the fact that an encryption root secret stored in a
key management system (KMS) may be backed by a hardware security module (HSM)
for additional security. Another significant benefit of storing the root
encryption secret in an external KMS is that it is in this case never stored on
a disk in the Swift cluster.
Swift supports fetching encryption root secrets from a `Barbican
<https://docs.openstack.org/barbican>`_ service or a KMIP_ service using the
``kms_keymaster`` or ``kmip_keymaster`` middleware respectively.
.. _KMIP: https://www.oasis-open.org/committees/kmip/
Encryption Root Secret in a Barbican KMS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Make sure the required dependencies are installed for retrieving an encryption
root secret from an external KMS. This can be done when installing Swift (add
the ``-e`` flag to install as a development version) by changing to the Swift
directory and running the following command to install Swift together with
the ``kms_keymaster`` extra dependencies::
sudo pip install .[kms_keymaster]
Another way to install the dependencies is by making sure the
following lines exist in the requirements.txt file, and installing them using
``pip install -r requirements.txt``::
cryptography>=1.6 # BSD/Apache-2.0
castellan>=0.6.0
.. note::
If any of the required packages is already installed, the ``--upgrade``
flag may be required for the ``pip`` commands in order for the required
minimum version to be installed.
To make use of an encryption root secret stored in an external KMS,
replace the keymaster middleware with the kms_keymaster middleware in the
proxy server WSGI pipeline in `proxy-server.conf`, in the order shown in this
example::
<other middleware> kms_keymaster encryption proxy-logging proxy-server
and add a section to the same file::
[filter:kms_keymaster]
use = egg:swift#kms_keymaster
keymaster_config_path = file_with_kms_keymaster_config
Create or edit the file `file_with_kms_keymaster_config` referenced above.
For further details on the middleware configuration options, see the
`keymaster.conf-sample` file. An example of the content of this file, with
optional parameters omitted, is below::
[kms_keymaster]
key_id = changeme
username = swift
password = password
project_name = swift
auth_endpoint = http://keystonehost:5000/v3
The encryption root secret shall be created and stored in the external key
management system before it can be used by the keymaster. It shall be stored
as a symmetric key, with content type ``application/octet-stream``,
``base64`` content encoding, ``AES`` algorithm, bit length ``256``, and secret
type ``symmetric``. The mode ``ctr`` may also be stored for informational
purposes - it is not currently checked by the keymaster.
The following command can be used to store the currently configured
``encryption_root_secret`` value from the `proxy-server.conf` file
in Barbican::
openstack secret store --name swift_root_secret \
--payload-content-type="application/octet-stream" \
--payload-content-encoding="base64" --algorithm aes --bit-length 256 \
--mode ctr --secret-type symmetric --payload <base64_encoded_root_secret>
Alternatively, the existing root secret can also be stored in Barbican using
`curl <https://docs.openstack.org/api-guide/key-manager/secrets.html>`__.
.. note::
The credentials used to store the secret in Barbican shall be the same
ones that the proxy server uses to retrieve the secret, i.e., the ones
configured in the `keymaster.conf` file. For clarity reasons the commands
shown here omit the credentials - they may be specified explicitly, or in
environment variables.
Instead of using an existing root secret, Barbican can also be asked to
generate a new 256-bit root secret, with content type
``application/octet-stream`` and algorithm ``AES`` (the ``mode`` parameter is
currently optional)::
openstack secret order create --name swift_root_secret \
--payload-content-type="application/octet-stream" --algorithm aes \
--bit-length 256 --mode ctr key
The ``order create`` creates an asynchronous request to create the actual
secret.
The order can be retrieved using ``openstack secret order get``, and once the
order completes successfully, the output will show the key id of the generated
root secret.
Keys currently stored in Barbican can be listed using the
``openstack secret list`` command.
.. note::
Both the order (the asynchronous request for creating or storing a secret),
and the actual secret itself, have similar unique identifiers. Once the
order has been completed, the key id is shown in the output of the ``order
get`` command.
The keymaster uses the explicitly configured username and password (and
project name etc.) from the `keymaster.conf` file for retrieving the encryption
root secret from an external key management system. The `Castellan library
<https://docs.openstack.org/castellan/latest/>`_ is used to communicate with
Barbican.
For the proxy server, reading the encryption root secret directly from the
`proxy-server.conf` file, from the `keymaster.conf` file pointed to
from the `proxy-server.conf` file, or from an external key management system
such as Barbican, are all functionally equivalent. In case reading the
encryption root secret from the external key management system fails, the
proxy server will not start up. If the encryption root secret is retrieved
successfully, it is cached in memory in the proxy server.
For further details on the configuration options, see the
`[filter:kms_keymaster]` section in the `proxy-server.conf-sample` file, and
the `keymaster.conf-sample` file.
Encryption Root Secret in a KMIP service
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This middleware enables Swift to fetch a root secret from a KMIP_ service. The
root secret is expected to have been previously created in the KMIP_ service
and is referenced by its unique identifier. The secret should be an AES-256
symmetric key.
To use this middleware Swift must be installed with the extra required
dependencies::
sudo pip install .[kmip_keymaster]
Add the ``-e`` flag to install as a development version.
Edit the swift `proxy-server.conf` file to insert the middleware in the wsgi
pipeline, replacing any other keymaster middleware::
[pipeline:main]
pipeline = catch_errors gatekeeper healthcheck proxy-logging \
<other middleware> kmip_keymaster encryption proxy-logging proxy-server
and add a new filter section::
[filter:kmip_keymaster]
use = egg:swift#kmip_keymaster
key_id = <unique id of secret to be fetched from the KMIP service>
host = <KMIP server host>
port = <KMIP server port>
certfile = /path/to/client/cert.pem
keyfile = /path/to/client/key.pem
ca_certs = /path/to/server/cert.pem
username = <KMIP username>
password = <KMIP password>
Apart from ``use`` and ``key_id`` the options are as defined for a PyKMIP
client. The authoritative definition of these options can be found at
`<https://pykmip.readthedocs.io/en/latest/client.html>`_.
The value of the ``key_id`` option should be the unique identifier for a secret
that will be retrieved from the KMIP_ service.
The keymaster configuration can alternatively be defined in a separate config
file by using the ``keymaster_config_path`` option::
[filter:kmip_keymaster]
use = egg:swift#kmip_keymaster
keymaster_config_path = /etc/swift/kmip_keymaster.conf
In this case, the ``filter:kmip_keymaster`` section should contain no other
options than ``use`` and ``keymaster_config_path``. All other options should be
defined in the separate config file in a section named ``kmip_keymaster``. For
example::
[kmip_keymaster]
key_id = 1234567890
host = 127.0.0.1
port = 5696
certfile = /etc/swift/kmip_client.crt
keyfile = /etc/swift/kmip_client.key
ca_certs = /etc/swift/kmip_server.crt
username = swift
password = swift_password
Changing the encryption root secret of external KMS's
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Because the KMS and KMIP keymaster's derive from the default KeyMaster they
also have to ability to define multiple keys. The only difference is the key
option names. Instead of using the form `encryption_root_secret_<secret_id>`
both external KMS's use `key_id_<secret_id>`, as it is an extension of their
existing configuration. For example::
...
key_id = 1234567890
key_id_foo = 0987654321
key_id_bar = 5432106789
active_root_secret_id = foo
...
Other then that, the process is the same as :ref:`changing_the_root_secret`.
Upgrade Considerations
----------------------
When upgrading an existing cluster to deploy encryption, the following sequence
of steps is recommended:
#. Upgrade all object servers
#. Upgrade all proxy servers
#. Add keymaster and encryption middlewares to every proxy server's middleware
pipeline with the encryption ``disable_encryption`` option set to ``True``
and the keymaster ``encryption_root_secret`` value set as described above.
#. If required, follow the steps for :ref:`container_sync_client_config`.
#. Finally, change the encryption ``disable_encryption`` option to ``False``
Objects that existed in the cluster prior to the keymaster and encryption
middlewares being deployed are still readable with GET and HEAD requests. The
content of those objects will not be encrypted unless they are written again by
a PUT or COPY request. Any user metadata of those objects will not be encrypted
unless it is written again by a PUT, POST or COPY request.
Disabling Encryption
--------------------
Once deployed, the keymaster and encryption middlewares should not be removed
from the pipeline. To do so will cause encrypted object data and/or metadata to
be returned in response to GET or HEAD requests for objects that were
previously encrypted.
Encryption of inbound object data may be disabled by setting the encryption
``disable_encryption`` option to ``True``, in which case existing encrypted
objects will remain encrypted but new data written with PUT, POST or COPY
requests will not be encrypted. The keymaster and encryption middlewares should
remain in the pipeline even when encryption of new objects is not required. The
encryption middleware is needed to handle GET requests for objects that may
have been previously encrypted. The keymaster is needed to provide keys for
those requests.
.. _container_sync_client_config:
Container sync configuration
----------------------------
If container sync is being used then the keymaster and encryption middlewares
must be added to the container sync internal client pipeline. The following
configuration steps are required:
#. Create a custom internal client configuration file for container sync (if
one is not already in use) based on the sample file
`internal-client.conf-sample`. For example, copy
`internal-client.conf-sample` to `/etc/swift/container-sync-client.conf`.
#. Modify this file to include the middlewares in the pipeline in
the same way as described above for the proxy server.
#. Modify the container-sync section of all container server config files to
point to this internal client config file using the
``internal_client_conf_path`` option. For example::
internal_client_conf_path = /etc/swift/container-sync-client.conf
.. note::
The ``encryption_root_secret`` value is necessary to recover any encrypted
data from the storage system, and therefore, it must be guarded against
accidental loss. Its value (and consequently, the custom internal client
configuration file) should not be stored on any disk that is in any
account, container or object ring.
.. note::
These container sync configuration steps will be necessary for container
sync probe tests to pass if the encryption middlewares are included in the
proxy pipeline of a test cluster.
--------------
Implementation
--------------
Encryption scheme
-----------------
Plaintext data is encrypted to ciphertext using the AES cipher with 256-bit
keys implemented by the python `cryptography package
<https://pypi.org/project/cryptography>`_. The cipher is used in counter
(CTR) mode so that any byte or range of bytes in the ciphertext may be
decrypted independently of any other bytes in the ciphertext. This enables very
simple handling of ranged GETs.
In general an item of unencrypted data, ``plaintext``, is transformed to an
item of encrypted data, ``ciphertext``::
ciphertext = E(plaintext, k, iv)
where ``E`` is the encryption function, ``k`` is an encryption key and ``iv``
is a unique initialization vector (IV) chosen for each encryption context. For
example, the object body is one encryption context with a randomly chosen IV.
The IV is stored as metadata of the encrypted item so that it is available for
decryption::
plaintext = D(ciphertext, k, iv)
where ``D`` is the decryption function.
The implementation of CTR mode follows `NIST SP800-38A
<http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf>`_, and the
full IV passed to the encryption or decryption function serves as the initial
counter block.
In general any encrypted item has accompanying crypto-metadata that describes
the IV and the cipher algorithm used for the encryption::
crypto_metadata = {"iv": <16 byte value>,
"cipher": "AES_CTR_256"}
This crypto-metadata is stored either with the ciphertext (for user
metadata and etags) or as a separate header (for object bodies).
Key management
--------------
A keymaster middleware is responsible for providing the keys required for each
encryption and decryption operation. Two keys are required when handling object
requests: a `container key` that is uniquely associated with the container path
and an `object key` that is uniquely associated with the object path. These
keys are made available to the encryption middleware via a callback function
that the keymaster installs in the WSGI request environ.
The current keymaster implementation derives container and object keys from the
``encryption_root_secret`` in a deterministic way by constructing a SHA256
HMAC using the ``encryption_root_secret`` as a key and the container or object
path as a message, for example::
object_key = HMAC(encryption_root_secret, "/a/c/o")
Other strategies for providing object and container keys may be employed by
future implementations of alternative keymaster middleware.
During each object PUT, a random key is generated to encrypt the object body.
This random key is then encrypted using the object key provided by the
keymaster. This makes it safe to store the encrypted random key alongside the
encrypted object data and metadata.
This process of `key wrapping` enables more efficient re-keying events when the
object key may need to be replaced and consequently any data encrypted using
that key must be re-encrypted. Key wrapping minimizes the amount of data
encrypted using those keys to just other randomly chosen keys which can be
re-wrapped efficiently without needing to re-encrypt the larger amounts of data
that were encrypted using the random keys.
.. note::
Re-keying is not currently implemented. Key wrapping is implemented
in anticipation of future re-keying operations.
Encryption middleware
---------------------
The encryption middleware is composed of an `encrypter` component and a
`decrypter` component.
Encrypter operation
^^^^^^^^^^^^^^^^^^^
Custom user metadata
++++++++++++++++++++
The encrypter encrypts each item of custom user metadata using the object key
provided by the keymaster and an IV that is randomly chosen for that metadata
item. The encrypted values are stored as :ref:`transient_sysmeta` with
associated crypto-metadata appended to the encrypted value. For example::
X-Object-Meta-Private1: value1
X-Object-Meta-Private2: value2
are transformed to::
X-Object-Transient-Sysmeta-Crypto-Meta-Private1:
E(value1, object_key, header_iv_1); swift_meta={"iv": header_iv_1,
"cipher": "AES_CTR_256"}
X-Object-Transient-Sysmeta-Crypto-Meta-Private2:
E(value2, object_key, header_iv_2); swift_meta={"iv": header_iv_2,
"cipher": "AES_CTR_256"}
The unencrypted custom user metadata headers are removed.
Object body
+++++++++++
Encryption of an object body is performed using a randomly chosen body key
and a randomly chosen IV::
body_ciphertext = E(body_plaintext, body_key, body_iv)
The body_key is wrapped using the object key provided by the keymaster and a
randomly chosen IV::
wrapped_body_key = E(body_key, object_key, body_key_iv)
The encrypter stores the associated crypto-metadata in a system metadata
header::
X-Object-Sysmeta-Crypto-Body-Meta:
{"iv": body_iv,
"cipher": "AES_CTR_256",
"body_key": {"key": wrapped_body_key,
"iv": body_key_iv}}
Note that in this case there is an extra item of crypto-metadata which stores
the wrapped body key and its IV.
Entity tag
++++++++++
While encrypting the object body the encrypter also calculates the ETag (md5
digest) of the plaintext body. This value is encrypted using the object key
provided by the keymaster and a randomly chosen IV, and saved as an item of
system metadata, with associated crypto-metadata appended to the encrypted
value::
X-Object-Sysmeta-Crypto-Etag:
E(md5(plaintext), object_key, etag_iv); swift_meta={"iv": etag_iv,
"cipher": "AES_CTR_256"}
The encrypter also forces an encrypted version of the plaintext ETag to be sent
with container updates by adding an update override header to the PUT request.
The associated crypto-metadata is appended to the encrypted ETag value of this
update override header::
X-Object-Sysmeta-Container-Update-Override-Etag:
E(md5(plaintext), container_key, override_etag_iv);
meta={"iv": override_etag_iv, "cipher": "AES_CTR_256"}
The container key is used for this encryption so that the decrypter is able
to decrypt the ETags in container listings when handling a container request,
since object keys may not be available in that context.
Since the plaintext ETag value is only known once the encrypter has completed
processing the entire object body, the ``X-Object-Sysmeta-Crypto-Etag`` and
``X-Object-Sysmeta-Container-Update-Override-Etag`` headers are sent after the
encrypted object body using the proxy server's support for request footers.
.. _conditional_requests:
Conditional Requests
++++++++++++++++++++
In general, an object server evaluates conditional requests with
``If[-None]-Match`` headers by comparing values listed in an
``If[-None]-Match`` header against the ETag that is stored in the object
metadata. This is not possible when the ETag stored in object metadata has been
encrypted. The encrypter therefore calculates an HMAC using the object key and
the ETag while handling object PUT requests, and stores this under the metadata
key ``X-Object-Sysmeta-Crypto-Etag-Mac``::
X-Object-Sysmeta-Crypto-Etag-Mac: HMAC(object_key, md5(plaintext))
Like other ETag-related metadata, this is sent after the encrypted object body
using the proxy server's support for request footers.
The encrypter similarly calculates an HMAC for each ETag value included in
``If[-None]-Match`` headers of conditional GET or HEAD requests, and appends
these to the ``If[-None]-Match`` header. The encrypter also sets the
``X-Backend-Etag-Is-At`` header to point to the previously stored
``X-Object-Sysmeta-Crypto-Etag-Mac`` metadata so that the object server
evaluates the conditional request by comparing the HMAC values included in the
``If[-None]-Match`` with the value stored under
``X-Object-Sysmeta-Crypto-Etag-Mac``. For example, given a conditional request
with header::
If-Match: match_etag
the encrypter would transform the request headers to include::
If-Match: match_etag,HMAC(object_key, match_etag)
X-Backend-Etag-Is-At: X-Object-Sysmeta-Crypto-Etag-Mac
This enables the object server to perform an encrypted comparison to check
whether the ETags match, without leaking the ETag itself or leaking information
about the object body.
Decrypter operation
^^^^^^^^^^^^^^^^^^^
For each GET or HEAD request to an object, the decrypter inspects the response
for encrypted items (revealed by crypto-metadata headers), and if any are
discovered then it will:
#. Fetch the object and container keys from the keymaster via its callback
#. Decrypt the ``X-Object-Sysmeta-Crypto-Etag`` value
#. Decrypt the ``X-Object-Sysmeta-Container-Update-Override-Etag`` value
#. Decrypt metadata header values using the object key
#. Decrypt the wrapped body key found in ``X-Object-Sysmeta-Crypto-Body-Meta``
#. Decrypt the body using the body key
For each GET request to a container that would include ETags in its response
body, the decrypter will:
#. GET the response body with the container listing
#. Fetch the container key from the keymaster via its callback
#. Decrypt any encrypted ETag entries in the container listing using the
container key
Impact on other Swift services and features
-------------------------------------------
Encryption has no impact on :ref:`versioned_writes` other than that any
previously unencrypted objects will be encrypted as they are copied to or from
the versions container. Keymaster and encryption middlewares should be placed
after ``versioned_writes`` in the proxy server pipeline, as described in
:ref:`encryption_deployment`.
`Container Sync` uses an internal client to GET objects that are to be sync'd.
This internal client must be configured to use the keymaster and encryption
middlewares as described :ref:`above <container_sync_client_config>`.
Encryption has no impact on the `object-auditor` service. Since the ETag
header saved with the object at rest is the md5 sum of the encrypted object
body then the auditor will verify that encrypted data is valid.
Encryption has no impact on the `object-expirer` service. ``X-Delete-At`` and
``X-Delete-After`` headers are not encrypted.
Encryption has no impact on the `object-replicator` and `object-reconstructor`
services. These services are unaware of the object or EC fragment data being
encrypted.
Encryption has no impact on the `container-reconciler` service. The
`container-reconciler` uses an internal client to move objects between
different policy rings. The reconciler's pipeline *MUST NOT* have encryption
enabled. The destination object has the same URL as the source object and the
object is moved without re-encryption.
Considerations for developers
-----------------------------
Developers should be aware that keymaster and encryption middlewares rely on
the path of an object remaining unchanged. The included keymaster derives keys
for containers and objects based on their paths and the
``encryption_root_secret``. The keymaster does not rely on object metadata to
inform its generation of keys for GET and HEAD requests because when handling
:ref:`conditional_requests` it is required to provide the object key before any
metadata has been read from the object.
Developers should therefore give careful consideration to any new features that
would relocate object data and metadata within a Swift cluster by means that do
not cause the object data and metadata to pass through the encryption
middlewares in the proxy pipeline and be re-encrypted.
The crypto-metadata associated with each encrypted item does include some
`key_id` metadata that is provided by the keymaster and contains the path used
to derive keys. This `key_id` metadata is persisted in anticipation of future
scenarios when it may be necessary to decrypt an object that has been relocated
without re-encrypting, in which case the metadata could be used to derive the
keys that were used for encryption. However, this alone is not sufficient to
handle conditional requests and to decrypt container listings where objects
have been relocated, and further work will be required to solve those issues.