From 7f88c24e8374f669ba1e12c3d5ff06892de3b04c Mon Sep 17 00:00:00 2001 From: Curtis Castrapel Date: Thu, 17 Jan 2019 14:56:04 -0800 Subject: [PATCH 1/2] Fix LetsEncrypt Dyn flow for duplicate CN/SAN --- lemur/common/utils.py | 11 +++++++++++ lemur/plugins/lemur_acme/dyn.py | 8 ++++++-- lemur/sources/service.py | 5 +++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lemur/common/utils.py b/lemur/common/utils.py index 62e59d69..f26f07df 100644 --- a/lemur/common/utils.py +++ b/lemur/common/utils.py @@ -12,6 +12,7 @@ import string import sqlalchemy from cryptography import x509 from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import rsa, ec from cryptography.hazmat.primitives.serialization import load_pem_private_key from flask_restful.reqparse import RequestParser @@ -226,3 +227,13 @@ def truthiness(s): """If input string resembles something truthy then return True, else False.""" return s.lower() in ('true', 'yes', 'on', 't', '1') + + +def find_matching_certificates_by_hash(cert, matching_certs): + """Given a Cryptography-formatted certificate cert, and Lemur-formatted certificates (matching_certs), + determine if any of the certificate hashes match and return the matches.""" + matching = [] + for c in matching_certs: + if parse_certificate(c).fingerprint(hashes.SHA256()) == cert.body.fingerprint(hashes.SHA256()): + matching.append(c) + return matching diff --git a/lemur/plugins/lemur_acme/dyn.py b/lemur/plugins/lemur_acme/dyn.py index 9bab3a65..5d419f7f 100644 --- a/lemur/plugins/lemur_acme/dyn.py +++ b/lemur/plugins/lemur_acme/dyn.py @@ -5,7 +5,7 @@ import dns.exception import dns.name import dns.query import dns.resolver -from dyn.tm.errors import DynectCreateError +from dyn.tm.errors import DynectCreateError, DynectGetError from dyn.tm.session import DynectSession from dyn.tm.zones import Node, Zone, get_all_zones from flask import current_app @@ -119,7 +119,11 @@ def delete_txt_record(change_id, account_number, domain, token): zone = Zone(zone_name) node = Node(zone_name, fqdn) - all_txt_records = node.get_all_records_by_type('TXT') + try: + all_txt_records = node.get_all_records_by_type('TXT') + except DynectGetError: + # No Text Records remain or host is not in the zone anymore because all records have been deleted. + return for txt_record in all_txt_records: if txt_record.txtdata == ("{}".format(token)): current_app.logger.debug("Deleting TXT record name: {0}".format(fqdn)) diff --git a/lemur/sources/service.py b/lemur/sources/service.py index 227f1bce..55d2ee62 100644 --- a/lemur/sources/service.py +++ b/lemur/sources/service.py @@ -17,7 +17,7 @@ from lemur.endpoints import service as endpoint_service from lemur.destinations import service as destination_service from lemur.certificates.schemas import CertificateUploadInputSchema -from lemur.common.utils import parse_certificate +from lemur.common.utils import find_matching_certificates_by_hash, parse_certificate from lemur.common.defaults import serial from lemur.plugins.base import plugins @@ -126,7 +126,8 @@ def sync_certificates(source, user): if not exists: cert = parse_certificate(certificate['body']) - exists = certificate_service.get_by_serial(serial(cert)) + matching_serials = certificate_service.get_by_serial(serial(cert)) + exists = find_matching_certificates_by_hash(cert, matching_serials) if not certificate.get('owner'): certificate['owner'] = user.email From d689f5cda3aad42ff7b6363c40332160ca3a395a Mon Sep 17 00:00:00 2001 From: Curtis Castrapel Date: Thu, 17 Jan 2019 14:59:57 -0800 Subject: [PATCH 2/2] Fix LetsEncrypt for duplicate CN/SAN --- requirements-dev.txt | 5 ++--- requirements-docs.txt | 18 +++++++++--------- requirements-tests.txt | 8 ++++---- requirements.txt | 16 ++++++++-------- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 21156588..c1f55581 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,12 +14,11 @@ flake8==3.5.0 identify==1.1.8 # via pre-commit idna==2.8 # via requests importlib-metadata==0.8 # 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 pkginfo==1.5.0.1 # via twine -pre-commit==1.14.0 +pre-commit==1.14.2 pycodestyle==2.3.1 # via flake8 pyflakes==1.6.0 # via flake8 pygments==2.3.1 # via readme-renderer @@ -29,7 +28,7 @@ requests-toolbelt==0.8.0 # via twine requests==2.21.0 # via requests-toolbelt, twine six==1.12.0 # via bleach, cfgv, pre-commit, readme-renderer toml==0.10.0 # via pre-commit -tqdm==4.29.0 # via twine +tqdm==4.29.1 # via twine twine==1.12.1 urllib3==1.24.1 # via requests virtualenv==16.2.0 # via pre-commit diff --git a/requirements-docs.txt b/requirements-docs.txt index f3182456..a7df5395 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -7,18 +7,18 @@ acme==0.30.0 alabaster==0.7.12 # via sphinx alembic-autogenerate-enums==0.0.2 -alembic==1.0.5 -amqp==2.3.2 +alembic==1.0.6 +amqp==2.4.0 aniso8601==4.1.0 arrow==0.13.0 asn1crypto==0.24.0 asyncpool==1.0 babel==2.6.0 # via sphinx -bcrypt==3.1.5 +bcrypt==3.1.6 billiard==3.5.0.5 blinker==1.4 -boto3==1.9.76 -botocore==1.12.76 +boto3==1.9.80 +botocore==1.12.80 celery[redis]==4.2.1 certifi==2018.11.29 cffi==1.11.5 @@ -54,7 +54,7 @@ lockfile==0.12.2 mako==1.0.7 markupsafe==1.1.0 marshmallow-sqlalchemy==0.15.0 -marshmallow==2.17.0 +marshmallow==2.18.0 mock==2.0.0 ndg-httpsclient==0.5.1 packaging==18.0 # via sphinx @@ -69,7 +69,7 @@ pygments==2.3.1 # via sphinx pyjwt==1.7.1 pynacl==1.3.0 pyopenssl==18.0.0 -pyparsing==2.3.0 # via packaging +pyparsing==2.3.1 # via packaging pyrfc3339==1.1 python-dateutil==2.7.5 python-editor==1.0.3 @@ -87,8 +87,8 @@ sphinx-rtd-theme==0.4.2 sphinx==1.8.3 sphinxcontrib-httpdomain==1.7.0 sphinxcontrib-websupport==1.1.0 # via sphinx -sqlalchemy-utils==0.33.10 -sqlalchemy==1.2.15 +sqlalchemy-utils==0.33.11 +sqlalchemy==1.2.16 tabulate==0.8.2 urllib3==1.24.1 vine==1.2.0 diff --git a/requirements-tests.txt b/requirements-tests.txt index 490d74d1..2d54dce6 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.76 # via moto +boto3==1.9.80 # via moto boto==2.49.0 # via moto -botocore==1.12.76 # via boto3, moto, s3transfer +botocore==1.12.80 # via boto3, moto, s3transfer certifi==2018.11.29 # via requests cffi==1.11.5 # via cryptography chardet==3.0.4 # via requests @@ -18,7 +18,7 @@ click==7.0 # via flask coverage==4.5.2 cryptography==2.4.2 # via moto docker-pycreds==0.4.0 # via docker -docker==3.6.0 # via moto +docker==3.7.0 # via moto docutils==0.14 # via botocore ecdsa==0.13 # via python-jose factory-boy==2.11.1 @@ -46,7 +46,7 @@ pycryptodome==3.7.2 # via python-jose pyflakes==2.0.0 pytest-flask==0.14.0 pytest-mock==1.10.0 -pytest==4.1.0 +pytest==4.1.1 python-dateutil==2.7.5 # via botocore, faker, freezegun, moto python-jose==2.0.2 # via moto pytz==2018.9 # via moto diff --git a/requirements.txt b/requirements.txt index bc72db0a..79268c8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,17 +6,17 @@ # acme==0.30.0 alembic-autogenerate-enums==0.0.2 -alembic==1.0.5 # via flask-migrate -amqp==2.3.2 # via kombu +alembic==1.0.6 # via flask-migrate +amqp==2.4.0 # via kombu aniso8601==4.1.0 # via flask-restful arrow==0.13.0 asn1crypto==0.24.0 # via cryptography asyncpool==1.0 -bcrypt==3.1.5 # via flask-bcrypt, paramiko +bcrypt==3.1.6 # via flask-bcrypt, paramiko billiard==3.5.0.5 # via celery blinker==1.4 # via flask-mail, flask-principal, raven -boto3==1.9.76 -botocore==1.12.76 +boto3==1.9.80 +botocore==1.12.80 celery[redis]==4.2.1 certifi==2018.11.29 cffi==1.11.5 # via bcrypt, cryptography, pynacl @@ -51,7 +51,7 @@ lockfile==0.12.2 mako==1.0.7 # via alembic markupsafe==1.1.0 # via jinja2, mako marshmallow-sqlalchemy==0.15.0 -marshmallow==2.17.0 +marshmallow==2.18.0 mock==2.0.0 # via acme ndg-httpsclient==0.5.1 paramiko==2.4.2 @@ -77,8 +77,8 @@ requests[security]==2.21.0 retrying==1.3.3 s3transfer==0.1.13 # via boto3 six==1.12.0 -sqlalchemy-utils==0.33.10 -sqlalchemy==1.2.15 # via alembic, flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils +sqlalchemy-utils==0.33.11 +sqlalchemy==1.2.16 # via alembic, flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils tabulate==0.8.2 urllib3==1.24.1 # via botocore, requests vine==1.2.0 # via amqp