2a8d2638b3
This patch set implements the PKICatalog [0] requirements as well as PeglegManagedDocument [1] generation requirements outlined in the spec [2]. Included in this patch set: * New CLI entry point called "pegleg site secrets generate-pki" * PeglegManagedDocument generation logic in engine.cache.managed_document * Refactored PKICatalog logic in engine.cache.pki_catalog derived from the Promenade PKI implementation [3], responsible for generating certificates, CAs, and keypairs * Refactored PKIGenerator logic in engine.cache.pki_generator derived from Promenade Generator implementation [4], responsible for reading in pegleg/PKICatalog/v1 documents (as well as promenade/PKICatalog/v1 documents for backwards compatibility) and generating required secrets and storing them into the paths specified under [0] * Unit tests for all of the above [5] * Example pki-catalog.yaml document under pegleg/site_yamls * Validation schema for pki-catalog.yaml (TODO: implement validation logic here: [6]) * Updates to CLI documentation and inclusion of PKICatalog and PeglegManagedDocument documentation * Documentation updates with PKI information [7] TODO (in follow-up patch sets): * Expand on overview documentation to include new Pegleg responsibilities * Allow the original repository (not the copied one) to be the destination where the secrets are written to * Finish up cert expiry/revocation logic [0] https://airship-specs.readthedocs.io/en/latest/specs/approved/pegleg-secrets.html#document-generation [1] https://airship-specs.readthedocs.io/en/latest/specs/approved/pegleg-secrets.html#peglegmanageddocument [2] https://airship-specs.readthedocs.io/en/latest/specs/approved/pegleg-secrets.html [3] https://github.com/openstack/airship-promenade/blob/master/promenade/pki.py [4] https://github.com/openstack/airship-promenade/blob/master/promenade/generator.py [5] https://review.openstack.org/#/c/611739/ [6] https://review.openstack.org/#/c/608159/ [7] https://review.openstack.org/#/c/611738/ Change-Id: I3010d04cac6d22c656d144f0dafeaa5e19a13068
116 lines
4.0 KiB
Python
116 lines
4.0 KiB
Python
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import os
|
|
|
|
from pegleg import config
|
|
from pegleg.engine.util import git
|
|
|
|
MANAGED_DOCUMENT_SCHEMA = 'pegleg/PeglegManagedDocument/v1'
|
|
SUPPORTED_SCHEMAS = (
|
|
'deckhand/CertificateAuthority/v1',
|
|
'deckhand/CertificateAuthorityKey/v1',
|
|
'deckhand/Certificate/v1',
|
|
'deckhand/CertificateKey/v1',
|
|
'deckhand/PublicKey/v1',
|
|
'deckhand/PrivateKey/v1',
|
|
)
|
|
|
|
_KIND_TO_PATH = {
|
|
'CertificateAuthority': 'certificates',
|
|
'CertificateAuthorityKey': 'certificates',
|
|
'Certificate': 'certificates',
|
|
'CertificateKey': 'certificates',
|
|
'PublicKey': 'keypairs',
|
|
'PrivateKey': 'keypairs'
|
|
}
|
|
|
|
|
|
def is_managed_document(document):
|
|
"""Utility for determining whether a document is wrapped by
|
|
``pegleg/PeglegManagedDocument/v1`` pattern.
|
|
|
|
:param dict document: Document to check.
|
|
:returns: True if document is managed, else False.
|
|
:rtype: bool
|
|
|
|
"""
|
|
|
|
return document.get('schema') == "pegleg/PeglegManagedDocument/v1"
|
|
|
|
|
|
def get_document_path(sitename, wrapper_document, cert_to_ca_map=None):
|
|
"""Get path for outputting generated certificates or keys to.
|
|
|
|
Also updates the provenance path (``data.generated.specifiedBy.path``)
|
|
for ``wrapper_document``.
|
|
|
|
* Certificates ar written to: ``<site>/secrets/certificates``
|
|
* Keypairs are written to: ``<site>/secrets/keypairs``
|
|
* Passphrases are written to: ``<site>/secrets/passphrases``
|
|
|
|
* 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``.
|
|
|
|
:param str sitename: Name of site.
|
|
:param dict wrapper_document: Generated ``PeglegManagedDocument``.
|
|
:param dict cert_to_ca_map: Dict that maps certificate names to
|
|
their respective CA name.
|
|
:returns: Path to write document out to.
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
cert_to_ca_map = cert_to_ca_map or {}
|
|
|
|
managed_document = wrapper_document['data']['managedDocument']
|
|
kind = managed_document['schema'].split("/")[1]
|
|
name = managed_document['metadata']['name']
|
|
|
|
path = "%s/secrets/%s" % (sitename, _KIND_TO_PATH[kind])
|
|
|
|
if 'authority' in kind.lower():
|
|
filename_structure = '%s_ca.yaml'
|
|
elif 'certificate' in kind.lower():
|
|
ca_name = cert_to_ca_map[name]
|
|
filename_structure = ca_name + '_%s_certificate.yaml'
|
|
elif 'public' in kind.lower() or 'private' in kind.lower():
|
|
filename_structure = '%s.yaml'
|
|
|
|
# Dashes in the document names are converted to underscores for
|
|
# consistency.
|
|
filename = (filename_structure % name).replace('-', '_')
|
|
fullpath = os.path.join(path, filename)
|
|
|
|
# Not all managed documents are generated. Only update path provenance
|
|
# information for those that are.
|
|
if wrapper_document['data'].get('generated'):
|
|
wrapper_document['data']['generated']['specifiedBy']['path'] = fullpath
|
|
return fullpath
|
|
|
|
|
|
def _get_repo_url_and_rev():
|
|
repo_path_or_url = config.get_site_repo()
|
|
repo_url = git.repo_url(repo_path_or_url)
|
|
repo_rev = config.get_site_rev()
|
|
return repo_url, repo_rev
|