Merge branch 'master' of github.com:Netflix/lemur into key_type_column

This commit is contained in:
sayali 2020-09-15 15:16:13 -07:00
commit d8cca855e8
8 changed files with 324 additions and 20 deletions

View File

@ -653,13 +653,20 @@ Active Directory Certificate Services Plugin
:noindex:
Template to be used for certificate issuing. Usually display name w/o spaces
.. data:: ADCS_TEMPLATE_<upper(authority.name)>
:noindex:
If there is a config variable ADCS_TEMPLATE_<upper(authority.name)> take the value as Cert template else default to ADCS_TEMPLATE to be compatible with former versions. Template to be used for certificate issuing. Usually display name w/o spaces
.. data:: ADCS_START
:noindex:
Used in ADCS-Sourceplugin. Minimum id of the first certificate to be returned. ID is increased by one until ADCS_STOP. Missing cert-IDs are ignored
.. data:: ADCS_STOP
:noindex:
Used for ADCS-Sourceplugin. Maximum id of the certificates returned.
.. data:: ADCS_ISSUING
:noindex:
@ -672,6 +679,68 @@ Active Directory Certificate Services Plugin
Contains the root cert of the CA
Entrust Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Enables the creation of Entrust certificates. You need to set the API access up with Entrust support. Check the information in the Entrust Portal as well.
Certificates are created as "SERVER_AND_CLIENT_AUTH".
Caution: Sometimes the entrust API does not respond in a timely manner. This error is handled and reported by the plugin. Should this happen you just have to hit the create button again after to create a valid certificate.
The following parameters have to be set in the configuration files.
.. data:: ENTRUST_URL
:noindex:
This is the url for the Entrust API. Refer to the API documentation.
.. data:: ENTRUST_API_CERT
:noindex:
Path to the certificate file in PEM format. This certificate is created in the onboarding process. Refer to the API documentation.
.. data:: ENTRUST_API_KEY
:noindex:
Path to the key file in RSA format. This certificate is created in the onboarding process. Refer to the API documentation. Caution: the request library cannot handle encrypted keys. The keyfile therefore has to contain the unencrypted key. Please put this in a secure location on the server.
.. data:: ENTRUST_API_USER
:noindex:
String with the API user. This user is created in the onboarding process. Refer to the API documentation.
.. data:: ENTRUST_API_PASS
:noindex:
String with the password for the API user. This password is created in the onboarding process. Refer to the API documentation.
.. data:: ENTRUST_NAME
:noindex:
String with the name that should appear as certificate owner in the Entrust portal. Refer to the API documentation.
.. data:: ENTRUST_EMAIL
:noindex:
String with the email address that should appear as certificate contact email in the Entrust portal. Refer to the API documentation.
.. data:: ENTRUST_PHONE
:noindex:
String with the phone number that should appear as certificate contact in the Entrust portal. Refer to the API documentation.
.. data:: ENTRUST_ISSUING
:noindex:
Contains the issuing cert of the CA
.. data:: ENTRUST_ROOT
:noindex:
Contains the root cert of the CA
.. data:: ENTRUST_PRODUCT_<upper(authority.name)>
:noindex:
If there is a config variable ENTRUST_PRODUCT_<upper(authority.name)> take the value as cert product name else default to "STANDARD_SSL". Refer to the API documentation for valid products names.
Verisign Issuer Plugin
~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,5 @@
"""Set the version information."""
try:
VERSION = __import__("pkg_resources").get_distribution(__name__).version
except Exception as e:
VERSION = "unknown"

View File

@ -0,0 +1,228 @@
from lemur.plugins.bases import IssuerPlugin, SourcePlugin
import arrow
import requests
import json
from lemur.plugins import lemur_entrust as ENTRUST
from flask import current_app
from lemur.extensions import metrics
from lemur.common.utils import validate_conf
def log_status_code(r, *args, **kwargs):
"""
Is a request hook that logs all status codes to the ENTRUST api.
:param r:
:param args:
:param kwargs:
:return:
"""
metrics.send("ENTRUST_status_code_{}".format(r.status_code), "counter", 1)
def determine_end_date(end_date):
"""
Determine appropriate end date
:param end_date:
:return: validity_end
"""
# ENTRUST only allows 13 months of max certificate duration
max_validity_end = arrow.utcnow().shift(years=1, months=+1).format('YYYY-MM-DD')
if not end_date:
end_date = max_validity_end
if end_date > max_validity_end:
end_date = max_validity_end
return end_date
def process_options(options):
"""
Processes and maps the incoming issuer options to fields/options that
Entrust understands
:param options:
:return: dict of valid entrust options
"""
# if there is a config variable ENTRUST_PRODUCT_<upper(authority.name)>
# take the value as Cert product-type
# else default to "STANDARD_SSL"
authority = options.get("authority").name.upper()
product_type = current_app.config.get("ENTRUST_PRODUCT_{0}".format(authority), "STANDARD_SSL")
if options.get("validity_end"):
validity_end = determine_end_date(options.get("validity_end"))
else:
validity_end = determine_end_date(False)
tracking_data = {
"requesterName": current_app.config.get("ENTRUST_NAME"),
"requesterEmail": current_app.config.get("ENTRUST_EMAIL"),
"requesterPhone": current_app.config.get("ENTRUST_PHONE")
}
data = {
"signingAlg": "SHA-2",
"eku": "SERVER_AND_CLIENT_AUTH",
"certType": product_type,
"certExpiryDate": validity_end,
"tracking": tracking_data
}
return data
def handle_response(my_response):
"""
Helper function for parsing responses from the Entrust API.
:param content:
:return: :raise Exception:
"""
msg = {
200: "The request had the validateOnly flag set to true and validation was successful.",
201: "Certificate created",
202: "Request accepted and queued for approval",
400: "Invalid request parameters",
404: "Unknown jobId",
429: "Too many requests"
}
try:
d = json.loads(my_response.content)
except Exception as e:
# catch an empty jason object here
d = {'errors': 'No detailled message'}
s = my_response.status_code
if s > 399:
raise Exception("ENTRUST error: {0}\n{1}".format(msg.get(s, s), d['errors']))
current_app.logger.info("Response: {0}, {1} ".format(s, d))
return d
class EntrustIssuerPlugin(IssuerPlugin):
title = "ENTRUST"
slug = "entrust-issuer"
description = "Enables the creation of certificates by ENTRUST"
version = ENTRUST.VERSION
author = "sirferl"
author_url = "https://github.com/sirferl/lemur"
def __init__(self, *args, **kwargs):
"""Initialize the issuer with the appropriate details."""
required_vars = [
"ENTRUST_API_CERT",
"ENTRUST_API_KEY",
"ENTRUST_API_USER",
"ENTRUST_API_PASS",
"ENTRUST_URL",
"ENTRUST_ROOT",
"ENTRUST_NAME",
"ENTRUST_EMAIL",
"ENTRUST_PHONE",
"ENTRUST_ISSUING",
]
validate_conf(current_app, required_vars)
self.session = requests.Session()
cert_file = current_app.config.get("ENTRUST_API_CERT")
key_file = current_app.config.get("ENTRUST_API_KEY")
user = current_app.config.get("ENTRUST_API_USER")
password = current_app.config.get("ENTRUST_API_PASS")
self.session.cert = (cert_file, key_file)
self.session.auth = (user, password)
self.session.hooks = dict(response=log_status_code)
# self.session.config['keep_alive'] = False
super(EntrustIssuerPlugin, self).__init__(*args, **kwargs)
def create_certificate(self, csr, issuer_options):
"""
Creates an Entrust certificate.
:param csr:
:param issuer_options:
:return: :raise Exception:
"""
current_app.logger.info(
"Requesting options: {0}".format(issuer_options)
)
url = current_app.config.get("ENTRUST_URL") + "/certificates"
data = process_options(issuer_options)
data["csr"] = csr
try:
response = self.session.post(url, json=data, timeout=(15, 40))
except requests.exceptions.Timeout:
raise Exception("Timeout for POST")
except requests.exceptions.RequestException as e:
raise Exception("Error for POST {0}".format(e))
response_dict = handle_response(response)
external_id = response_dict['trackingId']
cert = response_dict['endEntityCert']
chain = response_dict['chainCerts'][1]
current_app.logger.info(
"Received Chain: {0}".format(chain)
)
return cert, chain, external_id
def revoke_certificate(self, certificate, comments):
"""Revoke a Digicert certificate."""
base_url = current_app.config.get("ENTRUST_URL")
# make certificate revoke request
revoke_url = "{0}/certificates/{1}/revocations".format(
base_url, certificate.external_id
)
metrics.send("entrust_revoke_certificate", "counter", 1)
if comments == '' or not comments:
comments = "revoked via API"
data = {
"crlReason": "superseded",
"revocationComment": comments
}
response = self.session.post(revoke_url, json=data)
data = handle_response(response)
@staticmethod
def create_authority(options):
"""Create an authority.
Creates an authority, this authority is then used by Lemur to
allow a user to specify which Certificate Authority they want
to sign their certificate.
:param options:
:return:
"""
entrust_root = current_app.config.get("ENTRUST_ROOT")
entrust_issuing = current_app.config.get("ENTRUST_ISSUING")
role = {"username": "", "password": "", "name": "entrust"}
current_app.logger.info("Creating Auth: {0} {1}".format(options, entrust_issuing))
return entrust_root, "", [role]
def get_ordered_certificate(self, order_id):
raise NotImplementedError("Not implemented\n", self, order_id)
def canceled_ordered_certificate(self, pending_cert, **kwargs):
raise NotImplementedError("Not implemented\n", self, pending_cert, **kwargs)
class EntrustSourcePlugin(SourcePlugin):
title = "ENTRUST"
slug = "entrust-source"
description = "Enables the collecion of certificates"
version = ENTRUST.VERSION
author = "sirferl"
author_url = "https://github.com/sirferl/lemur"
def get_certificates(self, options, **kwargs):
# Not needed for ENTRUST
raise NotImplementedError("Not implemented\n", self, options, **kwargs)
def get_endpoints(self, options, **kwargs):
# There are no endpoints in ENTRUST
raise NotImplementedError("Not implemented\n", self, options, **kwargs)

View File

@ -39,7 +39,7 @@
"gulp-uglify": "^2.0.0",
"gulp-useref": "^3.1.2",
"gulp-util": "^3.0.1",
"http-proxy": "~1.16.2",
"http-proxy": ">=1.18.1",
"jshint-stylish": "^2.2.1",
"karma": "^4.4.1",
"karma-jasmine": "^1.1.0",

View File

@ -4,7 +4,7 @@
#
# pip-compile --no-index --output-file=requirements-docs.txt requirements-docs.in
#
acme==1.7.0 # via -r requirements.txt
acme==1.8.0 # via -r requirements.txt
alabaster==0.7.12 # via sphinx
alembic-autogenerate-enums==0.0.2 # via -r requirements.txt
alembic==1.4.2 # via -r requirements.txt, flask-migrate
@ -17,8 +17,8 @@ bcrypt==3.1.7 # via -r requirements.txt, flask-bcrypt, paramiko
beautifulsoup4==4.9.1 # via -r requirements.txt, cloudflare
billiard==3.6.3.0 # via -r requirements.txt, celery
blinker==1.4 # via -r requirements.txt, flask-mail, flask-principal, raven
boto3==1.14.56 # via -r requirements.txt
botocore==1.17.56 # via -r requirements.txt, boto3, s3transfer
boto3==1.14.61 # via -r requirements.txt
botocore==1.17.61 # via -r requirements.txt, boto3, s3transfer
celery[redis]==4.4.2 # via -r requirements.txt
certifi==2020.6.20 # via -r requirements.txt, requests
certsrv==2.1.1 # via -r requirements.txt

View File

@ -10,22 +10,22 @@ aws-sam-translator==1.22.0 # via cfn-lint
aws-xray-sdk==2.5.0 # via moto
bandit==1.6.2 # via -r requirements-tests.in
black==20.8b1 # via -r requirements-tests.in
boto3==1.14.56 # via aws-sam-translator, moto
boto3==1.14.61 # via aws-sam-translator, moto
boto==2.49.0 # via moto
botocore==1.17.56 # via aws-xray-sdk, boto3, moto, s3transfer
botocore==1.17.61 # via aws-xray-sdk, boto3, moto, s3transfer
certifi==2020.6.20 # via requests
cffi==1.14.0 # via cryptography
cfn-lint==0.29.5 # via moto
chardet==3.0.4 # via requests
click==7.1.2 # via black, flask
coverage==5.2.1 # via -r requirements-tests.in
cryptography==3.1 # via moto, sshpubkeys
coverage==5.3 # via -r requirements-tests.in
cryptography==3.1 # via moto, python-jose, sshpubkeys
decorator==4.4.2 # via networkx
docker==4.2.0 # via moto
docutils==0.15.2 # via botocore
ecdsa==0.15 # via python-jose, sshpubkeys
ecdsa==0.14.1 # via moto, python-jose, sshpubkeys
factory-boy==3.0.1 # via -r requirements-tests.in
faker==4.1.2 # via -r requirements-tests.in, factory-boy
faker==4.1.3 # via -r requirements-tests.in, factory-boy
fakeredis==1.4.3 # via -r requirements-tests.in
flask==1.1.2 # via pytest-flask
freezegun==1.0.0 # via -r requirements-tests.in
@ -43,10 +43,10 @@ jsonpatch==1.25 # via cfn-lint
jsonpickle==1.4 # via aws-xray-sdk
jsonpointer==2.0 # via jsonpatch
jsonschema==3.2.0 # via aws-sam-translator, cfn-lint
markupsafe==1.1.1 # via jinja2
markupsafe==1.1.1 # via jinja2, moto
mock==4.0.2 # via moto
more-itertools==8.2.0 # via pytest
moto==1.3.14 # via -r requirements-tests.in
more-itertools==8.2.0 # via moto, pytest
moto==1.3.16 # via -r requirements-tests.in
mypy-extensions==0.4.3 # via black
networkx==2.4 # via cfn-lint
nose==1.3.7 # via -r requirements-tests.in
@ -62,9 +62,9 @@ pyparsing==2.4.7 # via packaging
pyrsistent==0.16.0 # via jsonschema
pytest-flask==1.0.0 # via -r requirements-tests.in
pytest-mock==3.3.1 # via -r requirements-tests.in
pytest==6.0.1 # via -r requirements-tests.in, pytest-flask, pytest-mock
pytest==6.0.2 # via -r requirements-tests.in, pytest-flask, pytest-mock
python-dateutil==2.8.1 # via botocore, faker, freezegun, moto
python-jose==3.1.0 # via moto
python-jose[cryptography]==3.1.0 # via moto
pytz==2019.3 # via moto
pyyaml==5.3.1 # via -r requirements-tests.in, bandit, cfn-lint, moto
redis==3.5.3 # via fakeredis
@ -88,7 +88,7 @@ websocket-client==0.57.0 # via docker
werkzeug==1.0.1 # via flask, moto, pytest-flask
wrapt==1.12.1 # via aws-xray-sdk
xmltodict==0.12.0 # via moto
zipp==3.1.0 # via importlib-metadata
zipp==3.1.0 # via importlib-metadata, moto
# The following packages are considered to be unsafe in a requirements file:
# setuptools

View File

@ -4,7 +4,7 @@
#
# pip-compile --no-index --output-file=requirements.txt requirements.in
#
acme==1.7.0 # via -r requirements.in
acme==1.8.0 # via -r requirements.in
alembic-autogenerate-enums==0.0.2 # via -r requirements.in
alembic==1.4.2 # via flask-migrate
amqp==2.5.2 # via kombu
@ -15,8 +15,8 @@ bcrypt==3.1.7 # via flask-bcrypt, paramiko
beautifulsoup4==4.9.1 # via cloudflare
billiard==3.6.3.0 # via celery
blinker==1.4 # via flask-mail, flask-principal, raven
boto3==1.14.56 # via -r requirements.in
botocore==1.17.56 # via -r requirements.in, boto3, s3transfer
boto3==1.14.61 # via -r requirements.in
botocore==1.17.61 # via -r requirements.in, boto3, s3transfer
celery[redis]==4.4.2 # via -r requirements.in
certifi==2020.6.20 # via -r requirements.in, requests
certsrv==2.1.1 # via -r requirements.in

View File

@ -153,7 +153,9 @@ setup(
'vault_source = lemur.plugins.lemur_vault_dest.plugin:VaultSourcePlugin',
'vault_desination = lemur.plugins.lemur_vault_dest.plugin:VaultDestinationPlugin',
'adcs_issuer = lemur.plugins.lemur_adcs.plugin:ADCSIssuerPlugin',
'adcs_source = lemur.plugins.lemur_adcs.plugin:ADCSSourcePlugin'
'adcs_source = lemur.plugins.lemur_adcs.plugin:ADCSSourcePlugin',
'entrust_issuer = lemur.plugins.lemur_entrust.plugin:EntrustIssuerPlugin',
'entrust_source = lemur.plugins.lemur_entrust.plugin:EntrustSourcePlugin'
],
},
classifiers=[