Merge "Implement encryption for genesis/join scripts"

This commit is contained in:
Zuul 2018-09-19 20:13:47 +00:00 committed by Gerrit Code Review
commit 13d6831aa5
28 changed files with 1936 additions and 30 deletions

View File

@ -36,7 +36,7 @@ CHARTS := $(patsubst charts/%/.,%,$(wildcard charts/*/.))
all: charts lint
.PHONY: tests
tests: gate-lint
tests: external-deps gate-lint
tox
.PHONY: tests-security
@ -48,9 +48,13 @@ docs:
tox -e docs
.PHONY: tests-unit
tests-unit:
tests-unit: external-deps
tox -e py35
.PHONY: external-deps
external-deps:
./tools/install-external-deps.sh
.PHONY: tests-pep8
tests-pep8:
tox -e pep8

View File

@ -0,0 +1,33 @@
EncryptionPolicy
================
Encryption policy defines how encryption should be applied via Promenade. The
primary use-case for this is to encrypt ``genesis.sh`` or ``join.sh`` scripts.
Sample Document
---------------
.. code-block:: yaml
---
schema: promenade/EncryptionPolicy/v1
metadata:
schema: metadata/Document/v1
name: encryption-policy
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
scripts:
genesis:
gpg: {}
...
Scripts
-------
The genesis and join scripts can be built with sensitive content encrypted.
Currently the only encryption method available is ``gpg``, which can be enabled
by setting that key to an empty dictionary.

View File

@ -12,6 +12,7 @@ Details about Promenade-specific documents can be found here:
:caption: Documents
docker
encryption-policy
genesis
host-system
kubelet

View File

@ -0,0 +1,14 @@
---
schema: promenade/EncryptionPolicy/v1
metadata:
schema: metadata/Document/v1
name: encryption-policy
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
scripts:
genesis:
gpg: {}
...

View File

@ -1,4 +1,4 @@
from . import logging, renderer
from . import encryption_method, logging, renderer
from beaker.cache import CacheManager
from beaker.util import parse_cache_config_options
@ -72,6 +72,14 @@ class Builder:
_write_script(output_dir, 'validate-cluster.sh', validate_script)
def build_genesis(self, *, output_dir):
script = self.build_genesis_script()
_write_script(output_dir, 'genesis.sh', script)
if self.validators:
validate_script = self._build_genesis_validate_script()
_write_script(output_dir, 'validate-genesis.sh', validate_script)
def build_genesis_script(self):
LOG.info('Building genesis script')
sub_config = self.config.extract_genesis_config()
tarball = renderer.build_tarball_from_roles(
@ -79,17 +87,23 @@ class Builder:
roles=['common', 'genesis'],
file_specs=self.file_cache.values())
script = renderer.render_template(
(encrypted_tarball, decrypt_setup_command, decrypt_command,
decrypt_teardown_command) = _encrypt_genesis(sub_config, tarball)
return renderer.render_template(
sub_config,
template='scripts/genesis.sh',
context={'tarball': tarball})
context={
'decrypt_command': decrypt_command,
'decrypt_setup_command': decrypt_setup_command,
'decrypt_teardown_command': decrypt_teardown_command,
'encrypted_tarball': encrypted_tarball,
})
_write_script(output_dir, 'genesis.sh', script)
if self.validators:
validate_script = renderer.render_template(
sub_config, template='scripts/validate-genesis.sh')
_write_script(output_dir, 'validate-genesis.sh', validate_script)
def _build_genesis_validate_script(self):
sub_config = self.config.extract_genesis_config()
return renderer.render_template(
sub_config, template='scripts/validate-genesis.sh')
def build_node(self, node_document, *, output_dir):
node_name = node_document['metadata']['name']
@ -112,10 +126,18 @@ class Builder:
tarball = renderer.build_tarball_from_roles(
config=sub_config, roles=['common', 'join'], file_specs=file_specs)
(encrypted_tarball, decrypt_setup_command, decrypt_command,
decrypt_teardown_command) = _encrypt_node(sub_config, tarball)
return renderer.render_template(
sub_config,
template='scripts/join.sh',
context={'tarball': tarball})
context={
'decrypt_command': decrypt_command,
'decrypt_setup_command': decrypt_setup_command,
'decrypt_teardown_command': decrypt_teardown_command,
'encrypted_tarball': encrypted_tarball,
})
def _build_node_validate_script(self, node_name):
sub_config = self.config.extract_node_config(node_name)
@ -123,6 +145,24 @@ class Builder:
sub_config, template='scripts/validate-join.sh')
def _encrypt_genesis(config, data):
return _encrypt(config.get_path('EncryptionPolicy:scripts.genesis'), data)
def _encrypt_node(config, data):
return _encrypt(config.get_path('EncryptionPolicy:scripts.join'), data)
def _encrypt(cfg_dict, data):
method = encryption_method.EncryptionMethod.from_config(cfg_dict)
encrypted_data = method.encrypt(data)
decrypt_setup_command = method.get_decrypt_setup_command()
decrypt_command = method.get_decrypt_command()
decrypt_teardown_command = method.get_decrypt_teardown_command()
return (encrypted_data, decrypt_setup_command, decrypt_command,
decrypt_teardown_command)
@CACHE.cache('fetch_tarball_content', expire=72 * 3600)
def _fetch_tar_content(url, path):
content = _fetch_tar_url(url)

View File

@ -0,0 +1,182 @@
from . import exceptions, logging
import abc
import os
# Ignore bandit false positive: B404:blacklist
# The purpose of this module is to safely encapsulate calls via fork.
import subprocess # nosec
import tempfile
__all__ = ['EncryptionMethod']
LOG = logging.getLogger(__name__)
class EncryptionMethod(metaclass=abc.ABCMeta):
@abc.abstractmethod
def encrypt(self, data):
pass
@abc.abstractmethod
def get_decrypt_setup_command(self):
pass
@abc.abstractmethod
def get_decrypt_command(self):
pass
@abc.abstractmethod
def get_decrypt_teardown_command(self):
pass
@staticmethod
def from_config(config):
LOG.debug('Building EncryptionMethod from: %s', config)
if config:
# NOTE(mark-burnett): Relying on the schema to ensure valid
# configuration.
name = list(config.keys())[0]
kwargs = config[name]
if name == 'gpg':
return GPGEncryptionMethod(**kwargs)
else:
raise NotImplementedError('Unknown Encryption method')
else:
return NullEncryptionMethod()
def notify_user(self, message):
print('=== BEGIN NOTICE ===')
print(message)
print('=== END NOTICE ===')
class NullEncryptionMethod(EncryptionMethod):
def encrypt(self, data):
LOG.debug('Performing NOOP encryption')
return data
def get_decrypt_setup_command(self):
return ''
def get_decrypt_command(self):
return 'cat'
def get_decrypt_teardown_command(self):
return ''
class GPGEncryptionMethod(EncryptionMethod):
ENCRYPTION_KEY_ENV_NAME = 'PROMENADE_ENCRYPTION_KEY'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._gpg_version = _detect_gpg_version()
def encrypt(self, data):
key = self._get_key()
return self._encrypt_data(key, data)
def get_decrypt_setup_command(self):
return '''
export DECRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY:-"NONE"}
if [[ ${PROMENADE_ENCRYPTION_KEY} = "NONE" ]]; then
read -p "Script decryption key: " -s DECRYPTION_KEY
fi
'''
def get_decrypt_command(self):
return ('/usr/bin/gpg --verbose --decrypt '
'--passphrase "${DECRYPTION_KEY}"')
def get_decrypt_teardown_command(self):
return 'unset DECRYPTION_KEY'
def _get_key(self):
key = os.environ.get(self.ENCRYPTION_KEY_ENV_NAME)
if key is None:
key = _generate_key()
self.notify_user('Copy this decryption key for use during script '
'execution:\n%s' % key)
else:
LOG.info('Using encryption key from %s',
self.ENCRYPTION_KEY_ENV_NAME)
return key
def _encrypt_data(self, key, data):
with tempfile.TemporaryDirectory() as tmp:
# Ignore bandit false positive:
# B603:subprocess_without_shell_equals_true
# Here user input is allowed to be arbitrary, as it's simply input
# to the specified encryption algorithm. Regardless, we only put a
# tarball here.
p = subprocess.Popen( # nosec
[
'/usr/bin/gpg',
'--verbose',
'--symmetric',
'--homedir',
tmp,
'--passphrase',
key,
] + self._gpg_encrypt_options(),
cwd=tmp,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
try:
out, err = p.communicate(data, timeout=120)
except subprocess.TimeoutExpired:
p.kill()
out, err = p.communicate()
if p.returncode != 0:
LOG.error('Got errors from gpg encrypt: %s', err)
raise exceptions.EncryptionException(description=str(err))
return out
def _gpg_encrypt_options(self):
options = {
1: [],
2: ['--pinentry-mode', 'loopback'],
}
return options[self._gpg_version[0]]
DETECTION_PREFIX = 'gpg (GnuPG) '
def _detect_gpg_version():
with tempfile.TemporaryDirectory() as tmp:
# Ignore bandit false positive:
# B603:subprocess_without_shell_equals_true
# This method takes no input and simply queries the version of gpg.
output = subprocess.check_output( # nosec
[
'/usr/bin/gpg',
'--version',
], cwd=tmp)
lines = output.decode('utf-8').strip().splitlines()
if lines:
version = lines[0][len(DETECTION_PREFIX):]
LOG.debug('Found GPG version %s', version)
return tuple(map(int, version.split('.')[:2]))
else:
raise exceptions.GPGDetectionException()
def _generate_key():
# Ignore bandit false positive:
# B603:subprocess_without_shell_equals_true
# This method takes no input and generates random output.
result = subprocess.run( # nosec
['/usr/bin/openssl', 'rand', '-hex', '48'],
check=True,
env={
'RANDFILE': '/tmp/rnd',
},
stdout=subprocess.PIPE,
)
return result.stdout.decode().strip()

View File

@ -343,6 +343,16 @@ class NodeNotFoundException(KubernetesApiError):
status = falcon.HTTP_404
class EncryptionException(ApiError):
title = 'Payload encryption error'
status = falcon.HTTP_500
class GPGDetectionException(ApiError):
title = 'Failed to detect GPG version'
status = falcon.HTTP_500
def massage_error_list(error_list, placeholder_description):
"""
Returns a best-effort attempt to make a nice error list

View File

@ -10,17 +10,16 @@ LOG = logging.getLogger(__name__)
class Generator:
def __init__(self, config):
def __init__(self, config, block_strings=True):
self.config = config
self.keys = pki.PKI()
self.documents = []
self.keys = pki.PKI(block_strings=block_strings)
self.outputs = collections.defaultdict(dict)
@property
def cluster_domain(self):
return self.config['KubernetesNetwork:dns.cluster_domain']
def generate(self, output_dir):
def generate(self, output_dir=None):
for catalog in self.config.iterate(kind='PKICatalog'):
for ca_name, ca_def in catalog['data'].get(
'certificate_authorities', {}).items():
@ -40,7 +39,8 @@ class Generator:
document_name = keypair_def['name']
self.get_or_gen_keypair(document_name)
self._write(output_dir)
if output_dir:
self._write(output_dir)
def get_or_gen_ca(self, document_name):
kinds = [
@ -126,18 +126,21 @@ class Generator:
return result
def _write(self, output_dir):
docs = list(
itertools.chain.from_iterable(
v.values() for v in self.outputs.values()))
documents = self.get_documents()
with open(os.path.join(output_dir, 'certificates.yaml'), 'w') as f:
# Don't use safe_dump_all so we can block format certificate data.
yaml.dump_all(
docs,
documents,
stream=f,
default_flow_style=False,
explicit_start=True,
indent=2)
def get_documents(self):
return list(
itertools.chain.from_iterable(
v.values() for v in self.outputs.values()))
def get_host_list(service_names):
service_list = []

View File

@ -13,7 +13,8 @@ LOG = logging.getLogger(__name__)
class PKI:
def __init__(self):
def __init__(self, *, block_strings=True):
self.block_strings = block_strings
self._ca_config_string = None
@property
@ -116,9 +117,11 @@ class PKI:
# Ignore bandit false positive:
# B603:subprocess_without_shell_equals_true
# This method wraps cfssl calls originating from this module.
return json.loads( # nosec
subprocess.check_output(
['cfssl'] + command, cwd=tmp, stderr=subprocess.PIPE))
result = subprocess.check_output( # nosec
['cfssl'] + command, cwd=tmp, stderr=subprocess.PIPE)
if not isinstance(result, str):
result = result.decode('utf-8')
return json.loads(result)
def _openssl(self, command, *, files=None):
if not files:
@ -175,9 +178,15 @@ class PKI:
},
'storagePolicy': 'cleartext',
},
'data': block_literal(data),
'data': self._block_literal(data),
}
def _block_literal(self, data):
if self.block_strings:
return block_literal(data)
else:
return data
class block_literal(str):
pass

View File

@ -0,0 +1,33 @@
---
schema: deckhand/DataSchema/v1
metadata:
schema: metadata/Control/v1
name: promenade/EncryptionPolicy/v1
labels:
application: promenade
data:
$schema: http://json-schema.org/schema#
definitions:
script_encryption:
oneof:
- { $ref: '#/definitions/encryption_method_gpg' }
encryption_method_gpg:
properties:
gpg:
type: object
additionalProperties: false
required:
- gpg
additionalProperties: false
properties:
scripts:
properties:
genesis:
$ref: '#/definitions/script_encryption'
join:
$ref: '#/definitions/script_encryption'
additionalProperties: false
...

View File

@ -10,7 +10,10 @@ chmod 700 /etc/kubernetes
set +x
log
log === Extracting prepared files ===
echo "{{ tarball | b64enc }}" | base64 -d | tar -zxv -C / | tee /etc/promenade-manifest
{{ decrypt_setup_command }}
echo "{{ encrypted_tarball | b64enc }}" | base64 -d | {{ decrypt_command }} | tar -zxv -C / | tee /etc/promenade-manifest
{{ decrypt_teardown_command }}
set -x
# Adding apt repositories
#

View File

@ -105,7 +105,7 @@ def check_schema(document, schemas=None):
except jsonschema.ValidationError as e:
raise exceptions.ValidationException(str(e))
else:
LOG.warning('Skipping validation for unknown schema: %s', schema_name)
LOG.debug('Skipping validation for unknown schema: %s', schema_name)
SCHEMAS = {}

View File

@ -0,0 +1,18 @@
---
schema: promenade/Docker/v1
metadata:
schema: metadata/Document/v1
name: docker
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
config:
insecure-registries:
- registry:5000
live-restore: true
max-concurrent-downloads: 10
oom-score-adjust: -999
storage-driver: overlay2
...

View File

@ -0,0 +1,16 @@
---
schema: promenade/EncryptionPolicy/v1
metadata:
schema: metadata/Document/v1
name: testingpolicy
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
scripts:
genesis:
gpg: {}
join:
gpg: {}
...

View File

@ -0,0 +1,45 @@
---
schema: promenade/Genesis/v1
metadata:
schema: metadata/Document/v1
name: genesis
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
hostname: n0
ip: 192.168.77.10
apiserver:
command_prefix:
- /apiserver
- --authorization-mode=Node,RBAC
- --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
- --service-cluster-ip-range=10.96.0.0/16
- --endpoint-reconciler-type=lease
armada:
target_manifest: cluster-bootstrap
labels:
dynamic:
- calico-etcd=enabled
- coredns=enabled
- kubernetes-apiserver=enabled
- kubernetes-controller-manager=enabled
- kubernetes-etcd=enabled
- kubernetes-scheduler=enabled
- promenade-genesis=enabled
- ucp-control-plane=enabled
images:
armada: quay.io/airshipit/armada:master
helm:
tiller: gcr.io/kubernetes-helm/tiller:v2.9.1
kubernetes:
apiserver: gcr.io/google_containers/hyperkube-amd64:v1.10.2
controller-manager: gcr.io/google_containers/hyperkube-amd64:v1.10.2
etcd: quay.io/coreos/etcd:v3.2.14
scheduler: gcr.io/google_containers/hyperkube-amd64:v1.10.2
files:
- path: /var/lib/anchor/calico-etcd-bootstrap
content: "# placeholder for triggering calico etcd bootstrapping"
mode: 0644
...

View File

@ -0,0 +1,86 @@
---
schema: promenade/HostSystem/v1
metadata:
schema: metadata/Document/v1
name: host-system
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
files:
# NOTE(mark-burnett): A kubelet would be required for a real deployment
# (either here or via debian package; however, these unit tests don't
# attempt to actually run Kubernetes, only to construct the genesis and
# join scripts.
# - path: /opt/kubernetes/bin/kubelet
# tar_url: https://dl.k8s.io/v1.10.2/kubernetes-node-linux-amd64.tar.gz
# tar_path: kubernetes/node/bin/kubelet
# mode: 0555
- path: /etc/logrotate.d/json-logrotate
mode: 0444
content: |-
/var/lib/docker/containers/*/*-json.log
{
compress
copytruncate
create 0644 root root
daily
dateext
dateformat -%Y%m%d-%s
maxsize 10M
missingok
notifempty
su root root
rotate 1
}
images:
haproxy: haproxy:1.8.3
helm:
helm: lachlanevenson/k8s-helm:v2.9.1
kubernetes:
kubectl: gcr.io/google_containers/hyperkube-amd64:v1.10.2
packages:
repositories:
- deb http://apt.dockerproject.org/repo ubuntu-xenial main
keys:
- |-
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFWln24BEADrBl5p99uKh8+rpvqJ48u4eTtjeXAWbslJotmC/CakbNSqOb9o
ddfzRvGVeJVERt/Q/mlvEqgnyTQy+e6oEYN2Y2kqXceUhXagThnqCoxcEJ3+KM4R
mYdoe/BJ/J/6rHOjq7Omk24z2qB3RU1uAv57iY5VGw5p45uZB4C4pNNsBJXoCvPn
TGAs/7IrekFZDDgVraPx/hdiwopQ8NltSfZCyu/jPpWFK28TR8yfVlzYFwibj5WK
dHM7ZTqlA1tHIG+agyPf3Rae0jPMsHR6q+arXVwMccyOi+ULU0z8mHUJ3iEMIrpT
X+80KaN/ZjibfsBOCjcfiJSB/acn4nxQQgNZigna32velafhQivsNREFeJpzENiG
HOoyC6qVeOgKrRiKxzymj0FIMLru/iFF5pSWcBQB7PYlt8J0G80lAcPr6VCiN+4c
NKv03SdvA69dCOj79PuO9IIvQsJXsSq96HB+TeEmmL+xSdpGtGdCJHHM1fDeCqkZ
hT+RtBGQL2SEdWjxbF43oQopocT8cHvyX6Zaltn0svoGs+wX3Z/H6/8P5anog43U
65c0A+64Jj00rNDr8j31izhtQMRo892kGeQAaaxg4Pz6HnS7hRC+cOMHUU4HA7iM
zHrouAdYeTZeZEQOA7SxtCME9ZnGwe2grxPXh/U/80WJGkzLFNcTKdv+rwARAQAB
tDdEb2NrZXIgUmVsZWFzZSBUb29sIChyZWxlYXNlZG9ja2VyKSA8ZG9ja2VyQGRv
Y2tlci5jb20+iQI4BBMBAgAiBQJVpZ9uAhsvBgsJCAcDAgYVCAIJCgsEFgIDAQIe
AQIXgAAKCRD3YiFXLFJgnbRfEAC9Uai7Rv20QIDlDogRzd+Vebg4ahyoUdj0CH+n
Ak40RIoq6G26u1e+sdgjpCa8jF6vrx+smpgd1HeJdmpahUX0XN3X9f9qU9oj9A4I
1WDalRWJh+tP5WNv2ySy6AwcP9QnjuBMRTnTK27pk1sEMg9oJHK5p+ts8hlSC4Sl
uyMKH5NMVy9c+A9yqq9NF6M6d6/ehKfBFFLG9BX+XLBATvf1ZemGVHQusCQebTGv
0C0V9yqtdPdRWVIEhHxyNHATaVYOafTj/EF0lDxLl6zDT6trRV5n9F1VCEh4Aal8
L5MxVPcIZVO7NHT2EkQgn8CvWjV3oKl2GopZF8V4XdJRl90U/WDv/6cmfI08GkzD
YBHhS8ULWRFwGKobsSTyIvnbk4NtKdnTGyTJCQ8+6i52s+C54PiNgfj2ieNn6oOR
7d+bNCcG1CdOYY+ZXVOcsjl73UYvtJrO0Rl/NpYERkZ5d/tzw4jZ6FCXgggA/Zxc
jk6Y1ZvIm8Mt8wLRFH9Nww+FVsCtaCXJLP8DlJLASMD9rl5QS9Ku3u7ZNrr5HWXP
HXITX660jglyshch6CWeiUATqjIAzkEQom/kEnOrvJAtkypRJ59vYQOedZ1sFVEL
MXg2UCkD/FwojfnVtjzYaTCeGwFQeqzHmM241iuOmBYPeyTY5veF49aBJA1gEJOQ
TvBR8Q==
=Fm3p
-----END PGP PUBLIC KEY BLOCK-----
additional:
- curl
- jq
required:
docker: docker-engine=1.13.1-0~ubuntu-xenial
socat: socat=1.7.3.1-1
validation:
pod_logs:
image: busybox:1.28.3
...

View File

@ -0,0 +1,21 @@
---
schema: promenade/Kubelet/v1
metadata:
schema: metadata/Document/v1
name: kubelet
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
arguments:
- --cni-bin-dir=/opt/cni/bin
- --cni-conf-dir=/etc/cni/net.d
- --eviction-max-pod-grace-period=-1
- --network-plugin=cni
- --node-status-update-frequency=5s
- --serialize-image-pulls=false
- --v=5
images:
pause: gcr.io/google_containers/pause-amd64:3.0
...

View File

@ -0,0 +1,43 @@
---
schema: promenade/KubernetesNetwork/v1
metadata:
schema: metadata/Document/v1
name: kubernetes-network
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
dns:
cluster_domain: cluster.local
service_ip: 10.96.0.10
bootstrap_validation_checks:
- calico-etcd.kube-system.svc.cluster.local
- google.com
- kubernetes-etcd.kube-system.svc.cluster.local
- kubernetes.default.svc.cluster.local
upstream_servers:
- 8.8.8.8
- 8.8.4.4
kubernetes:
apiserver_port: 6443
haproxy_port: 6553
pod_cidr: 10.97.0.0/16
service_cidr: 10.96.0.0/16
service_ip: 10.96.0.1
etcd:
container_port: 2379
haproxy_port: 2378
hosts_entries:
- ip: 192.168.77.1
names:
- registry
# proxy:
# url: http://proxy.example.com:8080
# additional_no_proxy:
# - 10.0.1.1
...

View File

@ -0,0 +1,31 @@
---
schema: promenade/KubernetesNode/v1
metadata:
schema: metadata/Document/v1
name: n1
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
hostname: n1
ip: 192.169.77.11
join_ip: 192.169.77.10
labels:
dynamic:
- calico-etcd=enabled
- ceph-mds=enabled
- ceph-mon=enabled
- ceph-osd=enabled
- ceph-rgw=enabled
- ceph-mgr=enabled
- coredns=enabled
- kubernetes-apiserver=enabled
- kubernetes-controller-manager=enabled
- kubernetes-etcd=enabled
- kubernetes-scheduler=enabled
- openstack-compute-node=enabled
- openstack-control-plane=enabled
- openvswitch=enabled
- ucp-control-plane=enabled
...

View File

@ -0,0 +1,11 @@
---
schema: deckhand/LayeringPolicy/v1
metadata:
schema: metadata/Control/v1
name: layering-policy
data:
layerOrder:
- global
- type
- site
...

View File

@ -0,0 +1,167 @@
---
schema: promenade/PKICatalog/v1
metadata:
schema: metadata/Document/v1
name: cluster-certificates
layeringDefinition:
abstract: false
layer: site
storagePolicy: cleartext
data:
certificate_authorities:
kubernetes:
description: CA for Kubernetes components
certificates:
- document_name: apiserver
description: Service certificate for Kubernetes apiserver
common_name: apiserver
hosts:
- localhost
- 127.0.0.1
- 10.96.0.1
kubernetes_service_names:
- kubernetes.default.svc.cluster.local
- document_name: kubelet-genesis
common_name: system:node:n0
hosts:
- n0
- 192.168.77.10
groups:
- system:nodes
- document_name: kubelet-n0
common_name: system:node:n0
hosts:
- n0
- 192.168.77.10
groups:
- system:nodes
- document_name: kubelet-n1
common_name: system:node:n1
hosts:
- n1
- 192.168.77.11
groups:
- system:nodes
- document_name: scheduler
description: Service certificate for Kubernetes scheduler
common_name: system:kube-scheduler
- document_name: controller-manager
description: certificate for controller-manager
common_name: system:kube-controller-manager
- document_name: admin
common_name: admin
groups:
- system:masters
- document_name: armada
common_name: armada
groups:
- system:masters
kubernetes-etcd:
description: Certificates for Kubernetes's etcd servers
certificates:
- document_name: apiserver-etcd
description: etcd client certificate for use by Kubernetes apiserver
common_name: apiserver
# NOTE(mark-burnett): hosts not required for client certificates
- document_name: kubernetes-etcd-anchor
description: anchor
common_name: anchor
- document_name: kubernetes-etcd-genesis
common_name: kubernetes-etcd-genesis
hosts:
- n0
- 192.168.77.10
- 127.0.0.1
- localhost
- kubernetes-etcd.kube-system.svc.cluster.local
- document_name: kubernetes-etcd-n0
common_name: kubernetes-etcd-n0
hosts:
- n0
- 192.168.77.10
- 127.0.0.1
- localhost
- kubernetes-etcd.kube-system.svc.cluster.local
- document_name: kubernetes-etcd-n1
common_name: kubernetes-etcd-n1
hosts:
- n1
- 192.168.77.11
- 127.0.0.1
- localhost
- kubernetes-etcd.kube-system.svc.cluster.local
kubernetes-etcd-peer:
certificates:
- document_name: kubernetes-etcd-genesis-peer
common_name: kubernetes-etcd-genesis-peer
hosts:
- n0
- 192.168.77.10
- 127.0.0.1
- localhost
- kubernetes-etcd.kube-system.svc.cluster.local
- document_name: kubernetes-etcd-n0-peer
common_name: kubernetes-etcd-n0-peer
hosts:
- n0
- 192.168.77.10
- 127.0.0.1
- localhost
- kubernetes-etcd.kube-system.svc.cluster.local
- document_name: kubernetes-etcd-n1-peer
common_name: kubernetes-etcd-n1-peer
hosts:
- n1
- 192.168.77.11
- 127.0.0.1
- localhost
- kubernetes-etcd.kube-system.svc.cluster.local
calico-etcd:
description: Certificates for Calico etcd client traffic
certificates:
- document_name: calico-etcd-anchor
description: anchor
common_name: anchor
- document_name: calico-etcd-n0
common_name: calico-etcd-n0
hosts:
- n0
- 192.168.77.10
- 127.0.0.1
- localhost
- 10.96.232.136
- document_name: calico-etcd-n1
common_name: calico-etcd-n1
hosts:
- n1
- 192.168.77.11
- 127.0.0.1
- localhost
- 10.96.232.136
- document_name: calico-node
common_name: calcico-node
calico-etcd-peer:
description: Certificates for Calico etcd clients
certificates:
- document_name: calico-etcd-n0-peer
common_name: calico-etcd-n0-peer
hosts:
- n0
- 192.168.77.10
- 127.0.0.1
- localhost
- 10.96.232.136
- document_name: calico-etcd-n1-peer
common_name: calico-etcd-n1-peer
hosts:
- n1
- 192.168.77.11
- 127.0.0.1
- localhost
- 10.96.232.136
- document_name: calico-node-peer
common_name: calcico-node-peer
keypairs:
- name: service-account
description: Service account signing key for use by Kubernetes controller-manager.
...

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
# 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.
from promenade import builder, generator, config, encryption_method
import copy
import os
import pytest
def load_full_config(dirname):
this_dir = os.path.dirname(os.path.realpath(__file__))
search_dir = os.path.join(this_dir, 'builder_data', dirname)
streams = []
for filename in os.listdir(search_dir):
stream = open(os.path.join(search_dir, filename))
streams.append(stream)
raw_config = config.Configuration.from_streams(
allow_missing_substitutions=True,
debug=True,
streams=streams,
substitute=True,
validate=False,
)
g = generator.Generator(raw_config, block_strings=False)
g.generate()
documents = copy.deepcopy(raw_config.documents)
documents.extend(copy.deepcopy(g.get_documents()))
return config.Configuration(
allow_missing_substitutions=False,
debug=True,
documents=documents,
substitute=True,
validate=True,
)
def test_build_simple():
b = builder.Builder(load_full_config('simple'))
genesis_script = b.build_genesis_script()
assert len(genesis_script) > 0
n1_join_script = b.build_node_script('n1')
assert len(n1_join_script) > 0

View File

@ -6,6 +6,7 @@ export NGINX_DIR="${TEMP_DIR}/nginx"
export NGINX_URL="http://192.168.77.1:7777"
export PROMENADE_BASE_URL="http://promenade-api.ucp.svc.cluster.local"
export PROMENADE_DEBUG=${PROMENADE_DEBUG:-0}
export PROMENADE_ENCRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY:-testkey}
export REGISTRY_DATA_DIR=${REGISTRY_DATA_DIR:-/mnt/registry}
export VIRSH_POOL=${VIRSH_POOL:-promenade}
export VIRSH_POOL_PATH=${VIRSH_POOL_PATH:-/var/lib/libvirt/promenade}

View File

@ -13,6 +13,7 @@ docker run --rm -t \
-w /target \
-v "${TEMP_DIR}:/target" \
-e "PROMENADE_DEBUG=${PROMENADE_DEBUG}" \
-e "PROMENADE_ENCRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY}" \
"${IMAGE_PROMENADE}" \
promenade \
build-all \

View File

@ -7,7 +7,7 @@ source "${GATE_UTILS}"
rsync_cmd "${TEMP_DIR}/scripts"/*genesis* "${GENESIS_NAME}:/root/promenade/"
set -o pipefail
ssh_cmd "${GENESIS_NAME}" /root/promenade/genesis.sh 2>&1 | tee -a "${LOG_FILE}"
ssh_cmd "${GENESIS_NAME}" env "PROMENADE_ENCRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY}" /root/promenade/genesis.sh 2>&1 | tee -a "${LOG_FILE}"
ssh_cmd "${GENESIS_NAME}" /root/promenade/validate-genesis.sh 2>&1 | tee -a "${LOG_FILE}"
set +o pipefail

16
tools/install-external-deps.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
# Installs external dependencies required for basic testing
set -ex
CFSSL_URL=${CFSSL_URL:-https://pkg.cfssl.org/R1.2/cfssl_linux-amd64}
if [[ ! $(which cfssl) ]]; then
TMP_DIR=$(mktemp -d)
pushd "${TMP_DIR}"
curl -Lo cfssl "${CFSSL_URL}"
chmod 755 cfssl
sudo mv cfssl /usr/local/bin/
popd
rm -rf "${TMP_DIR}"
fi

View File

@ -12,7 +12,7 @@ done
if [[ -x $(which shellcheck) ]]; then
echo Checking shell scripts..
shellcheck -s bash -e SC2029 "${WORKSPACE}"/tools/cleanup.sh "${WORKSPACE}"/tools/*gate*.sh "${WORKSPACE}"/tools/g2/stages/* "${WORKSPACE}"/tools/g2/lib/*
shellcheck -s bash -e SC2029 "${WORKSPACE}"/tools/cleanup.sh "${WORKSPACE}"/tools/*gate*.sh "${WORKSPACE}"/tools/g2/stages/* "${WORKSPACE}"/tools/g2/lib/* "${WORKSPACE}"/tools/install-external-deps.sh
else
echo No shellcheck executable found. Please, install it.
exit 1