Merge remote-tracking branch 'upstream/master' into elb-ssl-automation
This commit is contained in:
commit
96c3ab7f9d
|
@ -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
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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']
|
||||
})
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 ''
|
||||
|
|
Loading…
Reference in New Issue