Merge remote-tracking branch 'upstream/master' into elb-ssl-automation

This commit is contained in:
Jeremy Heffner 2015-08-20 15:46:11 -07:00
commit 96c3ab7f9d
18 changed files with 82 additions and 82 deletions

View File

@ -27,7 +27,10 @@ function browserSyncInit(baseDir, files, browser) {
browserSync.instance = browserSync.init(files, {
startPath: '/index.html',
server: {
baseDir: baseDir
baseDir: baseDir,
routes: {
'/bower_components': './bower_components'
}
},
browser: browser,
ghostMode: false

View File

@ -16,25 +16,19 @@ operator_permission = Permission(RoleNeed('operator'))
admin_permission = Permission(RoleNeed('admin'))
CertificateCreator = namedtuple('certificate', ['method', 'value'])
CertificateCreatorNeed = partial(CertificateCreator, 'certificateView')
CertificateOwner = namedtuple('certificate', ['method', 'value'])
CertificateOwnerNeed = partial(CertificateOwner, 'certificateView')
CertificateCreatorNeed = partial(CertificateCreator, 'key')
class ViewKeyPermission(Permission):
def __init__(self, certificate_id, owner_id):
def __init__(self, certificate_id, owner):
c_need = CertificateCreatorNeed(str(certificate_id))
o_need = CertificateOwnerNeed(str(owner_id))
super(ViewKeyPermission, self).__init__(o_need, c_need, RoleNeed('admin'))
super(ViewKeyPermission, self).__init__(c_need, RoleNeed(owner), RoleNeed('admin'))
class UpdateCertificatePermission(Permission):
def __init__(self, role_id, certificate_id):
def __init__(self, certificate_id, owner):
c_need = CertificateCreatorNeed(str(certificate_id))
o_need = CertificateOwnerNeed(str(role_id))
super(UpdateCertificatePermission, self).__init__(o_need, c_need, RoleNeed('admin'))
super(UpdateCertificatePermission, self).__init__(c_need, RoleNeed(owner), RoleNeed('admin'))
RoleUser = namedtuple('role', ['method', 'value'])

View File

@ -29,7 +29,7 @@ from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
from lemur.users import service as user_service
from lemur.auth.permissions import CertificateOwnerNeed, CertificateCreatorNeed, \
from lemur.auth.permissions import CertificateCreatorNeed, \
AuthorityCreatorNeed, ViewRoleCredentialsNeed
@ -165,7 +165,6 @@ def on_identity_loaded(sender, identity):
# identity with the roles that the user provides
if hasattr(user, 'roles'):
for role in user.roles:
identity.provides.add(CertificateOwnerNeed(role.id))
identity.provides.add(ViewRoleCredentialsNeed(role.id))
identity.provides.add(RoleNeed(role.name))

View File

@ -446,7 +446,8 @@ class CertificatePrivateKey(AuthenticatedResource):
role = role_service.get_by_name(cert.owner)
permission = ViewKeyPermission(certificate_id, hasattr(role, 'id'))
if role:
permission = ViewKeyPermission(certificate_id, role.name)
if permission.can():
response = make_response(jsonify(key=cert.private_key), 200)
@ -572,7 +573,7 @@ class Certificates(AuthenticatedResource):
cert = service.get(certificate_id)
role = role_service.get_by_name(cert.owner)
permission = UpdateCertificatePermission(certificate_id, hasattr(role, 'id'))
permission = UpdateCertificatePermission(certificate_id, role.name)
if permission.can():
return service.update(

View File

@ -9,10 +9,9 @@
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import current_app
from sqlalchemy import exc
from sqlalchemy.sql import and_, or_
from sqlalchemy.orm.exc import NoResultFound
from lemur.extensions import db
from lemur.exceptions import AttrNotFound, DuplicateError
@ -126,8 +125,7 @@ def get(model, value, field="id"):
query = session_query(model)
try:
return query.filter(getattr(model, field) == value).one()
except Exception as e:
current_app.logger.exception(e)
except NoResultFound as e:
return

View File

@ -77,7 +77,6 @@ LEMUR_RESTRICTED_DOMAINS = []
LEMUR_EMAIL = ''
LEMUR_SECURITY_TEAM_EMAIL = []
LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS = [30, 15, 2]
# Logging
@ -172,18 +171,17 @@ def generate_settings():
@manager.option('-s', '--sources', dest='labels', default='', required=False)
@manager.option('-l', '--list', dest='view', default=False, required=False)
def sync_sources(labels, view):
def sync_sources(labels):
"""
Attempts to run several methods Certificate discovery. This is
run on a periodic basis and updates the Lemur datastore with the
information it discovers.
"""
if view:
if not labels:
sys.stdout.write("Active\tLabel\tDescription\n")
for source in source_service.get_all():
sys.stdout.write(
"[{active}]\t{label}\t{description}!\n".format(
"{active}\t{label}\t{description}!\n".format(
label=source.label,
description=source.description,
active=source.active

View File

@ -38,7 +38,10 @@ def _get_message_data(cert):
:return:
"""
cert_dict = cert.as_dict()
if cert.user:
cert_dict['creator'] = cert.user.email
cert_dict['domains'] = [x .name for x in cert.domains]
cert_dict['superseded'] = list(set([x.name for x in _find_superseded(cert) if cert.name != x]))
return cert_dict
@ -56,6 +59,10 @@ def _deduplicate(messages):
for m, r, o in roll_ups:
if r == targets:
for cert in m:
if cert['body'] == data['body']:
break
else:
m.append(data)
current_app.logger.info(
"Sending expiration alert about {0} to {1}".format(
@ -63,6 +70,7 @@ def _deduplicate(messages):
break
else:
roll_ups.append(([data], targets, options))
return roll_ups

View File

@ -6,6 +6,7 @@
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from boto.exception import BotoServerError
from lemur.plugins.bases import DestinationPlugin, SourcePlugin
from lemur.plugins.lemur_aws import iam, elb
from lemur.plugins import lemur_aws as aws
@ -42,7 +43,11 @@ class AWSDestinationPlugin(DestinationPlugin):
# }
def upload(self, name, body, private_key, cert_chain, options, **kwargs):
try:
iam.upload_cert(find_value('accountNumber', options), name, body, private_key, cert_chain=cert_chain)
except BotoServerError as e:
if e.error_code != 'EntityAlreadyExists':
raise Exception(e)
e = find_value('elb', options)
if e:

View File

@ -326,11 +326,11 @@ class CloudCASourcePlugin(SourcePlugin, CloudCA):
'pollRate': {'type': 'int', 'default': '60'}
}
def get_certificates(self, **kwargs):
def get_certificates(self, options, **kwargs):
certs = []
for authority in self.get_authorities():
certs += self.get_cert(ca_name=authority)
return
return certs
def get_cert(self, ca_name=None, cert_handle=None):
"""
@ -355,7 +355,7 @@ class CloudCASourcePlugin(SourcePlugin, CloudCA):
certs.append({
'public_certificate': cert,
'intermediate_cert': "\n".join(intermediates),
'intermediate_certificate': "\n".join(intermediates),
'owner': c['ownerEmail']
})

View File

@ -53,9 +53,9 @@ class EmailNotificationPlugin(ExpirationNotificationPlugin):
# jinja template depending on type
template = env.get_template('{}.html'.format(event_type))
body = template.render(**kwargs)
body = template.render(dict(messages=message, hostname=current_app.config.get('LEMUR_HOSTNAME')))
s_type = current_app.config.get("LEMUR_EMAIL_SENDER").lower()
s_type = current_app.config.get("LEMUR_EMAIL_SENDER", 'ses').lower()
if s_type == 'ses':
conn = boto.connect_ses()
conn.send_email(current_app.config.get("LEMUR_EMAIL"), subject, body, targets, format='html')

View File

@ -1,4 +1,5 @@
from jinja2 import Environment, PackageLoader
import os
from jinja2 import Environment, FileSystemLoader
loader = PackageLoader('lemur')
loader = FileSystemLoader(searchpath=os.path.dirname(os.path.realpath(__file__)))
env = Environment(loader=loader)

View File

@ -52,8 +52,13 @@
<span style="color: #29abe0">Notice: Your SSL certificates are expiring!</span>
<hr />
</div>
<p>
Lemur, Netflix's SSL management portal has noticed that the following certificates are expiring soon, if you rely on these certificates
you should create new certificates to replace the certificates that are expiring. Visit https://lemur.netflix.com/#/certificates/create to reissue them.
you should create new certificates to replace the certificates that are expiring.
</p>
<p>
Visit https://{{ hostname }}/#/certificates/create to reissue them.
</p>
</td>
</tr>
{% for message in messages %}
@ -78,6 +83,12 @@
<tr>
<td>{{ message.creator }}</td>
</tr>
<tr>
<td><strong>Description</strong></td>
</tr>
<tr>
<td>{{ message.description }}</td>
</tr>
<tr>
<td><strong>Not Before</strong></td>
</tr>
@ -104,20 +115,6 @@
<td>Unknown</td>
</tr>
{% endif %}
<tr>
<td><strong>Associated ELBs</strong></td>
</tr>
{% if message.listeners %}
{% for name in message.listeners %}
<tr>
<td>{{ name }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td>None</td>
</tr>
{% endif %}
<tr>
<td><strong>Potentially Superseded by</strong> (Lemur's best guess)</td>
</tr>
@ -139,7 +136,7 @@
</tr>
<tr>
<td style="padding-top: 0px" align="center" valign="top">
<em style="font-style:italic; font-size: 12px; color: #aaa;">Lemur is broken regularly by <a style="color: #29abe0; text-decoration: none;" href="mailto:secops@netflix.com">Security Operations</a></em>
<em style="font-style:italic; font-size: 12px; color: #aaa;">Lemur is broken regularly by Netflix</em>
</td>
</tr>
</table>

View File

@ -39,6 +39,7 @@ def _disassociate_certs_from_source(current_certificates, found_certificates, so
def sync_create(certificate, source):
cert = cert_service.import_certificate(**certificate)
cert.description = "This certificate was automatically discovered by Lemur"
cert.sources.append(source)
sync_update_destination(cert, source)
database.update(cert)

View File

@ -107,7 +107,6 @@ angular.module('lemur')
title: certificate.name,
body: 'Successfully created!'
});
$location.path('/certificates');
},
function (response) {
toaster.pop({
@ -120,13 +119,20 @@ angular.module('lemur')
};
CertificateService.update = function (certificate) {
return LemurRestangular.copy(certificate).put().then(function () {
return LemurRestangular.copy(certificate).put().then(
function () {
toaster.pop({
type: 'success',
title: certificate.name,
body: 'Successfully updated!'
});
$location.path('certificates');
},
function (response) {
toaster.pop({
type: 'error',
title: certificate.name,
body: 'Failed to update ' + response.data.message
});
});
};
@ -138,7 +144,6 @@ angular.module('lemur')
title: certificate.name,
body: 'Successfully uploaded!'
});
$location.path('/certificates');
},
function (response) {
toaster.pop({

View File

@ -34,16 +34,6 @@ angular.module('lemur')
});
});
PluginService.getByType('destination').then(function (plugins) {
$scope.plugins = plugins;
_.each($scope.plugins, function (plugin) {
if (plugin.slug === $scope.destination.pluginName) {
plugin.pluginOptions = $scope.destination.destinationOptions;
$scope.destination.plugin = plugin;
}
});
});
$scope.save = function (destination) {
DestinationService.update(destination).then(function () {
$modalInstance.close();

View File

@ -21,7 +21,7 @@ angular.module('lemur')
}, {
total: 0, // length of data
getData: function ($defer, params) {
DomainApi.getList().then(function (data) {
DomainApi.getList(params.url()).then(function (data) {
params.total(data.total);
$defer.resolve(data);
});

View File

@ -87,7 +87,7 @@
</div>
<footer class="footer">
<div class="container">
<p class="text-muted">Lemur is maintained by <a href="mailto:secops@netflix.com">Security Operations</a>.</p>
<p class="text-muted">Lemur is broken regularly by <a href="https://github.com/Netflix/lemur.git">Netflix</a>.</p>
</div>
</footer>
</body>

View File

@ -15,6 +15,6 @@ def get_key():
:return:
"""
try:
return current_app.config.get('LEMUR_ENCRYPTION_KEY')
return current_app.config.get('LEMUR_ENCRYPTION_KEY').strip()
except RuntimeError:
return ''