Merge branch 'master' into unicode-in-issuer-name

This commit is contained in:
Curtis 2018-12-21 07:59:20 -08:00 committed by GitHub
commit b7332957e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 51 additions and 33 deletions

View File

@ -11,12 +11,14 @@
.. moduleauthor:: Mikhail Khodorovskiy <mikhail.khodorovskiy@jivesoftware.com> .. moduleauthor:: Mikhail Khodorovskiy <mikhail.khodorovskiy@jivesoftware.com>
""" """
import base64 import base64
import os
import urllib
import requests
import itertools import itertools
import os
from lemur.certificates.models import Certificate import requests
from flask import current_app
from lemur.common.defaults import common_name
from lemur.common.utils import parse_certificate
from lemur.plugins.bases import DestinationPlugin from lemur.plugins.bases import DestinationPlugin
DEFAULT_API_VERSION = 'v1' DEFAULT_API_VERSION = 'v1'
@ -26,16 +28,21 @@ def ensure_resource(k8s_api, k8s_base_uri, namespace, kind, name, data):
# _resolve_uri(k8s_base_uri, namespace, kind, name, api_ver=DEFAULT_API_VERSION) # _resolve_uri(k8s_base_uri, namespace, kind, name, api_ver=DEFAULT_API_VERSION)
url = _resolve_uri(k8s_base_uri, namespace, kind) url = _resolve_uri(k8s_base_uri, namespace, kind)
current_app.logger.debug("K8S POST request URL: %s", url)
create_resp = k8s_api.post(url, json=data) create_resp = k8s_api.post(url, json=data)
current_app.logger.debug("K8S POST response: %s", create_resp)
if 200 <= create_resp.status_code <= 299: if 200 <= create_resp.status_code <= 299:
return None return None
elif create_resp.json().get('reason', '') != 'AlreadyExists':
elif create_resp.json()['reason'] != 'AlreadyExists':
return create_resp.content return create_resp.content
update_resp = k8s_api.put(_resolve_uri(k8s_base_uri, namespace, kind, name), json=data) url = _resolve_uri(k8s_base_uri, namespace, kind, name)
current_app.logger.debug("K8S PUT request URL: %s", url)
update_resp = k8s_api.put(url, json=data)
current_app.logger.debug("K8S PUT response: %s", update_resp)
if not 200 <= update_resp.status_code <= 299: if not 200 <= update_resp.status_code <= 299:
return update_resp.content return update_resp.content
@ -61,6 +68,12 @@ def _resolve_uri(k8s_base_uri, namespace, kind, name=None, api_ver=DEFAULT_API_V
])) ]))
# Performs Base64 encoding of string to string using the base64.b64encode() function
# which encodes bytes to bytes.
def base64encode(string):
return base64.b64encode(string.encode()).decode()
class KubernetesDestinationPlugin(DestinationPlugin): class KubernetesDestinationPlugin(DestinationPlugin):
title = 'Kubernetes' title = 'Kubernetes'
slug = 'kubernetes-destination' slug = 'kubernetes-destination'
@ -74,28 +87,28 @@ class KubernetesDestinationPlugin(DestinationPlugin):
'name': 'kubernetesURL', 'name': 'kubernetesURL',
'type': 'str', 'type': 'str',
'required': True, 'required': True,
'validation': '@(https?|http)://(-\.)?([^\s/?\.#-]+\.?)+(/[^\s]*)?$@iS', 'validation': 'https?://[a-zA-Z0-9.-]+(?::[0-9]+)?',
'helpMessage': 'Must be a valid Kubernetes server URL!', 'helpMessage': 'Must be a valid Kubernetes server URL!',
}, },
{ {
'name': 'kubernetesAuthToken', 'name': 'kubernetesAuthToken',
'type': 'str', 'type': 'str',
'required': True, 'required': True,
'validation': '/^$|\s+/', 'validation': '[0-9a-zA-Z-_.]+',
'helpMessage': 'Must be a valid Kubernetes server Token!', 'helpMessage': 'Must be a valid Kubernetes server Token!',
}, },
{ {
'name': 'kubernetesServerCertificate', 'name': 'kubernetesServerCertificate',
'type': 'str', 'type': 'textarea',
'required': True, 'required': True,
'validation': '/^$|\s+/', 'validation': '-----BEGIN CERTIFICATE-----[a-zA-Z0-9/+\\s\\r\\n]+-----END CERTIFICATE-----',
'helpMessage': 'Must be a valid Kubernetes server Certificate!', 'helpMessage': 'Must be a valid Kubernetes server Certificate!',
}, },
{ {
'name': 'kubernetesNamespace', 'name': 'kubernetesNamespace',
'type': 'str', 'type': 'str',
'required': True, 'required': True,
'validation': '/^$|\s+/', 'validation': '[a-z0-9]([-a-z0-9]*[a-z0-9])?',
'helpMessage': 'Must be a valid Kubernetes Namespace!', 'helpMessage': 'Must be a valid Kubernetes Namespace!',
}, },
@ -106,33 +119,38 @@ class KubernetesDestinationPlugin(DestinationPlugin):
def upload(self, name, body, private_key, cert_chain, options, **kwargs): def upload(self, name, body, private_key, cert_chain, options, **kwargs):
k8_bearer = self.get_option('kubernetesAuthToken', options) try:
k8_cert = self.get_option('kubernetesServerCertificate', options) k8_bearer = self.get_option('kubernetesAuthToken', options)
k8_namespace = self.get_option('kubernetesNamespace', options) k8_cert = self.get_option('kubernetesServerCertificate', options)
k8_base_uri = self.get_option('kubernetesURL', options) k8_namespace = self.get_option('kubernetesNamespace', options)
k8_base_uri = self.get_option('kubernetesURL', options)
k8s_api = K8sSession(k8_bearer, k8_cert) k8s_api = K8sSession(k8_bearer, k8_cert)
cert = Certificate(body=body) cn = common_name(parse_certificate(body))
# in the future once runtime properties can be passed-in - use passed-in secret name # in the future once runtime properties can be passed-in - use passed-in secret name
secret_name = 'certs-' + urllib.quote_plus(cert.name) secret_name = 'certs-' + cn
err = ensure_resource(k8s_api, k8s_base_uri=k8_base_uri, namespace=k8_namespace, kind="secret", name=secret_name, data={ err = ensure_resource(k8s_api, k8s_base_uri=k8_base_uri, namespace=k8_namespace, kind="secret", name=secret_name, data={
'apiVersion': 'v1', 'apiVersion': 'v1',
'kind': 'Secret', 'kind': 'Secret',
'metadata': { 'metadata': {
'name': secret_name, 'name': secret_name,
}, },
'data': { 'data': {
'combined.pem': base64.b64encode(body + private_key), 'combined.pem': base64encode('%s\n%s' % (body, private_key)),
'ca.crt': base64.b64encode(cert_chain), 'ca.crt': base64encode(cert_chain),
'service.key': base64.b64encode(private_key), 'service.key': base64encode(private_key),
'service.crt': base64.b64encode(body), 'service.crt': base64encode(body),
} }
}) })
except Exception as e:
current_app.logger.exception("Exception in upload: {}".format(e), exc_info=True)
raise
if err is not None: if err is not None:
current_app.logger.debug("Error deploying resource: %s", err)
raise Exception("Error uploading secret: " + err) raise Exception("Error uploading secret: " + err)