diff --git a/.circleci/config.yml b/.circleci/config.yml index 65e5aa8..2171ddf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,7 +29,7 @@ jobs: pipenv run make lint - run: - name: Lint the examples manifests + name: Lint the example manifests command: | pipenv run make yaml-lint @@ -45,6 +45,16 @@ jobs: sudo .circleci/setup-e2e.sh pipenv run make e2e-test + - run: + name: Run static analysis with mypy + command: | + pipenv run make static-analysis + + - run: + name: Scan the codebase for security issues with bandit + command: | + pipenv run make scan + build: working_directory: ~/repo docker: diff --git a/Dockerfile b/Dockerfile index 012ac27..6eeb446 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ FROM python:3.6-slim COPY Pipfile* / -RUN set -ex && pip install pipenv --no-cache-dir --disable-pip-version-check \ +RUN set -ex && pip install pipenv==2018.10.13 --no-cache-dir --disable-pip-version-check \ && pipenv --python 3.6 lock -r > requirements.txt # ----------------------------------------------------------------------------------------- diff --git a/README.md b/README.md index 9ac2945..710afe0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # pkictl [![Python](https://img.shields.io/badge/Python-3.6+-blue.svg)](#) -[![Version](https://img.shields.io/badge/version-0.1.2-green.svg)](#) +[![Version](https://img.shields.io/badge/version-0.2.0-green.svg)](#) [![License](https://img.shields.io/badge/license-MPL-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.en.html) [![Coverage Status](https://coveralls.io/repos/github/bincyber/pkictl/badge.svg?branch=master)](https://coveralls.io/github/bincyber/pkictl?branch=master) [![CircleCI](https://circleci.com/gh/bincyber/pkictl.svg?style=svg)](https://circleci.com/gh/bincyber/pkictl) @@ -78,8 +78,9 @@ Create a [manifest file](docs/examples/manifest.yaml): --- kind: RootCA - name: demo-root-ca - description: pkictl demo Root CA + metadata: + name: demo-root-ca + description: pkictl demo Root CA spec: key_type: ec key_bits: 384 @@ -94,10 +95,11 @@ Create a [manifest file](docs/examples/manifest.yaml): province: California --- kind: IntermediateCA - name: demo-intermediate-ca - description: pkictl demo Intermediate CA - issuer: demo-root-ca - kv_backend: demo-kv-engine + metadata: + name: demo-intermediate-ca + description: pkictl demo Intermediate CA + issuer: demo-root-ca + kv_backend: demo-kv-engine spec: type: exported key_type: rsa @@ -154,8 +156,9 @@ Create a [manifest file](docs/examples/manifest.yaml): } --- kind: KV - name: demo-kv-engine - description: pkictl demo KV v1 engine + metadata: + name: demo-kv-engine + description: pkictl demo KV v1 engine spec: options: version: 1 diff --git a/docs/Environment Variables.md b/docs/Environment Variables.md index 3ee2ca5..e948a4b 100644 --- a/docs/Environment Variables.md +++ b/docs/Environment Variables.md @@ -3,6 +3,10 @@ _pkictl_ supports a subset of the [environment variables](https://www.vaultproject.io/docs/commands/#environment-variables) that the Vault CLI does. The following environment variables are supported: -* VAULT_TOKEN * VAULT_ADDR +* VAULT_TOKEN * VAULT_SKIP_VERIFY + +If the `-u` flag or `VAULT_ADDR` is not specified, the address of the Vault server will be prompted for. + +If `VAULT_TOKEN` is not specified, it will be prompted for. The token cannot be supplied any other way. diff --git a/docs/PKI as a Service.md b/docs/PKI as a Service.md index 72b5f99..446d5c7 100644 --- a/docs/PKI as a Service.md +++ b/docs/PKI as a Service.md @@ -10,8 +10,9 @@ Create the YAML manifest file: --- kind: RootCA - name: root - description: PKI-as-a-Service Root CA + metadata: + name: root + description: PKI-as-a-Service Root CA spec: key_type: rsa key_bits: 4096 @@ -21,9 +22,10 @@ Create the YAML manifest file: common_name: Root CA --- kind: IntermediateCA - name: intermediate - description: PKI-as-a-Service Intermediate CA - issuer: root + metadata: + name: intermediate + description: PKI-as-a-Service Intermediate CA + issuer: root spec: type: internal key_type: rsa diff --git a/docs/Provision PKI for Kubernetes.md b/docs/Provision PKI for Kubernetes.md index 2d5db77..33c8f9c 100644 --- a/docs/Provision PKI for Kubernetes.md +++ b/docs/Provision PKI for Kubernetes.md @@ -6,26 +6,31 @@ _pkictl_ can be used to simplify the process of provisioning the PKI for Kuberne ## Provision the Certificate Authorities -This example is for provisioning a control plane which uses external etcd. Define the Certificate Authorities to provision in the YAML manifest file: +This example is for provisioning a control plane which uses external etcd. Define the Certificate Authorities to provision in the YAML manifest [file](docs/examples/kubernetes.yaml): + + $ vim kubernetes.yaml --- kind: KV - name: kv/kube-ca - description: exported PKI secrets for the Kubernetes CA + metadata: + name: kv/kube-ca + description: exported PKI secrets for the Kubernetes CA spec: options: version: 1 --- kind: KV - name: kv/kube-fp-ca - description: exported PKI secrets for the Kubernetes Front Proxy CA + metadata: + name: kv/kube-fp-ca + description: exported PKI secrets for the Kubernetes Front Proxy CA spec: options: version: 1 --- kind: RootCA - name: pki/kube-root-ca - description: Kubernetes Root CA + metadata: + name: pki/kube-root-ca + description: Kubernetes Root CA spec: key_type: rsa key_bits: 4096 @@ -35,9 +40,10 @@ This example is for provisioning a control plane which uses external etcd. Defin common_name: Kubernetes Root Certificate Authority --- kind: IntermediateCA - name: pki/etcd-ca - description: Intermediate CA for etcd - issuer: pki/root-ca + metadata: + name: pki/etcd-ca + description: Intermediate CA for etcd + issuer: pki/root-ca spec: type: internal key_type: rsa @@ -96,10 +102,11 @@ This example is for provisioning a control plane which uses external etcd. Defin } --- kind: IntermediateCA - name: pki/kube-ca - description: Kubernetes CA - issuer: pki/kube-root-ca - kv_backend: kv/kube-ca + metadata: + name: pki/kube-ca + description: Kubernetes CA + issuer: pki/kube-root-ca + kv_backend: kv/kube-ca spec: type: exported key_type: rsa @@ -115,10 +122,11 @@ This example is for provisioning a control plane which uses external etcd. Defin } --- kind: IntermediateCA - name: pki/kube-fp-ca - description: Kubernetes Front Proxy CA - issuer: pki/kube-root-ca - kv_backend: kv/kube-fp-ca + metadata: + name: pki/kube-fp-ca + description: Kubernetes Front Proxy CA + issuer: pki/kube-root-ca + kv_backend: kv/kube-fp-ca spec: type: exported key_type: rsa @@ -149,6 +157,44 @@ This example is for provisioning a control plane which uses external etcd. Defin capabilities = ["read"] } + + $ pkictl apply -f kubernetes.yaml + + [*] pkictl - the Vault server has been initialized and is not sealed + [*] pkictl - Mounted KV secrets engine: kv/kube-ca + [*] pkictl - Mounted KV secrets engine: kv/kube-fp-ca + [*] pkictl - Mounted PKI secrets engine: pki/kube-root-ca + [*] pkictl - Generated Root CA: pki/kube-root-ca + [*] pkictl - Mounted PKI secrets engine: pki/kube-fp-ca + [*] pkictl - Created intermediate CA: pki/kube-fp-ca + [*] pkictl - Signed intermediate CA 'pki/kube-fp-ca' with issuing CA: pki/kube-root-ca + [*] pkictl - Set signed certificate for intermediate CA: pki/kube-fp-ca + [*] pkictl - Configured URLs for CA: pki/kube-fp-ca + [*] pkictl - Set CRL configuration for CA: pki/kube-fp-ca + [*] pkictl - Stored private key for 'pki/kube-fp-ca' in KV engine: kv/kube-fp-ca + [*] pkictl - Configured role 'client' for intermediate CA: pki/kube-fp-ca + [*] pkictl - Configured policy 'kv-kube-fp-ca-policy' for intermediate CA: pki/kube-fp-ca + [*] pkictl - Configured policy 'kube-fp-ca-client-policy' for intermediate CA: pki/kube-fp-ca + [*] pkictl - Mounted PKI secrets engine: pki/kube-ca + [*] pkictl - Created intermediate CA: pki/kube-ca + [*] pkictl - Signed intermediate CA 'pki/kube-ca' with issuing CA: pki/kube-root-ca + [*] pkictl - Set signed certificate for intermediate CA: pki/kube-ca + [*] pkictl - Configured URLs for CA: pki/kube-ca + [*] pkictl - Set CRL configuration for CA: pki/kube-ca + [*] pkictl - Stored private key for 'pki/kube-ca' in KV engine: kv/kube-ca + [*] pkictl - Configured policy 'kv-kube-ca-policy' for intermediate CA: pki/kube-ca + [*] pkictl - Mounted PKI secrets engine: pki/etcd-ca + [*] pkictl - Created intermediate CA: pki/etcd-ca + [*] pkictl - Signed intermediate CA 'pki/etcd-ca' with issuing CA: pki/kube-root-ca + [*] pkictl - Set signed certificate for intermediate CA: pki/etcd-ca + [*] pkictl - Configured URLs for CA: pki/etcd-ca + [*] pkictl - Set CRL configuration for CA: pki/etcd-ca + [*] pkictl - Configured role 'peer' for intermediate CA: pki/etcd-ca + [*] pkictl - Configured role 'server' for intermediate CA: pki/etcd-ca + [*] pkictl - Configured role 'client' for intermediate CA: pki/etcd-ca + [*] pkictl - Configured policy 'etcd-ca-server-policy' for intermediate CA: pki/etcd-ca + [*] pkictl - Configured policy 'etcd-ca-client-policy' for intermediate CA: pki/etcd-ca + The above will create: - a Root CA for the Kubernetes cluster with a TTL of 10 years - an Intermediate CA for etcd with a TTL of 5 years diff --git a/docs/examples/kubernetes.yaml b/docs/examples/kubernetes.yaml index 6408a91..739b1d6 100644 --- a/docs/examples/kubernetes.yaml +++ b/docs/examples/kubernetes.yaml @@ -1,21 +1,24 @@ --- kind: KV -name: kv/kube-ca -description: exported PKI secrets for the Kubernetes CA +metadata: + name: kv/kube-ca + description: exported PKI secrets for the Kubernetes CA spec: options: version: 1 --- kind: KV -name: kv/kube-fp-ca -description: exported PKI secrets for the Kubernetes Front Proxy CA +metadata: + name: kv/kube-fp-ca + description: exported PKI secrets for the Kubernetes Front Proxy CA spec: options: version: 1 --- kind: RootCA -name: pki/kube-root-ca -description: Kubernetes Root CA +metadata: + name: pki/kube-root-ca + description: Kubernetes Root CA spec: key_type: rsa key_bits: 4096 @@ -25,9 +28,10 @@ spec: common_name: Kubernetes Root Certificate Authority --- kind: IntermediateCA -name: pki/etcd-ca -description: Intermediate CA for etcd -issuer: pki/kube-root-ca +metadata: + name: pki/etcd-ca + description: Intermediate CA for etcd + issuer: pki/kube-root-ca spec: type: internal key_type: rsa @@ -92,10 +96,11 @@ spec: } --- kind: IntermediateCA -name: pki/kube-ca -description: Kubernetes CA -issuer: pki/kube-root-ca -kv_backend: kv/kube-ca +metadata: + name: pki/kube-ca + description: Kubernetes CA + issuer: pki/kube-root-ca + kv_engine: kv/kube-ca spec: type: exported key_type: rsa @@ -117,10 +122,11 @@ spec: } --- kind: IntermediateCA -name: pki/kube-fp-ca -description: Kubernetes Front Proxy CA -issuer: pki/kube-root-ca -kv_backend: kv/kube-fp-ca +metadata: + name: pki/kube-fp-ca + description: Kubernetes Front Proxy CA + issuer: pki/kube-root-ca + kv_engine: kv/kube-fp-ca spec: type: exported key_type: rsa diff --git a/docs/examples/manifest.yaml b/docs/examples/manifest.yaml index b746cb4..7577234 100644 --- a/docs/examples/manifest.yaml +++ b/docs/examples/manifest.yaml @@ -1,7 +1,8 @@ --- kind: RootCA -name: demo-root-ca -description: pkictl demo Root CA +metadata: + name: demo-root-ca + description: pkictl demo Root CA spec: key_type: ec key_bits: 384 @@ -16,10 +17,11 @@ spec: province: California --- kind: IntermediateCA -name: demo-intermediate-ca -description: pkictl demo Intermediate CA -issuer: demo-root-ca -kv_backend: demo-kv-engine +metadata: + name: demo-intermediate-ca + description: pkictl demo Intermediate CA + issuer: demo-root-ca + kv_engine: demo-kv-engine spec: type: exported key_type: rsa @@ -76,8 +78,9 @@ spec: } --- kind: KV -name: demo-kv-engine -description: pkictl demo KV v1 engine +metadata: + name: demo-kv-engine + description: pkictl demo KV v1 engine spec: options: version: 1 diff --git a/docs/examples/pki-as-a-service.yaml b/docs/examples/pki-as-a-service.yaml index 8d887eb..67a3c1f 100644 --- a/docs/examples/pki-as-a-service.yaml +++ b/docs/examples/pki-as-a-service.yaml @@ -1,7 +1,8 @@ --- kind: RootCA -name: root -description: PKI-as-a-Service Root CA +metadata: + name: root + description: PKI-as-a-Service Root CA spec: key_type: rsa key_bits: 4096 @@ -11,9 +12,10 @@ spec: common_name: Root CA --- kind: IntermediateCA -name: intermediate -description: PKI-as-a-Service Intermediate CA -issuer: root +metadata: + name: intermediate + description: PKI-as-a-Service Intermediate CA + issuer: root spec: type: internal key_type: rsa diff --git a/pkictl/models.py b/pkictl/models.py index 21646d6..5768162 100644 --- a/pkictl/models.py +++ b/pkictl/models.py @@ -5,8 +5,8 @@ class CertificateAuthority: def __init__(self, baseurl, manifest): self.baseurl = baseurl self.dict = manifest - self.name = manifest['name'] - self.description = manifest['description'] + self.name = manifest['metadata']['name'] + self.description = manifest['metadata']['description'] @property def spec(self): @@ -56,15 +56,15 @@ def __init__(self, baseurl, manifest): @property def issuer(self): - return self.dict['issuer'] + return self.dict['metadata']['issuer'] @property - def catype(self): - return self.spec['type'] + def kv_engine(self): + return self.dict['metadata']['kv_engine'] @property - def kv_backend(self): - return self.dict['kv_backend'] + def catype(self): + return self.spec['type'] @property def csr(self): @@ -107,8 +107,8 @@ def set_signed_url(self): return urljoin(self.baseurl, f"/v1/{self.name}/intermediate/set-signed") @property - def kv_backend_url(self): - return urljoin(self.baseurl, f"/v1/{self.kv_backend}/{self.name}") + def kv_engine_url(self): + return urljoin(self.baseurl, f"/v1/{self.kv_engine}/{self.name}") @property def spec(self): @@ -143,14 +143,14 @@ class KeyValueEngine: def __init__(self, baseurl, manifest): self.baseurl = baseurl self.dict = manifest - self.name = manifest['name'] + self.name = manifest['metadata']['name'] @property def spec(self): spec = self.dict['spec'].copy() spec.update({ 'type': self.dict['kind'].lower(), - 'description': self.dict['description'] + 'description': self.dict['metadata']['description'] }) return spec diff --git a/pkictl/pkictl.py b/pkictl/pkictl.py index e51874c..e6f51da 100644 --- a/pkictl/pkictl.py +++ b/pkictl/pkictl.py @@ -61,10 +61,10 @@ def main(): if sealed: sys.exit(1) - roots, intermediates, kvbackends = utils.get_validated_manifests(documents) + roots, intermediates, kv_engines = utils.get_validated_manifests(documents) # mount KV engines - for kve in kvbackends: + for kve in kv_engines: kvengine = KeyValueEngine(args.baseurl, kve) vault_client.mount_kv_engine(kvengine) diff --git a/pkictl/schemas.py b/pkictl/schemas.py index ede364c..ba7c4ce 100644 --- a/pkictl/schemas.py +++ b/pkictl/schemas.py @@ -4,8 +4,10 @@ RootCASchema = Schema({ Required('kind'): All('RootCA', msg="Must be 'RootCA'"), - Required('name'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), - Required('description'): str, + Required('metadata'): { + Required('name'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), + Required('description'): str + }, Required('spec'): { Required('key_type'): Any('rsa', 'ec', msg="Must be 'rsa' or 'ec'"), Required('key_bits'): Range(min=256, max=4096), @@ -24,10 +26,12 @@ IntermediateCASchema = Schema({ Required('kind'): All('IntermediateCA', msg="Must be 'IntermediateCA'"), - Required('name'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), - Required('description'): str, - Required('issuer'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), - Optional('kv_backend'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), + Required('metadata'): { + Required('name'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), + Required('description'): str, + Required('issuer'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), + Optional('kv_engine'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string") + }, Required('spec'): { Required('type'): Any('internal', 'exported', msg="Must be 'internal' or 'exported'"), Required('key_type'): Any('rsa', 'ec', msg="Must be 'rsa' or 'ec'"), @@ -73,8 +77,10 @@ KeyValueSchema = Schema({ Required('kind'): All('KV', msg="Must be 'KV'"), - Required('name'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), - Required('description'): str, + Required('metadata'): { + Required('name'): Match(MOUNT_PATH_REGEX, msg="Must be lowercase alphanumberic string"), + Required('description'): str + }, Required('spec'): { Optional("config"): { Optional('default_lease_ttl', default='8766h'): Match(r'\d+[hms]'), diff --git a/pkictl/tests/manifests/multi/intermediate.yaml b/pkictl/tests/manifests/multi/intermediate.yaml index c916e0b..588e44f 100644 --- a/pkictl/tests/manifests/multi/intermediate.yaml +++ b/pkictl/tests/manifests/multi/intermediate.yaml @@ -1,9 +1,10 @@ --- kind: IntermediateCA -name: test-intermediate-ca -description: Intermediate Certificate Authority -issuer: test-root-ca -kv_backend: test-kv +metadata: + name: test-intermediate-ca + description: Intermediate Certificate Authority + issuer: test-root-ca + kv_engine: test-kv spec: type: exported key_type: rsa diff --git a/pkictl/tests/manifests/multi/kv.yaml b/pkictl/tests/manifests/multi/kv.yaml index 11d896a..cd321d1 100644 --- a/pkictl/tests/manifests/multi/kv.yaml +++ b/pkictl/tests/manifests/multi/kv.yaml @@ -1,7 +1,8 @@ --- kind: KV -name: test-kv -description: KV v1 engine for testing +metadata: + name: test-kv + description: KV v1 engine for testing spec: options: version: 1 diff --git a/pkictl/tests/manifests/multi/root.yaml b/pkictl/tests/manifests/multi/root.yaml index 83652d7..743d160 100644 --- a/pkictl/tests/manifests/multi/root.yaml +++ b/pkictl/tests/manifests/multi/root.yaml @@ -1,7 +1,8 @@ --- kind: RootCA -name: test-root-ca -description: Test Root CA +metadata: + name: test-root-ca + description: Test Root CA spec: key_type: 'ec' key_bits: 384 diff --git a/pkictl/tests/manifests/pki.yaml b/pkictl/tests/manifests/pki.yaml index 1d7a685..6237151 100644 --- a/pkictl/tests/manifests/pki.yaml +++ b/pkictl/tests/manifests/pki.yaml @@ -1,7 +1,8 @@ --- kind: RootCA -name: pki/root-ca-1 -description: Root CA 1 +metadata: + name: pki/root-ca-1 + description: Root CA 1 spec: key_type: ec key_bits: 384 @@ -11,10 +12,11 @@ spec: common_name: Root Certificate Authority 1 --- kind: IntermediateCA -name: pki/intermediate-ca-dev -description: Intermediate CA for development -issuer: pki/intermediate-ca-staging -kv_backend: kv/intermediate-ca-dev +metadata: + name: pki/intermediate-ca-dev + description: Intermediate CA for development + issuer: pki/intermediate-ca-staging + kv_engine: kv/intermediate-ca-dev spec: type: exported key_type: rsa @@ -55,9 +57,10 @@ spec: } --- kind: IntermediateCA -name: pki/intermediate-ca-production -description: Intermediate CA for Production -issuer: pki/root-ca-1 +metadata: + name: pki/intermediate-ca-production + description: Intermediate CA for Production + issuer: pki/root-ca-1 spec: type: internal key_type: rsa @@ -85,10 +88,11 @@ spec: } --- kind: IntermediateCA -name: pki/intermediate-ca-staging -description: Intermediate CA for Staging -issuer: pki/root-ca-2 -kv_backend: kv/intermediate-ca-staging +metadata: + name: pki/intermediate-ca-staging + description: Intermediate CA for Staging + issuer: pki/root-ca-2 + kv_engine: kv/intermediate-ca-staging spec: type: exported key_type: ec @@ -129,8 +133,9 @@ spec: } --- kind: RootCA -name: pki/root-ca-2 -description: Root CA 2 +metadata: + name: pki/root-ca-2 + description: Root CA 2 spec: key_type: rsa key_bits: 4096 @@ -139,15 +144,17 @@ spec: common_name: Root Certificate Authority 2 --- kind: KV -name: kv/intermediate-ca-staging -description: secrets for intermediate-ca-staging CA +metadata: + name: kv/intermediate-ca-staging + description: secrets for intermediate-ca-staging CA spec: options: version: 1 --- kind: KV -name: kv/intermediate-ca-dev -description: secrets for intermediate-ca-dev CA +metadata: + name: kv/intermediate-ca-dev + description: secrets for intermediate-ca-dev CA spec: options: version: 1 diff --git a/pkictl/tests/test_models.py b/pkictl/tests/test_models.py index d31904a..28b01a2 100644 --- a/pkictl/tests/test_models.py +++ b/pkictl/tests/test_models.py @@ -14,8 +14,8 @@ def test_root_ca(self): rootca = RootCA(self.baseurl, d) - self.assertEqual(rootca.name, d['name']) - self.assertEqual(rootca.description, d['description']) + self.assertEqual(rootca.name, d['metadata']['name']) + self.assertEqual(rootca.description, d['metadata']['description']) self.assertNotIn('subject', rootca.spec) self.assertIn('common_name', rootca.spec) self.assertEqual(rootca.ttl, d['spec']['ttl']) @@ -33,9 +33,9 @@ def test_intermediate_ca(self): intermediate_ca = IntermediateCA(self.baseurl, d) - self.assertEqual(intermediate_ca.name, d['name']) - self.assertEqual(intermediate_ca.description, d['description']) - self.assertEqual(intermediate_ca.issuer, d['issuer']) + self.assertEqual(intermediate_ca.name, d['metadata']['name']) + self.assertEqual(intermediate_ca.description, d['metadata']['description']) + self.assertEqual(intermediate_ca.issuer, d['metadata']['issuer']) self.assertNotIn('subject', intermediate_ca.spec) self.assertNotIn('policies', intermediate_ca.spec) self.assertNotIn('roles', intermediate_ca.spec) @@ -55,8 +55,10 @@ def test_keyvalue_engine(self): kv_engine = KeyValueEngine(self.baseurl, d) - self.assertEqual(kv_engine.name, d['name']) - self.assertEqual(kv_engine.url, f"{self.baseurl}/v1/sys/mounts/{d['name']}") + name = d['metadata']['name'] + + self.assertEqual(kv_engine.name, name) + self.assertEqual(kv_engine.url, f"{self.baseurl}/v1/sys/mounts/{name}") self.assertNotIn('kind', kv_engine.spec) self.assertNotIn('name', kv_engine.spec) diff --git a/pkictl/tests/test_schemas.py b/pkictl/tests/test_schemas.py index c311b31..ad4a4ed 100644 --- a/pkictl/tests/test_schemas.py +++ b/pkictl/tests/test_schemas.py @@ -10,8 +10,10 @@ def test_root_schema_valid(self): test_data = { 'kind': 'RootCA', - 'name': 'test-root-ca', - 'description': 'Test Root CA', + 'metadata': { + 'name': 'test-root-ca', + 'description': 'Test Root CA' + }, 'spec': { 'key_type': 'rsa', 'key_bits': 2048, @@ -28,8 +30,10 @@ def test_root_schema_invalid(self): test_data = { 'kind': 'RootCA', - 'name': 'test-root-ca', - 'description': 'Test Root CA', + 'metadata': { + 'name': 'test-root-ca', + 'description': 'Test Root CA' + }, 'spec': { 'key_type': 'rsa', 'key_bits': 4096, @@ -43,9 +47,11 @@ def test_intermediate_schema_valid(self): test_data = { 'kind': 'IntermediateCA', - 'name': 'test-intermediate-ca-1', - 'description': 'Test Intermediate CA 1', - 'issuer': 'test-root-ca', + 'metadata': { + 'name': 'test-intermediate-ca-1', + 'description': 'Test Intermediate CA 1', + 'issuer': 'test-root-ca' + }, 'spec': { 'type': 'internal', 'key_type': 'rsa', @@ -77,9 +83,11 @@ def test_intermediate_schema_valid(self): test_data = { 'kind': 'IntermediateCA', - 'name': 'test-intermediate-ca-2', - 'description': 'Test Intermediate CA 2', - 'issuer': 'test-root-ca', + 'metadata': { + 'name': 'test-intermediate-ca-2', + 'description': 'Test Intermediate CA 2', + 'issuer': 'test-root-ca' + }, 'spec': { 'type': 'exported', 'key_type': 'ec', @@ -114,9 +122,11 @@ def test_intermediate_schema_invalid(self): test_data = { 'kind': 'IntermediateCA', - 'name': 'test-intermediate-ca', - 'description': 'Test Intermediate CA', - 'issuer': 'test-root-ca', + 'metadata': { + 'name': 'test-intermediate-ca', + 'description': 'Test Intermediate CA', + 'issuer': 'test-root-ca' + }, 'spec': { 'type': 'internal', 'key_type': 'ec', @@ -160,8 +170,10 @@ def test_kvengine_schema_valid(self): test_data = { 'kind': 'KV', - 'name': 'test-kv-engine', - 'description': 'Test KV engine', + 'metadata': { + 'name': 'test-kv-engine', + 'description': 'Test KV engine' + }, 'spec': { 'config': { 'default_lease_ttl': '100h' @@ -178,8 +190,10 @@ def test_kvengine_schema_invalid(self): test_data = { 'kind': 'KV', - 'name': 'test-kv-engine', - 'description': 'Test KV engine', + 'metadata': { + 'name': 'test-kv-engine', + 'description': 'Test KV engine' + } } with self.assertRaises(voluptuous.MultipleInvalid): diff --git a/pkictl/tests/test_utils.py b/pkictl/tests/test_utils.py index 929de15..415e149 100644 --- a/pkictl/tests/test_utils.py +++ b/pkictl/tests/test_utils.py @@ -105,25 +105,27 @@ def test_read_manifest_file_permission_denied(self): def test_get_validated_manifests(self): d = utils.read_manifest_file(PKI_MANIFEST_YAML) - roots, intermediates, kv_backends = utils.get_validated_manifests(d) + roots, intermediates, kv_engines = utils.get_validated_manifests(d) self.assertIsInstance(roots, list) self.assertEqual(len(roots), 2) self.assertIsInstance(intermediates, list) self.assertEqual(len(intermediates), 3) - self.assertIsInstance(kv_backends, list) + self.assertIsInstance(kv_engines, list) - def test_get_validated_manifests_missing_kv_backend(self): + def test_get_validated_manifests_missing_kv_engine(self): d = [{ 'kind': 'IntermediateCA', - 'name': 'test-intermediate-ca', + 'metadata': { + 'name': 'test-intermediate-ca', + }, 'spec': { 'type': 'exported' } }] with self.assertRaises(SystemExit) as e: utils.get_validated_manifests(d) - self.assertEqual(e.exception.args[0], "[-] pkictl - Error: kv_backend not defined for exported intermediate CA: test-intermediate-ca") + self.assertEqual(e.exception.args[0], "[-] pkictl - Error: kv_engine not defined for exported intermediate CA: test-intermediate-ca") def test_get_validated_manifests_unsupported(self): d = [{'kind': 'AWS'}] @@ -169,46 +171,62 @@ def test_sort_intermediate_certificate_authorities(self): intermediates = [ { 'kind': 'IntermediateCA', - 'name': 'blah-ca', - 'issuer': 'bar-ca' + 'metadata': { + 'name': 'blah-ca', + 'issuer': 'bar-ca' + } }, { 'kind': 'IntermediateCA', - 'name': 'foo-ca', - 'issuer': 'root-ca' + 'metadata': { + 'name': 'foo-ca', + 'issuer': 'root-ca' + } }, { 'kind': 'IntermediateCA', - 'name': 'bar-ca', - 'issuer': 'foo-ca' + 'metadata': { + 'name': 'bar-ca', + 'issuer': 'foo-ca' + } }, { 'kind': 'IntermediateCA', - 'name': 'kungfoo-ca', - 'issuer': 'root-ca' + 'metadata': { + 'name': 'kungfoo-ca', + 'issuer': 'root-ca' + } } ] expected = [ { 'kind': 'IntermediateCA', - 'name': 'kungfoo-ca', - 'issuer': 'root-ca' + 'metadata': { + 'name': 'kungfoo-ca', + 'issuer': 'root-ca' + } }, { 'kind': 'IntermediateCA', - 'name': 'foo-ca', - 'issuer': 'root-ca' + 'metadata': { + 'name': 'foo-ca', + 'issuer': 'root-ca' + } }, { 'kind': 'IntermediateCA', - 'name': 'bar-ca', - 'issuer': 'foo-ca' + 'metadata': { + 'name': 'bar-ca', + 'issuer': 'foo-ca' + } }, { 'kind': 'IntermediateCA', - 'name': 'blah-ca', - 'issuer': 'bar-ca' + 'metadata': { + 'name': 'blah-ca', + 'issuer': 'bar-ca' + } } ] diff --git a/pkictl/utils.py b/pkictl/utils.py index f2107b4..8ad63df 100644 --- a/pkictl/utils.py +++ b/pkictl/utils.py @@ -54,7 +54,7 @@ def read_manifest_file(path: str) -> List[dict]: def get_validated_manifests(documents: List[dict]=[]) -> Tuple[List[dict], List[dict], List[dict]]: roots: List[dict] = [] intermediates: List[dict] = [] - kv_backends: List[dict] = [] + kv_engines: List[dict] = [] for i in documents: schema_type = i.get('kind') @@ -63,21 +63,21 @@ def get_validated_manifests(documents: List[dict]=[]) -> Tuple[List[dict], List[ roots.append(schemas.RootCASchema(i)) elif schema_type == 'IntermediateCA': - ca_name = i['name'] + ca_name = i['metadata']['name'] ca_type = i['spec'].get('type') - kv_backend = i.get('kv_backend', None) + kv_engine = i['metadata'].get('kv_engine', None) - if ca_type == 'exported' and kv_backend is None: - exit_with_message(f"kv_backend not defined for exported intermediate CA: {ca_name}") + if ca_type == 'exported' and kv_engine is None: + exit_with_message(f"kv_engine not defined for exported intermediate CA: {ca_name}") intermediates.append(schemas.IntermediateCASchema(i)) elif schema_type == 'KV': - kv_backends.append(schemas.KeyValueSchema(i)) + kv_engines.append(schemas.KeyValueSchema(i)) else: exit_with_message("Unsupported schema defined in manifest file") - return roots, intermediates, kv_backends + return roots, intermediates, kv_engines def write_vault_master_keys(master_keys: List[str]=[], file: str='vault.log', debug: bool=False) -> None: @@ -105,9 +105,9 @@ def write_vault_root_token(root_token: str=None, file: str='.vault-token', debug output_message(f"Successfully wrote the Vault root token to {file}") -def get_index(array: List[dict], key: str, value: str) -> Optional[int]: +def get_issuer_index(array: List[dict], key: str, value: str) -> Optional[int]: for i, d in enumerate(array): - if d[key] == value: + if d['metadata'][key] == value: return i return None @@ -115,13 +115,11 @@ def get_index(array: List[dict], key: str, value: str) -> Optional[int]: def sort_intermediate_certificate_authorities(intermediates: List[dict]): sorted_intermediates: List[dict] = [] for ca in intermediates: - issuer = ca.get('issuer') + issuer = ca['metadata']['issuer'] - if issuer: - index = get_index(intermediates, key='name', value=issuer) - - if index is None: - sorted_intermediates.insert(0, ca) - else: - sorted_intermediates.insert(index, ca) + index = get_issuer_index(intermediates, key='name', value=issuer) + if index is None: + sorted_intermediates.insert(0, ca) + else: + sorted_intermediates.insert(index, ca) return sorted_intermediates diff --git a/pkictl/vault.py b/pkictl/vault.py index 3b90a9c..181353f 100644 --- a/pkictl/vault.py +++ b/pkictl/vault.py @@ -112,12 +112,12 @@ def mount_kv_engine(self, kvengine): def store_ca_private_key(self, ca): """ stores the private key for a CA in the specified KV engine """ - response = self.request(method='PUT', url=ca.kv_backend_url, headers=self.headers, json=ca.private_key) + response = self.request(method='PUT', url=ca.kv_engine_url, headers=self.headers, json=ca.private_key) if response.status_code == 204: - utils.output_message(f"Stored private key for '{ca.name}' in KV engine: {ca.kv_backend}") + utils.output_message(f"Stored private key for '{ca.name}' in KV engine: {ca.kv_engine}") else: - utils.exit_with_message(f"Failed to store private key for '{ca.name}' in KV engine: {ca.kv_backend}") + utils.exit_with_message(f"Failed to store private key for '{ca.name}' in KV engine: {ca.kv_engine}") def mount_pki_engine(self, ca): """ mounts a PKI secrets engine """ diff --git a/setup.py b/setup.py index 42b44f9..55c70d7 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup -version = "0.1.2" +version = "0.2.0" requirements = [ 'PyYAML',