Spec: Pegleg secret generation and encryption
This patchset adds a spec outlining the work effort and high-level implementation considerations for adding the ability to generate, encrypt, and decrypt secrets from Pegleg. Change-Id: Iacc99008be96a4b0bac145d5fa84143cc64e9b67 Story: 2003708 Task: 26363
This commit is contained in:
parent
145307c84b
commit
d1fca12693
@ -1,8 +0,0 @@
|
|||||||
.. placeholder:
|
|
||||||
|
|
||||||
===========
|
|
||||||
Placeholder
|
|
||||||
===========
|
|
||||||
|
|
||||||
This file is a placeholder and should be deleted when the first spec is moved
|
|
||||||
to this directory.
|
|
372
specs/approved/pegleg-secrets.rst
Normal file
372
specs/approved/pegleg-secrets.rst
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
..
|
||||||
|
This work is licensed under a Creative Commons Attribution 3.0 Unported
|
||||||
|
License.
|
||||||
|
|
||||||
|
http://creativecommons.org/licenses/by/3.0/legalcode
|
||||||
|
|
||||||
|
.. index::
|
||||||
|
single: template
|
||||||
|
single: creating specs
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Pegleg Secret Generation and Encryption
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Pegleg is responsible for shepherding deployment manifest documents from their
|
||||||
|
resting places in Git repositories to a consumable format that is ready
|
||||||
|
for ingestion into Airship. This spec expands its responsibility to
|
||||||
|
account for secure generation and encryption of secrets that are
|
||||||
|
required within an Airship-based deployment.
|
||||||
|
|
||||||
|
Links
|
||||||
|
=====
|
||||||
|
|
||||||
|
The work to author and implement this spec will be tracked under this
|
||||||
|
`Storyboard Story`_.
|
||||||
|
|
||||||
|
Problem description
|
||||||
|
===================
|
||||||
|
|
||||||
|
Airship supports the ability to identify secret information
|
||||||
|
required for functioning deployments, such as passwords and keys; to
|
||||||
|
ingest it into the site in a least-privilege-oriented fashion; and
|
||||||
|
to encrypt it at rest within Deckhand. However, lifecycle management of
|
||||||
|
the secrets outside the site should be made automatable and
|
||||||
|
repeatable, to facilitate operational needs such as periodic password
|
||||||
|
rotation, and to ensure that unencrypted secrets are only accessible by
|
||||||
|
authorized individuals.
|
||||||
|
|
||||||
|
Impacted components
|
||||||
|
===================
|
||||||
|
|
||||||
|
The following Airship components will be impacted by this solution:
|
||||||
|
|
||||||
|
#. Pegleg: enhanced to generate, rotate, encrypt, and decrypt secrets.
|
||||||
|
#. Promenade: PKICatalog will move to Pegleg.
|
||||||
|
#. Treasuremap: site manifests augmented to support the updated Secrets schema.
|
||||||
|
#. Airship-in-a-Bottle: site manifests augmented to support the updated
|
||||||
|
Secrets schema.
|
||||||
|
|
||||||
|
Proposed change
|
||||||
|
===============
|
||||||
|
|
||||||
|
PeglegManagedDocument
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
With this spec, the role of Pegleg grows from being a custodian of deployment
|
||||||
|
manifests to additionally being the author of certain manifests. A new YAML
|
||||||
|
schema will be created to describe these documents:
|
||||||
|
``pegleg/PeglegManagedDocument/v1``.
|
||||||
|
Documents of this type will have one or both of the following data elements,
|
||||||
|
although more may be added in the future: ``generated``, ``encrypted``.
|
||||||
|
PeglegManagedDocuments serve as wrappers around other documents, and the
|
||||||
|
wrapping serves to capture additional metadata that is necessary, but
|
||||||
|
separate from the managed document proper.
|
||||||
|
The managed document data will live in the ``data.managedDocument`` portion
|
||||||
|
of a PeglegManagedDocument.
|
||||||
|
|
||||||
|
If a PeglegManagedDocument is ``generated``, then its contents have been
|
||||||
|
created by Pegleg, and it must include provenance information per this
|
||||||
|
example::
|
||||||
|
|
||||||
|
schema: pegleg/PeglegManagedDocument/v1
|
||||||
|
metadata:
|
||||||
|
name: matches-document-name
|
||||||
|
schema: deckhand/Document/v1
|
||||||
|
labels:
|
||||||
|
matching: wrapped-doc
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
# Pegleg will initially support generation at site level only
|
||||||
|
layer: site
|
||||||
|
storagePolicy: encrypted
|
||||||
|
data:
|
||||||
|
generated:
|
||||||
|
at: <timestamp>
|
||||||
|
by: <author>
|
||||||
|
specifiedBy:
|
||||||
|
repo: <...>
|
||||||
|
reference: <git ref-head or similar>
|
||||||
|
path: <PKICatalog/PassphraseCatalog details>
|
||||||
|
managedDocument:
|
||||||
|
metadata:
|
||||||
|
storagePolicy: encrypted
|
||||||
|
schema: <as appropriate for wrapped document>
|
||||||
|
<metadata from parent PeglegManagedDocument>
|
||||||
|
<any other metadata as appropriate>
|
||||||
|
data: <generated data>
|
||||||
|
|
||||||
|
If a PeglegManagedDocument is ``encrypted``, then its contents have been
|
||||||
|
encrypted by Pegleg, and it must include provenance information per this
|
||||||
|
example::
|
||||||
|
|
||||||
|
schema: pegleg/PeglegManagedDocument/v1
|
||||||
|
metadata:
|
||||||
|
name: matches-document-name
|
||||||
|
schema: deckhand/Document/v1
|
||||||
|
labels:
|
||||||
|
matching: wrapped-doc
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: matching-wrapped-doc
|
||||||
|
storagePolicy: encrypted
|
||||||
|
data:
|
||||||
|
encrypted:
|
||||||
|
at: <timestamp>
|
||||||
|
by: <author>
|
||||||
|
managedDocument:
|
||||||
|
metadata:
|
||||||
|
storagePolicy: encrypted
|
||||||
|
schema: <as appropriate for wrapped document>
|
||||||
|
<metadata from parent PeglegManagedDocument>
|
||||||
|
<any other metadata as appropriate>
|
||||||
|
data: <encrypted string blob>
|
||||||
|
|
||||||
|
A PeglegManagedDocument that is both generated via a Catalog, and encrypted
|
||||||
|
(as specified by the catalog) will contain both ``generated`` and
|
||||||
|
``encrypted`` stanzas.
|
||||||
|
|
||||||
|
Note that this ``encrypted`` has a different purpose than the Deckhand
|
||||||
|
``storagePolicy: encrypted`` metadata, which indicates an *intent* for Deckhand
|
||||||
|
to store a document encrypted at rest in the cluster. The two can be used
|
||||||
|
together to ensure security, however: if a document is marked as
|
||||||
|
``storagePolicy: encrypted``, then automation may validate that it is only
|
||||||
|
persisted (e.g. to a Git repository) if it is in fact encrypted within
|
||||||
|
a PeglegManagedDocument.
|
||||||
|
|
||||||
|
Document Generation
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Document generation will follow the pattern established by Promenade's
|
||||||
|
PKICatalog pattern. In fact, PKICatalog management responsibility will move
|
||||||
|
to Pegleg as part of this effort. The types of documents that are expected
|
||||||
|
to be generated are certificates and keys, which are defined via PKICatalog
|
||||||
|
documents now, and passphrases, which will be defined via a new
|
||||||
|
``pegleg/PassphraseCatalog/v1`` document. Longer-term, these specifications
|
||||||
|
may be combined, or split further (into a CertificateCatalog and
|
||||||
|
KeypairCatalog), but this is not needed in the initial implementation in
|
||||||
|
Pegleg. A collection of manifests
|
||||||
|
may define more than one of each of these secret catalog documents if desired.
|
||||||
|
|
||||||
|
The documents generated via PKICatalog and PassphraseCatalog will follow the
|
||||||
|
PeglegManagedDocument schema above; note that this is a change to existing
|
||||||
|
PKICatalog behavior. The PKICatalog schema and associated code should be
|
||||||
|
copied to Pegleg (and renamed to ``pegleg/PKICatalog/v1``), and during a
|
||||||
|
transition period the old and new PKICatalog implementations will exist
|
||||||
|
side-by-side with slightly different semantics. Promenade's PKICatalog can
|
||||||
|
be removed once all deployment manifests have been updated to use the new one.
|
||||||
|
|
||||||
|
Pegleg will place generated document files in ``<site>/secrets/passphrases/``,
|
||||||
|
``<site>/secrets/certificates``, or ``<site>/secrets/keypairs`` as appropriate:
|
||||||
|
|
||||||
|
* The generated filenames for passphrases will follow the pattern
|
||||||
|
``<passphrase-doc-name>.yaml``.
|
||||||
|
* The generated filenames for certificate authorities will follow the pattern
|
||||||
|
``<ca-name>_ca.yaml``.
|
||||||
|
* The generated filenames for certificates will follow the pattern
|
||||||
|
``<ca-name>_<certificate-doc-name>_certificate.yaml``.
|
||||||
|
* The generated filenames for certificate keys will follow the pattern
|
||||||
|
``<ca-name>_<certificate-doc-name>_key.yaml``.
|
||||||
|
* The generated filenames for keypairs will follow the pattern
|
||||||
|
``<keypair-doc-name>.yaml``.
|
||||||
|
* Dashes in the document names will be converted to underscores for consistency.
|
||||||
|
|
||||||
|
A PassphraseCatalog will capture the following example structure::
|
||||||
|
|
||||||
|
schema: pegleg/PassphraseCatalog/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: cluster-passphrases
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
passphrases:
|
||||||
|
- document_name: osh-nova-password
|
||||||
|
description: Service password for Nova
|
||||||
|
encrypted: true
|
||||||
|
- document_name: osh-nova-oslo-db-password
|
||||||
|
description: Database password for Nova
|
||||||
|
encrypted: true
|
||||||
|
length: 12
|
||||||
|
|
||||||
|
The nonobvious bits of the document described above are:
|
||||||
|
|
||||||
|
* ``encrypted`` is optional, and denotes whether the generated
|
||||||
|
PeglegManagedDocument will be ``encrypted``, as well as whether the wrapped
|
||||||
|
document will have ``storagePolicy: encrypted`` or
|
||||||
|
``storagePolicy: cleartext`` metadata.
|
||||||
|
If absent, ``encrypted`` defaults to ``true``.
|
||||||
|
* ``document_name`` is required, and is used to create the filename of the
|
||||||
|
generated PeglegManagedDocument manifest, and the ``metadata.name`` of
|
||||||
|
the wrapped ``deckhand/Passphrase/v1`` document. In both cases, Pegleg will
|
||||||
|
replace dashes in the ``document_name`` with underscores.
|
||||||
|
* ``length`` is optional, and denotes the length in characters of the
|
||||||
|
generated cleartext passphrase data. If absent, ``length`` defaults
|
||||||
|
to ``24``.
|
||||||
|
* ``description`` is optional.
|
||||||
|
|
||||||
|
The ``encrypted`` key will be added to the PKICatalog schema, and adds the same
|
||||||
|
semantics to PKICatalog-based generation as are described above for
|
||||||
|
PassphraseCatalog.
|
||||||
|
|
||||||
|
Pegleg CLI Changes
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The Pegleg CLI interface will be extended as follows. These
|
||||||
|
commands will create PeglegManagedDocument manifests in the local repository.
|
||||||
|
Committing and pushing the changes will be left to the
|
||||||
|
operator or to script-based automation.
|
||||||
|
|
||||||
|
For the CLI commands below which encrypt or decrypt secrets, an environment
|
||||||
|
variable (e.g. ``$PEGLEG_KEY`` will be use to capture the key/passphrase to use.
|
||||||
|
``pegleg site secrets rotate`` will use a second variable
|
||||||
|
(e.g. ``$PEGLEG_PREVIOUS_KEY``) to hold the key/passphrase being rotated
|
||||||
|
out.
|
||||||
|
|
||||||
|
``pegleg site secrets generate passphrases``: Generate passphrases according to
|
||||||
|
all PassphraseCatalog documents in the site.
|
||||||
|
Note that regenerating passphrases can be accomplished
|
||||||
|
simply by re-running ``pegleg site secrets generate passphrases``.
|
||||||
|
|
||||||
|
``pegleg site secrets generate pki``: Generate certificates and keys according
|
||||||
|
to all PKICatalog documents in the site.
|
||||||
|
Note that regenerating certificates can be accomplished
|
||||||
|
simply by re-running ``pegleg site secrets generate pki``.
|
||||||
|
|
||||||
|
``pegleg site secrets generate``: Combines the two commands above.
|
||||||
|
May be expanded in the future to include other manifest generation activities.
|
||||||
|
|
||||||
|
``pegleg site bootstrap``: For now, a synonym for
|
||||||
|
``pegleg site secrets generate``,
|
||||||
|
and may be expanded in the future to include other bootstrapping activities.
|
||||||
|
|
||||||
|
``pegleg site secrets encrypt``: Encrypt all site documents which have
|
||||||
|
``metadata.storagePolicy: encrypted``, and which are not already encrypted
|
||||||
|
within a wrapping PeglegManagedDocument. Note that the
|
||||||
|
``pegleg site secrets generate`` commands encrypt generated secrets as
|
||||||
|
specified, so ``pegleg site secrets encrypt`` is intended mainly for
|
||||||
|
external-facing secrets which a deployment engineer brings to the site
|
||||||
|
manifests.
|
||||||
|
The output PeglegManagedDocument will be written back to the filename that
|
||||||
|
served as its source.
|
||||||
|
|
||||||
|
``pegleg site secrets decrypt <document YAML file>``: Decrypt a specific
|
||||||
|
PeglegManagedDocument manifest, unwrapping it and outputting the cleartext
|
||||||
|
original document YAML to standard output. This is intended to be used when
|
||||||
|
an authorized deployment engineer needs to determine a particular cleartext
|
||||||
|
secret for a specific operational purpose.
|
||||||
|
|
||||||
|
``pegleg site secrets rotate``: This action re-encrypts encrypted secrets
|
||||||
|
with a new key/passphrase, and it takes the previously-used key and a new
|
||||||
|
key as input. It accomplishes its task via two activities:
|
||||||
|
|
||||||
|
* For encrypted secrets that were imported from outside of Pegleg
|
||||||
|
(i.e. PeglegManagedDocuments which lack the ``generated`` stanza),
|
||||||
|
decrypt them with the old key (in-memory), re-encrypt them with
|
||||||
|
the new key, and output the results.
|
||||||
|
* Perform a fresh ``pegleg site secrets generate`` process using the new key.
|
||||||
|
This will replace all ``generated`` secrets with new secret values
|
||||||
|
for added security. There is an assumption here that the only actors
|
||||||
|
that need to know generated secrets are the services within the
|
||||||
|
Airship-managed cluster, not external services or deployment engineers,
|
||||||
|
except perhaps for point-in-time troubleshooting or operational
|
||||||
|
exercises.
|
||||||
|
|
||||||
|
Driving deployment of a site directly via Pegleg is follow-on functionality
|
||||||
|
which will
|
||||||
|
collect site documents, use them to create the ``genesis.sh`` script, and then
|
||||||
|
interact directly with Shipyard to drive deployments. Its details are beyond
|
||||||
|
the scope of this spec, but when implemented, it should decrypt documents
|
||||||
|
wrapped by applicable PeglegManagedDocuments at the lst responsible moment,
|
||||||
|
and take care not to write, log, or stdout them to disk as cleartext.
|
||||||
|
|
||||||
|
Note that existing ``pegleg collect`` functionality should **not** be changed
|
||||||
|
to decrypt encrypted secrets; this is because it writes its output to disk.
|
||||||
|
If ``pegleg collect`` is called, at this point in time, the
|
||||||
|
PeglegManagedDocuments will be written (encrypted) to disk.
|
||||||
|
To enable special case full site secret decryption, a ``--force-decrypt`` flag
|
||||||
|
will be added to ``pegleg collect`` to do this under controlled circumstances,
|
||||||
|
and to help bridge the gap with existing CICD pipelines until Pegleg-driven
|
||||||
|
site deployment is in place. It will leverage the ``$PEGLEG_KEY``
|
||||||
|
variable described above.
|
||||||
|
|
||||||
|
Secret Generation
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The ``rstr`` library should be invoked to generate secrets of the
|
||||||
|
appropriate length and character set.
|
||||||
|
This library uses the ``os.urandom()`` function,
|
||||||
|
which in turn leverages ``/dev/urandom`` on Linux,
|
||||||
|
and it is suitable for cryptographic purposes.
|
||||||
|
|
||||||
|
Characters in generated secrets will be evenly distributed across lower-
|
||||||
|
and upper-case letters, digits, and punctuation in
|
||||||
|
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~. Note this is equivalent to the union of
|
||||||
|
Python string.ascii_letters, string.digits, and string.punctuation.
|
||||||
|
|
||||||
|
Secret Encryption
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Details around encryption will be defined in a follow-on patch set to this spec.
|
||||||
|
|
||||||
|
Security impact
|
||||||
|
===============
|
||||||
|
|
||||||
|
These changes will result in a system that handles site secrets in a highly
|
||||||
|
secure manner, in the face of multiple roles and day 2 operational needs.
|
||||||
|
|
||||||
|
Performance impact
|
||||||
|
==================
|
||||||
|
|
||||||
|
Performance impact to existing flows will be minimal. Pegleg will need to
|
||||||
|
additionally decrypt secrets as part of site deployment, but this will be
|
||||||
|
an efficient operation performed once per deployment.
|
||||||
|
|
||||||
|
Alternatives
|
||||||
|
============
|
||||||
|
|
||||||
|
The Python ``secrets`` library presents a convenient interface for generating
|
||||||
|
random strings. However, it was introduced in Python 3.6, and it would be
|
||||||
|
limiting to introduce this constraint on Airship CICD pipelines.
|
||||||
|
|
||||||
|
The ``strgen`` library presents an even more convenient interface for
|
||||||
|
generating pseudo-random strings; however, it leverages the Python ``random``
|
||||||
|
library, which is unsuitably random for cryptographic purposes.
|
||||||
|
|
||||||
|
Deckhand already supports a ``storagePolicy`` element which indicates whether
|
||||||
|
whether Deckhand will persist document data in an encrypted state, and this
|
||||||
|
flag could have been re-used by Pegleg to indicate whether a secret is
|
||||||
|
(or should be) encrypted. However, "should this data be encrypted" is a
|
||||||
|
fundamentally different question than "is this data encrypted now", and
|
||||||
|
additional metadata-esque parameters (``generated``, ``generatedLength``)
|
||||||
|
were desired as well, so this proposal adds ``data.encrypted`` to indicate
|
||||||
|
the point-in-time encryption status. ``storagePolicy`` is still valuable
|
||||||
|
in this context to make sure everything that *should* be encrypted *is*,
|
||||||
|
prior to performing actions with it (e.g. Git commits).
|
||||||
|
|
||||||
|
This proposed implementation writes the output of generation/encryption events
|
||||||
|
back to the same source files from which the original data came. This is a
|
||||||
|
destructive operation; however, it wasn't evident that it is problematic in
|
||||||
|
any anticipated workflow. In addition, it sidesteps challenges around
|
||||||
|
naming of generated files, and cleanup of original files.
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
==============
|
||||||
|
|
||||||
|
Please refer to the `Storyboard Story`_ for implementation planning information.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
============
|
||||||
|
|
||||||
|
This work should be based on the patchset to add `Git branch and revision
|
||||||
|
support`_ to Pegleg, if it is not merged by the time implementation begins.
|
||||||
|
This patchset alters the CLI interface and Git repository management code,
|
||||||
|
and basing on it will avoid future refactoring.
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. _Storyboard Story: https://storyboard.openstack.org/#!/story/2003708
|
||||||
|
.. _Git branch and revision support: https://review.openstack.org/#/c/577886/
|
Loading…
x
Reference in New Issue
Block a user