If the blueprint specifies that the component uses external TLS via the `external_cert` property, the `Deployment` includes an additional init container and the component's container. The init container populates the external TLS certificate artifacts in mounted volume. The container requires CMPv2 CertService to work properly.
Issue-ID: DCAEGEN2-2252
Signed-off-by: Joanna Jeremicz <joanna.jeremicz@nokia.com>
Change-Id: I90cb79120ffaf634fc1f5b8a03a83abb30deb2b7
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
+## [3.1.0]
+* DCAEGEN2-2252 - Add support to request certificates from CMPv2 server in DCAE cloudify blueprints
+
## [3.0.0]
* DCAEGEN2-1791 - eliminate the ContainerizedPlatformComponent type
* DCAEGEN2-2215 - allow environment variables to be set via docker_config
- If the blueprint specifies that the component uses TLS (HTTPS) via the `tls_info` property, the `Deployment` includes an init container,
a volume that holds TLS certificate artifacts, and volume mounts on the init container and the component's container. The init container
populates the TLS certificate artifacts volume with certificates, keys, keystores, etc.
+ - If the blueprint specifies that the component uses external TLS via the `external_cert` property, the `Deployment` includes an additional init container
+ and the component's container. The init container populates the external TLS certificate artifacts in mounted volume. The container requires CMPv2 CertService to work properly.
- If the blueprint indicates that the component exposes any ports, the plugin will create a Kubernetes `Service` that allocates an address
in the Kubernetes network address space that will route traffic to a container that's running the component. This `Service` provides a
fixed "virtual IP" for the component.
- `tls`: object containing configuration for setting up TLS init container
- `cert_path`: mount point for the TLS certificate artifact volume in the init container
- `image`: Docker image to use for the TLS init container
+ - `external_cert`: object containing configuration for setting up external TLS init container
+ - `image_tag`: CertService client image name and version
+ - `request_url`: URL to Cert Service API
+ - `timeout`: Request timeout
+ - `country`: Country name in ISO 3166-1 alpha-2 format, for which certificate will be created
+ - `organization`: Organization name, for which certificate will be created
+ - `state`: State name, for which certificate will be created
+ - `organizational_unit`: Organizational unit name, for which certificate will be created
+ - `location`: Location name, for which certificate will be created
#### Kubernetes access information
```
Note that the `node_ids` list is required by the `execute_operation` workflow. The list contains all of the nodes that are being targeted by the workflow. For an `update_image` operation, the list typically has only one element.
-Note also that the `update_image` operation targets the container running the application code (i.e., the container running the image specified in the `image` node property). This plugin may deploy "sidecar" containers running supporting code--for example, the "filebeat" container that relays logs to the central log server. The `update_image` operation does not touch any "sidecar" containers.
\ No newline at end of file
+Note also that the `update_image` operation targets the container running the application code (i.e., the container running the image specified in the `image` node property). This plugin may deploy "sidecar" containers running supporting code--for example, the "filebeat" container that relays logs to the central log server. The `update_image` operation does not touch any "sidecar" containers.
# ================================================================================
# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# Copyright (c) 2019 Pantheon.tech. All rights reserved.
+# Copyright (c) 2020 Nokia. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
TLS_COMP_CERT_PATH = "/opt/dcae/cacert"
TLS_CA_CONFIGMAP = "dcae-cacert-configmap"
+EXT_TLS_IMAGE = "nexus3.onap.org:10001/onap/org.onap.aaf.certservice.aaf-certservice-client:1.2.0"
+EXT_TLS_REQUEST_URL = "https://aaf-cert-service:8443/v1/certificate/"
+EXT_TLS_TIMEOUT = "30000"
+EXT_TLS_COUNTRY = "US"
+EXT_TLS_ORGANIZATION = "Linux-Foundation"
+EXT_TLS_STATE = "California"
+EXT_TLS_ORGANIZATIONAL_UNIT = "ONAP"
+EXT_TLS_LOCATION = "San-Francisco"
+EXT_TLS_KEYSTORE_PASSWORD = "secret"
+EXT_TLS_TRUSTSTORE_PASSWORD = "secret"
+
CBS_BASE_URL = "https://config-binding-service:10443/service_component_all"
def _set_defaults():
"image": TLS_IMAGE, # Docker image to use for TLS init container
"component_cert_dir": TLS_COMP_CERT_PATH # default mount point for certificate volume in component container
},
+ "external_cert": {
+ "image_tag": EXT_TLS_IMAGE, # Docker image to use for external TLS init container
+ "request_url" : EXT_TLS_REQUEST_URL, # URL to Cert Service API
+ "timeout" : EXT_TLS_TIMEOUT, # Request timeout
+ "country" : EXT_TLS_COUNTRY, # Country name in ISO 3166-1 alpha-2 format, for which certificate will be created
+ "organization" : EXT_TLS_ORGANIZATION, # Organization name, for which certificate will be created
+ "state" : EXT_TLS_STATE, # State name, for which certificate will be created
+ "organizational_unit" : EXT_TLS_ORGANIZATIONAL_UNIT, # Organizational unit name, for which certificate will be created
+ "location" : EXT_TLS_LOCATION, # Location name, for which certificate will be created
+ "keystore_password" : EXT_TLS_KEYSTORE_PASSWORD, # Password to keystore file
+ "truststore_password" : EXT_TLS_TRUSTSTORE_PASSWORD # Password to truststore file
+ },
"cbs": {
"base_url" : CBS_BASE_URL # URL prefix for accessing config binding service
}
# ================================================================================
# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
# Copyright (c) 2020 Pantheon.tech. All rights reserved.
+# Copyright (c) 2020 Nokia. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# group 4: host port
PORTS = re.compile("^([0-9]+)(/(udp|UDP|tcp|TCP))?:([0-9]+)$")
+# Constants for external_cert
+MOUNT_PATH = "/etc/onap/aaf/certservice/certs/"
+KEYSTORE_PATH = MOUNT_PATH + "certServiceClient-keystore.jks"
+TRUSTSTORE_PATH = MOUNT_PATH + "truststore.jks"
+CERT_SECRET_NAME = "aaf-cert-service-client-tls-secret"
+
def _create_deployment_name(component_name):
return "dep-{0}".format(component_name)[:63]
# Create the init container
init_containers.append(_create_container_object("init-tls", tls_config["image"], False, volume_mounts=init_volume_mounts, env=env))
+def _add_external_tls_init_container(init_containers, volumes, external_cert, external_tls_config):
+ env = {}
+ output_path = external_cert.get("external_cert_directory")
+ if not output_path.endswith('/'):
+ output_path += '/'
+
+ env["REQUEST_URL"] = external_tls_config.get("request_url")
+ env["REQUEST_TIMEOUT"] = external_tls_config.get("timeout")
+ env["OUTPUT_PATH"] = output_path + "external"
+ env["OUTPUT_TYPE"] = external_cert.get("cert_type")
+ env["CA_NAME"] = external_cert.get("ca_name")
+ env["COMMON_NAME"] = external_cert.get("external_certificate_parameters").get("common_name")
+ env["ORGANIZATION"] = external_tls_config.get("organization")
+ env["ORGANIZATION_UNIT"] = external_tls_config.get("organizational_unit")
+ env["LOCATION"] = external_tls_config.get("location")
+ env["STATE"] = external_tls_config.get("state")
+ env["COUNTRY"] = external_tls_config.get("country")
+ env["SANS"] = external_cert.get("external_certificate_parameters").get("sans")
+ env["KEYSTORE_PATH"] = KEYSTORE_PATH
+ env["KEYSTORE_PASSWORD"] = external_tls_config.get("keystore_password")
+ env["TRUSTSTORE_PATH"] = TRUSTSTORE_PATH
+ env["TRUSTSTORE_PASSWORD"] = external_tls_config.get("truststore_password")
+
+ # Create the volumes and volume mounts
+ sec = client.V1SecretVolumeSource(secret_name=CERT_SECRET_NAME)
+ volumes.append(client.V1Volume(name="tls-volume", secret=sec))
+ init_volume_mounts = [client.V1VolumeMount(name="tls-info", mount_path=external_cert.get("external_cert_directory")),
+ client.V1VolumeMount(name="tls-volume", mount_path=MOUNT_PATH)]
+
+ # Create the init container
+ init_containers.append(_create_container_object("cert-service-client", external_tls_config["image_tag"], False, volume_mounts=init_volume_mounts, env=env))
+
def _process_port_map(port_map):
service_ports = [] # Ports exposed internally on the k8s network
exposed_ports = [] # Ports to be mapped to ports on the k8s nodes via NodePort
{"log_directory": "/path/to/container/log/directory", "alternate_fb_path" : "/alternate/sidecar/log/path"}
- tls_info: an object with info for setting up TLS (HTTPS), with the form:
{"use_tls": true, "cert_directory": "/path/to/container/cert/directory" }
+ - external_cert: an object with information for setting up the init container for external certificates creation, with the form:
+ {"external_cert":
+ "external_cert_directory": "/path/to/directory_where_certs_should_be_placed",
+ "use_external_tls": true or false,
+ "ca_name": "ca-name-value",
+ "cert_type": "P12" or "JKS" or "PEM",
+ "external_certificate_parameters":
+ "common_name": "common-name-value",
+ "sans": "sans-value"}
- labels: dict with label-name/label-value pairs, e.g. {"cfydeployment" : "lsdfkladflksdfsjkl", "cfynode":"mycomponent"}
These label will be set on all the pods deployed as a result of this deploy() invocation.
- resources: dict with optional "limits" and "requests" resource requirements, each a dict containing:
# Set up TLS information
_add_tls_init_container(init_containers, volumes, volume_mounts, kwargs.get("tls_info") or {}, k8sconfig.get("tls"))
+ # Set up external TLS information
+ external_cert = kwargs.get("external_cert")
+ if external_cert and external_cert.get("use_external_tls"):
+ _add_external_tls_init_container(init_containers, volumes, external_cert, k8sconfig.get("external_cert"))
+
# Create the container for the component
# Make it the first container in the pod
container_args = {key: kwargs.get(key) for key in ("env", "readiness", "liveness", "resources")}
# ================================================================================
# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# Copyright (c) 2020 Pantheon.tech. All rights reserved.
+# Copyright (c) 2020 Nokia. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
{"log_directory": "/path/to/container/log/directory", "alternate_fb_path" : "/alternate/sidecar/log/path"}"
- tls_info: an object with information for setting up the component to act as a TLS server, with the form:
{"use_tls" : true_or_false, "cert_directory": "/path/to/directory_where_certs_should_be_placed" }
+ - external_cert: an object with information for setting up the init container for external certificates creation, with the form:
+ {"external_cert":
+ "external_cert_directory": "/path/to/directory_where_certs_should_be_placed",
+ "use_external_tls": true or false,
+ "ca_name": "ca-name-value",
+ "cert_type": "P12" or "JKS" or "PEM",
+ "external_certificate_parameters":
+ "common_name": "common-name-value",
+ "sans": "sans-value"}
- replicas: number of replicas to be launched initially
- readiness: object with information needed to create a readiness check
- liveness: object with information needed to create a liveness check
volumes=kwargs.get("volumes", []),
ports=kwargs.get("ports", []),
tls_info=kwargs.get("tls_info"),
+ external_cert=kwargs.get("external_cert"),
env=env,
labels=kwargs.get("labels", {}),
log_info=kwargs.get("log_info"),
if "tls_info" in ctx.node.properties:
kwargs["tls_info"] = ctx.node.properties["tls_info"]
+ # Pick up external TLS info if present
+ if "external_cert" in ctx.node.properties:
+ kwargs["external_cert"] = ctx.node.properties["external_cert"]
+
# Pick up replica count and always_pull_image flag
if "replicas" in ctx.node.properties:
kwargs["replicas"] = ctx.node.properties["replicas"]
"envs": kwargs.get("envs", {}),
"log_info": kwargs.get("log_info", {}),
"tls_info": kwargs.get("tls_info", {}),
+ "external_cert": kwargs.get("external_cert", {}),
"labels": kwargs.get("labels", {}),
"resource_config": kwargs.get("resource_config",{}),
"readiness": kwargs.get("readiness",{}),
# ================================================================================
# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# Copyright (c) 2020 Pantheon.tech. All rights reserved.
+# Copyright (c) 2020 Nokia. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
k8s:
executor: 'central_deployment_agent'
package_name: k8splugin
- package_version: 3.0.0
+ package_version: 3.1.0
data_types:
type: boolean
required: false
+ dcae.types.ExternalCertParameters:
+ description: >
+ Certificate parameters for external TLS info
+ properties:
+ common_name:
+ description: >
+ Common name which should be present in certificate
+ type: string
+ required: false
+ default: ""
+ sans:
+ description: >
+ List of Subject Alternative Names (SANs) which should be present in certificate
+ type: string
+ required: false
+ default: ""
+
+ dcae.types.ExternalTLSInfo:
+ description: >
+ Information for using external TLS.
+ properties:
+ external_cert_directory:
+ description: >
+ The path in the container where the component expects to find TLS-related data.
+ type: string
+ required: false
+ default: "/opt/app/dcae-certificate/external"
+ use_external_tls:
+ description: >
+ Flag indicating whether external TLS is to be used
+ type: boolean
+ required: false
+ default: true
+ ca_name:
+ description: >
+ Name of Certificate Authority configured on CertService side
+ type: string
+ required: false
+ default: "RA"
+ cert_type:
+ description: >
+ Type of output certificates
+ type: string
+ required: false
+ default: "P12"
+ external_certificate_parameters:
+ description: >
+ Certificate parameters for external TLS info
+ type: dcae.types.ExternalCertParameters
+ required: false
+
node_types:
dcae.nodes.ContainerizedComponent:
# Base type for all containerized components
Information for setting up TLS (HTTPS).
required: false
+ external_cert:
+ type: dcae.types.ExternalTLSInfo
+ description: >
+ Information for setting up external TLS
+ required: false
+
replicas:
type: integer
description: >
================================================================================
Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
Copyright (c) 2020 Pantheon.tech. All rights reserved.
+Copyright (c) 2020 Nokia. All rights reserved.
================================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
<groupId>org.onap.dcaegen2.platform.plugins</groupId>
<artifactId>k8s</artifactId>
<name>k8s-plugin</name>
- <version>3.0.0-SNAPSHOT</version>
+ <version>3.1.0-SNAPSHOT</version>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
# ================================================================================
# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# Copyright (c) 2020 Pantheon.tech. All rights reserved.
+# Copyright (c) 2020 Nokia. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
setup(
name='k8splugin',
description='Cloudify plugin for containerized components deployed using Kubernetes',
- version="3.0.0",
- author='J. F. Lucas, Michael Hwang, Tommy Carpenter',
+ version="3.1.0",
+ author='J. F. Lucas, Michael Hwang, Tommy Carpenter, Joanna Jeremicz',
packages=['k8splugin','k8sclient','configure'],
zip_safe=False,
install_requires=[
# ================================================================================
# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
# Copyright (c) 2020 Pantheon.tech. All rights reserved.
+# Copyright (c) 2020 Nokia. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
"image": "tlsrepo/tls-init-container:1.2.3",
"component_cert_dir": "/opt/dcae/cacert"
},
+ "external_cert": {
+ "image_tag": "repo/aaf-certservice-client:1.2.3",
+ "request_url" : "https://request:1010/url",
+ "timeout" : "30000",
+ "country" : "US",
+ "organization" : "Linux-Foundation",
+ "state" : "California",
+ "organizational_unit" : "ONAP",
+ "location" : "San-Francisco",
+ "keystore_password" : "secret1",
+ "truststore_password" : "secret2"
+ },
"cbs": {
"base_url": "https://config-binding-service:10443/service_component_all/test-component"
}
# Needs to be correctly labeled so that the Service can find it
assert dep.spec.template.metadata.labels["app"] == "testcomponent"
+def verify_external_cert(dep):
+ cert_container = dep.spec.template.spec.init_containers[1]
+ print(cert_container)
+ assert cert_container.image == "repo/aaf-certservice-client:1.2.3"
+ assert cert_container.name == "cert-service-client"
+ assert len(cert_container.volume_mounts) == 2
+ assert cert_container.volume_mounts[0].name == "tls-info"
+ assert cert_container.volume_mounts[0].mount_path == "/path/to/container/cert/directory/"
+ assert cert_container.volume_mounts[1].name == "tls-volume"
+ assert cert_container.volume_mounts[1].mount_path == "/etc/onap/aaf/certservice/certs/"
+
+ expected_envs = {
+ "REQUEST_URL": "https://request:1010/url",
+ "REQUEST_TIMEOUT": "30000",
+ "OUTPUT_PATH": "/path/to/container/cert/directory/external",
+ "OUTPUT_TYPE": "P12",
+ "CA_NAME": "myname",
+ "COMMON_NAME": "mycommonname",
+ "ORGANIZATION": "Linux-Foundation",
+ "ORGANIZATION_UNIT": "ONAP",
+ "LOCATION": "San-Francisco",
+ "STATE": "California",
+ "COUNTRY": "US",
+ "SANS": "mysans",
+ "KEYSTORE_PATH": "/etc/onap/aaf/certservice/certs/certServiceClient-keystore.jks",
+ "KEYSTORE_PASSWORD": "secret1",
+ "TRUSTSTORE_PATH": "/etc/onap/aaf/certservice/certs/truststore.jks",
+ "TRUSTSTORE_PASSWORD": "secret2"}
+
+ envs = {k.name: k.value for k in cert_container.env}
+ for k in expected_envs:
+ assert (k in envs and expected_envs[k] == envs[k])
def do_deploy(tls_info=None):
''' Common deployment operations '''
verify_common(dep, deployment_description)
return dep, deployment_description
+
+
+def do_deploy_ext(ext_tls_info):
+ ''' Common deployment operations '''
+ import k8sclient.k8sclient
+
+ k8s_test_config = _set_k8s_configuration()
+
+ kwargs = _set_common_kwargs()
+ kwargs['resources'] = _set_resources()
+ kwargs["external_cert"] = ext_tls_info
+
+ dep, deployment_description = k8sclient.k8sclient.deploy("k8stest", "testcomponent", "example.com/testcomponent:1.4.3", 1, False, k8s_test_config, **kwargs)
+
+ # Make sure all of the basic k8s parameters are correct
+ verify_common(dep, deployment_description)
+
+ return dep, deployment_description
# org.onap.dcae
# ================================================================================
# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2020 Nokia. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import pytest
from common import do_deploy
+from common import do_deploy_ext
+from common import verify_external_cert
def test_deploy_full_tls(mockk8sapi):
''' Deploy component with a full TLS configuration, to act as a server '''
app_container = dep.spec.template.spec.containers[0]
assert app_container.volume_mounts[2].mount_path == "/opt/dcae/cacert"
+
+def test_deploy_external_cert(mockk8sapi):
+ ''' Deploy component with external TLS configuration '''
+
+ dep, deployment_description = do_deploy_ext({"external_cert_directory": "/path/to/container/cert/directory/",
+ "use_external_tls": True,
+ "cert_type": "P12",
+ "ca_name": "myname",
+ "external_certificate_parameters": {
+ "common_name": "mycommonname",
+ "sans": "mysans"}
+ })
+
+ app_container = dep.spec.template.spec.containers[0]
+ assert app_container.volume_mounts[2].mount_path == "/opt/dcae/cacert"
+
+ # Make sure all of the external init container parameters are correct
+ verify_external_cert(dep)