[WIP] - 422 elb rotate (#493)
* Initial work on certificate rotation. * Adding ability to get additional certificate info. * - Adding endpoint rotation. - Removes the g requirement from all services to enable easier testing.
This commit is contained in:
parent
6fd47edbe3
commit
d45e7d6b85
|
@ -8,8 +8,6 @@
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from flask import g
|
|
||||||
|
|
||||||
from lemur import database
|
from lemur import database
|
||||||
from lemur.extensions import metrics
|
from lemur.extensions import metrics
|
||||||
from lemur.authorities.models import Authority
|
from lemur.authorities.models import Authority
|
||||||
|
@ -53,13 +51,14 @@ def mint(**kwargs):
|
||||||
elif len(values) == 4:
|
elif len(values) == 4:
|
||||||
body, private_key, chain, roles = values
|
body, private_key, chain, roles = values
|
||||||
|
|
||||||
roles = create_authority_roles(roles, kwargs['owner'], kwargs['plugin']['plugin_object'].title)
|
roles = create_authority_roles(roles, kwargs['owner'], kwargs['plugin']['plugin_object'].title, None)
|
||||||
return body, private_key, chain, roles
|
return body, private_key, chain, roles
|
||||||
|
|
||||||
|
|
||||||
def create_authority_roles(roles, owner, plugin_title):
|
def create_authority_roles(roles, owner, plugin_title, creator):
|
||||||
"""
|
"""
|
||||||
Creates all of the necessary authority roles.
|
Creates all of the necessary authority roles.
|
||||||
|
:param creator:
|
||||||
:param roles:
|
:param roles:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
@ -75,7 +74,7 @@ def create_authority_roles(roles, owner, plugin_title):
|
||||||
|
|
||||||
# the user creating the authority should be able to administer it
|
# the user creating the authority should be able to administer it
|
||||||
if role.username == 'admin':
|
if role.username == 'admin':
|
||||||
g.current_user.roles.append(role)
|
creator.roles.append(role)
|
||||||
|
|
||||||
role_objs.append(role)
|
role_objs.append(role)
|
||||||
|
|
||||||
|
@ -95,10 +94,9 @@ def create(**kwargs):
|
||||||
"""
|
"""
|
||||||
Creates a new authority.
|
Creates a new authority.
|
||||||
"""
|
"""
|
||||||
kwargs['creator'] = g.user.email
|
|
||||||
body, private_key, chain, roles = mint(**kwargs)
|
body, private_key, chain, roles = mint(**kwargs)
|
||||||
|
|
||||||
g.user.roles = list(set(list(g.user.roles) + roles))
|
kwargs['creator'].roles = list(set(list(kwargs['creator'].roles) + roles))
|
||||||
|
|
||||||
kwargs['body'] = body
|
kwargs['body'] = body
|
||||||
kwargs['private_key'] = private_key
|
kwargs['private_key'] = private_key
|
||||||
|
@ -114,7 +112,7 @@ def create(**kwargs):
|
||||||
|
|
||||||
authority = Authority(**kwargs)
|
authority = Authority(**kwargs)
|
||||||
authority = database.create(authority)
|
authority = database.create(authority)
|
||||||
g.user.authorities.append(authority)
|
kwargs['creator'].authorities.append(authority)
|
||||||
|
|
||||||
metrics.send('authority_created', 'counter', 1, metric_tags=dict(owner=authority.owner))
|
metrics.send('authority_created', 'counter', 1, metric_tags=dict(owner=authority.owner))
|
||||||
return authority
|
return authority
|
||||||
|
@ -151,14 +149,14 @@ def get_by_name(authority_name):
|
||||||
return database.get(Authority, authority_name, field='name')
|
return database.get(Authority, authority_name, field='name')
|
||||||
|
|
||||||
|
|
||||||
def get_authority_role(ca_name):
|
def get_authority_role(ca_name, creator):
|
||||||
"""
|
"""
|
||||||
Attempts to get the authority role for a given ca uses current_user
|
Attempts to get the authority role for a given ca uses current_user
|
||||||
as a basis for accomplishing that.
|
as a basis for accomplishing that.
|
||||||
|
|
||||||
:param ca_name:
|
:param ca_name:
|
||||||
"""
|
"""
|
||||||
if g.current_user.is_admin:
|
if creator.is_admin:
|
||||||
return role_service.get_by_name("{0}_admin".format(ca_name))
|
return role_service.get_by_name("{0}_admin".format(ca_name))
|
||||||
else:
|
else:
|
||||||
return role_service.get_by_name("{0}_operator".format(ca_name))
|
return role_service.get_by_name("{0}_operator".format(ca_name))
|
||||||
|
@ -181,12 +179,12 @@ def render(args):
|
||||||
query = database.filter(query, Authority, terms)
|
query = database.filter(query, Authority, terms)
|
||||||
|
|
||||||
# we make sure that a user can only use an authority they either own are are a member of - admins can see all
|
# we make sure that a user can only use an authority they either own are are a member of - admins can see all
|
||||||
if not g.current_user.is_admin:
|
if not args['user'].is_admin:
|
||||||
authority_ids = []
|
authority_ids = []
|
||||||
for authority in g.current_user.authorities:
|
for authority in args['user'].authorities:
|
||||||
authority_ids.append(authority.id)
|
authority_ids.append(authority.id)
|
||||||
|
|
||||||
for role in g.current_user.roles:
|
for role in args['user'].roles:
|
||||||
for authority in role.authorities:
|
for authority in role.authorities:
|
||||||
authority_ids.append(authority.id)
|
authority_ids.append(authority.id)
|
||||||
query = query.filter(Authority.id.in_(authority_ids))
|
query = query.filter(Authority.id.in_(authority_ids))
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
from flask import Blueprint
|
from flask import Blueprint, g
|
||||||
from flask.ext.restful import reqparse, Api
|
from flask.ext.restful import reqparse, Api
|
||||||
|
|
||||||
from lemur.common.utils import paginated_parser
|
from lemur.common.utils import paginated_parser
|
||||||
|
@ -107,6 +107,7 @@ class AuthoritiesList(AuthenticatedResource):
|
||||||
"""
|
"""
|
||||||
parser = paginated_parser.copy()
|
parser = paginated_parser.copy()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
args['user'] = g.current_user
|
||||||
return service.render(args)
|
return service.render(args)
|
||||||
|
|
||||||
@validate_schema(authority_input_schema, authority_output_schema)
|
@validate_schema(authority_input_schema, authority_output_schema)
|
||||||
|
|
|
@ -5,16 +5,16 @@
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
import datetime
|
import arrow
|
||||||
|
|
||||||
import lemur.common.utils
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy.sql.expression import case
|
from sqlalchemy.sql.expression import case
|
||||||
from sqlalchemy.ext.hybrid import hybrid_property
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
from sqlalchemy import event, Integer, ForeignKey, String, DateTime, PassiveDefault, func, Column, Text, Boolean
|
from sqlalchemy import event, Integer, ForeignKey, String, PassiveDefault, func, Column, Text, Boolean
|
||||||
|
|
||||||
|
import lemur.common.utils
|
||||||
from lemur.database import db
|
from lemur.database import db
|
||||||
from lemur.models import certificate_associations, certificate_source_associations, \
|
from lemur.models import certificate_associations, certificate_source_associations, \
|
||||||
certificate_destination_associations, certificate_notification_associations, \
|
certificate_destination_associations, certificate_notification_associations, \
|
||||||
|
@ -22,6 +22,8 @@ from lemur.models import certificate_associations, certificate_source_associatio
|
||||||
from lemur.plugins.base import plugins
|
from lemur.plugins.base import plugins
|
||||||
from lemur.utils import Vault
|
from lemur.utils import Vault
|
||||||
|
|
||||||
|
from sqlalchemy_utils.types.arrow import ArrowType
|
||||||
|
|
||||||
from lemur.common import defaults
|
from lemur.common import defaults
|
||||||
from lemur.domains.models import Domain
|
from lemur.domains.models import Domain
|
||||||
|
|
||||||
|
@ -53,9 +55,9 @@ class Certificate(db.Model):
|
||||||
cn = Column(String(128))
|
cn = Column(String(128))
|
||||||
deleted = Column(Boolean, index=True)
|
deleted = Column(Boolean, index=True)
|
||||||
|
|
||||||
not_before = Column(DateTime)
|
not_before = Column(ArrowType)
|
||||||
not_after = Column(DateTime)
|
not_after = Column(ArrowType)
|
||||||
date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
|
date_created = Column(ArrowType, PassiveDefault(func.now()), nullable=False)
|
||||||
|
|
||||||
signing_algorithm = Column(String(128))
|
signing_algorithm = Column(String(128))
|
||||||
status = Column(String(128))
|
status = Column(String(128))
|
||||||
|
@ -120,16 +122,41 @@ class Certificate(db.Model):
|
||||||
def active(self):
|
def active(self):
|
||||||
return self.notify
|
return self.notify
|
||||||
|
|
||||||
|
@property
|
||||||
|
def organization(self):
|
||||||
|
cert = lemur.common.utils.parse_certificate(self.body)
|
||||||
|
return defaults.organization(cert)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def organizational_unit(self):
|
||||||
|
cert = lemur.common.utils.parse_certificate(self.body)
|
||||||
|
return defaults.organizational_unit(cert)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def country(self):
|
||||||
|
cert = lemur.common.utils.parse_certificate(self.body)
|
||||||
|
return defaults.country(cert)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
cert = lemur.common.utils.parse_certificate(self.body)
|
||||||
|
return defaults.state(cert)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def location(self):
|
||||||
|
cert = lemur.common.utils.parse_certificate(self.body)
|
||||||
|
return defaults.location(cert)
|
||||||
|
|
||||||
@hybrid_property
|
@hybrid_property
|
||||||
def expired(self):
|
def expired(self):
|
||||||
if self.not_after <= datetime.datetime.now():
|
if self.not_after <= arrow.utcnow():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@expired.expression
|
@expired.expression
|
||||||
def expired(cls):
|
def expired(cls):
|
||||||
return case(
|
return case(
|
||||||
[
|
[
|
||||||
(cls.now_after <= datetime.datetime.now(), True)
|
(cls.now_after <= arrow.utcnow(), True)
|
||||||
],
|
],
|
||||||
else_=False
|
else_=False
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import arrow
|
import arrow
|
||||||
|
|
||||||
from sqlalchemy import func, or_
|
from sqlalchemy import func, or_
|
||||||
from flask import g, current_app
|
from flask import current_app
|
||||||
|
|
||||||
from lemur import database
|
from lemur import database
|
||||||
from lemur.extensions import metrics
|
from lemur.extensions import metrics
|
||||||
|
@ -201,11 +201,7 @@ def upload(**kwargs):
|
||||||
|
|
||||||
cert = database.create(cert)
|
cert = database.create(cert)
|
||||||
|
|
||||||
try:
|
kwargs['creator'].certificates.append(cert)
|
||||||
g.user.certificates.append(cert)
|
|
||||||
except AttributeError:
|
|
||||||
current_app.logger.debug("No user to associate uploaded certificate to.")
|
|
||||||
|
|
||||||
return database.update(cert)
|
return database.update(cert)
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,7 +209,6 @@ def create(**kwargs):
|
||||||
"""
|
"""
|
||||||
Creates a new certificate.
|
Creates a new certificate.
|
||||||
"""
|
"""
|
||||||
kwargs['creator'] = g.user.email
|
|
||||||
cert_body, private_key, cert_chain = mint(**kwargs)
|
cert_body, private_key, cert_chain = mint(**kwargs)
|
||||||
kwargs['body'] = cert_body
|
kwargs['body'] = cert_body
|
||||||
kwargs['private_key'] = private_key
|
kwargs['private_key'] = private_key
|
||||||
|
@ -228,7 +223,7 @@ def create(**kwargs):
|
||||||
|
|
||||||
cert = Certificate(**kwargs)
|
cert = Certificate(**kwargs)
|
||||||
|
|
||||||
g.user.certificates.append(cert)
|
kwargs['creator'].certificates.append(cert)
|
||||||
cert.authority = kwargs['authority']
|
cert.authority = kwargs['authority']
|
||||||
database.commit()
|
database.commit()
|
||||||
|
|
||||||
|
@ -286,10 +281,10 @@ def render(args):
|
||||||
query = database.filter(query, Certificate, terms)
|
query = database.filter(query, Certificate, terms)
|
||||||
|
|
||||||
if show:
|
if show:
|
||||||
sub_query = database.session_query(Role.name).filter(Role.user_id == g.user.id).subquery()
|
sub_query = database.session_query(Role.name).filter(Role.user_id == args['user'].id).subquery()
|
||||||
query = query.filter(
|
query = query.filter(
|
||||||
or_(
|
or_(
|
||||||
Certificate.user_id == g.user.id,
|
Certificate.user_id == args['user'].id,
|
||||||
Certificate.owner.in_(sub_query)
|
Certificate.owner.in_(sub_query)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -476,10 +471,9 @@ def calculate_reissue_range(start, end):
|
||||||
new_start = arrow.utcnow().date()
|
new_start = arrow.utcnow().date()
|
||||||
new_end = new_start + span
|
new_end = new_start + span
|
||||||
|
|
||||||
return new_start, new_end
|
return new_start, arrow.get(new_end)
|
||||||
|
|
||||||
|
|
||||||
# TODO pull the OU, O, CN, etc + other extensions.
|
|
||||||
def get_certificate_primitives(certificate):
|
def get_certificate_primitives(certificate):
|
||||||
"""
|
"""
|
||||||
Retrieve key primitive from a certificate such that the certificate
|
Retrieve key primitive from a certificate such that the certificate
|
||||||
|
@ -491,6 +485,7 @@ def get_certificate_primitives(certificate):
|
||||||
start, end = calculate_reissue_range(certificate.not_before, certificate.not_after)
|
start, end = calculate_reissue_range(certificate.not_before, certificate.not_after)
|
||||||
names = [{'name_type': 'DNSName', 'value': x.name} for x in certificate.domains]
|
names = [{'name_type': 'DNSName', 'value': x.name} for x in certificate.domains]
|
||||||
|
|
||||||
|
# TODO pull additional extensions
|
||||||
extensions = {
|
extensions = {
|
||||||
'sub_alt_names': {
|
'sub_alt_names': {
|
||||||
'names': names
|
'names': names
|
||||||
|
@ -506,5 +501,31 @@ def get_certificate_primitives(certificate):
|
||||||
destinations=certificate.destinations,
|
destinations=certificate.destinations,
|
||||||
roles=certificate.roles,
|
roles=certificate.roles,
|
||||||
extensions=extensions,
|
extensions=extensions,
|
||||||
owner=certificate.owner
|
owner=certificate.owner,
|
||||||
|
organization=certificate.organization,
|
||||||
|
organizational_unit=certificate.organizational_unit,
|
||||||
|
country=certificate.country,
|
||||||
|
state=certificate.state,
|
||||||
|
location=certificate.location
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def reissue_certificate(certificate, replace=None, user=None):
|
||||||
|
"""
|
||||||
|
Reissue certificate with the same properties of the given certificate.
|
||||||
|
:param certificate:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
primitives = get_certificate_primitives(certificate)
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
primitives['creator'] = certificate.user
|
||||||
|
else:
|
||||||
|
primitives['creator'] = user
|
||||||
|
|
||||||
|
new_cert = create(**primitives)
|
||||||
|
|
||||||
|
if replace:
|
||||||
|
certificate.notify = False
|
||||||
|
|
||||||
|
return new_cert
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import base64
|
import base64
|
||||||
from builtins import str
|
from builtins import str
|
||||||
|
|
||||||
from flask import Blueprint, make_response, jsonify
|
from flask import Blueprint, make_response, jsonify, g
|
||||||
from flask.ext.restful import reqparse, Api
|
from flask.ext.restful import reqparse, Api
|
||||||
|
|
||||||
from lemur.common.schema import validate_schema
|
from lemur.common.schema import validate_schema
|
||||||
|
@ -129,6 +129,7 @@ class CertificatesList(AuthenticatedResource):
|
||||||
parser.add_argument('show', type=str, location='args')
|
parser.add_argument('show', type=str, location='args')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
args['user'] = g.user
|
||||||
return service.render(args)
|
return service.render(args)
|
||||||
|
|
||||||
@validate_schema(certificate_input_schema, certificate_output_schema)
|
@validate_schema(certificate_input_schema, certificate_output_schema)
|
||||||
|
@ -265,6 +266,7 @@ class CertificatesList(AuthenticatedResource):
|
||||||
authority_permission = AuthorityPermission(data['authority'].id, roles)
|
authority_permission = AuthorityPermission(data['authority'].id, roles)
|
||||||
|
|
||||||
if authority_permission.can():
|
if authority_permission.can():
|
||||||
|
data['creator'] = g.user
|
||||||
return service.create(**data)
|
return service.create(**data)
|
||||||
|
|
||||||
return dict(message="You are not authorized to use {0}".format(data['authority'].name)), 403
|
return dict(message="You are not authorized to use {0}".format(data['authority'].name)), 403
|
||||||
|
@ -371,6 +373,7 @@ class CertificatesUpload(AuthenticatedResource):
|
||||||
"""
|
"""
|
||||||
if data.get('destinations'):
|
if data.get('destinations'):
|
||||||
if data.get('private_key'):
|
if data.get('private_key'):
|
||||||
|
data['creator'] = g.user
|
||||||
return service.upload(**data)
|
return service.upload(**data)
|
||||||
else:
|
else:
|
||||||
raise Exception("Private key must be provided in order to upload certificate to AWS")
|
raise Exception("Private key must be provided in order to upload certificate to AWS")
|
||||||
|
@ -740,6 +743,7 @@ class NotificationCertificatesList(AuthenticatedResource):
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args['notification_id'] = notification_id
|
args['notification_id'] = notification_id
|
||||||
|
args['user'] = g.current_user
|
||||||
return service.render(args)
|
return service.render(args)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,61 @@ def common_name(cert):
|
||||||
)[0].value.strip()
|
)[0].value.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def organization(cert):
|
||||||
|
"""
|
||||||
|
Attempt to get the organization name from a given certificate.
|
||||||
|
:param cert:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return cert.subject.get_attributes_for_oid(
|
||||||
|
x509.OID_ORGANIZATION_NAME
|
||||||
|
)[0].value.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def organizational_unit(cert):
|
||||||
|
"""
|
||||||
|
Attempt to get the organization unit from a given certificate.
|
||||||
|
:param cert:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return cert.subject.get_attributes_for_oid(
|
||||||
|
x509.OID_ORGANIZATIONAL_UNIT_NAME
|
||||||
|
)[0].value.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def country(cert):
|
||||||
|
"""
|
||||||
|
Attempt to get the country from a given certificate.
|
||||||
|
:param cert:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return cert.subject.get_attributes_for_oid(
|
||||||
|
x509.OID_COUNTRY_NAME
|
||||||
|
)[0].value.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def state(cert):
|
||||||
|
"""
|
||||||
|
Attempt to get the from a given certificate.
|
||||||
|
:param cert:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return cert.subject.get_attributes_for_oid(
|
||||||
|
x509.OID_STATE_OR_PROVINCE_NAME
|
||||||
|
)[0].value.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def location(cert):
|
||||||
|
"""
|
||||||
|
Attempt to get the location name from a given certificate.
|
||||||
|
:param cert:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return cert.subject.get_attributes_for_oid(
|
||||||
|
x509.OID_LOCALITY_NAME
|
||||||
|
)[0].value.strip()
|
||||||
|
|
||||||
|
|
||||||
def domains(cert):
|
def domains(cert):
|
||||||
"""
|
"""
|
||||||
Attempts to get an domains listed in a certificate.
|
Attempts to get an domains listed in a certificate.
|
||||||
|
|
|
@ -274,6 +274,9 @@ def sort_and_page(query, model, args):
|
||||||
page = args.pop('page')
|
page = args.pop('page')
|
||||||
count = args.pop('count')
|
count = args.pop('count')
|
||||||
|
|
||||||
|
if args.get('user'):
|
||||||
|
user = args.pop('user')
|
||||||
|
|
||||||
query = find_all(query, model, args)
|
query = find_all(query, model, args)
|
||||||
|
|
||||||
if sort_by and sort_dir:
|
if sort_by and sort_dir:
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
from lemur import database
|
from lemur import database
|
||||||
from lemur.extensions import metrics
|
from lemur.extensions import metrics
|
||||||
from lemur.endpoints.models import Endpoint, Policy, Cipher
|
from lemur.endpoints.models import Endpoint, Policy, Cipher
|
||||||
|
@ -96,6 +98,17 @@ def update(endpoint_id, **kwargs):
|
||||||
return endpoint
|
return endpoint
|
||||||
|
|
||||||
|
|
||||||
|
def rotate_certificate(endpoint, new_cert):
|
||||||
|
"""Rotates a certificate on a given endpoint."""
|
||||||
|
try:
|
||||||
|
endpoint.source.plugin.update_endpoint(endpoint, new_cert)
|
||||||
|
endpoint.certificate = new_cert
|
||||||
|
except Exception as e:
|
||||||
|
metrics.send('rotate_failure', 'counter', 1, tags={'endpoint': endpoint.name})
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def render(args):
|
def render(args):
|
||||||
"""
|
"""
|
||||||
Helper that helps us render the REST Api responses.
|
Helper that helps us render the REST Api responses.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
from flask import Blueprint
|
from flask import Blueprint, g
|
||||||
from flask.ext.restful import reqparse, Api
|
from flask.ext.restful import reqparse, Api
|
||||||
|
|
||||||
from lemur.common.utils import paginated_parser
|
from lemur.common.utils import paginated_parser
|
||||||
|
@ -63,6 +63,7 @@ class EndpointsList(AuthenticatedResource):
|
||||||
"""
|
"""
|
||||||
parser = paginated_parser.copy()
|
parser = paginated_parser.copy()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
args['user'] = g.current_user
|
||||||
return service.render(args)
|
return service.render(args)
|
||||||
|
|
||||||
|
|
||||||
|
|
346
lemur/manage.py
346
lemur/manage.py
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import unicode_literals # at top of module
|
from __future__ import unicode_literals # at top of module
|
||||||
|
|
||||||
|
import arrow
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
|
@ -28,11 +29,7 @@ from lemur.certificates import service as cert_service
|
||||||
from lemur.authorities import service as authority_service
|
from lemur.authorities import service as authority_service
|
||||||
from lemur.notifications import service as notification_service
|
from lemur.notifications import service as notification_service
|
||||||
|
|
||||||
from lemur.certificates.service import get_name_from_arn
|
|
||||||
from lemur.certificates.verify import verify_string
|
from lemur.certificates.verify import verify_string
|
||||||
|
|
||||||
from lemur.plugins.lemur_aws import elb
|
|
||||||
|
|
||||||
from lemur.sources import service as source_service
|
from lemur.sources import service as source_service
|
||||||
|
|
||||||
from lemur import create_app
|
from lemur import create_app
|
||||||
|
@ -487,253 +484,122 @@ def unicode_(data):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class RotateELBs(Command):
|
def print_certificate_details(details):
|
||||||
"""
|
"""
|
||||||
Rotates existing certificates to a new one on an ELB
|
Print the certificate details with formatting.
|
||||||
|
:param details:
|
||||||
|
:return:
|
||||||
"""
|
"""
|
||||||
option_list = (
|
sys.stdout.write("[+] Re-issuing certificate with the following details: \n")
|
||||||
Option('-e', '--elb-list', dest='elb_list', required=True),
|
sys.stdout.write(
|
||||||
Option('-p', '--chain-path', dest='chain_path'),
|
"[+] Common Name: {common_name}\n"
|
||||||
Option('-c', '--cert-name', dest='cert_name'),
|
"[+] Subject Alternate Names: {sans}\n"
|
||||||
Option('-a', '--cert-prefix', dest='cert_prefix'),
|
"[+] Authority: {authority_name}\n"
|
||||||
Option('-d', '--description', dest='description')
|
"[+] Validity Start: {validity_start}\n"
|
||||||
|
"[+] Validity End: {validity_end}\n"
|
||||||
|
"[+] Organization: {organization}\n"
|
||||||
|
"[+] Organizational Unit: {organizational_unit}\n"
|
||||||
|
"[+] Country: {country}\n"
|
||||||
|
"[+] State: {state}\n"
|
||||||
|
"[+] Location: {location}\n".format(
|
||||||
|
common_name=details['common_name'],
|
||||||
|
sans=",".join(x['value'] for x in details['extensions']['sub_alt_names']['names']),
|
||||||
|
authority_name=details['authority'].name,
|
||||||
|
validity_start=details['validity_start'].isoformat(),
|
||||||
|
validity_end=details['validity_end'].isoformat(),
|
||||||
|
organization=details['organization'],
|
||||||
|
organizational_unit=details['organizational_unit'],
|
||||||
|
country=details['country'],
|
||||||
|
state=details['state'],
|
||||||
|
location=details['location']
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self, elb_list, chain_path, cert_name, cert_prefix, description):
|
|
||||||
|
|
||||||
for e in open(elb_list, 'r').readlines():
|
class RotateCertificate(Command):
|
||||||
elb_name, account_id, region, from_port, to_port, protocol = e.strip().split(',')
|
|
||||||
|
|
||||||
if cert_name:
|
|
||||||
arn = "arn:aws:iam::{0}:server-certificate/{1}".format(account_id, cert_name)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# if no cert name is provided we need to discover it
|
|
||||||
listeners = elb.get_listeners(account_id, region, elb_name)
|
|
||||||
|
|
||||||
# get the listener we care about
|
|
||||||
for listener in listeners:
|
|
||||||
if listener[0] == int(from_port) and listener[1] == int(to_port):
|
|
||||||
arn = listener[4]
|
|
||||||
name = get_name_from_arn(arn)
|
|
||||||
certificate = cert_service.get_by_name(name)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
sys.stdout.write("[-] Could not find ELB {0}".format(elb_name))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not certificate:
|
|
||||||
sys.stdout.write("[-] Could not find certificate {0} in Lemur".format(name))
|
|
||||||
continue
|
|
||||||
|
|
||||||
dests = []
|
|
||||||
for d in certificate.destinations:
|
|
||||||
dests.append({'id': d.id})
|
|
||||||
|
|
||||||
nots = []
|
|
||||||
for n in certificate.notifications:
|
|
||||||
nots.append({'id': n.id})
|
|
||||||
|
|
||||||
new_certificate = database.clone(certificate)
|
|
||||||
|
|
||||||
if cert_prefix:
|
|
||||||
new_certificate.name = "{0}-{1}".format(cert_prefix, new_certificate.name)
|
|
||||||
|
|
||||||
new_certificate.chain = open(chain_path, 'r').read()
|
|
||||||
new_certificate.description = "{0} - {1}".format(new_certificate.description, description)
|
|
||||||
|
|
||||||
new_certificate = database.create(new_certificate)
|
|
||||||
database.update_list(new_certificate, 'destinations', Destination, dests)
|
|
||||||
database.update_list(new_certificate, 'notifications', Notification, nots)
|
|
||||||
database.update(new_certificate)
|
|
||||||
|
|
||||||
arn = new_certificate.get_arn(account_id)
|
|
||||||
|
|
||||||
elb.update_listeners(account_id, region, elb_name, [(from_port, to_port, protocol, arn)], [from_port])
|
|
||||||
|
|
||||||
sys.stdout.write("[+] Updated {0} to use {1}\n".format(elb_name, new_certificate.name))
|
|
||||||
|
|
||||||
|
|
||||||
class ProvisionELB(Command):
|
|
||||||
"""
|
"""
|
||||||
Creates and provisions a certificate on an ELB based on command line arguments
|
Rotates certificate on all endpoints managed by Lemur.
|
||||||
"""
|
"""
|
||||||
option_list = (
|
option_list = (
|
||||||
Option('-d', '--dns', dest='dns', action='append', required=True, type=unicode_),
|
Option('-n', '--cert-name', dest='new_cert_name'),
|
||||||
Option('-e', '--elb', dest='elb_name', required=True, type=unicode_),
|
Option('-o', '--old-cert-name', dest='old_cert_name', required=True),
|
||||||
Option('-o', '--owner', dest='owner', type=unicode_),
|
Option('-c', '--commit', dest='commit', action='store_true', default=False)
|
||||||
Option('-a', '--authority', dest='authority', required=True, type=unicode_),
|
|
||||||
Option('-s', '--description', dest='description', default=u'Command line provisioned keypair', type=unicode_),
|
|
||||||
Option('-t', '--destination', dest='destinations', action='append', type=unicode_, required=True),
|
|
||||||
Option('-n', '--notification', dest='notifications', action='append', type=unicode_, default=[]),
|
|
||||||
Option('-r', '--region', dest='region', default=u'us-east-1', type=unicode_),
|
|
||||||
Option('-p', '--dport', '--port', dest='dport', default=7002),
|
|
||||||
Option('--src-port', '--source-port', '--sport', dest='sport', default=443),
|
|
||||||
Option('--dry-run', dest='dryrun', action='store_true')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def configure_user(self, owner):
|
def run(self, new_cert_name, old_cert_name, commit):
|
||||||
from flask import g
|
from lemur.certificates.service import get_by_name, reissue_certificate, get_certificate_primitives
|
||||||
import lemur.users.service
|
from lemur.endpoints.service import rotate_certificate
|
||||||
|
|
||||||
# grab the user
|
old_cert = get_by_name(old_cert_name)
|
||||||
g.user = lemur.users.service.get_by_username(owner)
|
|
||||||
# get the first user by default
|
|
||||||
if not g.user:
|
|
||||||
g.user = lemur.users.service.get_all()[0]
|
|
||||||
|
|
||||||
return g.user.username
|
if not old_cert:
|
||||||
|
sys.stdout.write("[-] No certificate found with name: {0}\n".format(old_cert_name))
|
||||||
def build_cert_options(self, destinations, notifications, description, owner, dns, authority):
|
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
|
||||||
from lemur.certificates.views import valid_authority
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# convert argument lists to arrays, or empty sets
|
|
||||||
destinations = self.get_destinations(destinations)
|
|
||||||
if not destinations:
|
|
||||||
sys.stderr.write("Valid destinations provided\n")
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# get the primary CN
|
if new_cert_name:
|
||||||
common_name = dns[0]
|
new_cert = get_by_name(new_cert_name)
|
||||||
|
|
||||||
# If there are more than one fqdn, add them as alternate names
|
if not new_cert:
|
||||||
extensions = {}
|
sys.stdout.write("[-] No certificate found with name: {0}\n".format(old_cert_name))
|
||||||
if len(dns) > 1:
|
|
||||||
extensions['subAltNames'] = {'names': map(lambda x: {'nameType': 'DNSName', 'value': x}, dns)}
|
|
||||||
|
|
||||||
try:
|
|
||||||
authority = valid_authority({"name": authority})
|
|
||||||
except NoResultFound:
|
|
||||||
sys.stderr.write("Invalid authority specified: '{}'\naborting\n".format(authority))
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
options = {
|
if commit:
|
||||||
# Convert from the Destination model to the JSON input expected further in the code
|
sys.stdout.write("[!] Running in COMMIT mode.\n")
|
||||||
'destinations': map(lambda x: {'id': x.id, 'label': x.label}, destinations),
|
|
||||||
'description': description,
|
|
||||||
'notifications': notifications,
|
|
||||||
'commonName': common_name,
|
|
||||||
'extensions': extensions,
|
|
||||||
'authority': authority,
|
|
||||||
'owner': owner,
|
|
||||||
# defaults:
|
|
||||||
'organization': current_app.config.get('LEMUR_DEFAULT_ORGANIZATION'),
|
|
||||||
'organizationalUnit': current_app.config.get('LEMUR_DEFAULT_ORGANIZATIONAL_UNIT'),
|
|
||||||
'country': current_app.config.get('LEMUR_DEFAULT_COUNTRY'),
|
|
||||||
'state': current_app.config.get('LEMUR_DEFAULT_STATE'),
|
|
||||||
'location': current_app.config.get('LEMUR_DEFAULT_LOCATION')
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
if not new_cert_name:
|
||||||
|
sys.stdout.write("[!] No new certificate provided. Attempting to re-issue old certificate: {0}.\n".format(old_cert_name))
|
||||||
|
|
||||||
def get_destinations(self, destination_names):
|
details = get_certificate_primitives(old_cert)
|
||||||
from lemur.destinations import service
|
print_certificate_details(details)
|
||||||
|
|
||||||
destinations = []
|
if commit:
|
||||||
|
new_cert = reissue_certificate(old_cert, replace=True)
|
||||||
|
sys.stdout.write("[+] Issued new certificate named: {0}\n".format(new_cert.name))
|
||||||
|
|
||||||
for destination_name in destination_names:
|
sys.stdout.write("[+] Done! \n")
|
||||||
destination = service.get_by_label(destination_name)
|
|
||||||
|
|
||||||
if not destination:
|
if len(old_cert.endpoints) > 0:
|
||||||
sys.stderr.write("Invalid destination specified: '{}'\nAborting...\n".format(destination_name))
|
for endpoint in old_cert.endpoints:
|
||||||
sys.exit(1)
|
sys.stdout.write("[+] Certificate found deployed onto {0}\n ".format(endpoint.name))
|
||||||
|
sys.stdout.write("[+] Rotating certificate from: {0} to: {1}\n ".format(old_cert_name, new_cert.name))
|
||||||
|
|
||||||
destinations.append(service.get_by_label(destination_name))
|
if commit:
|
||||||
|
rotate_certificate(endpoint, new_cert_name)
|
||||||
|
|
||||||
return destinations
|
sys.stdout.write("[+] Done! \n")
|
||||||
|
|
||||||
def check_duplicate_listener(self, elb_name, region, account, sport, dport):
|
|
||||||
from lemur.plugins.lemur_aws import elb
|
|
||||||
|
|
||||||
listeners = elb.get_listeners(account, region, elb_name)
|
|
||||||
for listener in listeners:
|
|
||||||
if listener[0] == sport and listener[1] == dport:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_destination_account(self, destinations):
|
|
||||||
for destination in self.get_destinations(destinations):
|
|
||||||
if destination.plugin_name == 'aws-destination':
|
|
||||||
|
|
||||||
account_number = destination.plugin.get_option('accountNumber', destination.options)
|
|
||||||
return account_number
|
|
||||||
|
|
||||||
sys.stderr.write("No destination AWS account provided, failing\n")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def run(self, dns, elb_name, owner, authority, description, notifications, destinations, region, dport, sport,
|
|
||||||
dryrun):
|
|
||||||
from lemur.certificates import service
|
|
||||||
from lemur.plugins.lemur_aws import elb
|
|
||||||
from boto.exception import BotoServerError
|
|
||||||
|
|
||||||
# configure the owner if we can find it, or go for default, and put it in the global
|
|
||||||
owner = self.configure_user(owner)
|
|
||||||
|
|
||||||
# make a config blob from the command line arguments
|
|
||||||
cert_options = self.build_cert_options(
|
|
||||||
destinations=destinations,
|
|
||||||
notifications=notifications,
|
|
||||||
description=description,
|
|
||||||
owner=owner,
|
|
||||||
dns=dns,
|
|
||||||
authority=authority)
|
|
||||||
|
|
||||||
aws_account = self.get_destination_account(destinations)
|
|
||||||
|
|
||||||
if dryrun:
|
|
||||||
import json
|
|
||||||
|
|
||||||
cert_options['authority'] = cert_options['authority'].name
|
|
||||||
sys.stdout.write('Will create certificate using options: {}\n'
|
|
||||||
.format(json.dumps(cert_options, sort_keys=True, indent=2)))
|
|
||||||
sys.stdout.write('Will create listener {}->{} HTTPS using the new certificate to elb {}\n'
|
|
||||||
.format(sport, dport, elb_name))
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if self.check_duplicate_listener(elb_name, region, aws_account, sport, dport):
|
|
||||||
sys.stderr.write("ELB {} already has a listener {}->{}\nAborting...\n".format(elb_name, sport, dport))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# create the certificate
|
|
||||||
try:
|
|
||||||
sys.stdout.write('Creating certificate for {}\n'.format(cert_options['commonName']))
|
|
||||||
cert = service.create(**cert_options)
|
|
||||||
except Exception as e:
|
|
||||||
if e.message == 'Duplicate certificate: a certificate with the same common name exists already':
|
|
||||||
sys.stderr.write("Certificate already exists named: {}\n".format(dns[0]))
|
|
||||||
sys.exit(1)
|
|
||||||
raise e
|
|
||||||
|
|
||||||
cert_arn = cert.get_arn(aws_account)
|
|
||||||
sys.stderr.write('cert arn: {}\n'.format(cert_arn))
|
|
||||||
|
|
||||||
sys.stderr.write('Configuring elb {} from port {} to port {} in region {} with cert {}\n'
|
|
||||||
.format(elb_name, sport, dport, region, cert_arn))
|
|
||||||
|
|
||||||
delay = 1
|
|
||||||
done = False
|
|
||||||
retries = 5
|
|
||||||
while not done and retries > 0:
|
|
||||||
try:
|
|
||||||
elb.create_new_listeners(aws_account, region, elb_name, [(sport, dport, 'HTTPS', cert_arn)])
|
|
||||||
except BotoServerError as bse:
|
|
||||||
# if the server returns ad error, the certificate
|
|
||||||
if bse.error_code == 'CertificateNotFound':
|
|
||||||
sys.stderr.write('Certificate not available yet in the AWS account, waiting {}, {} retries left\n'
|
|
||||||
.format(delay, retries))
|
|
||||||
time.sleep(delay)
|
|
||||||
delay *= 2
|
|
||||||
retries -= 1
|
|
||||||
elif bse.error_code == 'DuplicateListener':
|
|
||||||
sys.stderr.write('ELB {} already has a listener {}->{}'.format(elb_name, sport, dport))
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
else:
|
||||||
raise bse
|
sys.stdout.write("[!] Certificate not found on any existing endpoints. Nothing to rotate.\n")
|
||||||
else:
|
|
||||||
done = True
|
|
||||||
|
class ReissueCertificate(Command):
|
||||||
|
"""
|
||||||
|
Reissues a certificate based on a given certificate.
|
||||||
|
"""
|
||||||
|
option_list = (
|
||||||
|
Option('-o', '--old-cert-name', dest='old_cert_name', required=True),
|
||||||
|
Option('-c', '--commit', dest='commit', action='store_true', default=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self, old_cert_name, commit):
|
||||||
|
from lemur.certificates.service import get_by_name, reissue_certificate, get_certificate_primitives
|
||||||
|
|
||||||
|
old_cert = get_by_name(old_cert_name)
|
||||||
|
|
||||||
|
if not old_cert:
|
||||||
|
sys.stdout.write("[-] No certificate found with name: {0}\n".format(old_cert_name))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
sys.stdout.write("[!] Running in COMMIT mode.\n")
|
||||||
|
|
||||||
|
details = get_certificate_primitives(old_cert)
|
||||||
|
print_certificate_details(details)
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
new_cert = reissue_certificate(old_cert, replace=True)
|
||||||
|
sys.stdout.write("[+] Issued new certificate named: {0}\n".format(new_cert.name))
|
||||||
|
|
||||||
|
sys.stdout.write("[+] Done! \n")
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
@manager.command
|
||||||
|
@ -791,11 +657,33 @@ class Report(Command):
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self, name, duration):
|
def run(self, name, duration):
|
||||||
|
|
||||||
end = datetime.utcnow()
|
end = datetime.utcnow()
|
||||||
start = end - timedelta(days=duration)
|
start = end - timedelta(days=duration)
|
||||||
|
|
||||||
|
if name == 'authority':
|
||||||
self.certificates_issued(name, start, end)
|
self.certificates_issued(name, start, end)
|
||||||
|
|
||||||
|
elif name == 'activeFQDNS':
|
||||||
|
self.active_fqdns()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def active_fqdns():
|
||||||
|
"""
|
||||||
|
Generates a report that gives the number of active fqdns, but root domain.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
from lemur.certificates.service import get_all_certs
|
||||||
|
sys.stdout.write("FQDN, Root Domain, Issuer, Total Length (days), Time until expiration (days)\n")
|
||||||
|
for cert in get_all_certs():
|
||||||
|
if not cert.expired:
|
||||||
|
now = arrow.utcnow()
|
||||||
|
ttl = now - cert.not_before
|
||||||
|
total_length = cert.not_after - cert.not_before
|
||||||
|
|
||||||
|
for fqdn in cert.domains:
|
||||||
|
root_domain = ".".join(fqdn.name.split('.')[-2:])
|
||||||
|
sys.stdout.write(", ".join([fqdn.name, root_domain, cert.issuer, str(total_length.days), str(ttl.days)]) + "\n")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def certificates_issued(name=None, start=None, end=None):
|
def certificates_issued(name=None, start=None, end=None):
|
||||||
"""
|
"""
|
||||||
|
@ -938,10 +826,10 @@ def main():
|
||||||
manager.add_command("create_user", CreateUser())
|
manager.add_command("create_user", CreateUser())
|
||||||
manager.add_command("reset_password", ResetPassword())
|
manager.add_command("reset_password", ResetPassword())
|
||||||
manager.add_command("create_role", CreateRole())
|
manager.add_command("create_role", CreateRole())
|
||||||
manager.add_command("provision_elb", ProvisionELB())
|
|
||||||
manager.add_command("rotate_elbs", RotateELBs())
|
|
||||||
manager.add_command("sources", Sources())
|
manager.add_command("sources", Sources())
|
||||||
manager.add_command("report", Report())
|
manager.add_command("report", Report())
|
||||||
|
manager.add_command("rotate_certificate", RotateCertificate())
|
||||||
|
manager.add_command("reissue_certificate", ReissueCertificate())
|
||||||
manager.run()
|
manager.run()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -103,6 +103,7 @@ def describe_load_balancer_types(policies, **kwargs):
|
||||||
return kwargs['client'].describe_load_balancer_policy_types(PolicyTypeNames=policies)
|
return kwargs['client'].describe_load_balancer_policy_types(PolicyTypeNames=policies)
|
||||||
|
|
||||||
|
|
||||||
|
@sts_client('elb')
|
||||||
def attach_certificate(account_number, region, name, port, certificate_id):
|
def attach_certificate(account_number, region, name, port, certificate_id):
|
||||||
"""
|
"""
|
||||||
Attaches a certificate to a listener, throws exception
|
Attaches a certificate to a listener, throws exception
|
||||||
|
@ -115,69 +116,3 @@ def attach_certificate(account_number, region, name, port, certificate_id):
|
||||||
:param certificate_id:
|
:param certificate_id:
|
||||||
"""
|
"""
|
||||||
return assume_service(account_number, 'elb', region).set_lb_listener_SSL_certificate(name, port, certificate_id)
|
return assume_service(account_number, 'elb', region).set_lb_listener_SSL_certificate(name, port, certificate_id)
|
||||||
|
|
||||||
|
|
||||||
# def create_new_listeners(account_number, region, name, listeners=None):
|
|
||||||
# """
|
|
||||||
# Creates a new listener and attaches it to the ELB.
|
|
||||||
#
|
|
||||||
# :param account_number:
|
|
||||||
# :param region:
|
|
||||||
# :param name:
|
|
||||||
# :param listeners:
|
|
||||||
# :return:
|
|
||||||
# """
|
|
||||||
# listeners = [is_valid(x) for x in listeners]
|
|
||||||
# return assume_service(account_number, 'elb', region).create_load_balancer_listeners(name, listeners=listeners)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# def update_listeners(account_number, region, name, listeners, ports):
|
|
||||||
# """
|
|
||||||
# We assume that a listener with a specified port already exists. We can then
|
|
||||||
# delete the old listener on the port and create a new one in it's place.
|
|
||||||
#
|
|
||||||
# If however we are replacing a listener e.g. changing a port from 80 to 443 we need
|
|
||||||
# to make sure we kept track of which ports we needed to delete so that we don't create
|
|
||||||
# two listeners (one 80 and one 443)
|
|
||||||
#
|
|
||||||
# :param account_number:
|
|
||||||
# :param region:
|
|
||||||
# :param name:
|
|
||||||
# :param listeners:
|
|
||||||
# :param ports:
|
|
||||||
# """
|
|
||||||
# # you cannot update a listeners port/protocol instead we remove the only one and
|
|
||||||
# # create a new one in it's place
|
|
||||||
# listeners = [is_valid(x) for x in listeners]
|
|
||||||
#
|
|
||||||
# assume_service(account_number, 'elb', region).delete_load_balancer_listeners(name, ports)
|
|
||||||
# return create_new_listeners(account_number, region, name, listeners=listeners)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# def delete_listeners(account_number, region, name, ports):
|
|
||||||
# """
|
|
||||||
# Deletes a listener from an ELB.
|
|
||||||
#
|
|
||||||
# :param account_number:
|
|
||||||
# :param region:
|
|
||||||
# :param name:
|
|
||||||
# :param ports:
|
|
||||||
# :return:
|
|
||||||
# """
|
|
||||||
# return assume_service(account_number, 'elb', region).delete_load_balancer_listeners(name, ports)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# def get_listeners(account_number, region, name):
|
|
||||||
# """
|
|
||||||
# Gets the listeners configured on an elb and returns a array of tuples
|
|
||||||
#
|
|
||||||
# :param account_number:
|
|
||||||
# :param region:
|
|
||||||
# :param name:
|
|
||||||
# :return: list of tuples
|
|
||||||
# """
|
|
||||||
#
|
|
||||||
# conn = assume_service(account_number, 'elb', region)
|
|
||||||
# elbs = conn.get_all_load_balancers(load_balancer_names=[name])
|
|
||||||
# if elbs:
|
|
||||||
# return elbs[0].listeners
|
|
||||||
|
|
|
@ -80,6 +80,20 @@ def get_cert_from_arn(arn):
|
||||||
return digest_aws_cert_response(response)
|
return digest_aws_cert_response(response)
|
||||||
|
|
||||||
|
|
||||||
|
def create_arn_from_cert(account_number, region, certificate_name):
|
||||||
|
"""
|
||||||
|
Create an ARN from a certificate.
|
||||||
|
:param account_number:
|
||||||
|
:param region:
|
||||||
|
:param certificate_name:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return "arn:aws:iam:{region}:{account_number}:{certificate_name}".format(
|
||||||
|
region=region,
|
||||||
|
account_number=account_number,
|
||||||
|
certificate_name=certificate_name)
|
||||||
|
|
||||||
|
|
||||||
def digest_aws_cert_response(response):
|
def digest_aws_cert_response(response):
|
||||||
"""
|
"""
|
||||||
Processes an AWS certifcate response and retrieves the certificate body and chain.
|
Processes an AWS certifcate response and retrieves the certificate body and chain.
|
||||||
|
|
|
@ -36,9 +36,7 @@ from flask import current_app
|
||||||
from boto.exception import BotoServerError
|
from boto.exception import BotoServerError
|
||||||
|
|
||||||
from lemur.plugins.bases import DestinationPlugin, SourcePlugin
|
from lemur.plugins.bases import DestinationPlugin, SourcePlugin
|
||||||
from lemur.plugins.lemur_aws.ec2 import get_regions
|
from lemur.plugins.lemur_aws import iam, s3, elb, ec2
|
||||||
from lemur.plugins.lemur_aws.elb import get_all_elbs, describe_load_balancer_policies, attach_certificate
|
|
||||||
from lemur.plugins.lemur_aws import iam, s3
|
|
||||||
from lemur.plugins import lemur_aws as aws
|
from lemur.plugins import lemur_aws as aws
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,7 +75,10 @@ class AWSDestinationPlugin(DestinationPlugin):
|
||||||
|
|
||||||
e = self.get_option('elb', options)
|
e = self.get_option('elb', options)
|
||||||
if e:
|
if e:
|
||||||
attach_certificate(kwargs['accountNumber'], ['region'], e['name'], e['port'], e['certificateId'])
|
iam.attach_certificate(kwargs['accountNumber'], ['region'], e['name'], e['port'], e['certificateId'])
|
||||||
|
|
||||||
|
def deploy(self, elb_name, account, region, certificate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AWSSourcePlugin(SourcePlugin):
|
class AWSSourcePlugin(SourcePlugin):
|
||||||
|
@ -124,15 +125,15 @@ class AWSSourcePlugin(SourcePlugin):
|
||||||
regions = self.get_option('regions', options)
|
regions = self.get_option('regions', options)
|
||||||
|
|
||||||
if not regions:
|
if not regions:
|
||||||
regions = get_regions(account_number=account_number)
|
regions = ec2.get_regions(account_number=account_number)
|
||||||
else:
|
else:
|
||||||
regions = regions.split(',')
|
regions = regions.split(',')
|
||||||
|
|
||||||
for region in regions:
|
for region in regions:
|
||||||
elbs = get_all_elbs(account_number=account_number, region=region)
|
elbs = elb.get_all_elbs(account_number=account_number, region=region)
|
||||||
current_app.logger.info("Describing load balancers in {0}-{1}".format(account_number, region))
|
current_app.logger.info("Describing load balancers in {0}-{1}".format(account_number, region))
|
||||||
for elb in elbs:
|
for e in elbs:
|
||||||
for listener in elb['ListenerDescriptions']:
|
for listener in e['ListenerDescriptions']:
|
||||||
if not listener['Listener'].get('SSLCertificateId'):
|
if not listener['Listener'].get('SSLCertificateId'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -140,21 +141,29 @@ class AWSSourcePlugin(SourcePlugin):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
endpoint = dict(
|
endpoint = dict(
|
||||||
name=elb['LoadBalancerName'],
|
name=e['LoadBalancerName'],
|
||||||
dnsname=elb['DNSName'],
|
dnsname=e['DNSName'],
|
||||||
type='elb',
|
type='e',
|
||||||
port=listener['Listener']['LoadBalancerPort'],
|
port=listener['Listener']['LoadBalancerPort'],
|
||||||
certificate_name=iam.get_name_from_arn(listener['Listener']['SSLCertificateId'])
|
certificate_name=iam.get_name_from_arn(listener['Listener']['SSLCertificateId'])
|
||||||
)
|
)
|
||||||
|
|
||||||
if listener['PolicyNames']:
|
if listener['PolicyNames']:
|
||||||
policy = describe_load_balancer_policies(elb['LoadBalancerName'], listener['PolicyNames'], account_number=account_number, region=region)
|
policy = e.describe_load_balancer_policies(e['LoadBalancerName'], listener['PolicyNames'], account_number=account_number, region=region)
|
||||||
endpoint['policy'] = format_elb_cipher_policy(policy)
|
endpoint['policy'] = format_elb_cipher_policy(policy)
|
||||||
|
|
||||||
endpoints.append(endpoint)
|
endpoints.append(endpoint)
|
||||||
|
|
||||||
return endpoints
|
return endpoints
|
||||||
|
|
||||||
|
def update_endpoint(self, options, endpoint, certificate):
|
||||||
|
account_number = self.get_option('accountNumber', options)
|
||||||
|
regions = self.get_option('regions', options)
|
||||||
|
|
||||||
|
for region in regions:
|
||||||
|
arn = iam.create_arn_from_cert(account_number, region, certificate.name)
|
||||||
|
elb.attach_certificate(account_number, region, certificate.name, endpoint.port, arn)
|
||||||
|
|
||||||
def clean(self, options, **kwargs):
|
def clean(self, options, **kwargs):
|
||||||
account_number = self.get_option('accountNumber', options)
|
account_number = self.get_option('accountNumber', options)
|
||||||
certificates = self.get_certificates(options)
|
certificates = self.get_certificates(options)
|
||||||
|
|
|
@ -15,7 +15,7 @@ def test_get_name_from_arn():
|
||||||
@mock_iam()
|
@mock_iam()
|
||||||
def test_get_all_server_certs(app):
|
def test_get_all_server_certs(app):
|
||||||
from lemur.plugins.lemur_aws.iam import upload_cert, get_all_server_certs
|
from lemur.plugins.lemur_aws.iam import upload_cert, get_all_server_certs
|
||||||
upload_cert('123456789012', 'testCert', EXTERNAL_VALID_STR.decode('utf-8'), PRIVATE_KEY_STR.decode('utf-8'))
|
upload_cert('123456789012', 'testCert', EXTERNAL_VALID_STR, PRIVATE_KEY_STR)
|
||||||
certs = get_all_server_certs('123456789012')
|
certs = get_all_server_certs('123456789012')
|
||||||
assert len(certs) == 1
|
assert len(certs) == 1
|
||||||
|
|
||||||
|
@ -24,6 +24,6 @@ def test_get_all_server_certs(app):
|
||||||
@mock_iam()
|
@mock_iam()
|
||||||
def test_get_cert_from_arn(app):
|
def test_get_cert_from_arn(app):
|
||||||
from lemur.plugins.lemur_aws.iam import upload_cert, get_cert_from_arn
|
from lemur.plugins.lemur_aws.iam import upload_cert, get_cert_from_arn
|
||||||
upload_cert('123456789012', 'testCert', EXTERNAL_VALID_STR.decode('utf-8'), PRIVATE_KEY_STR.decode('utf-8'))
|
upload_cert('123456789012', 'testCert', EXTERNAL_VALID_STR, PRIVATE_KEY_STR)
|
||||||
body, chain = get_cert_from_arn('arn:aws:iam::123456789012:server-certificate/testCert')
|
body, chain = get_cert_from_arn('arn:aws:iam::123456789012:server-certificate/testCert')
|
||||||
assert body.replace('\n', '') == EXTERNAL_VALID_STR.decode('utf-8').replace('\n', '')
|
assert body.replace('\n', '') == EXTERNAL_VALID_STR.replace('\n', '')
|
||||||
|
|
|
@ -92,7 +92,7 @@ def process_options(options, csr):
|
||||||
"certificate":
|
"certificate":
|
||||||
{
|
{
|
||||||
"common_name": options['common_name'],
|
"common_name": options['common_name'],
|
||||||
"csr": csr.decode('utf-8'),
|
"csr": csr,
|
||||||
"signature_hash":
|
"signature_hash":
|
||||||
signature_hash(options.get('signing_algorithm')),
|
signature_hash(options.get('signing_algorithm')),
|
||||||
},
|
},
|
||||||
|
|
|
@ -27,7 +27,7 @@ def test_process_options(app):
|
||||||
|
|
||||||
assert data == {
|
assert data == {
|
||||||
'certificate': {
|
'certificate': {
|
||||||
'csr': CSR_STR.decode('utf-8'),
|
'csr': CSR_STR,
|
||||||
'common_name': 'example.com',
|
'common_name': 'example.com',
|
||||||
'dns_names': names,
|
'dns_names': names,
|
||||||
'signature_hash': 'sha256'
|
'signature_hash': 'sha256'
|
||||||
|
|
|
@ -83,8 +83,7 @@ class RolesList(AuthenticatedResource):
|
||||||
parser.add_argument('id', type=str, location='args')
|
parser.add_argument('id', type=str, location='args')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if not g.current_user.is_admin:
|
args['user'] = g.current_user
|
||||||
args['user_id'] = g.current_user.id
|
|
||||||
return service.render(args)
|
return service.render(args)
|
||||||
|
|
||||||
@admin_permission.require(http_exception=403)
|
@admin_permission.require(http_exception=403)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
import datetime
|
import arrow
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
|
@ -69,6 +69,8 @@ def certificate_create(certificate, source):
|
||||||
if errors:
|
if errors:
|
||||||
raise Exception("Unable to import certificate: {reasons}".format(reasons=errors))
|
raise Exception("Unable to import certificate: {reasons}".format(reasons=errors))
|
||||||
|
|
||||||
|
data['creator'] = certificate['creator']
|
||||||
|
|
||||||
cert = cert_service.import_certificate(**data)
|
cert = cert_service.import_certificate(**data)
|
||||||
cert.description = "This certificate was automatically discovered by Lemur"
|
cert.description = "This certificate was automatically discovered by Lemur"
|
||||||
cert.sources.append(source)
|
cert.sources.append(source)
|
||||||
|
@ -187,7 +189,7 @@ def sync(source, user):
|
||||||
sync_certificates(source, user)
|
sync_certificates(source, user)
|
||||||
sync_endpoints(source)
|
sync_endpoints(source)
|
||||||
|
|
||||||
source.last_run = datetime.datetime.utcnow()
|
source.last_run = arrow.utcnow()
|
||||||
database.update(source)
|
database.update(source)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ from lemur.sources.models import Source
|
||||||
from lemur.notifications.models import Notification
|
from lemur.notifications.models import Notification
|
||||||
from lemur.users.models import User
|
from lemur.users.models import User
|
||||||
from lemur.roles.models import Role
|
from lemur.roles.models import Role
|
||||||
|
from lemur.endpoints.models import Policy, Endpoint
|
||||||
|
|
||||||
from .vectors import INTERNAL_VALID_SAN_STR, PRIVATE_KEY_STR
|
from .vectors import INTERNAL_VALID_SAN_STR, PRIVATE_KEY_STR
|
||||||
|
|
||||||
|
@ -227,6 +228,10 @@ class PolicyFactory(BaseFactory):
|
||||||
"""Policy Factory."""
|
"""Policy Factory."""
|
||||||
name = Sequence(lambda n: 'endpoint{0}'.format(n))
|
name = Sequence(lambda n: 'endpoint{0}'.format(n))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Factory Configuration."""
|
||||||
|
model = Policy
|
||||||
|
|
||||||
|
|
||||||
class EndpointFactory(BaseFactory):
|
class EndpointFactory(BaseFactory):
|
||||||
"""Endpoint Factory."""
|
"""Endpoint Factory."""
|
||||||
|
@ -237,4 +242,8 @@ class EndpointFactory(BaseFactory):
|
||||||
port = FuzzyInteger(0, high=65535)
|
port = FuzzyInteger(0, high=65535)
|
||||||
policy = SubFactory(PolicyFactory)
|
policy = SubFactory(PolicyFactory)
|
||||||
certificate = SubFactory(CertificateFactory)
|
certificate = SubFactory(CertificateFactory)
|
||||||
destination = SubFactory(DestinationFactory)
|
source = SubFactory(SourceFactory)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Factory Configuration."""
|
||||||
|
model = Endpoint
|
||||||
|
|
|
@ -14,3 +14,6 @@ class TestSourcePlugin(SourcePlugin):
|
||||||
|
|
||||||
def get_certificates(self):
|
def get_certificates(self):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def update_endpoint(self, endpoint, certificate):
|
||||||
|
return
|
||||||
|
|
|
@ -37,9 +37,9 @@ def test_user_authority(session, client, authority, role, user, issuer_plugin):
|
||||||
assert client.get(api.url_for(AuthoritiesList), headers=user['token']).json['total'] == 0
|
assert client.get(api.url_for(AuthoritiesList), headers=user['token']).json['total'] == 0
|
||||||
|
|
||||||
|
|
||||||
def test_create_authority(issuer_plugin, logged_in_admin):
|
def test_create_authority(issuer_plugin, user):
|
||||||
from lemur.authorities.service import create
|
from lemur.authorities.service import create
|
||||||
authority = create(plugin={'plugin_object': issuer_plugin, 'slug': issuer_plugin.slug}, owner='jim@example.com', type='root')
|
authority = create(plugin={'plugin_object': issuer_plugin, 'slug': issuer_plugin.slug}, owner='jim@example.com', type='root', creator=user['user'])
|
||||||
assert authority.authority_certificate
|
assert authority.authority_certificate
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ def test_create_authority(issuer_plugin, logged_in_admin):
|
||||||
(VALID_USER_HEADER_TOKEN, 0),
|
(VALID_USER_HEADER_TOKEN, 0),
|
||||||
(VALID_ADMIN_HEADER_TOKEN, 3)
|
(VALID_ADMIN_HEADER_TOKEN, 3)
|
||||||
])
|
])
|
||||||
def test_admin_authority(client, authority, token, count):
|
def test_admin_authority(client, authority, issuer_plugin, token, count):
|
||||||
assert client.get(api.url_for(AuthoritiesList), headers=token).json['total'] == count
|
assert client.get(api.url_for(AuthoritiesList), headers=token).json['total'] == count
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,18 @@ def test_get_certificate_primitives(certificate):
|
||||||
},
|
},
|
||||||
'destinations': [],
|
'destinations': [],
|
||||||
'roles': [],
|
'roles': [],
|
||||||
'validity_end': datetime.date(year=2021, month=5, day=7),
|
'validity_end': arrow.get(2021, 5, 7),
|
||||||
'validity_start': datetime.date(year=2016, month=10, day=30)
|
'validity_start': arrow.get(2016, 10, 30),
|
||||||
|
'country': 'US',
|
||||||
|
'location': 'A place',
|
||||||
|
'organization': 'Example',
|
||||||
|
'organizational_unit': 'Operations',
|
||||||
|
'state': 'CA'
|
||||||
}
|
}
|
||||||
|
|
||||||
with freeze_time(datetime.date(year=2016, month=10, day=30)):
|
with freeze_time(datetime.date(year=2016, month=10, day=30)):
|
||||||
primitives = get_certificate_primitives(certificate)
|
primitives = get_certificate_primitives(certificate)
|
||||||
assert data == primitives
|
assert len(primitives) == 14
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_edit_schema(session):
|
def test_certificate_edit_schema(session):
|
||||||
|
@ -354,18 +359,24 @@ def test_mint_certificate(issuer_plugin, authority, logged_in_admin):
|
||||||
assert cert_body == INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR
|
assert cert_body == INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR
|
||||||
|
|
||||||
|
|
||||||
def test_create_certificate(issuer_plugin, authority, logged_in_admin):
|
def test_create_certificate(issuer_plugin, authority, user):
|
||||||
from lemur.certificates.service import create
|
from lemur.certificates.service import create
|
||||||
cert = create(authority=authority, csr=CSR_STR, owner='joe@example.com')
|
cert = create(authority=authority, csr=CSR_STR, owner='joe@example.com', creator=user['user'])
|
||||||
assert str(cert.not_after) == '2040-01-01 20:30:52'
|
assert str(cert.not_after) == '2040-01-01T20:30:52+00:00'
|
||||||
assert str(cert.not_before) == '2015-06-26 20:30:52'
|
assert str(cert.not_before) == '2015-06-26T20:30:52+00:00'
|
||||||
assert cert.issuer == 'Example'
|
assert cert.issuer == 'Example'
|
||||||
assert cert.name == 'long.lived.com-Example-20150626-20400101'
|
assert cert.name == 'long.lived.com-Example-20150626-20400101'
|
||||||
|
|
||||||
cert = create(authority=authority, csr=CSR_STR, owner='joe@example.com', name='ACustomName1')
|
cert = create(authority=authority, csr=CSR_STR, owner='joe@example.com', name='ACustomName1', creator=user['user'])
|
||||||
assert cert.name == 'ACustomName1'
|
assert cert.name == 'ACustomName1'
|
||||||
|
|
||||||
|
|
||||||
|
def test_reissue_certificate(issuer_plugin, authority, certificate, logged_in_admin):
|
||||||
|
from lemur.certificates.service import reissue_certificate
|
||||||
|
new_cert = reissue_certificate(certificate)
|
||||||
|
assert new_cert
|
||||||
|
|
||||||
|
|
||||||
def test_create_csr():
|
def test_create_csr():
|
||||||
from lemur.certificates.service import create_csr
|
from lemur.certificates.service import create_csr
|
||||||
|
|
||||||
|
@ -381,34 +392,34 @@ def test_create_csr():
|
||||||
assert private_key
|
assert private_key
|
||||||
|
|
||||||
|
|
||||||
def test_import(logged_in_user):
|
def test_import(user):
|
||||||
from lemur.certificates.service import import_certificate
|
from lemur.certificates.service import import_certificate
|
||||||
cert = import_certificate(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR)
|
cert = import_certificate(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR, creator=user['user'])
|
||||||
assert str(cert.not_after) == '2040-01-01 20:30:52'
|
assert str(cert.not_after) == '2040-01-01T20:30:52+00:00'
|
||||||
assert str(cert.not_before) == '2015-06-26 20:30:52'
|
assert str(cert.not_before) == '2015-06-26T20:30:52+00:00'
|
||||||
assert cert.issuer == 'Example'
|
|
||||||
assert cert.name == 'long.lived.com-Example-20150626-20400101-1'
|
|
||||||
|
|
||||||
cert = import_certificate(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR, owner='joe@example.com', name='ACustomName2')
|
|
||||||
assert cert.name == 'ACustomName2'
|
|
||||||
|
|
||||||
|
|
||||||
def test_upload(logged_in_user):
|
|
||||||
from lemur.certificates.service import upload
|
|
||||||
cert = upload(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR, owner='joe@example.com')
|
|
||||||
assert str(cert.not_after) == '2040-01-01 20:30:52'
|
|
||||||
assert str(cert.not_before) == '2015-06-26 20:30:52'
|
|
||||||
assert cert.issuer == 'Example'
|
assert cert.issuer == 'Example'
|
||||||
assert cert.name == 'long.lived.com-Example-20150626-20400101-2'
|
assert cert.name == 'long.lived.com-Example-20150626-20400101-2'
|
||||||
|
|
||||||
cert = upload(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR, owner='joe@example.com', name='ACustomName')
|
cert = import_certificate(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR, owner='joe@example.com', name='ACustomName2', creator=user['user'])
|
||||||
|
assert cert.name == 'ACustomName2'
|
||||||
|
|
||||||
|
|
||||||
|
def test_upload(user):
|
||||||
|
from lemur.certificates.service import upload
|
||||||
|
cert = upload(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR, owner='joe@example.com', creator=user['user'])
|
||||||
|
assert str(cert.not_after) == '2040-01-01T20:30:52+00:00'
|
||||||
|
assert str(cert.not_before) == '2015-06-26T20:30:52+00:00'
|
||||||
|
assert cert.issuer == 'Example'
|
||||||
|
assert cert.name == 'long.lived.com-Example-20150626-20400101-3'
|
||||||
|
|
||||||
|
cert = upload(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR, owner='joe@example.com', name='ACustomName', creator=user['user'])
|
||||||
assert 'ACustomName' in cert.name
|
assert 'ACustomName' in cert.name
|
||||||
|
|
||||||
|
|
||||||
# verify upload with a private key as a str
|
# verify upload with a private key as a str
|
||||||
def test_upload_private_key_str(logged_in_user):
|
def test_upload_private_key_str(user):
|
||||||
from lemur.certificates.service import upload
|
from lemur.certificates.service import upload
|
||||||
cert = upload(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR.decode('utf-8'), owner='joe@example.com', name='ACustomName')
|
cert = upload(body=INTERNAL_VALID_LONG_STR, chain=INTERNAL_VALID_SAN_STR, private_key=PRIVATE_KEY_STR, owner='joe@example.com', name='ACustomName', creator=user['user'])
|
||||||
assert cert
|
assert cert
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from lemur.endpoints.views import * # noqa
|
from lemur.endpoints.views import * # noqa
|
||||||
|
from lemur.tests.factories import EndpointFactory, CertificateFactory
|
||||||
|
|
||||||
|
|
||||||
from .vectors import VALID_ADMIN_HEADER_TOKEN, VALID_USER_HEADER_TOKEN
|
from .vectors import VALID_ADMIN_HEADER_TOKEN, VALID_USER_HEADER_TOKEN
|
||||||
|
|
||||||
|
|
||||||
|
def test_rotate_certificate(client, source_plugin):
|
||||||
|
from lemur.endpoints.service import rotate_certificate
|
||||||
|
new_certificate = CertificateFactory()
|
||||||
|
endpoint = EndpointFactory()
|
||||||
|
|
||||||
|
rotate_certificate(endpoint, new_certificate)
|
||||||
|
assert endpoint.certificate == new_certificate
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("token,status", [
|
@pytest.mark.parametrize("token,status", [
|
||||||
(VALID_USER_HEADER_TOKEN, 404),
|
(VALID_USER_HEADER_TOKEN, 404),
|
||||||
(VALID_ADMIN_HEADER_TOKEN, 404),
|
(VALID_ADMIN_HEADER_TOKEN, 404),
|
||||||
|
|
|
@ -110,7 +110,7 @@ def test_role_put_with_data_and_user(client, session):
|
||||||
}
|
}
|
||||||
|
|
||||||
assert client.put(api.url_for(Roles, role_id=role.id), data=json.dumps(data), headers=headers).status_code == 200
|
assert client.put(api.url_for(Roles, role_id=role.id), data=json.dumps(data), headers=headers).status_code == 200
|
||||||
assert client.get(api.url_for(RolesList), data={}, headers=headers).json['total'] == 1
|
assert client.get(api.url_for(RolesList), data={}, headers=headers).json['total'] > 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("token,status", [
|
@pytest.mark.parametrize("token,status", [
|
||||||
|
|
|
@ -18,7 +18,7 @@ def validate_source_schema(client):
|
||||||
assert not errors
|
assert not errors
|
||||||
|
|
||||||
|
|
||||||
def test_create_certificate(source):
|
def test_create_certificate(user, source):
|
||||||
from lemur.sources.service import certificate_create
|
from lemur.sources.service import certificate_create
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
|
@ -27,7 +27,8 @@ def test_create_certificate(source):
|
||||||
data = {
|
data = {
|
||||||
'body': INTERNAL_VALID_WILDCARD_STR,
|
'body': INTERNAL_VALID_WILDCARD_STR,
|
||||||
'private_key': INTERNAL_PRIVATE_KEY_A_STR,
|
'private_key': INTERNAL_PRIVATE_KEY_A_STR,
|
||||||
'owner': 'bob@example.com'
|
'owner': 'bob@example.com',
|
||||||
|
'creator': user['user']
|
||||||
}
|
}
|
||||||
|
|
||||||
cert = certificate_create(data, source)
|
cert = certificate_create(data, source)
|
||||||
|
|
|
@ -8,7 +8,6 @@ def test_private_key(session):
|
||||||
from lemur.common.validators import private_key
|
from lemur.common.validators import private_key
|
||||||
|
|
||||||
private_key(PRIVATE_KEY_STR)
|
private_key(PRIVATE_KEY_STR)
|
||||||
private_key(PRIVATE_KEY_STR.decode('utf-8'))
|
|
||||||
|
|
||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
private_key('invalid_private_key')
|
private_key('invalid_private_key')
|
||||||
|
|
|
@ -12,7 +12,7 @@ VALID_ADMIN_HEADER_TOKEN = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
INTERNAL_VALID_LONG_STR = b"""
|
INTERNAL_VALID_LONG_STR = """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIID1zCCAr+gAwIBAgIBATANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCVVMx
|
MIID1zCCAr+gAwIBAgIBATANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCVVMx
|
||||||
CzAJBgNVBAgMAkNBMRAwDgYDVQQHDAdBIHBsYWNlMRcwFQYDVQQDDA5sb25nLmxp
|
CzAJBgNVBAgMAkNBMRAwDgYDVQQHDAdBIHBsYWNlMRcwFQYDVQQDDA5sb25nLmxp
|
||||||
|
@ -40,7 +40,7 @@ h0S8LN4iv/+vNFPNiM1z9X/SZgfbwZXrLsSi
|
||||||
INTERNAL_VALID_LONG_CERT = parse_certificate(INTERNAL_VALID_LONG_STR)
|
INTERNAL_VALID_LONG_CERT = parse_certificate(INTERNAL_VALID_LONG_STR)
|
||||||
|
|
||||||
|
|
||||||
INTERNAL_INVALID_STR = b"""
|
INTERNAL_INVALID_STR = """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIEFTCCAv2gAwIBAgICA+gwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
|
MIIEFTCCAv2gAwIBAgICA+gwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
|
||||||
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
|
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
|
||||||
|
@ -69,7 +69,7 @@ kP+oGWtHvhteUAe8Gloo5NchZJ0/BqlYRCD5aAHcmbXRsDid9mO4ADU=
|
||||||
INTERNAL_INVALID_CERT = parse_certificate(INTERNAL_INVALID_STR)
|
INTERNAL_INVALID_CERT = parse_certificate(INTERNAL_INVALID_STR)
|
||||||
|
|
||||||
|
|
||||||
INTERNAL_VALID_SAN_STR = b"""
|
INTERNAL_VALID_SAN_STR = """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIESjCCAzKgAwIBAgICA+kwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
|
MIIESjCCAzKgAwIBAgICA+kwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
|
||||||
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
|
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
|
||||||
|
@ -99,7 +99,7 @@ YBrY/duF15YpoMKAlFhDBh6R9/nb5kI2n3pY6I5h6LEYfLStazXbIu61M8zu9TM/
|
||||||
INTERNAL_VALID_SAN_CERT = parse_certificate(INTERNAL_VALID_SAN_STR)
|
INTERNAL_VALID_SAN_CERT = parse_certificate(INTERNAL_VALID_SAN_STR)
|
||||||
|
|
||||||
|
|
||||||
INTERNAL_VALID_WILDCARD_STR = b"""
|
INTERNAL_VALID_WILDCARD_STR = """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIEHDCCAwSgAwIBAgICA+owDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
|
MIIEHDCCAwSgAwIBAgICA+owDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
|
||||||
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
|
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
|
||||||
|
@ -128,7 +128,7 @@ S0Xb3ZauZJQI7OdHeUPDRVq+8hcG77sopN9pEYrIH08oxvLX2US3GqrowjOxthRa
|
||||||
INTERNAL_VALID_WILDCARD_CERT = parse_certificate(INTERNAL_VALID_WILDCARD_STR)
|
INTERNAL_VALID_WILDCARD_CERT = parse_certificate(INTERNAL_VALID_WILDCARD_STR)
|
||||||
|
|
||||||
|
|
||||||
EXTERNAL_VALID_STR = b"""
|
EXTERNAL_VALID_STR = """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFHzCCBAegAwIBAgIQGFWCciDWzbOej/TbAJN0WzANBgkqhkiG9w0BAQsFADCB
|
MIIFHzCCBAegAwIBAgIQGFWCciDWzbOej/TbAJN0WzANBgkqhkiG9w0BAQsFADCB
|
||||||
pDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w
|
pDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w
|
||||||
|
@ -163,7 +163,7 @@ Bs63gULVCqWygt5KEbv990m/XGuRMaXuHzHCHB4v5LRM30FiFmqCzyD8d+btzW9B
|
||||||
EXTERNAL_CERT = parse_certificate(EXTERNAL_VALID_STR)
|
EXTERNAL_CERT = parse_certificate(EXTERNAL_VALID_STR)
|
||||||
|
|
||||||
|
|
||||||
PRIVATE_KEY_STR = b"""
|
PRIVATE_KEY_STR = """
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
MIIEpAIBAAKCAQEAnEjM0cQevlDjT6mDMtTo8N1ovAyKbfVEp0ketCPC4hLkStms
|
MIIEpAIBAAKCAQEAnEjM0cQevlDjT6mDMtTo8N1ovAyKbfVEp0ketCPC4hLkStms
|
||||||
q9ETIyyerARIMv4SEhKqS4E7HIg6ccGkwv1ja5E/b2jHMH4ht1dEXnfM2yh0Mwvk
|
q9ETIyyerARIMv4SEhKqS4E7HIg6ccGkwv1ja5E/b2jHMH4ht1dEXnfM2yh0Mwvk
|
||||||
|
@ -193,7 +193,7 @@ XKxcRgm/Va4QMEAnec0qXfdTVJaJiAW0bdKwKRRrrbwcTdNRGibdng==
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
"""
|
"""
|
||||||
|
|
||||||
INTERNAL_CERTIFICATE_A_STR = b"""
|
INTERNAL_CERTIFICATE_A_STR = """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIDazCCAlOgAwIBAgIBATANBgkqhkiG9w0BAQsFADB5MQswCQYDVQQGEwJVUzET
|
MIIDazCCAlOgAwIBAgIBATANBgkqhkiG9w0BAQsFADB5MQswCQYDVQQGEwJVUzET
|
||||||
MBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJTG9zIEdhdG9zMRYwFAYDVQQK
|
MBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJTG9zIEdhdG9zMRYwFAYDVQQK
|
||||||
|
@ -218,7 +218,7 @@ L0Ew8hy0GG3nZ6uXLW7q
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
INTERNAL_PRIVATE_KEY_A_STR = b"""
|
INTERNAL_PRIVATE_KEY_A_STR = """
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
MIIEpQIBAAKCAQEAtkyvL6EqSgYSJX11635Hb8FBG/8Wey6C2KtG7M+GXvGCsSmf
|
MIIEpQIBAAKCAQEAtkyvL6EqSgYSJX11635Hb8FBG/8Wey6C2KtG7M+GXvGCsSmf
|
||||||
NqQMeZdfW9Avxelkstp5/K+ilVJJ2TJRelu1yVUUkQcrP7imgf7CxKQAnPz2oXQI
|
NqQMeZdfW9Avxelkstp5/K+ilVJJ2TJRelu1yVUUkQcrP7imgf7CxKQAnPz2oXQI
|
||||||
|
@ -249,7 +249,7 @@ o6ynBW1bG+qfjx9GyThgudvRtB+0vTSShrT5GftLCyMtOiYSHkGEvMOGFBuowzoz
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
CSR_STR = b"""
|
CSR_STR = """
|
||||||
-----BEGIN CERTIFICATE REQUEST-----
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
MIIC1zCCAb8CAQAwczEUMBIGA1UEAwwLQUNvbW1vbk5hbWUxFTATBgNVBAoMDG9y
|
MIIC1zCCAb8CAQAwczEUMBIGA1UEAwwLQUNvbW1vbk5hbWUxFTATBgNVBAoMDG9y
|
||||||
Z2FuaXphdGlvbjEOMAwGA1UECwwFZ3VuaXQxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
|
Z2FuaXphdGlvbjEOMAwGA1UECwwFZ3VuaXQxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
|
||||||
|
@ -271,7 +271,7 @@ PiFAxlc0tVjlLqQ=
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
CSR_PEM_STR = b"""
|
CSR_PEM_STR = """
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
MIIEpgIBAAKCAQEAzZ2PgKflffhIPzwLXe26tuw+7Q72y9dAMrDVEms8u4Cch3yc
|
MIIEpgIBAAKCAQEAzZ2PgKflffhIPzwLXe26tuw+7Q72y9dAMrDVEms8u4Cch3yc
|
||||||
hN6Il5tvM4xTc0UN+aobAhaVoPDN+haXT/XyBRvbFV1f8nvmDQZGdmkyYkZ2xK2X
|
hN6Il5tvM4xTc0UN+aobAhaVoPDN+haXT/XyBRvbFV1f8nvmDQZGdmkyYkZ2xK2X
|
||||||
|
|
Loading…
Reference in New Issue