Compare commits

..

47 Commits
0.1.3 ... 0.1.4

Author SHA1 Message Date
64c9b11c09 Merge pull request #106 from kevgliss/release
version bump
2015-09-28 14:55:24 -07:00
5f87c87751 version bump 2015-09-28 14:54:58 -07:00
70f9022aae Merge pull request #104 from kevgliss/guide
Adding connections in user guides
2015-09-24 16:28:52 -07:00
43683fe554 changing readme language 2015-09-24 16:09:34 -07:00
002de6f5e4 adding docker Link 2015-09-24 16:03:15 -07:00
63a388236e adding a link to our techblog 2015-09-24 14:36:14 -07:00
9560791002 Merge pull request #99 from pandragoq/patch-1
Update index.rst
2015-09-24 14:28:06 -07:00
ed93b5a2c5 SSL 2015-09-24 09:36:11 -07:00
21e4cc9f4d Adding connections in user guides 2015-09-24 09:21:08 -07:00
73e628cbdf Merge pull request #103 from kevgliss/required
Marking fields as required
2015-09-24 08:52:46 -07:00
7ebd0bf5d4 making fields required 2015-09-24 08:42:31 -07:00
3f1902e0fe Merge pull request #100 from ivuk/fix-typo
Fix typos in docs/administration/index.rst
2015-09-23 16:44:49 -07:00
3e546eaa21 Fix typos in docs/administration/index.rst 2015-09-23 21:00:52 +02:00
e70deb155d Update index.rst
Right package for postgres is postgresql in ubuntu.
2015-09-22 16:57:53 -07:00
4f289c790b Merge pull request #98 from stacybird/grammar_fix
Fix grammar in index.rst
2015-09-22 15:48:37 -07:00
c15f525167 Fix grammar in index.rst 2015-09-22 15:33:37 -07:00
bcbf642122 Merge pull request #96 from kevgliss/install
clearing up docs based on feedback
2015-09-22 14:54:11 -07:00
1559727f2d Making make build the static assets 2015-09-22 14:49:37 -07:00
a596793a9a clearing up docs based on feedback 2015-09-22 14:18:38 -07:00
862bf3f619 Merge pull request #94 from kevgliss/notifications
Notifications
2015-09-22 13:37:51 -07:00
83a86c06a4 Merge pull request #93 from pandragoq/patch-1
Update index.rst
2015-09-22 13:37:40 -07:00
06a69c09a0 Fixing a bug where notifications associated during certificate creation would not be respected. 2015-09-22 13:01:05 -07:00
6a24e88d9a removing pip install instructions until available 2015-09-22 10:22:12 -07:00
be6a5b859e adding notification example 2015-09-22 09:46:54 -07:00
2444191bf2 Update index.rst
Typo on nginx spelling
2015-09-21 17:43:56 -07:00
9226b1eb4a Merge pull request #92 from konklone/patch-1
Rename SSL to TLS in many places
2015-09-21 15:25:00 -07:00
3f53629175 Re 2015-09-21 18:16:40 -04:00
baef329a4d Rename SSL to TLS 2015-09-21 18:16:19 -04:00
b103fc7bfb Rename SSL to TLS 2015-09-21 18:16:04 -04:00
a3385bd2ac Rename SSL to TLS 2015-09-21 18:15:25 -04:00
7cb50c654b Rename SSL to TLS 2015-09-21 18:15:06 -04:00
52ba538037 Rename SSL to TLS 2015-09-21 18:14:12 -04:00
0a0460529f Merge pull request #89 from kevgliss/cleanup
Cleaning up unneed/unused files
2015-09-20 10:21:04 -07:00
fc0a884d5f Cleaning up unneed/unused files 2015-09-20 09:49:16 -07:00
dbbea29e75 Merge pull request #88 from kevgliss/requirements
adding additional requirements so rtd can build the documation correctly
2015-09-19 11:32:57 -07:00
bcd0aae8c6 adding additional requirements so rtd can build the documation correctly 2015-09-19 11:31:31 -07:00
50d3e6aff2 Merge pull request #87 from kevgliss/typo
fixing typo
2015-09-19 10:25:52 -07:00
1d45926122 fixing typo 2015-09-19 10:24:56 -07:00
45626c947c Merge pull request #86 from kevgliss/docs
More documentation fixes
2015-09-19 10:21:56 -07:00
d7ca6d4327 More documentation fixes 2015-09-19 10:12:12 -07:00
6411bd56e9 Merge pull request #85 from kevgliss/documentation
Documentation
2015-09-19 09:48:25 -07:00
1486e7b8f6 adding information about sub commands 2015-09-19 09:41:50 -07:00
e73f2bcb2b setting default theme 2015-09-19 09:38:39 -07:00
a412569ff7 aligning doc version with tagged version 2015-09-19 09:34:48 -07:00
387194d651 Merge pull request #84 from kevgliss/docs
Adding flask sphinx auto-docs
2015-09-18 17:29:17 -07:00
13d0359041 Adding flask sphinx auto-docs 2015-09-18 17:28:48 -07:00
365d927efb Update README.rst 2015-09-18 16:28:45 -07:00
19 changed files with 207 additions and 548 deletions

View File

@ -9,6 +9,8 @@ develop: update-submodules setup-git
pip install -e .
pip install "file://`pwd`#egg=lemur[dev]"
pip install "file://`pwd`#egg=lemur[tests]"
node_modules/.bin/gulp build
node_modules/.bin/gulp package
@echo ""
dev-docs:

View File

@ -13,17 +13,20 @@ Lemur
:target: https://lemur.readthedocs.org
:alt: Latest Docs
.. image:: https://magnum.travis-ci.com/Netflix/lemur.svg?branch=master
:target: https://magnum.travis-ci.com/Netflix/lemur
.. image:: https://travis-ci.org/Netflix/lemur.svg
:target: https://travis-ci.org/Netflix/lemur
Lemur manages TLS certificate creation. While not able to issue certificates itself, Lemur acts as a broker between CAs
and environments providing a central portal for developers to issue TLS certificates with 'sane' defaults.
Lemur manages SSL certificate creation. It provides a central portal for developers to issuer their own SSL certificates with 'sane' defaults.
It works on CPython 2.7, 3.3, 3.4. We deploy on Ubuntu and develop on OS X.
Project resources
=================
- `Lemur Blog Post <http://techblog.netflix.com/2015/09/introducing-lemur.html>`_
- `Documentation <http://lemur.readthedocs.org/>`_
- `Source code <https://github.com/netflix/lemur>`_
- `Issue tracker <https://github.com/netflix/lemur/issues>`_
- `Docker <https://github.com/Netflix/lemur-docker>`_

View File

@ -2,7 +2,7 @@ Configuration
=============
.. warning::
There are many secrets that Lemur uses that must be protected. All of these options are set via the Lemur configruation
There are many secrets that Lemur uses that must be protected. All of these options are set via the Lemur configuration
file. It is highly advised that you do not store your secrets in this file! Lemur provides functions
that allow you to encrypt files at rest and decrypt them when it's time for deployment. See :ref:`Credential Management <CredentialManagement>`
for more information.
@ -151,7 +151,7 @@ Notification Options
--------------------
Lemur currently has very basic support for notifications. Currently only expiration notifications are supported. Actual notification
is handling by the notification plugins that you have configured. Lemur ships with the 'Email' notification that allows expiration emails
is handled by the notification plugins that you have configured. Lemur ships with the 'Email' notification that allows expiration emails
to be sent to subscribers.
Templates for expiration emails are located under `lemur/plugins/lemur_email/templates` and can be modified for your needs.
@ -209,8 +209,9 @@ Lemur supports sending certification expiration notifications through SES and SM
Authority Options
-----------------
Authorities will each have their own configuration options. There are currently two plugins bundled with Lemur,
Verisign/Symantec and CloudCA
Authorities will each have their own configuration options. There is currently just one plugin bundled with Lemur,
Verisign/Symantec. Additional plugins may define additional options. Refer to the plugins own documentation
for those plugins.
.. data:: VERISIGN_URL
:noindex:
@ -221,7 +222,7 @@ Verisign/Symantec and CloudCA
.. data:: VERISIGN_PEM_PATH
:noindex:
This is the path to the mutual SSL certificate used for communicating with Verisign
This is the path to the mutual TLS certificate used for communicating with Verisign
.. data:: VERISIGN_FIRST_NAME
@ -253,26 +254,9 @@ Verisign/Symantec and CloudCA
This is the root to be used for your CA chain
.. data:: CLOUDCA_URL
:noindex:
This is the URL for CLoudCA API
.. data:: CLOUDCA_PEM_PATH
:noindex:
This is the path to the mutual SSL Certificate use for communicating with CLOUDCA
.. data:: CLOUDCA_BUNDLE
:noindex:
This is the path to the CLOUDCA certificate bundle
Authentication
--------------
Lemur currently supports Basic Authentication and Ping OAuth2 out of the box, additional flows can be added relatively easily
Lemur currently supports Basic Authentication and Ping OAuth2 out of the box, additional flows can be added relatively easily.
If you are not using Ping you do not need to configure any of these options.
For more information about how to use social logins, see: `Satellizer <https://github.com/sahat/satellizer>`_
@ -313,7 +297,7 @@ AWS Plugin Configuration
In order for Lemur to manage it's own account and other accounts we must ensure it has the correct AWS permissions.
.. note:: AWS usage is completely optional. Lemur can upload, find and manage SSL certificates in AWS. But is not required to do so.
.. note:: AWS usage is completely optional. Lemur can upload, find and manage TLS certificates in AWS. But is not required to do so.
Setting up IAM roles
--------------------
@ -326,7 +310,7 @@ Lemur uses to STS to talk to different accounts. For managing one account this i
LemurInstanceProfile is the IAM role you will launch your instance with. It actually has almost no rights. In fact it should really only be able to use STS to assume role to the Lemur role.
Here is are example polices for the LemurInstanceProfile:
Here are example policies for the LemurInstanceProfile:
SES-SendEmail
@ -368,7 +352,7 @@ Next we will create the the Lemur IAM role. Lemur
..note::
The default IAM role that Lemur assumes into is called `Lemur`, if you need to change this ensure you set `LEMUR_INSTANCE_PROFILE` to your role name in the configuration.
The default IAM role that Lemur assumes into is called `Lemur`, if you need to change this ensure you set `LEMUR_INSTANCE_PROFILE` to your role name in the configuration.
Here is an example policy for Lemur:
@ -495,7 +479,7 @@ Upgrading Lemur
===============
Lemur provides an easy way to upgrade between versions. Simply download the newest
version of Lemur from pypi and then apply any schema cahnges with the following command.
version of Lemur from pypi and then apply any schema changes with the following command.
.. code-block:: bash
@ -568,24 +552,6 @@ All commands default to `~/.lemur/lemur.conf.py` if a configuration is not speci
lemur db upgrade
.. data:: create_user
Creates new users within Lemur.
::
lemur create_user -u jim -e jim@example.com
.. data:: create_role
Creates new roles within Lemur.
::
lemur create_role -n example -d "a new role"
.. data:: check_revoked
Traverses every certificate that Lemur is aware of and attempts to understand it's validity.
@ -610,11 +576,31 @@ All commands default to `~/.lemur/lemur.conf.py` if a configuration is not speci
lemur sync -list
Sub-commands
------------
Lemur includes several sub-commands for interacting with Lemur such as creating new users, creating new roles and even
issuing certificates.
The best way to discover these commands is by using the built in help pages
::
lemur --help
and to get help on sub-commands
::
lemur certificates --help
Identity and Access Management
==============================
Lemur uses a Role Based Access Control (RBAC) mechanism to control which users have access to which resources. When a
user is first created in Lemur the can be assigned one or more roles. These roles are typically dynamically created
user is first created in Lemur they can be assigned one or more roles. These roles are typically dynamically created
depending on a external identity provider (Google, LDAP, etc.,) or are hardcoded within Lemur and associated with special
meaning.

View File

@ -57,7 +57,7 @@ copyright = u'2015, Netflix Inc.'
# The short X.Y version.
version = '0.1'
# The full version, including alpha/beta/rc tags.
release = '0.1.1'
release = '0.1.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@ -102,7 +102,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'alabaster'
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the

View File

@ -1,20 +0,0 @@
lemur_cloudca Package
=====================
:mod:`lemur_cloudca` Package
----------------------------
.. automodule:: lemur.plugins.lemur_cloudca
:noindex:
:members:
:undoc-members:
:show-inheritance:
:mod:`plugin` Module
--------------------
.. automodule:: lemur.plugins.lemur_cloudca.plugin
:noindex:
:members:
:undoc-members:
:show-inheritance:

View File

@ -8,7 +8,7 @@ Several interfaces exist for extending Lemur:
* Source (lemur.plugins.base.source)
* Notification (lemur.plugins.base.notification)
Each interface has its own function that will need to be defined in order for
Each interface has its own functions that will need to be defined in order for
your plugin to work correctly. See :ref:`Plugin Interfaces <PluginInterfaces>` for details.
@ -91,7 +91,7 @@ Issuer
Issuer plugins are used when you have an external service that creates certificates or authorities.
In the simple case the third party only issues certificates (Verisign, DigiCert, etc.).
If you have a third party or internal service that creates authorities (CloudCA, EJBCA, etc.), Lemur has you covered,
If you have a third party or internal service that creates authorities (EJBCA, etc.), Lemur has you covered,
it can treat any issuer plugin as both a source of creating new certificates as well as new authorities.

View File

@ -3,46 +3,15 @@ User Guide
These guides are quick tutorials on how to perform basic tasks in Lemur.
Create a New User
~~~~~~~~~~~~~~~~~
.. figure:: settings.png
From the settings dropdown select "Users"
.. figure:: create.png
In the user table select "Create"
.. figure:: create_user.png
Enter the username, email and password for the user. You can also assign any
roles that the user will need when they login. While there is no deletion
(we want to track creators forever) you can mark a user as 'Inactive' that will
not allow them to login to Lemur.
Create a New Role
~~~~~~~~~~~~~~~~~
.. figure:: settings.png
From the settings dropdown select "Roles"
.. figure:: create.png
In the role table select "Create"
.. figure:: create_role.png
Enter a role name and short description about the role. You can optionally store
a user/password on the role. This is useful if your authority require specific roles.
You can then accurately map those roles onto Lemur users. Also optional you can assign
users to your new role.
Create a New Authority
~~~~~~~~~~~~~~~~~~~~~~
Before Lemur can issue certificates you must configure the authority you wish use. Lemur itself does
not issue certificates, it relies on external CAs and the plugins associated with those CAs to create the certificate
that Lemur can then manage.
.. figure:: create.png
In the authority table select "Create"
@ -92,4 +61,43 @@ Import an Existing Certificate
a certificate name but you can override that by passing a value to the `Custom Name` field.
You can add notification options and upload the created certificate to a destination, both
of these are editable features and can be changed after the certificate has been created.
of these are editable features and can be changed after the certificate has been created.
Create a New User
~~~~~~~~~~~~~~~~~
.. figure:: settings.png
From the settings dropdown select "Users"
.. figure:: create.png
In the user table select "Create"
.. figure:: create_user.png
Enter the username, email and password for the user. You can also assign any
roles that the user will need when they login. While there is no deletion
(we want to track creators forever) you can mark a user as 'Inactive' that will
not allow them to login to Lemur.
Create a New Role
~~~~~~~~~~~~~~~~~
.. figure:: settings.png
From the settings dropdown select "Roles"
.. figure:: create.png
In the role table select "Create"
.. figure:: create_role.png
Enter a role name and short description about the role. You can optionally store
a user/password on the role. This is useful if your authority require specific roles.
You can then accurately map those roles onto Lemur users. Also optional you can assign
users to your new role.

View File

@ -1,8 +1,8 @@
Lemur
=====
Lemur is a SSL management service. It attempts to help track and create certificates. By removing common issues with
CSR creation it gives normal developers 'sane' SSL defaults and helps security teams push SSL usage throughout an organization.
Lemur is a TLS management service. It attempts to help track and create certificates. By removing common issues with
CSR creation it gives normal developers 'sane' TLS defaults and helps security teams push TLS usage throughout an organization.
Installation
------------

View File

@ -6,18 +6,19 @@ There are several steps needed to make Lemur production ready. Here we focus on
Basics
======
Because of the sensitivity of the information stored and maintain by Lemur it is important that you follow standard host hardening practices:
Because of the sensitivity of the information stored and maintained by Lemur it is important that you follow standard host hardening practices:
- Run Lemur with a limited user
- Disabled any unneeded service
- Disabled any unneeded services
- Enable remote logging
- Restrict access to host
.. _CredentialManagement:
Credential Management
---------------------
Lemur often contains credentials such as mutual SSL keys that are used to communicate with third party resources and for encrypting stored secrets. Lemur comes with the ability
Lemur often contains credentials such as mutual TLS keys or API tokens that are used to communicate with third party resources and for encrypting stored secrets. Lemur comes with the ability
to automatically encrypt these keys such that your keys not be in clear text.
The keys are located within lemur/keys and broken down by environment
@ -30,7 +31,7 @@ and
``lemur unlock``
If you choose to use this feature ensure that the KEY are decrypted before Lemur starts as it will have trouble communicating with the database otherwise.
If you choose to use this feature ensure that the keys are decrypted before Lemur starts as it will have trouble communicating with the database otherwise.
Entropy
-------
@ -56,8 +57,8 @@ For additional information about OpenSSL entropy issues:
- `Managing and Understanding Entropy Usage <https://www.blackhat.com/docs/us-15/materials/us-15-Potter-Understanding-And-Managing-Entropy-Usage.pdf>`_
SSL
====
TLS/SSL
=======
Nginx
-----
@ -127,10 +128,10 @@ You can make some adjustments to get a better user experience::
}
This makes Nginx serve the favicon and static files which is is much better at than python.
This makes Nginx serve the favicon and static files which it is much better at than python.
It is highly recommended that you deploy SSL when deploying Lemur. This may be obvious given Lemur's purpose but the
sensitive nature of Lemur and what it controls makes this essential. This is a sample config for Lemur that also terminates SSL::
It is highly recommended that you deploy TLS when deploying Lemur. This may be obvious given Lemur's purpose but the
sensitive nature of Lemur and what it controls makes this essential. This is a sample config for Lemur that also terminates TLS::
server_tokens off;
add_header X-Frame-Options DENY;
@ -218,7 +219,7 @@ An example apache config::
...
</VirtualHost>
Also included in the configurations above are several best practices when it comes to deploying SSL. Things like enabling
Also included in the configurations above are several best practices when it comes to deploying TLS. Things like enabling
HSTS, disabling vulnerable ciphers are all good ideas when it comes to deploying Lemur into a production environment.
.. note::

View File

@ -14,9 +14,9 @@ Some basic prerequisites which you'll need in order to run Lemur:
* A UNIX-based operating system. We test on Ubuntu, develop on OS X
* Python 2.7
* PostgreSQL
* Ngnix
* Nginx
.. note:: Lemur was built with in AWS in mind. This means that things such as databases (RDS), mail (SES), and SSL (ELB),
.. note:: Lemur was built with in AWS in mind. This means that things such as databases (RDS), mail (SES), and TLS (ELB),
are largely handled for us. Lemur does **not** require AWS to function. Our guides and documentation try to be
be as generic as possible and are not intended to document every step of launching Lemur into a given environment.
@ -53,24 +53,7 @@ dependencies::
And optionally if your database is going to be on the same host as the webserver::
$ sudo apt-get install postgres
Installing Lemur
----------------
Once you've got the environment setup, you can install Lemur and all its dependencies with
the same command you used to grab virtualenv::
pip install -U lemur
Once everything is installed, you should be able to execute the Lemur CLI, via ``lemur``, and get something
like the following:
.. code-block:: bash
$ lemur
usage: lemur [--config=/path/to/settings.py] [command] [options]
$ sudo apt-get install postgresql
Installing from Source
@ -78,7 +61,14 @@ Installing from Source
If you're installing the Lemur source (e.g. from git), you'll also need to install **npm**.
Once your system is prepared, symlink your source into the virtualenv:
Once your system is prepared, ensure that you are in the virtualenv:
.. code-block:: bash
$ which python
And then run:
.. code-block:: bash
@ -171,8 +161,8 @@ Setup a Reverse Proxy
---------------------
By default, Lemur runs on port 5000. Even if you change this, under normal conditions you won't be able to bind to
port 80. To get around this (and to avoid running Lemur as a privileged user, which you shouldn't), we recommend
you setup a simple web proxy.
port 80. To get around this (and to avoid running Lemur as a privileged user, which you shouldn't), we need setup a
simple web proxy. There are many different web servers you can use for this, we like and recommend Nginx.
Proxying with Nginx
~~~~~~~~~~~~~~~~~~~
@ -280,7 +270,9 @@ Decrypts sensitive key material - Used to decrypt the secrets stored in source d
What's Next?
------------
The above gets you going, but for production there are several different security considerations to take into account,
Get familiar with how Lemur works by reviewing the :doc:`../guide/index`. When you're ready
see :doc:`../production/index` for more details on how to configure Lemur for production.
Remember the above just gets you going, but for production there are several different security considerations to take into account,
remember Lemur is handling sensitive data and security is imperative.
See :doc:`../production/index` for more details on how to configure Lemur for production.

View File

@ -2,4 +2,28 @@ Jinja2>=2.3
Pygments>=1.2
Sphinx>=1.3
docutils>=0.7
markupsafe
markupsafe
sphinxcontrib-httpdomain
Flask==0.10.1
Flask-RESTful==0.3.3
Flask-SQLAlchemy==2.0
Flask-Script==2.0.5
Flask-Migrate==1.4.0
Flask-Bcrypt==0.6.2
Flask-Principal==0.4.0
Flask-Mail==0.9.1
SQLAlchemy-Utils==0.30.11
BeautifulSoup4
requests==2.7.0
psycopg2==2.6.1
arrow==0.5.4
boto==2.38.0 # we might make this optional
six==1.9.0
gunicorn==19.3.0
pycrypto==2.6.1
cryptography==1.0.1
pyopenssl==0.15.1
pyjwt==1.0.1
xmltodict==0.9.2
lockfile==0.10.2
future==0.15.0

View File

@ -232,7 +232,7 @@ def create(**kwargs):
database.update_list(cert, 'notifications', Notification, kwargs.get('notifications'))
# create default notifications for this certificate if none are provided
notifications = []
notifications = cert.notifications
if not kwargs.get('notifications'):
notification_name = "DEFAULT_{0}".format(cert.owner.split('@')[0].upper())
notifications += notification_service.create_default_expiration_notifications(notification_name, [cert.owner])

View File

@ -208,6 +208,46 @@ class CertificatesList(AuthenticatedResource):
"notAfter": "2015-06-17T15:21:08",
"description": "dsfdsf"
},
"notifications": [
{
"description": "Default 30 day expiration notification",
"notificationOptions": [
{
"name": "interval",
"required": true,
"value": 30,
"helpMessage": "Number of days to be alert before expiration.",
"validation": "^\\d+$",
"type": "int"
},
{
"available": [
"days",
"weeks",
"months"
],
"name": "unit",
"required": true,
"value": "days",
"helpMessage": "Interval unit",
"validation": "",
"type": "select"
},
{
"name": "recipients",
"required": true,
"value": "bob@example.com",
"helpMessage": "Comma delimited list of email addresses",
"validation": "^([\\w+-.%]+@[\\w-.]+\\.[A-Za-z]{2,4},?)+$",
"type": "str"
}
],
"label": "DEFAULT_KGLISSON_30_DAY",
"pluginName": "email-notification",
"active": true,
"id": 7
}
],
"extensions": {
"basicConstraints": {},
"keyUsage": {
@ -276,18 +316,17 @@ class CertificatesList(AuthenticatedResource):
self.reqparse.add_argument('extensions', type=dict, location='json')
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
self.reqparse.add_argument('notifications', type=list, default=[], location='json')
self.reqparse.add_argument('owner', type=str, location='json')
self.reqparse.add_argument('validityStart', type=str, location='json') # TODO validate
self.reqparse.add_argument('validityEnd', type=str, location='json') # TODO validate
self.reqparse.add_argument('authority', type=valid_authority, location='json')
self.reqparse.add_argument('description', type=str, location='json')
self.reqparse.add_argument('country', type=str, location='json')
self.reqparse.add_argument('state', type=str, location='json')
self.reqparse.add_argument('location', type=str, location='json')
self.reqparse.add_argument('organization', type=str, location='json')
self.reqparse.add_argument('organizationalUnit', type=str, location='json')
self.reqparse.add_argument('owner', type=str, location='json')
self.reqparse.add_argument('commonName', type=str, location='json')
self.reqparse.add_argument('authority', type=valid_authority, location='json', required=True)
self.reqparse.add_argument('description', type=str, location='json', required=True)
self.reqparse.add_argument('country', type=str, location='json', required=True)
self.reqparse.add_argument('state', type=str, location='json', required=True)
self.reqparse.add_argument('location', type=str, location='json', required=True)
self.reqparse.add_argument('organization', type=str, location='json', required=True)
self.reqparse.add_argument('organizationalUnit', type=str, location='json', required=True)
self.reqparse.add_argument('owner', type=str, location='json', required=True)
self.reqparse.add_argument('commonName', type=str, location='json', required=True)
args = self.reqparse.parse_args()

View File

@ -112,13 +112,6 @@ SQLALCHEMY_DATABASE_URI = 'postgresql://lemur:lemur@localhost:5432/lemur'
# These will be dependent on which 3rd party that Lemur is
# configured to use.
# CLOUDCA_URL = ''
# CLOUDCA_PEM_PATH = ''
# CLOUDCA_BUNDLE = ''
# number of years to issue if not specified
# CLOUDCA_DEFAULT_VALIDITY = 2
# VERISIGN_URL = ''
# VERISIGN_PEM_PATH = ''
# VERISIGN_FIRST_NAME = ''

View File

@ -1,5 +0,0 @@
try:
VERSION = __import__('pkg_resources') \
.get_distribution(__name__).version
except Exception as e:
VERSION = 'unknown'

View File

@ -1,364 +0,0 @@
"""
.. module: lemur.common.services.issuers.plugins.cloudca
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import re
import ssl
import base64
from json import dumps
import arrow
import requests
from requests.adapters import HTTPAdapter
from requests.exceptions import ConnectionError
from flask import current_app
from lemur.exceptions import LemurException
from lemur.plugins.bases import IssuerPlugin, SourcePlugin
from lemur.plugins import lemur_cloudca as cloudca
from lemur.authorities import service as authority_service
class CloudCAException(LemurException):
def __init__(self, message):
self.message = message
current_app.logger.error(self)
def __str__(self):
return repr("CloudCA request failed: {0}".format(self.message))
class CloudCAHostNameCheckingAdapter(HTTPAdapter):
def cert_verify(self, conn, url, verify, cert):
super(CloudCAHostNameCheckingAdapter, self).cert_verify(conn, url, verify, cert)
conn.assert_hostname = False
def remove_none(options):
"""
Simple function that traverse the options and removed any None items
CloudCA really dislikes null values.
:param options:
:return:
"""
new_dict = {}
for k, v in options.items():
if v:
new_dict[k] = v
# this is super hacky and gross, cloudca doesn't like null values
if new_dict.get('extensions'):
if len(new_dict['extensions']['subAltNames']['names']) == 0:
del new_dict['extensions']['subAltNames']
return new_dict
def get_default_issuance(options):
"""
Gets the default time range for certificates
:param options:
:return:
"""
if not options.get('validityStart') and not options.get('validityEnd'):
start = arrow.utcnow()
options['validityStart'] = start.floor('second').isoformat()
options['validityEnd'] = start.replace(years=current_app.config.get('CLOUDCA_DEFAULT_VALIDITY'))\
.ceil('second').isoformat()
return options
def convert_to_pem(der):
"""
Converts DER to PEM Lemur uses PEM internally
:param der:
:return:
"""
decoded = base64.b64decode(der)
return ssl.DER_cert_to_PEM_cert(decoded)
def convert_date_to_utc_time(date):
"""
Converts a python `datetime` object to the current date + current time in UTC.
:param date:
:return:
"""
d = arrow.get(date)
return arrow.utcnow().replace(year=d.naive.year).replace(month=d.naive.month).replace(day=d.naive.day)\
.replace(microsecond=0)
def process_response(response):
"""
Helper function that processes responses from CloudCA.
:param response:
:return: :raise CloudCAException:
"""
if response.status_code == 200:
res = response.json()
if res['returnValue'] != 'success':
current_app.logger.debug(res)
if res.get('data'):
raise CloudCAException(" ".join([res['returnMessage'], res['data']['dryRunResultMessage']]))
else:
raise CloudCAException(res['returnMessage'])
else:
raise CloudCAException("There was an error with your request: {0}".format(response.status_code))
return response.json()
def get_auth_data(ca_name):
"""
Creates the authentication record needed to authenticate a user request to CloudCA.
:param ca_name:
:return: :raise CloudCAException:
"""
role = authority_service.get_authority_role(ca_name)
if role:
return {
"authInfo": {
"credType": "password",
"credentials": {
"username": role.username,
"password": role.password # we only decrypt when we need to
}
}
}
raise CloudCAException("You do not have the required role to issue certificates from {0}".format(ca_name))
class CloudCA(object):
def __init__(self, *args, **kwargs):
self.session = requests.Session()
self.session.mount('https://', CloudCAHostNameCheckingAdapter())
self.url = current_app.config.get('CLOUDCA_URL')
if current_app.config.get('CLOUDCA_PEM_PATH') and current_app.config.get('CLOUDCA_BUNDLE'):
self.session.cert = current_app.config.get('CLOUDCA_PEM_PATH')
self.ca_bundle = current_app.config.get('CLOUDCA_BUNDLE')
else:
current_app.logger.warning(
"No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA"
)
super(CloudCA, self).__init__(*args, **kwargs)
def post(self, endpoint, data):
"""
HTTP POST to CloudCA
:param endpoint:
:param data:
:return:
"""
data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))
# we set a low timeout, if cloudca is down it shouldn't bring down
# lemur
try:
response = self.session.post(self.url + endpoint, data=data, timeout=10, verify=self.ca_bundle)
except ConnectionError:
raise Exception("Could not talk to CloudCA, is it up?")
return process_response(response)
def get(self, endpoint):
"""
HTTP GET to CloudCA
:param endpoint:
:return:
"""
try:
response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)
except ConnectionError:
raise Exception("Could not talk to CloudCA, is it up?")
return process_response(response)
def random(self, length=10):
"""
Uses CloudCA as a decent source of randomness.
:param length:
:return:
"""
endpoint = '/v1/random/{0}'.format(length)
response = self.session.get(self.url + endpoint, verify=self.ca_bundle)
return response
def get_authorities(self):
"""
Retrieves authorities that were made outside of Lemur.
:return:
"""
endpoint = '{0}/listCAs'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))
authorities = []
for ca in self.get(endpoint)['data']['caList']:
try:
authorities.append(ca['caName'])
except AttributeError:
current_app.logger.error("No authority has been defined for {}".format(ca['caName']))
return authorities
class CloudCAIssuerPlugin(IssuerPlugin, CloudCA):
title = 'CloudCA'
slug = 'cloudca-issuer'
description = 'Enables the creation of certificates from the cloudca API.'
version = cloudca.VERSION
author = 'Kevin Glisson'
author_url = 'https://github.com/netflix/lemur'
def create_authority(self, options):
"""
Creates a new certificate authority
:param options:
:return:
"""
# this is weird and I don't like it
endpoint = '{0}/createCA'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))
options['caDN']['email'] = options['ownerEmail']
if options['caType'] == 'subca':
options = dict(options.items() + self.auth_data(options['caParent']).items())
options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()
options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).isoformat()
options['description'] = re.sub(r'[^a-zA-Z0-9]', '', options['caDescription'])
try:
response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10,
verify=self.ca_bundle)
except ConnectionError:
raise Exception("Could not communicate with CloudCA, is it up?")
json = process_response(response)
roles = []
for cred in json['data']['authInfo']:
role = {
'username': cred['credentials']['username'],
'password': cred['credentials']['password'],
'name': "_".join([options['caName'], cred['credentials']['username']])
}
roles.append(role)
if options['caType'] == 'subca':
cert = convert_to_pem(json['data']['certificate'])
else:
cert = convert_to_pem(json['data']['rootCertificate'])
intermediates = []
for i in json['data']['intermediateCertificates']:
intermediates.append(convert_to_pem(i))
return cert, "".join(intermediates), roles,
def create_certificate(self, csr, options):
"""
Creates a new certificate from cloudca
If no start and end date are specified the default issue range
will be used.
:param csr:
:param options:
"""
endpoint = '{0}/enroll'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))
# lets default to two years if it's not specified
# we do some last minute data massaging
options = get_default_issuance(options)
cloudca_options = {
'extensions': options['extensions'],
'validityStart': convert_date_to_utc_time(options['validityStart']).isoformat(),
'validityEnd': convert_date_to_utc_time(options['validityEnd']).isoformat(),
'creator': options['creator'],
'ownerEmail': options['owner'],
'caName': options['authority'].name,
'csr': csr,
'comment': re.sub(r'[^a-zA-Z0-9]', '', options['description'])
}
response = self.post(endpoint, remove_none(cloudca_options))
# we return a concatenated list of intermediate because that is what aws
# expects
cert = convert_to_pem(response['data']['certificate'])
intermediates = [convert_to_pem(response['data']['rootCertificate'])]
for i in response['data']['intermediateCertificates']:
intermediates.append(convert_to_pem(i))
return cert, "".join(intermediates),
class CloudCASourcePlugin(SourcePlugin, CloudCA):
title = 'CloudCA'
slug = 'cloudca-source'
description = 'Discovers all SSL certificates in CloudCA'
version = cloudca.VERSION
author = 'Kevin Glisson'
author_url = 'https://github.com/netflix/lemur'
options = {
'pollRate': {'type': 'int', 'default': '60'}
}
def get_certificates(self, options, **kwargs):
certs = []
for authority in self.get_authorities():
certs += self.get_cert(ca_name=authority)
return certs
def get_cert(self, ca_name=None, cert_handle=None):
"""
Returns a given cert from CloudCA.
:param ca_name:
:param cert_handle:
:return:
"""
endpoint = '{0}/getCert'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))
response = self.session.post(self.url + endpoint, data=dumps({'caName': ca_name}), timeout=10,
verify=self.ca_bundle)
raw = process_response(response)
certs = []
for c in raw['data']['certList']:
cert = convert_to_pem(c['certValue'])
intermediates = []
for i in c['intermediateCertificates']:
intermediates.append(convert_to_pem(i))
certs.append({
'public_certificate': cert,
'intermediate_certificate': "\n".join(intermediates),
'owner': c['ownerEmail']
})
return certs

View File

@ -49,7 +49,7 @@
<td class="container-padding" bgcolor="#ffffff" style="background-color: #ffffff; padding-left: 30px; padding-right: 30px; font-size: 14px; line-height: 20px; font-family: Helvetica, sans-serif; color: #333;">
<br />
<div style="font-weight: bold; font-size: 18px; line-height: 24px; color: #202d3b">
<span style="color: #29abe0">Notice: Your SSL certificates are expiring!</span>
<span style="color: #29abe0">Notice: Your TLS certificates are expiring!</span>
<hr />
</div>
<p>

View File

@ -1,12 +1,12 @@
<div class="jumbotron">
<h1>Hey there!</h1>
<p>Welcome to Lemur! A central portal for all (most) of your SSL needs.</p>
<p>Welcome to Lemur! A central portal for all (most) of your TLS needs.</p>
<p><a href="/#/certificates/create" class="btn btn-primary btn-lg" role="button">Create a Certificate</a></p>
</div>
<div class="row featurette">
<div class="col-md-10">
<h2 class="featurette-heading">SSL In The Cloud <span class="text-muted">Encrypt it all </span></h2>
<h2 class="featurette-heading">TLS In The Cloud <span class="text-muted">Encrypt it all </span></h2>
</div>
</div>

View File

@ -2,7 +2,7 @@
Lemur
=====
Is an SSL management and orchestration tool.
Is a TLS management and orchestration tool.
:copyright: (c) 2015 by Netflix, see AUTHORS for more
:license: Apache, see LICENSE for more details.
@ -112,17 +112,19 @@ class BuildStatic(Command):
def run(self):
log.info("running [npm install --quiet] in {0}".format(ROOT))
try:
check_output(['npm', 'install', '--quiet'], cwd=ROOT)
check_output(['npm', 'install', '--quiet'], cwd=ROOT)
log.info("running [gulp build]")
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'build'], cwd=ROOT)
log.info("running [gulp package]")
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'package'], cwd=ROOT)
log.info("running [gulp build]")
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'build'], cwd=ROOT)
log.info("running [gulp package]")
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'package'], cwd=ROOT)
except Exception as e:
log.warn("Unable to build static content")
setup(
name='lemur',
version='0.1.3',
version='0.1.4',
author='Kevin Glisson',
author_email='kglisson@netflix.com',
url='https://github.com/netflix/lemur',
@ -149,8 +151,6 @@ setup(
],
'lemur.plugins': [
'verisign_issuer = lemur.plugins.lemur_verisign.plugin:VerisignIssuerPlugin',
'cloudca_issuer = lemur.plugins.lemur_cloudca.plugin:CloudCAIssuerPlugin',
'cloudca_source = lemur.plugins.lemur_cloudca.plugin:CloudCASourcePlugin',
'aws_destination = lemur.plugins.lemur_aws.plugin:AWSDestinationPlugin',
'aws_source = lemur.plugins.lemur_aws.plugin:AWSSourcePlugin',
'email_notification = lemur.plugins.lemur_email.plugin:EmailNotificationPlugin',