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

This commit is contained in:
Hossein Shafagh 2019-01-14 17:45:35 -08:00
commit f9618def0b
10 changed files with 57 additions and 37 deletions

View File

@ -20,7 +20,6 @@ from lemur.common.utils import generate_private_key, truthiness
from lemur.destinations.models import Destination from lemur.destinations.models import Destination
from lemur.domains.models import Domain from lemur.domains.models import Domain
from lemur.extensions import metrics, sentry, signals from lemur.extensions import metrics, sentry, signals
from lemur.models import certificate_associations
from lemur.notifications.models import Notification from lemur.notifications.models import Notification
from lemur.pending_certificates.models import PendingCertificate from lemur.pending_certificates.models import PendingCertificate
from lemur.plugins.base import plugins from lemur.plugins.base import plugins
@ -307,7 +306,7 @@ def render(args):
if filt: if filt:
terms = filt.split(';') terms = filt.split(';')
term = '%{0}%'.format(terms[1]) term = '{0}%'.format(terms[1])
# Exact matches for quotes. Only applies to name, issuer, and cn # Exact matches for quotes. Only applies to name, issuer, and cn
if terms[1].startswith('"') and terms[1].endswith('"'): if terms[1].startswith('"') and terms[1].endswith('"'):
term = terms[1][1:-1] term = terms[1][1:-1]
@ -341,13 +340,13 @@ def render(args):
elif 'id' in terms: elif 'id' in terms:
query = query.filter(Certificate.id == cast(terms[1], Integer)) query = query.filter(Certificate.id == cast(terms[1], Integer))
elif 'name' in terms: elif 'name' in terms:
query = query.outerjoin(certificate_associations).outerjoin(Domain).filter( query = query.filter(
or_( or_(
Certificate.name.ilike(term), Certificate.name.ilike(term),
Domain.name.ilike(term), Certificate.domains.any(Domain.name.ilike(term)),
Certificate.cn.ilike(term), Certificate.cn.ilike(term),
) )
).group_by(Certificate.id) )
else: else:
query = database.filter(query, Certificate, terms) query = database.filter(query, Certificate, terms)

View File

@ -12,6 +12,7 @@ import string
import sqlalchemy import sqlalchemy
from cryptography import x509 from cryptography import x509
from cryptography.hazmat.backends import default_backend 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.asymmetric import rsa, ec
from cryptography.hazmat.primitives.serialization import load_pem_private_key from cryptography.hazmat.primitives.serialization import load_pem_private_key
from flask_restful.reqparse import RequestParser from flask_restful.reqparse import RequestParser
@ -226,3 +227,13 @@ def truthiness(s):
"""If input string resembles something truthy then return True, else False.""" """If input string resembles something truthy then return True, else False."""
return s.lower() in ('true', 'yes', 'on', 't', '1') 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.body).fingerprint(hashes.SHA256()) == cert.fingerprint(hashes.SHA256()):
matching.append(c)
return matching

View File

@ -95,7 +95,7 @@ def get_all_elbs_v2(**kwargs):
@sts_client('elbv2') @sts_client('elbv2')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def get_listener_arn_from_endpoint(endpoint_name, endpoint_port, **kwargs): def get_listener_arn_from_endpoint(endpoint_name, endpoint_port, **kwargs):
""" """
Get a listener ARN from an endpoint. Get a listener ARN from an endpoint.
@ -113,7 +113,7 @@ def get_listener_arn_from_endpoint(endpoint_name, endpoint_port, **kwargs):
@sts_client('elb') @sts_client('elb')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def get_elbs(**kwargs): def get_elbs(**kwargs):
""" """
Fetches one page elb objects for a given account and region. Fetches one page elb objects for a given account and region.
@ -123,7 +123,7 @@ def get_elbs(**kwargs):
@sts_client('elbv2') @sts_client('elbv2')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def get_elbs_v2(**kwargs): def get_elbs_v2(**kwargs):
""" """
Fetches one page of elb objects for a given account and region. Fetches one page of elb objects for a given account and region.
@ -136,7 +136,7 @@ def get_elbs_v2(**kwargs):
@sts_client('elbv2') @sts_client('elbv2')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def describe_listeners_v2(**kwargs): def describe_listeners_v2(**kwargs):
""" """
Fetches one page of listener objects for a given elb arn. Fetches one page of listener objects for a given elb arn.
@ -149,7 +149,7 @@ def describe_listeners_v2(**kwargs):
@sts_client('elb') @sts_client('elb')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def describe_load_balancer_policies(load_balancer_name, policy_names, **kwargs): def describe_load_balancer_policies(load_balancer_name, policy_names, **kwargs):
""" """
Fetching all policies currently associated with an ELB. Fetching all policies currently associated with an ELB.
@ -161,7 +161,7 @@ def describe_load_balancer_policies(load_balancer_name, policy_names, **kwargs):
@sts_client('elbv2') @sts_client('elbv2')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def describe_ssl_policies_v2(policy_names, **kwargs): def describe_ssl_policies_v2(policy_names, **kwargs):
""" """
Fetching all policies currently associated with an ELB. Fetching all policies currently associated with an ELB.
@ -173,7 +173,7 @@ def describe_ssl_policies_v2(policy_names, **kwargs):
@sts_client('elb') @sts_client('elb')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def describe_load_balancer_types(policies, **kwargs): def describe_load_balancer_types(policies, **kwargs):
""" """
Describe the policies with policy details. Describe the policies with policy details.
@ -185,7 +185,7 @@ def describe_load_balancer_types(policies, **kwargs):
@sts_client('elb') @sts_client('elb')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def attach_certificate(name, port, certificate_id, **kwargs): def attach_certificate(name, port, certificate_id, **kwargs):
""" """
Attaches a certificate to a listener, throws exception Attaches a certificate to a listener, throws exception
@ -205,7 +205,7 @@ def attach_certificate(name, port, certificate_id, **kwargs):
@sts_client('elbv2') @sts_client('elbv2')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=1000) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def attach_certificate_v2(listener_arn, port, certificates, **kwargs): def attach_certificate_v2(listener_arn, port, certificates, **kwargs):
""" """
Attaches a certificate to a listener, throws exception Attaches a certificate to a listener, throws exception

View File

@ -52,7 +52,7 @@ def create_arn_from_cert(account_number, region, certificate_name):
@sts_client('iam') @sts_client('iam')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=100) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def upload_cert(name, body, private_key, path, cert_chain=None, **kwargs): def upload_cert(name, body, private_key, path, cert_chain=None, **kwargs):
""" """
Upload a certificate to AWS Upload a certificate to AWS
@ -95,7 +95,7 @@ def upload_cert(name, body, private_key, path, cert_chain=None, **kwargs):
@sts_client('iam') @sts_client('iam')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=100) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def delete_cert(cert_name, **kwargs): def delete_cert(cert_name, **kwargs):
""" """
Delete a certificate from AWS Delete a certificate from AWS
@ -112,7 +112,7 @@ def delete_cert(cert_name, **kwargs):
@sts_client('iam') @sts_client('iam')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=100) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def get_certificate(name, **kwargs): def get_certificate(name, **kwargs):
""" """
Retrieves an SSL certificate. Retrieves an SSL certificate.
@ -126,7 +126,7 @@ def get_certificate(name, **kwargs):
@sts_client('iam') @sts_client('iam')
@retry(retry_on_exception=retry_throttled, stop_max_attempt_number=7, wait_exponential_multiplier=100) @retry(retry_on_exception=retry_throttled, wait_fixed=2000)
def get_certificates(**kwargs): def get_certificates(**kwargs):
""" """
Fetches one page of certificate objects for a given account. Fetches one page of certificate objects for a given account.

View File

@ -9,14 +9,22 @@ from functools import wraps
import boto3 import boto3
from botocore.config import Config
from flask import current_app from flask import current_app
config = Config(
retries=dict(
max_attempts=20
)
)
def sts_client(service, service_type='client'): def sts_client(service, service_type='client'):
def decorator(f): def decorator(f):
@wraps(f) @wraps(f)
def decorated_function(*args, **kwargs): def decorated_function(*args, **kwargs):
sts = boto3.client('sts') sts = boto3.client('sts', config=config)
arn = 'arn:aws:iam::{0}:role/{1}'.format( arn = 'arn:aws:iam::{0}:role/{1}'.format(
kwargs.pop('account_number'), kwargs.pop('account_number'),
current_app.config.get('LEMUR_INSTANCE_PROFILE', 'Lemur') current_app.config.get('LEMUR_INSTANCE_PROFILE', 'Lemur')
@ -31,7 +39,8 @@ def sts_client(service, service_type='client'):
region_name=kwargs.pop('region', 'us-east-1'), region_name=kwargs.pop('region', 'us-east-1'),
aws_access_key_id=role['Credentials']['AccessKeyId'], aws_access_key_id=role['Credentials']['AccessKeyId'],
aws_secret_access_key=role['Credentials']['SecretAccessKey'], aws_secret_access_key=role['Credentials']['SecretAccessKey'],
aws_session_token=role['Credentials']['SessionToken'] aws_session_token=role['Credentials']['SessionToken'],
config=config
) )
kwargs['client'] = client kwargs['client'] = client
elif service_type == 'resource': elif service_type == 'resource':
@ -40,7 +49,8 @@ def sts_client(service, service_type='client'):
region_name=kwargs.pop('region', 'us-east-1'), region_name=kwargs.pop('region', 'us-east-1'),
aws_access_key_id=role['Credentials']['AccessKeyId'], aws_access_key_id=role['Credentials']['AccessKeyId'],
aws_secret_access_key=role['Credentials']['SecretAccessKey'], aws_secret_access_key=role['Credentials']['SecretAccessKey'],
aws_session_token=role['Credentials']['SessionToken'] aws_session_token=role['Credentials']['SessionToken'],
config=config
) )
kwargs['resource'] = resource kwargs['resource'] = resource
return f(*args, **kwargs) return f(*args, **kwargs)

View File

@ -17,7 +17,7 @@ from lemur.endpoints import service as endpoint_service
from lemur.destinations import service as destination_service from lemur.destinations import service as destination_service
from lemur.certificates.schemas import CertificateUploadInputSchema 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.common.defaults import serial
from lemur.plugins.base import plugins from lemur.plugins.base import plugins
@ -126,7 +126,8 @@ def sync_certificates(source, user):
if not exists: if not exists:
cert = parse_certificate(certificate['body']) 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'): if not certificate.get('owner'):
certificate['owner'] = user.email certificate['owner'] = user.email

View File

@ -5,8 +5,7 @@
# pip-compile --no-index --output-file requirements-dev.txt requirements-dev.in # pip-compile --no-index --output-file requirements-dev.txt requirements-dev.in
# #
aspy.yaml==1.1.1 # via pre-commit aspy.yaml==1.1.1 # via pre-commit
bleach==3.0.2 # via readme-renderer bleach==3.1.0 # via readme-renderer
cached-property==1.5.1 # via pre-commit
certifi==2018.11.29 # via requests certifi==2018.11.29 # via requests
cfgv==1.4.0 # via pre-commit cfgv==1.4.0 # via pre-commit
chardet==3.0.4 # via requests chardet==3.0.4 # via requests
@ -19,8 +18,8 @@ importlib-resources==1.0.2 # via pre-commit
invoke==1.2.0 invoke==1.2.0
mccabe==0.6.1 # via flake8 mccabe==0.6.1 # via flake8
nodeenv==1.3.3 nodeenv==1.3.3
pkginfo==1.5.0 # via twine pkginfo==1.5.0.1 # via twine
pre-commit==1.13.0 pre-commit==1.14.0
pycodestyle==2.3.1 # via flake8 pycodestyle==2.3.1 # via flake8
pyflakes==1.6.0 # via flake8 pyflakes==1.6.0 # via flake8
pygments==2.3.1 # via readme-renderer pygments==2.3.1 # via readme-renderer

View File

@ -9,7 +9,7 @@ alabaster==0.7.12 # via sphinx
alembic-autogenerate-enums==0.0.2 alembic-autogenerate-enums==0.0.2
alembic==1.0.5 alembic==1.0.5
amqp==2.3.2 amqp==2.3.2
aniso8601==4.0.1 aniso8601==4.1.0
arrow==0.13.0 arrow==0.13.0
asn1crypto==0.24.0 asn1crypto==0.24.0
asyncpool==1.0 asyncpool==1.0
@ -17,8 +17,8 @@ babel==2.6.0 # via sphinx
bcrypt==3.1.5 bcrypt==3.1.5
billiard==3.5.0.5 billiard==3.5.0.5
blinker==1.4 blinker==1.4
boto3==1.9.75 boto3==1.9.76
botocore==1.12.75 botocore==1.12.76
celery[redis]==4.2.1 celery[redis]==4.2.1
certifi==2018.11.29 certifi==2018.11.29
cffi==1.11.5 cffi==1.11.5

View File

@ -8,9 +8,9 @@ asn1crypto==0.24.0 # via cryptography
atomicwrites==1.2.1 # via pytest atomicwrites==1.2.1 # via pytest
attrs==18.2.0 # via pytest attrs==18.2.0 # via pytest
aws-xray-sdk==0.95 # via moto aws-xray-sdk==0.95 # via moto
boto3==1.9.75 # via moto boto3==1.9.76 # via moto
boto==2.49.0 # via moto boto==2.49.0 # via moto
botocore==1.12.75 # via boto3, moto, s3transfer botocore==1.12.76 # via boto3, moto, s3transfer
certifi==2018.11.29 # via requests certifi==2018.11.29 # via requests
cffi==1.11.5 # via cryptography cffi==1.11.5 # via cryptography
chardet==3.0.4 # via requests chardet==3.0.4 # via requests
@ -38,7 +38,7 @@ more-itertools==5.0.0 # via pytest
moto==1.3.7 moto==1.3.7
nose==1.3.7 nose==1.3.7
pbr==5.1.1 # via mock pbr==5.1.1 # via mock
pluggy==0.8.0 # via pytest pluggy==0.8.1 # via pytest
py==1.7.0 # via pytest py==1.7.0 # via pytest
pyaml==18.11.0 # via moto pyaml==18.11.0 # via moto
pycparser==2.19 # via cffi pycparser==2.19 # via cffi
@ -60,5 +60,5 @@ text-unidecode==1.2 # via faker
urllib3==1.24.1 # via botocore, requests urllib3==1.24.1 # via botocore, requests
websocket-client==0.54.0 # via docker websocket-client==0.54.0 # via docker
werkzeug==0.14.1 # via flask, moto, pytest-flask werkzeug==0.14.1 # via flask, moto, pytest-flask
wrapt==1.10.11 # via aws-xray-sdk wrapt==1.11.0 # via aws-xray-sdk
xmltodict==0.11.0 # via moto xmltodict==0.11.0 # via moto

View File

@ -8,15 +8,15 @@ acme==0.30.0
alembic-autogenerate-enums==0.0.2 alembic-autogenerate-enums==0.0.2
alembic==1.0.5 # via flask-migrate alembic==1.0.5 # via flask-migrate
amqp==2.3.2 # via kombu amqp==2.3.2 # via kombu
aniso8601==4.0.1 # via flask-restful aniso8601==4.1.0 # via flask-restful
arrow==0.13.0 arrow==0.13.0
asn1crypto==0.24.0 # via cryptography asn1crypto==0.24.0 # via cryptography
asyncpool==1.0 asyncpool==1.0
bcrypt==3.1.5 # via flask-bcrypt, paramiko bcrypt==3.1.5 # via flask-bcrypt, paramiko
billiard==3.5.0.5 # via celery billiard==3.5.0.5 # via celery
blinker==1.4 # via flask-mail, flask-principal, raven blinker==1.4 # via flask-mail, flask-principal, raven
boto3==1.9.75 boto3==1.9.76
botocore==1.12.75 botocore==1.12.76
celery[redis]==4.2.1 celery[redis]==4.2.1
certifi==2018.11.29 certifi==2018.11.29
cffi==1.11.5 # via bcrypt, cryptography, pynacl cffi==1.11.5 # via bcrypt, cryptography, pynacl