From f02178c154922ea67c1b5b6cba64dafca7da6c39 Mon Sep 17 00:00:00 2001 From: sirferl Date: Thu, 20 Dec 2018 11:54:47 +0100 Subject: [PATCH 1/5] added ADCS issuer and source plugin --- lemur/plugins/lemur_adcs/__init__.py | 6 ++ lemur/plugins/lemur_adcs/plugin.py | 120 +++++++++++++++++++++++++++ requirements-dev.txt | 1 + requirements-docs.txt | 18 ++-- requirements-tests.txt | 4 +- requirements.in | 3 +- requirements.txt | 7 +- setup.py | 4 +- 8 files changed, 147 insertions(+), 16 deletions(-) create mode 100644 lemur/plugins/lemur_adcs/__init__.py create mode 100644 lemur/plugins/lemur_adcs/plugin.py diff --git a/lemur/plugins/lemur_adcs/__init__.py b/lemur/plugins/lemur_adcs/__init__.py new file mode 100644 index 00000000..6b61e936 --- /dev/null +++ b/lemur/plugins/lemur_adcs/__init__.py @@ -0,0 +1,6 @@ +"""Set the version information.""" +try: + VERSION = __import__('pkg_resources') \ + .get_distribution(__name__).version +except Exception as e: + VERSION = 'unknown' diff --git a/lemur/plugins/lemur_adcs/plugin.py b/lemur/plugins/lemur_adcs/plugin.py new file mode 100644 index 00000000..48a3e85b --- /dev/null +++ b/lemur/plugins/lemur_adcs/plugin.py @@ -0,0 +1,120 @@ +from lemur.plugins.bases import IssuerPlugin, SourcePlugin +import requests +import datetime +import lemur_adcs as ADCS +from certsrv import Certsrv +import ssl +from OpenSSL import crypto +from flask import current_app + +class ADCSIssuerPlugin(IssuerPlugin): + title = 'ADCS' + slug = 'adcs-issuer' + description = 'Enables the creation of certificates by ADCS (Active Direcory Certificate Services)' + version = ADCS.VERSION + + author = 'sirferl' + author_url = 'https://github.com/sirferl/lemur' + + def __init__(self, *args, **kwargs): + """Initialize the issuer with the appropriate details.""" + self.session = requests.Session() + super(ADCSIssuerPlugin, self).__init__(*args, **kwargs) + + @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: + """ + role = {'username': '', 'password': '', 'name': 'adcs'} + return constants.ADCS_ROOT, constants.ADCS_ISSUING, [role] + + def create_certificate(self, csr, issuer_options): + adcs_server = current_app.config.get('ADCS_SERVER') + adcs_user = current_app.config.get('ADCS_USER') + adcs_pwd = current_app.config.get('ADCS_PWD') + adcs_auth_method = current_app.config.get('ADCS_AUTH_METHOD') + ca_server = Certsrv(adcs_server, adcs_user, adcs_pwd, auth_method = adcs_auth_method) + current_app.logger.info("Requesting CSR: {0}".format(csr)) + current_app.logger.info("Issuer options: {0}".format(issuer_options)) + cert, req_id = ca_server.get_cert(csr, ADCS_TEMPLATE, encoding='b64').decode('utf-8').replace('\r\n', '\n') + chain = ca_server.get_ca_cert(encoding='b64').decode('utf-8').replace('\r\n', '\n') + return cert, chain, req_id + + def revoke_certificate(self, certificate, comments): + # requests.put('a third party') + raise NotImplementedError('Not implemented\n', self,certificate, comments) + + def get_ordered_certificate(self, order_id): + # requests.get('already existing certificate') + raise NotImplementedError('Not implemented\n',self, order_id) + + def canceled_ordered_certificate(self, pending_cert, **kwargs): + # requests.put('cancel an order that has yet to be issued') + raise NotImplementedError('Not implemented\n',self, pending_cert, **kwargs) + +class ADCSSourcePlugin(SourcePlugin): + title = 'ADCS' + slug = 'adcs-source' + description = 'Enables the collecion of certificates' + version = ADCS.VERSION + + author = 'sirferl' + author_url = 'https://github.com/sirferl/lemur' + options = [ + { + 'name': 'dummy', + 'type': 'str', + 'required': False, + 'validation': '/^[0-9]{12,12}$/', + 'helpMessage': 'Just to prevent error' + } + + ] + + def get_certificates(self,options, **kwargs): + adcs_server = current_app.config.get('ADCS_SERVER') + adcs_user = current_app.config.get('ADCS_USER') + adcs_pwd = current_app.config.get('ADCS_PWD') + adcs_auth_method = current_app.config.get('ADCS_AUTH_METHOD') + adcs_start = current_app.config.get('ADCS_START') + adcs_stop = current_app.config.get('ADCS_STOP') + ca_server = Certsrv(adcs_server, adcs_user, adcs_pwd, auth_method = adcs_auth_method) + out_certlist = [] + for id in range(adcs_start,adcs_stop): + try: + cert = ca_server.get_existing_cert(id, encoding='b64').decode('utf-8').replace('\r\n', '\n') + except Exception as err: + if '{0}'.format(err).find("CERTSRV_E_PROPERTY_EMPTY"): + #this error indicates end of certificate list(?), so we stop + break + else: + # We do nothing in case there is no certificate returned with the current id for other reasons + current_app.logger.info("Error with id {0}: {1}".format(id, err)) + else: + #we have a certificate + pubkey = crypto.load_certificate(crypto.FILETYPE_PEM, cert) + #loop through extensions to see if we find "TLS Web Server Authentication" + for e_id in range(0,pubkey.get_extension_count()-1): + try: + extension = '{0}'.format(pubkey.get_extension(e_id)) + except: + extensionn = '' + if extension.find("TLS Web Server Authentication") != -1: + out_certlist.append ( { + 'name': format(pubkey.get_subject().CN), + 'body' : cert}) + break + + return out_certlist + + + def get_endpoints(self, options, **kwargs): + # There are no endpoints in the ADCS + raise NotImplementedError('Not implemented\n',self, options, **kwargs) + diff --git a/requirements-dev.txt b/requirements-dev.txt index 7b427b20..d8c24e4d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -15,6 +15,7 @@ flake8==3.5.0 identify==1.1.7 # via pre-commit idna==2.8 # via requests importlib-metadata==0.7 # via pre-commit +importlib-resources==1.0.2 # via pre-commit invoke==1.2.0 mccabe==0.6.1 # via flake8 nodeenv==1.3.3 diff --git a/requirements-docs.txt b/requirements-docs.txt index 3f036915..80f38e5f 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -14,11 +14,11 @@ arrow==0.12.1 asn1crypto==0.24.0 asyncpool==1.0 babel==2.6.0 # via sphinx -bcrypt==3.1.4 +bcrypt==3.1.5 billiard==3.5.0.5 blinker==1.4 -boto3==1.9.60 -botocore==1.12.60 +boto3==1.9.67 +botocore==1.12.67 celery[redis]==4.2.1 certifi==2018.11.29 cffi==1.11.5 @@ -35,13 +35,13 @@ flask-cors==3.0.7 flask-mail==0.9.1 flask-migrate==2.3.1 flask-principal==0.4.0 -flask-restful==0.3.6 +flask-restful==0.3.7 flask-script==2.0.6 flask-sqlalchemy==2.3.2 flask==1.0.2 future==0.17.1 gunicorn==19.9.0 -idna==2.7 +idna==2.8 imagesize==1.1.0 # via sphinx inflection==0.3.1 itsdangerous==1.1.0 @@ -66,7 +66,7 @@ pyasn1-modules==0.2.2 pyasn1==0.4.4 pycparser==2.19 pygments==2.3.1 # via sphinx -pyjwt==1.7.0 +pyjwt==1.7.1 pynacl==1.3.0 pyopenssl==18.0.0 pyparsing==2.3.0 # via packaging @@ -78,17 +78,17 @@ pyyaml==3.13 raven[flask]==6.9.0 redis==2.10.6 requests-toolbelt==0.8.0 -requests[security]==2.20.1 +requests[security]==2.21.0 retrying==1.3.3 s3transfer==0.1.13 -six==1.11.0 +six==1.12.0 snowballstemmer==1.2.1 # via sphinx sphinx-rtd-theme==0.4.2 sphinx==1.8.2 sphinxcontrib-httpdomain==1.7.0 sphinxcontrib-websupport==1.1.0 # via sphinx sqlalchemy-utils==0.33.9 -sqlalchemy==1.2.14 +sqlalchemy==1.2.15 tabulate==0.8.2 urllib3==1.24.1 vine==1.1.4 diff --git a/requirements-tests.txt b/requirements-tests.txt index 59c626f7..47b83988 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -8,9 +8,9 @@ asn1crypto==0.24.0 # via cryptography atomicwrites==1.2.1 # via pytest attrs==18.2.0 # via pytest aws-xray-sdk==0.95 # via moto -boto3==1.9.67 # via moto +boto3==1.9.69 # via moto boto==2.49.0 # via moto -botocore==1.12.67 # via boto3, moto, s3transfer +botocore==1.12.69 # via boto3, moto, s3transfer certifi==2018.11.29 # via requests cffi==1.11.5 # via cryptography chardet==3.0.4 # via requests diff --git a/requirements.in b/requirements.in index 9824650b..0aea4591 100644 --- a/requirements.in +++ b/requirements.in @@ -8,6 +8,7 @@ boto3 botocore celery[redis] certifi +certsrv CloudFlare cryptography dnspython3 @@ -42,4 +43,4 @@ retrying six SQLAlchemy-Utils tabulate -xmltodict \ No newline at end of file +xmltodict diff --git a/requirements.txt b/requirements.txt index 7ee9a167..e88bcb90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,10 +15,11 @@ asyncpool==1.0 bcrypt==3.1.5 # via flask-bcrypt, paramiko billiard==3.5.0.5 # via celery blinker==1.4 # via flask-mail, flask-principal, raven -boto3==1.9.67 -botocore==1.12.67 +boto3==1.9.69 +botocore==1.12.69 celery[redis]==4.2.1 certifi==2018.11.29 +certsrv==2.1.0 cffi==1.11.5 # via bcrypt, cryptography, pynacl chardet==3.0.4 # via requests click==7.0 # via flask @@ -70,7 +71,7 @@ python-editor==1.0.3 # via alembic python-ldap==3.1.0 pytz==2018.7 # via acme, celery, flask-restful, pyrfc3339 pyyaml==3.13 # via cloudflare -raven[flask]==6.9.0 +raven[flask]==6.10.0 redis==2.10.6 requests-toolbelt==0.8.0 # via acme requests[security]==2.21.0 diff --git a/setup.py b/setup.py index 1511b013..882edb02 100644 --- a/setup.py +++ b/setup.py @@ -154,7 +154,9 @@ setup( 'digicert_cis_issuer = lemur.plugins.lemur_digicert.plugin:DigiCertCISIssuerPlugin', 'digicert_cis_source = lemur.plugins.lemur_digicert.plugin:DigiCertCISSourcePlugin', 'csr_export = lemur.plugins.lemur_csr.plugin:CSRExportPlugin', - 'sftp_destination = lemur.plugins.lemur_sftp.plugin:SFTPDestinationPlugin' + 'sftp_destination = lemur.plugins.lemur_sftp.plugin:SFTPDestinationPlugin', + 'adcs_issuer = lemur.plugins.lemur_adcs.plugin:ADCSIssuerPlugin', + 'adcs_source = lemur.plugins.lemur_adcs.plugin:ADCSSourcePlugin' ], }, classifiers=[ From c62bcd1456bc35198a5895588e6ab042d0213fe5 Mon Sep 17 00:00:00 2001 From: sirferl Date: Mon, 7 Jan 2019 10:02:37 +0100 Subject: [PATCH 2/5] repaired several lint errors --- lemur/plugins/lemur_adcs/plugin.py | 68 ++++++++++++++---------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/lemur/plugins/lemur_adcs/plugin.py b/lemur/plugins/lemur_adcs/plugin.py index 48a3e85b..31dba7b2 100644 --- a/lemur/plugins/lemur_adcs/plugin.py +++ b/lemur/plugins/lemur_adcs/plugin.py @@ -1,12 +1,11 @@ from lemur.plugins.bases import IssuerPlugin, SourcePlugin import requests -import datetime import lemur_adcs as ADCS from certsrv import Certsrv -import ssl from OpenSSL import crypto from flask import current_app + class ADCSIssuerPlugin(IssuerPlugin): title = 'ADCS' slug = 'adcs-issuer' @@ -27,36 +26,37 @@ class ADCSIssuerPlugin(IssuerPlugin): 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: """ + adcs_root = current_app.config.get('ADCS_ROOT') + adcs_issuing = current_app.config.get('ADCS_ISSUING') role = {'username': '', 'password': '', 'name': 'adcs'} - return constants.ADCS_ROOT, constants.ADCS_ISSUING, [role] + return adcs_root, adcs_issuing, [role] def create_certificate(self, csr, issuer_options): adcs_server = current_app.config.get('ADCS_SERVER') adcs_user = current_app.config.get('ADCS_USER') adcs_pwd = current_app.config.get('ADCS_PWD') adcs_auth_method = current_app.config.get('ADCS_AUTH_METHOD') - ca_server = Certsrv(adcs_server, adcs_user, adcs_pwd, auth_method = adcs_auth_method) + adcs_template = current_app.config.get('ADCS_TEMPLATE') + ca_server = Certsrv(adcs_server, adcs_user, adcs_pwd, auth_method=adcs_auth_method) current_app.logger.info("Requesting CSR: {0}".format(csr)) current_app.logger.info("Issuer options: {0}".format(issuer_options)) - cert, req_id = ca_server.get_cert(csr, ADCS_TEMPLATE, encoding='b64').decode('utf-8').replace('\r\n', '\n') + cert, req_id = ca_server.get_cert(csr, adcs_template, encoding='b64').decode('utf-8').replace('\r\n', '\n') chain = ca_server.get_ca_cert(encoding='b64').decode('utf-8').replace('\r\n', '\n') return cert, chain, req_id - + def revoke_certificate(self, certificate, comments): - # requests.put('a third party') - raise NotImplementedError('Not implemented\n', self,certificate, comments) - + raise NotImplementedError('Not implemented\n', self, certificate, comments) + def get_ordered_certificate(self, order_id): - # requests.get('already existing certificate') - raise NotImplementedError('Not implemented\n',self, order_id) - + raise NotImplementedError('Not implemented\n', self, order_id) + def canceled_ordered_certificate(self, pending_cert, **kwargs): - # requests.put('cancel an order that has yet to be issued') - raise NotImplementedError('Not implemented\n',self, pending_cert, **kwargs) + raise NotImplementedError('Not implemented\n', self, pending_cert, **kwargs) + class ADCSSourcePlugin(SourcePlugin): title = 'ADCS' @@ -67,54 +67,50 @@ class ADCSSourcePlugin(SourcePlugin): author = 'sirferl' author_url = 'https://github.com/sirferl/lemur' options = [ - { + { 'name': 'dummy', 'type': 'str', 'required': False, 'validation': '/^[0-9]{12,12}$/', 'helpMessage': 'Just to prevent error' } - ] - - def get_certificates(self,options, **kwargs): + + def get_certificates(self, options, **kwargs): adcs_server = current_app.config.get('ADCS_SERVER') adcs_user = current_app.config.get('ADCS_USER') adcs_pwd = current_app.config.get('ADCS_PWD') adcs_auth_method = current_app.config.get('ADCS_AUTH_METHOD') adcs_start = current_app.config.get('ADCS_START') adcs_stop = current_app.config.get('ADCS_STOP') - ca_server = Certsrv(adcs_server, adcs_user, adcs_pwd, auth_method = adcs_auth_method) + ca_server = Certsrv(adcs_server, adcs_user, adcs_pwd, auth_method=adcs_auth_method) out_certlist = [] - for id in range(adcs_start,adcs_stop): - try: + for id in range(adcs_start, adcs_stop): + try: cert = ca_server.get_existing_cert(id, encoding='b64').decode('utf-8').replace('\r\n', '\n') except Exception as err: if '{0}'.format(err).find("CERTSRV_E_PROPERTY_EMPTY"): - #this error indicates end of certificate list(?), so we stop + # this error indicates end of certificate list(?), so we stop break else: # We do nothing in case there is no certificate returned with the current id for other reasons current_app.logger.info("Error with id {0}: {1}".format(id, err)) - else: - #we have a certificate + else: + # we have a certificate pubkey = crypto.load_certificate(crypto.FILETYPE_PEM, cert) - #loop through extensions to see if we find "TLS Web Server Authentication" - for e_id in range(0,pubkey.get_extension_count()-1): + # loop through extensions to see if we find "TLS Web Server Authentication" + for e_id in range(0, pubkey.get_extension_count() - 1): try: extension = '{0}'.format(pubkey.get_extension(e_id)) - except: + except Exception: extensionn = '' - if extension.find("TLS Web Server Authentication") != -1: - out_certlist.append ( { + if extension.find("TLS Web Server Authentication") != -1: + out_certlist.append({ 'name': format(pubkey.get_subject().CN), - 'body' : cert}) + 'body': cert}) break - return out_certlist - - def get_endpoints(self, options, **kwargs): + def get_endpoints(self, options, **kwargs): # There are no endpoints in the ADCS - raise NotImplementedError('Not implemented\n',self, options, **kwargs) - + raise NotImplementedError('Not implemented\n', self, options, **kwargs) From a43476bc8702f56ac4261beff09fa8ca59a1f42d Mon Sep 17 00:00:00 2001 From: sirferl Date: Mon, 7 Jan 2019 11:04:27 +0100 Subject: [PATCH 3/5] minor errors after lint fix --- lemur/plugins/lemur_adcs/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lemur/plugins/lemur_adcs/plugin.py b/lemur/plugins/lemur_adcs/plugin.py index 31dba7b2..db068eb3 100644 --- a/lemur/plugins/lemur_adcs/plugin.py +++ b/lemur/plugins/lemur_adcs/plugin.py @@ -1,6 +1,6 @@ from lemur.plugins.bases import IssuerPlugin, SourcePlugin import requests -import lemur_adcs as ADCS +from lemur.plugins import lemur_adcs as ADCS from certsrv import Certsrv from OpenSSL import crypto from flask import current_app @@ -9,7 +9,7 @@ from flask import current_app class ADCSIssuerPlugin(IssuerPlugin): title = 'ADCS' slug = 'adcs-issuer' - description = 'Enables the creation of certificates by ADCS (Active Direcory Certificate Services)' + description = 'Enables the creation of certificates by ADCS (Active Directory Certificate Services)' version = ADCS.VERSION author = 'sirferl' From af88ad0f0da9d2dcee00f4455faf2b345594d905 Mon Sep 17 00:00:00 2001 From: sirferl Date: Mon, 7 Jan 2019 11:35:56 +0100 Subject: [PATCH 4/5] changed broken kombu ref. from 4.2.2 to 4.2.1 because travis build fails --- requirements-docs.txt | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index 80f38e5f..9c3cef5e 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -49,7 +49,7 @@ jinja2==2.10 jmespath==0.9.3 josepy==1.1.0 jsonlines==1.2.0 -kombu==4.2.2 +kombu==4.2.1 # 4.2.2 was broken sirferl lockfile==0.12.2 mako==1.0.7 markupsafe==1.1.0 diff --git a/requirements.txt b/requirements.txt index e88bcb90..c1658efc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -47,7 +47,7 @@ jinja2==2.10 jmespath==0.9.3 # via boto3, botocore josepy==1.1.0 # via acme jsonlines==1.2.0 # via cloudflare -kombu==4.2.2 # via celery +kombu==4.2.1 # via celery - 4.2.2. was removed sirferl lockfile==0.12.2 mako==1.0.7 # via alembic markupsafe==1.1.0 # via jinja2, mako From a1ca61d81365b6bfb6a26973ff7fe73337cea32c Mon Sep 17 00:00:00 2001 From: sirferl Date: Wed, 9 Jan 2019 09:50:26 +0100 Subject: [PATCH 5/5] changed a too long comment --- lemur/plugins/lemur_adcs/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lemur/plugins/lemur_adcs/plugin.py b/lemur/plugins/lemur_adcs/plugin.py index db068eb3..b7698474 100644 --- a/lemur/plugins/lemur_adcs/plugin.py +++ b/lemur/plugins/lemur_adcs/plugin.py @@ -93,7 +93,7 @@ class ADCSSourcePlugin(SourcePlugin): # this error indicates end of certificate list(?), so we stop break else: - # We do nothing in case there is no certificate returned with the current id for other reasons + # We do nothing in case there is no certificate returned for other reasons current_app.logger.info("Error with id {0}: {1}".format(id, err)) else: # we have a certificate