Compare commits

...

3087 Commits
0.1.5 ... 0.8.0

Author SHA1 Message Date
31fde453a8 Merge pull request #3250 from jtschladen/update-docs
Remove python-ldap from requirements-docs.txt
2020-11-13 18:27:34 -08:00
8a2621de44 Merge branch 'master' into update-docs 2020-11-13 18:18:09 -08:00
95b24cbadc Merge pull request #3251 from hosseinsh/release-0.8.0
preparing for release 0.8.0
2020-11-13 17:49:26 -08:00
afbd8b6930 Merge branch 'master' into update-docs 2020-11-13 17:39:37 -08:00
50483c01da preparing for release 0.8.0 2020-11-13 17:37:01 -08:00
bb11ff9f60 Remove python-ldap from requirements-docs.txt 2020-11-13 17:31:40 -08:00
2d2ecdeee2 Merge pull request #3245 from hosseinsh/aws-pluging-S3-remove-acme-token
AWS plugin s3 adding remove acme token
2020-11-13 12:01:07 -08:00
02c7093b32 Merge branch 'master' into aws-pluging-S3-remove-acme-token 2020-11-11 16:48:00 -08:00
95854c6f68 Merge pull request #3167 from unic/feature/acme-http-challenge
Add support for acme http-01 challenge
2020-11-11 16:47:08 -08:00
9fd3440cf6 Cleanup tests 2020-11-11 12:21:06 +01:00
453826c59c Get rid of unnecessary current_app patches 2020-11-11 12:11:13 +01:00
2b01bdb471 Refactor sftp plugin, to avoid duplicate code 2020-11-11 11:58:36 +01:00
648565d3e9 Improve exception handling in lemur_sftp, Add Authentication failure test 2020-11-11 11:45:57 +01:00
e12ee1d89c Implement delete file and delete token tests 2020-11-11 11:23:55 +01:00
ae7a044b9c Add test for upload_acme_token 2020-11-11 11:13:09 +01:00
df11a03bde Implement sftp upload tests 2020-11-11 11:02:15 +01:00
6e5aa4e979 Deduplicate chain/certificate extraction 2020-11-11 08:46:55 +01:00
5cdd88e033 Remove unnecessary token from delete_acme_token 2020-11-11 08:34:40 +01:00
7b1beb62b6 Add directory uri, to exception message 2020-11-11 08:05:59 +01:00
7a7f05ec9e Fix comments in sftp delete_files 2020-11-11 08:05:37 +01:00
252f84cf21 adding also response to upload acme token, just for future use-cases 2020-11-10 17:46:00 -08:00
ea77ef08aa testing for delete 2020-11-10 17:45:02 -08:00
8efa682858 add delete acme token 2020-11-10 17:43:35 -08:00
31b5f3df86 Remove duplicate code for revoke_certificate 2020-11-10 18:18:45 +01:00
fba1fdcc34 Improve exception handling during http challenge 2020-11-10 18:06:19 +01:00
9ebcdfc189 Check authorization state and skip already validated challenges 2020-11-10 17:10:43 +01:00
6ffe7bc526 Check if challenges are already validated, and skip them if possible 2020-11-10 16:47:56 +01:00
960b8e78e3 Implement cleanup_acme_token for http challenge 2020-11-10 16:22:25 +01:00
99ca0ac78d Add context fix to tests, Add regex, Flake8 2020-11-10 15:32:04 +01:00
4a181aff6e Merge branch 'master' into feature/acme-http-challenge 2020-11-10 15:20:47 +01:00
f42d9539fc Merge pull request #3237 from ExaneServerTeam/bugfix/group-lookup-fix-referral
Fix group lookup when AD DNS Referal is in lookup path
2020-11-09 14:38:12 -08:00
018f4a4b77 Merge branch 'master' into bugfix/group-lookup-fix-referral 2020-11-09 14:30:01 -08:00
a7a5a8fb72 Merge pull request #3244 from Netflix/dependabot/pip/boto3-1.16.14 2020-11-09 22:29:14 +00:00
65d9ac6a0f Bump boto3 from 1.16.9 to 1.16.14
Bumps [boto3](https://github.com/boto/boto3) from 1.16.9 to 1.16.14.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.16.9...1.16.14)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-09 22:22:52 +00:00
11386c6c7e Merge pull request #3243 from Netflix/dependabot/pip/botocore-1.19.14 2020-11-09 22:20:20 +00:00
7ec2860f88 Bump botocore from 1.19.9 to 1.19.14
Bumps [botocore](https://github.com/boto/botocore) from 1.19.9 to 1.19.14.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.19.9...1.19.14)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-09 21:49:57 +00:00
76fb92d970 Merge pull request #3241 from Netflix/dependabot/pip/certifi-2020.11.8 2020-11-09 21:47:20 +00:00
4c6645ca04 Bump certifi from 2020.6.20 to 2020.11.8
Bumps [certifi](https://github.com/certifi/python-certifi) from 2020.6.20 to 2020.11.8.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2020.06.20...2020.11.08)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-09 21:40:24 +00:00
021f530810 Merge pull request #3240 from Netflix/dependabot/pip/faker-4.14.2 2020-11-09 21:37:39 +00:00
a74b8aed15 Bump faker from 4.14.0 to 4.14.2
Bumps [faker](https://github.com/joke2k/faker) from 4.14.0 to 4.14.2.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/faker/compare/v4.14.0...v4.14.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-09 21:29:51 +00:00
307e4693c6 Merge pull request #3239 from Netflix/dependabot/pip/pytest-flask-1.1.0 2020-11-09 21:27:46 +00:00
d3e8921731 Bump pytest-flask from 1.0.0 to 1.1.0
Bumps [pytest-flask](https://github.com/pytest-dev/pytest-flask) from 1.0.0 to 1.1.0.
- [Release notes](https://github.com/pytest-dev/pytest-flask/releases)
- [Changelog](https://github.com/pytest-dev/pytest-flask/blob/master/docs/changelog.rst)
- [Commits](https://github.com/pytest-dev/pytest-flask/compare/1.0.0...1.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-09 21:20:48 +00:00
40ef6d06d7 Merge pull request #3192 from hosseinsh/expanding-S3-plugin
Expanding the S3 plugin with acme-http01-token support
2020-11-09 13:18:38 -08:00
57208fe198 Fix group lookup when AD DNS Referal is in lookup path
Fix an issue when the DNS AD referal is in the path.
An Exception is raised, with the following stacktrace::

  Traceback (most recent call last):
    File "/www/lemur/lemur/auth/views.py", line 317, in post
      user = ldap_principal.authenticate()
    File "/www/lemur/lemur/auth/ldap.py", line 147, in authenticate
      self._bind()
    File "/www/lemur/lemur/auth/ldap.py", line 216, in _bind
      self.ldap_groups.append(values["cn"][0].decode("ascii"))
  TypeError: list indices must be integers or slices, not str

This is issue is trigerred by some extra rows that referrences
the DNS subtree::

   ['ldaps://DomainDnsZones.xxxx']

Limiting the extraction to the expected dicts fix this issue.
2020-11-09 09:40:28 +01:00
7c779d6283 regex 2020-11-06 22:41:48 -08:00
519411b309 regex 2020-11-06 22:40:55 -08:00
6fe855e824 Merge branch 'master' into expanding-S3-plugin 2020-11-05 12:12:45 -08:00
cafc2c1d80 Merge pull request #3236 from charhate/ecc_changes
Version updates
2020-11-05 12:12:04 -08:00
320667935d flake8 version 3.8.4 2020-11-04 19:09:34 -08:00
894b74f523 Merge branch 'master' into expanding-S3-plugin 2020-11-04 18:39:23 -08:00
206d010c9a Version updates and making lint happy 2020-11-04 18:23:39 -08:00
92a555ba4b Merge pull request #3233 from charhate/ecc_changes
Fixing build and test warnings
2020-11-04 18:16:35 -08:00
7d2ce61303 Updating comment for application context 2020-11-04 18:04:57 -08:00
8990209411 Merge branch 'master' into ecc_changes 2020-11-04 17:00:09 -08:00
fb25d82eea Merge pull request #3235 from jtschladen/stop-repeating-certs-in-security-emails
Stop repeating certs when sending expiration notifications to security team email
2020-11-04 11:09:04 -08:00
4d32adb3bf Merge branch 'master' into stop-repeating-certs-in-security-emails 2020-11-04 10:58:20 -08:00
4cc0f6bb60 Stop repeating certs when sending expiration notifications to security team email 2020-11-04 10:53:27 -08:00
ab014873d0 invalid escape sequence warning for not an escape char 2020-11-03 19:33:13 -08:00
003779a112 Mock fix for DeprecationWarning: callable is None 2020-11-03 19:27:41 -08:00
dc7497e29d Fix Working outside of application context Test Failures in dev 2020-11-03 19:05:18 -08:00
2a61206fdf Merge branch 'master' into expanding-S3-plugin 2020-11-03 17:25:06 -08:00
c71dbcb0a0 Fix duplicate tests 2020-11-03 09:48:25 +01:00
bc564b574d Merge branch 'master' into feature/acme-http-challenge 2020-11-03 09:36:37 +01:00
3d64aa8d11 Fixing DeprecationWarning: callable is None: another syntax 2020-11-02 18:58:38 -08:00
86b2cfbe4a invalid escape sequence \ 2020-11-02 18:45:38 -08:00
b75bd56546 Check if ValueError assert works old way 2020-11-02 18:29:22 -08:00
6922d34825 invalid escape sequence \ 2020-11-02 18:16:15 -08:00
825a001a8b pass algorithm to jwt.decode() during login
api_jwt.py : pass "algorithms" argument when calling decode(). This argument will be mandatory in a future version
2020-11-02 17:37:04 -08:00
d88da028b1 Replace binary with LargeBinary
https://flask-appbuilder.readthedocs.io/en/latest/_modules/sqlalchemy/sql/sqltypes.html
2020-11-02 17:37:04 -08:00
d821024e35 Fixing DeprecationWarning: callable is None 2020-11-02 17:37:04 -08:00
2dac95c6fb Replacing PassiveDefault (deprecated) with DefaultClause 2020-11-02 17:37:04 -08:00
4ffced70f8 backref cannot be set for viewonly relationship
will be deprecated in SQLAlchemy 1.4, and will be disallowed in a future release
2020-11-02 17:37:04 -08:00
634339eac6 replacing imp (deprecated) with importlib 2020-11-02 17:37:04 -08:00
5569c9d8e1 Merge pull request #3232 from Netflix/hosseinsh-travis-ci-email
Disabling travis-ci emaill notification on success
2020-11-02 17:25:21 -08:00
4659df42d5 better formatting 2020-11-02 15:49:11 -08:00
e9b0e2eca7 Merge branch 'master' into hosseinsh-travis-ci-email 2020-11-02 14:27:03 -08:00
9ad9f349ba Merge pull request #3231 from Netflix/dependabot/pip/boto3-1.16.9 2020-11-02 19:28:40 +00:00
fa620e539d Bump boto3 from 1.16.5 to 1.16.9
Bumps [boto3](https://github.com/boto/boto3) from 1.16.5 to 1.16.9.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.16.5...1.16.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-02 19:21:05 +00:00
c4e3998715 Merge pull request #3228 from Netflix/dependabot/pip/pytest-6.1.2 2020-11-02 19:18:14 +00:00
fc2fce6c0b Bump pytest from 6.1.1 to 6.1.2
Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.1.1 to 6.1.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.1.1...6.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-02 19:10:18 +00:00
3f46b0b6d7 Merge pull request #3227 from Netflix/dependabot/pip/sphinx-3.3.0 2020-11-02 19:08:20 +00:00
2331638ed1 Bump sphinx from 3.2.1 to 3.3.0
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.2.1 to 3.3.0.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.2.1...v3.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-02 19:01:19 +00:00
01f31772bb Merge pull request #3229 from Netflix/dependabot/pip/pre-commit-2.8.2 2020-11-02 18:59:04 +00:00
771c272895 Bump pre-commit from 2.7.1 to 2.8.2
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.7.1 to 2.8.2.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.7.1...v2.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-02 18:51:46 +00:00
3fb0aae43a Merge pull request #3226 from Netflix/dependabot/pip/cryptography-3.2.1 2020-11-02 18:49:55 +00:00
a15e1831d0 turning off Travis-ci notification on success 2020-11-02 09:43:09 -08:00
a4d2f79a9b Bump cryptography from 3.2 to 3.2.1
Bumps [cryptography](https://github.com/pyca/cryptography) from 3.2 to 3.2.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/3.2...3.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-02 13:34:06 +00:00
9c6856bcdd adjusting the tests to the better naming 2020-10-30 18:36:32 -07:00
7bca42776b better comments 2020-10-30 18:28:34 -07:00
3dfafa0021 making lint happy 2020-10-30 18:28:10 -07:00
add0960579 more meaningful variable naming 2020-10-30 18:18:37 -07:00
e1ff89eb2d better return arguments 2020-10-30 18:18:14 -07:00
cc2aa5c1de cli for live testing 2020-10-30 18:17:34 -07:00
ba8eb7a3f5 better logging and metrics 2020-10-30 18:17:02 -07:00
c5769378cf making lint happy 2020-10-30 15:21:22 -07:00
f90041353c Merge branch 'master' into expanding-S3-plugin 2020-10-30 15:19:26 -07:00
8eba97fd14 Merge pull request #3223 from charhate/ecc_changes
Small fixes
2020-10-30 11:06:02 -07:00
d41daeb4af Merge branch 'master' into ecc_changes 2020-10-30 10:55:23 -07:00
507e3caee5 Merge pull request #3224 from Netflix/cname_01
Fixing floating comma in CNAME PR
2020-10-29 19:09:47 -07:00
cc05d21260 Merge branch 'master' into cname_01 2020-10-29 18:59:43 -07:00
a4178ca113 fixing floating comma in CNAME PR 2020-10-29 18:52:22 -07:00
69aa98c1c8 Merge branch 'master' into ecc_changes 2020-10-29 18:05:38 -07:00
03dfbf535d Consistent algo in UI and API
Removed '-' since UI displays only handful options
2020-10-29 17:59:31 -07:00
aec24ae132 Missing commit in downgrade 2020-10-29 17:58:37 -07:00
4e44dd3d8f Check if authority options is JSON Array 2020-10-29 17:57:54 -07:00
4330a42dd3 Merge pull request #3196 from GnunuX/url_context_path
Do not add urlContextPath to relative path
2020-10-29 16:20:05 -07:00
725eee549d Merge branch 'master' into url_context_path 2020-10-29 15:11:39 -07:00
497bd6a13c Merge pull request #3209 from jtschladen/notification-plugin-field-fix
Fix plugin field on notification edit
2020-10-29 15:08:12 -07:00
945ec0895b Merge branch 'master' into url_context_path 2020-10-29 15:06:50 -07:00
9aa2d2af76 Merge branch 'master' into notification-plugin-field-fix 2020-10-29 14:58:43 -07:00
4bc5899e24 Merge pull request #3221 from Netflix/cname_01
Delegated CNAME DNS validation for ACME
2020-10-29 14:50:19 -07:00
ccecb26816 Merge branch 'cname_01' of github.com:Netflix/lemur into cname_01 2020-10-29 14:43:14 -07:00
ca465e3c9e updating debug string with target_domain 2020-10-29 14:42:51 -07:00
a3a02a8077 Merge branch 'master' into notification-plugin-field-fix 2020-10-29 14:32:57 -07:00
2aec317127 Merge branch 'master' into cname_01 2020-10-29 14:32:23 -07:00
83d363fd9e Merge pull request #3219 from jtschladen/certificates-for-notification-fix
Fix notification view to actually show associated certs
2020-10-29 14:29:55 -07:00
86207db93b Merge branch 'master' into certificates-for-notification-fix 2020-10-29 14:21:25 -07:00
15a7921bf4 Merge branch 'master' into cname_01 2020-10-29 14:09:48 -07:00
84f8905cf1 Hide expired certs for notifications 2020-10-29 14:07:25 -07:00
cca4670745 Merge pull request #3220 from jtschladen/ses-arn-override
Add ability to override SourceArn for SES
2020-10-29 14:06:05 -07:00
14348a1f95 Merge branch 'master' into cname_01 2020-10-29 14:01:14 -07:00
28c6f8583a Merge branch 'master' into ses-arn-override 2020-10-29 13:52:51 -07:00
a1f99c29c0 Merge branch 'cname_01' of github.com:Netflix/lemur into cname_01 2020-10-29 13:51:58 -07:00
aa2e0aa2f9 Merge pull request #3222 from jtschladen/sns-date-subject
Add subject for SNS messages and correct date format
2020-10-29 13:51:26 -07:00
2b91077d92 updating variables based on feedback 2020-10-29 13:51:22 -07:00
28686fcf5d Merge branch 'ses-arn-override' of github.com:jtschladen/lemur into ses-arn-override 2020-10-29 13:48:55 -07:00
45cc9528d2 Cleaner syntax for default region 2020-10-29 13:48:43 -07:00
78afc060ae Add subject for SNS messages and correct date format 2020-10-29 13:41:47 -07:00
e967f2c676 Merge branch 'master' into ses-arn-override 2020-10-29 11:11:30 -07:00
2cea33cb11 Merge branch 'master' into expanding-S3-plugin 2020-10-29 11:09:00 -07:00
af348b1012 Merge branch 'master' into cname_01 2020-10-28 22:41:23 -07:00
33a006bbeb fixing delete with optional validation 2020-10-28 22:24:37 -07:00
b47667b73e cname redirection working 2020-10-28 20:51:35 -07:00
3e492e6310 Add ability to override SES region 2020-10-28 17:09:54 -07:00
ff83721720 Merge pull request #3211 from charhate/ecc_changes
Modify description during reissue
2020-10-28 17:08:03 -07:00
bbfc65813d Merge branch 'master' into ecc_changes 2020-10-28 17:00:45 -07:00
166dfa89ad Merge pull request #3204 from GnunuX/log_update
do not create db_upgrade.log during migrations
2020-10-28 17:00:22 -07:00
6adf94d28f Merge branch 'master' into log_update 2020-10-28 16:52:19 -07:00
43ebc5aac1 Merge pull request #3212 from hosseinsh/issuer-retry
Issuer retry
2020-10-28 16:51:58 -07:00
9fd61a37dc Merge branch 'master' into log_update 2020-10-28 16:39:18 -07:00
576302fdd5 Merge branch 'master' into issuer-retry 2020-10-28 16:35:25 -07:00
54566ad4c3 Merge pull request #3218 from Netflix/hosseinsh-travis-contact-update
updating notification contact for travis-ci
2020-10-28 16:34:57 -07:00
5e696f36bf Add ability to override SourceArnn for SES 2020-10-28 16:34:31 -07:00
acc95a4b66 Fix notification view to actually show associated certs 2020-10-28 16:12:27 -07:00
c25782468b Merge branch 'master' into log_update 2020-10-28 15:40:16 -07:00
c0bf111cb9 updating notification contact for travis 2020-10-28 15:02:22 -07:00
cc69b433ca Merge branch 'master' into notification-plugin-field-fix 2020-10-28 14:58:58 -07:00
d27f2a53af Merge branch 'master' of github.com:Netflix/lemur into cname_01 2020-10-28 14:03:23 -07:00
95b647ee1d Merge branch 'master' into ecc_changes 2020-10-28 13:54:14 -07:00
84d30b5d50 Merge branch 'master' into issuer-retry 2020-10-28 13:21:10 -07:00
31b9e2cd20 Merge pull request #3201 from jtschladen/sns
Support for SNS Expiration Notifications
2020-10-28 13:19:54 -07:00
13e8421c78 Merge branch 'master' into notification-plugin-field-fix 2020-10-28 08:50:46 -07:00
16ce7970d0 Merge branch 'master' into sns 2020-10-28 08:50:09 -07:00
a9d3b7a676 Merge branch 'master' into issuer-retry 2020-10-28 08:48:29 -07:00
adca20ade1 Merge pull request #3214 from hosseinsh/improving-cn-owner-search-endpooint
Improving cn/owner search endpoint
2020-10-28 08:39:10 -07:00
23e1700fad flake8 2020-10-28 13:47:57 +01:00
b656e0d75a Merge branch 'master' into feature/acme-http-challenge 2020-10-28 13:46:29 +01:00
b7b7e9022f Merge branch 'master' into sns 2020-10-27 17:41:29 -07:00
794e4d3855 Revert log to debug to be safe 2020-10-27 17:36:01 -07:00
61ef7f207d Merge branch 'master' into improving-cn-owner-search-endpooint 2020-10-27 17:01:27 -07:00
44e4100a39 Merge pull request #3217 from hosseinsh/travis-dist-update
Travis dist update
2020-10-27 17:00:52 -07:00
2dd9ea3d01 Merge branch 'master' of github.com:Netflix/lemur into travis-dist-update
* 'master' of github.com:Netflix/lemur:
  Bump boto3 from 1.15.16 to 1.16.5
  Bump cryptography from 3.1.1 to 3.2
  Bump fakeredis from 1.4.3 to 1.4.4
2020-10-27 16:43:13 -07:00
a1af7c89b1 we have been running on bionic since some time 2020-10-27 16:43:02 -07:00
c6a8034890 language 2020-10-27 16:13:05 -07:00
f77c262953 how did that happen 2020-10-27 14:44:18 -07:00
10aa02fd85 more compact design, thanks to Chad for the feedback 2020-10-27 14:42:51 -07:00
54c2245115 comments 2020-10-27 12:47:35 -07:00
d59a558d58 adopting ilike and not relying on ==
reducing redundancy
2020-10-27 12:44:38 -07:00
e9824a6808 change the log level to info if upgrade is successful 2020-10-27 20:38:18 +01:00
79647e3372 add reference to LOG_UPGRADE_FILE in toplevel comment 2020-10-27 20:38:18 +01:00
7f25e02589 Merge branch 'master' into expanding-S3-plugin 2020-10-27 12:02:14 -07:00
729a6e69f5 Merge branch 'master' into sns 2020-10-27 12:02:01 -07:00
46ec1798d3 Merge branch 'master' into log_update 2020-10-27 12:01:53 -07:00
437933c558 Merge branch 'master' into issuer-retry 2020-10-27 12:01:11 -07:00
259a8808f1 Merge branch 'master' into improving-cn-owner-search-endpooint 2020-10-27 12:00:45 -07:00
56061a7e3b Merge branch 'master' into url_context_path 2020-10-27 12:00:00 -07:00
7a982a731a Merge branch 'master' into ecc_changes 2020-10-27 11:58:47 -07:00
dfdb26f994 Merge pull request #3216 from Netflix/dependabot/pip/boto3-1.16.5 2020-10-27 18:56:52 +00:00
97f66276ec Merge branch 'master' into log_update 2020-10-27 10:53:20 -07:00
196a311084 Bump boto3 from 1.15.16 to 1.16.5
Bumps [boto3](https://github.com/boto/boto3) from 1.15.16 to 1.16.5.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.15.16...1.16.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-27 17:26:18 +00:00
3551437d9c Merge pull request #3215 from Netflix/dependabot/pip/cryptography-3.2 2020-10-27 17:23:05 +00:00
20b8c2fd93 PR feedback 2020-10-27 08:56:43 -07:00
5c3758731c Bump cryptography from 3.1.1 to 3.2
Bumps [cryptography](https://github.com/pyca/cryptography) from 3.1.1 to 3.2.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/3.1.1...3.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-27 13:45:59 +00:00
ccf87986c0 Add store_account to AcmeDnsIssuer 2020-10-27 12:15:07 +01:00
96fbcdaf70 Fix test_finalize_authorizations, dont reuse cleanup_dns_challenges in finalize_authorizations 2020-10-27 11:27:44 +01:00
103e107668 Fix patches for test_create_certificate 2020-10-27 11:16:29 +01:00
82bf8e2ac6 Remove unnecessary code from dnsChallenge, Fix patches in dns tests 2020-10-27 11:09:30 +01:00
2d98e71977 Replace deprecated assertRaisesRegexp with assertRaisesRegex 2020-10-27 10:44:04 +01:00
30c10b93f8 Fix patches for acme_handler tests 2020-10-27 10:37:30 +01:00
3b20a47603 Fix patches for acme_http tests, apparently isinstance is considered evil in python 2020-10-27 10:37:30 +01:00
4464c5890d Flake8 2020-10-27 10:37:30 +01:00
812e1dee92 Refactor Acme plugin into AcmeChallenge objects, dns01 2020-10-27 10:37:27 +01:00
b91cebf245 Refactor Acme plugin into AcmeChallenge objects, http01 2020-10-27 10:36:06 +01:00
6c1be02bfa Remove destination_list from AcmeHttpIssuer 2020-10-27 10:28:34 +01:00
ef0fce2661 Set timeout for finalize to 90s 2020-10-27 10:28:34 +01:00
235653b558 Refactor destination selection for acme-http authorities, to load destinations dynamically 2020-10-27 10:28:34 +01:00
81b078604c Implement revoke certificate for ACME 2020-10-27 10:28:34 +01:00
215070b327 Fix create_certificate tests 2020-10-27 10:28:34 +01:00
41ea59d7e3 Remove unneeded polling 2020-10-27 10:28:33 +01:00
d24fae0bac Fix permissions on acme token upload, dont append well-known automatically 2020-10-27 10:28:33 +01:00
66cab6abd3 Make http-01 challenge work for SAN certificates 2020-10-27 10:28:33 +01:00
e3e5ef7d66 Refactor AcmeHandler, Move DNS stuff into AcmeDnsHandler 2020-10-27 10:28:33 +01:00
76dcfbd528 Add more tests 2020-10-27 10:28:33 +01:00
d6719b729c Implement some test for AcmeHttpIssuerPlugin 2020-10-27 10:28:33 +01:00
b2de986652 Split tests into handler, and dns specifics 2020-10-27 10:28:30 +01:00
b93d271f31 Fix flake8 2020-10-27 10:25:31 +01:00
e06bdcf2a3 Implement create_certificate for HTTP-01 challenge 2020-10-27 10:25:31 +01:00
3012995c76 Improve naming, make it possible to create directories recursively with SFTP 2020-10-27 10:25:31 +01:00
348d8477dd Refactor destination plugin, to allow upload of ACME http-challenge tokens 2020-10-27 10:25:31 +01:00
d00dd9d295 Initial structure for ACME http challenge 2020-10-27 10:25:31 +01:00
faceee0f77 Merge branch 'master' into expanding-S3-plugin 2020-10-26 19:42:56 -07:00
56a4200d2c Merge branch 'master' into notification-plugin-field-fix 2020-10-26 19:42:39 -07:00
645b45401d Merge branch 'master' into sns 2020-10-26 19:42:21 -07:00
3b258447db addressing Chad's feedbakc 2020-10-26 19:16:40 -07:00
2430507e55 Merge branch 'master' of github.com:Netflix/lemur into improving-cn-owner-search-endpooint
* 'master' of github.com:Netflix/lemur:
  Bump fakeredis from 1.4.3 to 1.4.4
2020-10-26 18:34:40 -07:00
1ef6139f9b ignore rotated certs, since there is a new cert that can be used 2020-10-26 18:34:21 -07:00
6a1b4b4857 ignore expired certs 2020-10-26 18:33:33 -07:00
709a9808aa better structure of the query and and removing ilike 2020-10-26 18:32:53 -07:00
cb4f814478 Merge branch 'master' into issuer-retry 2020-10-26 18:01:12 -07:00
4fffb8ba5b Merge branch 'master' into log_update 2020-10-26 18:01:01 -07:00
a87bf0d50a Merge branch 'master' into url_context_path 2020-10-26 17:59:57 -07:00
831e5619e1 Merge pull request #3198 from Netflix/dependabot/pip/fakeredis-1.4.4
Bump fakeredis from 1.4.3 to 1.4.4
2020-10-26 17:58:27 -07:00
ab47db4cd4 Merge branch 'master' into log_update 2020-10-26 17:57:27 -07:00
392725ff30 Add description check in reissue unit test 2020-10-26 15:33:20 -07:00
749aa772ba First change to get CNAME redirection working 2020-10-26 11:57:33 -07:00
2d94b19c32 Merge branch 'notification-plugin-field-fix' of github.com:jtschladen/lemur into notification-plugin-field-fix 2020-10-26 11:27:53 -07:00
3f765b51ef Fix sources and destinations, and allow actually updating the notification type 2020-10-26 11:27:40 -07:00
6723e3c80d now fixing the month to minute bug 2020-10-26 11:27:40 -07:00
3290d6634b fixing testing 2020-10-26 11:27:40 -07:00
fa62023b2d fixing the time bug, sub-second to second, and month to minute! 2020-10-26 11:27:40 -07:00
37f05a89f2 Bump botocore from 1.18.16 to 1.18.18
Bumps [botocore](https://github.com/boto/botocore) from 1.18.16 to 1.18.18.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.18.16...1.18.18)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-26 11:27:40 -07:00
d7478a5c5c use an alternative logger for the upgrade 2020-10-25 19:24:17 +01:00
f6554a9a1e typo, fixing abstract class complaints 2020-10-23 18:03:55 -07:00
0e02abbb37 Entrust just looks into CSR for RSA/EC key type 2020-10-23 18:03:27 -07:00
9957120a7f adding missing import 2020-10-23 18:03:07 -07:00
7e573d6d51 fixing typo 2020-10-23 18:02:54 -07:00
6891077501 readability 2020-10-23 18:02:35 -07:00
75bc3a5b20 refactoring and adding retry 2020-10-23 18:02:05 -07:00
d233490c8a simple retry 2020-10-23 18:01:14 -07:00
2c1e7b19a2 10x 10s delay might be too long for the load balancer request 2020-10-23 17:59:58 -07:00
db0b245b6c Merge branch 'master' into sns 2020-10-23 17:12:06 -07:00
2c22d42a57 Modify description during reissue
Include the certificate ID being reissued and mention that this is created by Lemur as part of reissue
2020-10-23 17:07:14 -07:00
0a05f99741 Merge branch 'master' into notification-plugin-field-fix 2020-10-23 16:39:01 -07:00
d9bbf42480 Merge branch 'master' into dependabot/pip/fakeredis-1.4.4 2020-10-23 16:37:34 -07:00
d58b32a19c Merge pull request #3199 from Netflix/dependabot/pip/botocore-1.18.18 2020-10-23 22:56:13 +00:00
3d83db6f8f Merge branch 'master' into expanding-S3-plugin 2020-10-23 14:13:30 -07:00
2b7cb0d44f Merge branch 'master' into url_context_path 2020-10-23 14:13:25 -07:00
fd16edb3e5 Merge branch 'master' into notification-plugin-field-fix 2020-10-23 14:13:21 -07:00
30915d30be Merge branch 'master' into log_update 2020-10-23 14:13:17 -07:00
5b523bb8ed Merge branch 'master' into dependabot/pip/fakeredis-1.4.4 2020-10-23 14:12:39 -07:00
584159c916 Merge branch 'master' into dependabot/pip/botocore-1.18.18 2020-10-23 14:12:35 -07:00
01bd357b1c Merge branch 'master' into sns 2020-10-23 11:38:35 -07:00
fd12d4848c Grammar fixes 2020-10-23 11:26:11 -07:00
582c7b0771 Merge pull request #3210 from hosseinsh/digicert-time-bug-fix
Digicert time bug fix
2020-10-23 10:48:18 -07:00
1495fb3595 now fixing the month to minute bug 2020-10-23 10:18:24 -07:00
bc6fb02fc2 fixing testing 2020-10-23 10:16:38 -07:00
e01863097b fixing the time bug, sub-second to second, and month to minute! 2020-10-23 10:16:23 -07:00
a5cea4fb9a Skip revoked certs when looking for certs to notify 2020-10-23 09:42:03 -07:00
233f9768e8 Fix error handling 2020-10-23 09:35:46 -07:00
5ccc99bbfa Merge branch 'master' into dependabot/pip/botocore-1.18.18 2020-10-23 09:08:47 -07:00
98962ae5f5 Merge branch 'master' into sns 2020-10-23 08:50:26 -07:00
41ac43013d Merge branch 'master' into notification-plugin-field-fix 2020-10-23 08:43:29 -07:00
2ea39a51e3 Merge pull request #3208 from hosseinsh/improved-logging
Improved issuer logging
2020-10-23 08:43:11 -07:00
2b274f723a Merge branch 'master' into improved-logging 2020-10-23 07:59:30 -07:00
e87cf040f3 Merge pull request #3207 from hosseinsh/entrust-deactivate
Entrust deactivate test certificates
2020-10-23 07:59:15 -07:00
71df6b8560 Fix plugin field on notification edit 2020-10-22 18:15:26 -07:00
8610af8b83 more precise language 2020-10-22 17:54:46 -07:00
820106e333 Merge branch 'master' into expanding-S3-plugin 2020-10-22 17:35:20 -07:00
9ce0010bf1 handle_respone can also handle the no data response 2020-10-22 17:33:39 -07:00
cf87e178c8 making lint happy 2020-10-22 17:33:02 -07:00
97f80b79dc adjusting digicert test to support seconds 2020-10-22 17:23:33 -07:00
9acd974b74 fixing the test to support seconds 2020-10-22 17:20:47 -07:00
ae1e9d120b consistent messaging 2020-10-22 17:13:58 -07:00
2e7652962c refactoring of the error handling 2020-10-22 17:11:02 -07:00
1c96ea9ab1 better messaging of exceptions 2020-10-22 17:10:32 -07:00
02c040865d more meaningful message 2020-10-22 16:05:29 -07:00
8fa90a2ce5 digicert expects also seconds, though not yet honoring it 2020-10-22 16:01:09 -07:00
c60645bec4 improved logging for all responses 2020-10-22 16:00:26 -07:00
c2fe2b5e03 improved logging for all responses 2020-10-22 15:59:59 -07:00
03d1af16e7 better logging for exceptions around all plugins 2020-10-22 15:59:38 -07:00
3e1e17998e Merge branch 'master' into url_context_path 2020-10-22 12:04:11 -07:00
2b876f22a5 Merge branch 'master' into log_update 2020-10-22 12:00:51 -07:00
2e7e3a82fa Update cli.py
logging in exception
2020-10-22 11:57:54 -07:00
c40ecd12cb improved naming 2020-10-22 10:58:16 -07:00
2cc03088cd creating a celery task 2020-10-21 19:53:08 -07:00
a4dba0cb35 creating a cli to handle entrust deactivation 2020-10-21 19:52:51 -07:00
906b3b2337 better handling of status code 2020-10-21 19:52:25 -07:00
92eec5cc9c revocation should only check for not expired and not revoked certs 2020-10-21 18:52:55 -07:00
55f219e97a Merge pull request #3206 from charhate/ecc_changes
Check if present - Organization, State, Country
2020-10-21 16:25:03 -07:00
adf8f37718 Merge branch 'master' into log_update 2020-10-21 16:03:46 -07:00
43483cb1c7 Check if present - Organization, State, Country 2020-10-21 15:44:53 -07:00
2ccb7034b5 Merge pull request #3205 from charhate/ecc_changes
Check if OU and L is present in subject
2020-10-21 13:11:04 -07:00
0986a7a3ff Merge branch 'master' into ecc_changes 2020-10-21 12:32:35 -07:00
757e190b60 Check if OU and L is present in subject
fixing index out of range
2020-10-21 12:11:41 -07:00
9374adaa46 do not create db_upgrade.log during migrations 2020-10-21 11:17:54 +02:00
c1bf192bd8 Merge pull request #3203 from charhate/ecc_changes
Removing ECC 192 and 521 from UI
2020-10-20 18:14:08 -07:00
18fdd420a7 Merge branch 'master' into ecc_changes 2020-10-20 18:08:16 -07:00
4997165235 Removing ECC 192 and 521 from UI
not CAB supported. Keeping 521 for authority
2020-10-20 17:59:50 -07:00
59a97cde1d Merge pull request #3202 from charhate/ecc_changes
Fix cert reissue when L/OU is not set
2020-10-20 17:23:31 -07:00
01dddd2a55 iterate over subject details 2020-10-20 17:17:28 -07:00
788703ce12 Fix cert reissue when L/OU is not set
get_certificate_primitives complains with None L/OU
2020-10-20 16:44:17 -07:00
1fc9cd2ff8 Merge branch 'master' into sns 2020-10-20 12:13:51 -07:00
4f552cb636 Code cleanup 2020-10-20 12:02:36 -07:00
d6075ebc11 Merge 2020-10-20 11:48:54 -07:00
63ace016f9 Merge branch 'master' into url_context_path 2020-10-20 10:23:08 -07:00
a3b90c1a6b Bump botocore from 1.18.16 to 1.18.18
Bumps [botocore](https://github.com/boto/botocore) from 1.18.16 to 1.18.18.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.18.16...1.18.18)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-20 09:23:10 +00:00
58798fbc2e Bump fakeredis from 1.4.3 to 1.4.4
Bumps [fakeredis](https://github.com/jamesls/fakeredis) from 1.4.3 to 1.4.4.
- [Release notes](https://github.com/jamesls/fakeredis/releases)
- [Commits](https://github.com/jamesls/fakeredis/compare/1.4.3...1.4.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-20 09:21:21 +00:00
ea33fe9979 Merge pull request #3197 from charhate/ecc_changes
Show only few supported ECC algorithms on UI
2020-10-19 17:53:48 -07:00
5cf9ea4830 Merge branch 'master' into url_context_path 2020-10-19 17:50:43 -07:00
855baadfee Show only few supported ECC algorithms on UI 2020-10-19 17:42:52 -07:00
669a4273c2 Merge branch 'master' of github.com:jtschladen/lemur into sns 2020-10-19 16:29:33 -07:00
ad07b41763 Merge pull request #3193 from jtschladen/notification-fixes
Miscellaneous notification fixes and tests
2020-10-19 16:17:57 -07:00
b5f0fc5a19 Fix syntax error 2020-10-19 15:21:34 -07:00
ecd4d6ebe3 Change string formatting pattern 2020-10-19 15:12:48 -07:00
af3afe36e1 Merge branch 'master' into expanding-S3-plugin 2020-10-19 14:23:01 -07:00
591c8cf524 Do not add urlContextPath to relative path 2020-10-19 22:35:10 +02:00
e90b08b363 Correct typo and enable Slack notification test 2020-10-16 17:08:44 -07:00
6a1889787d Correct log attributes 2020-10-16 16:30:21 -07:00
2c92fc6eb9 Merge branch 'notification-fixes' of github.com:jtschladen/lemur into notification-fixes 2020-10-16 16:22:28 -07:00
072b337f37 Restructure log messages 2020-10-16 16:21:43 -07:00
fe5d75c7f8 Merge branch 'master' into notification-fixes 2020-10-16 15:20:42 -07:00
60bb0037f0 Miscellaneous notification fixes and tests 2020-10-16 15:13:12 -07:00
cb2f340277 Merge pull request #3190 from charhate/cab_compliant
Use cab_compliant option instead of authority name list
2020-10-16 14:59:51 -07:00
dbdfa9eab8 Merge branch 'master' into expanding-S3-plugin 2020-10-16 11:35:38 -07:00
a04cce6044 Initial implementation 2020-10-16 10:40:11 -07:00
503530e935 the test requires region param for sts 2020-10-16 10:32:10 -07:00
11ce540246 formatting 2020-10-16 10:31:19 -07:00
9c04a888d8 adjusting the S3 test 2020-10-16 09:52:04 -07:00
17e528b5dd adding testing for acme_upload method 2020-10-16 09:50:35 -07:00
d705e3ae3b expanding the S3 destination plugin to support the acme token upload inteface 2020-10-16 09:49:56 -07:00
7d8eb1c61e improving test 2020-10-16 09:49:26 -07:00
6aad37e1f9 cleaning up code 2020-10-16 09:49:00 -07:00
d73db59d23 revsering removing region 2020-10-16 09:48:47 -07:00
ab91d58a03 Merge branch 'master' into cab_compliant 2020-10-16 08:33:04 -07:00
67c184a97c Merge pull request #3191 from unic/hotfix/fix-alembic-url-escaping
Fix "ValueError: invalid interpolation syntax in" for special chars in SQLALCHEMY_DATABASE_URI
2020-10-16 08:32:51 -07:00
55658c5f23 Add double % for escaped SQLALCHEMY_DATABASE_URI 2020-10-16 10:43:52 +02:00
bfe89e131e adding delete and put interfaces for the S3 plugin 2020-10-15 18:13:50 -07:00
29f3dd43f2 Update administration.rst
language
2020-10-15 15:18:04 -07:00
32c0c5fb00 Merge pull request #1 from Netflix/master
Merge pull request #3188 from charhate/cab_compliant
2020-10-15 11:18:47 -07:00
9dc476f393 Use cab_compliant option instead of authority name list 2020-10-15 10:44:46 -07:00
cd29b2b870 Merge pull request #3188 from charhate/cab_compliant
Cab compliant
2020-10-15 10:22:41 -07:00
f38380d156 Check if option is present 2020-10-14 17:38:32 -07:00
4d5e712e85 Remove option reset from test 2020-10-14 15:40:23 -07:00
ee1d07000a Test subject details in reissue with cab_compliant option 2020-10-14 14:49:53 -07:00
90839b4d4b Unit test for cab_compliant = true 2020-10-14 14:49:53 -07:00
0e3638b86c Merge branch 'master' into cab_compliant 2020-10-14 13:33:34 -07:00
c75d44864e Merge pull request #3189 from Netflix/dependabot/pip/faker-4.14.0 2020-10-14 20:31:01 +00:00
62d099b500 Unit tests to check cab_compliant option 2020-10-14 12:41:56 -07:00
ddf94e04da Bump faker from 4.4.0 to 4.14.0
Bumps [faker](https://github.com/joke2k/faker) from 4.4.0 to 4.14.0.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/faker/compare/v4.4.0...v4.14.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-14 18:09:48 +00:00
16766b1f84 Merge pull request #3183 from Netflix/dependabot/pip/boto3-1.15.16 2020-10-14 18:07:49 +00:00
f08b50a952 Bump boto3 from 1.15.12 to 1.15.16
Bumps [boto3](https://github.com/boto/boto3) from 1.15.12 to 1.15.16.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.15.12...1.15.16)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-14 17:45:18 +00:00
ea915282b2 Merge pull request #3186 from Netflix/dependabot/pip/acme-1.9.0 2020-10-14 17:42:24 +00:00
499bbca42e Bump acme from 1.8.0 to 1.9.0
Bumps [acme](https://github.com/letsencrypt/letsencrypt) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/letsencrypt/letsencrypt/releases)
- [Commits](https://github.com/letsencrypt/letsencrypt/compare/v1.8.0...v1.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-14 17:16:30 +00:00
a0ebfad89d Merge pull request #3184 from Netflix/dependabot/pip/arrow-0.17.0 2020-10-14 17:14:13 +00:00
60a8219869 Merge branch 'master' into cab_compliant 2020-10-14 10:04:11 -07:00
409e12a9d6 Update models.py
lint
2020-10-14 10:03:44 -07:00
2fefbb6dea Bump arrow from 0.16.0 to 0.17.0
Bumps [arrow](https://github.com/arrow-py/arrow) from 0.16.0 to 0.17.0.
- [Release notes](https://github.com/arrow-py/arrow/releases)
- [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/arrow-py/arrow/compare/0.16.0...0.17.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-14 16:58:24 +00:00
04fd7c83c1 Merge pull request #3182 from Netflix/dependabot/pip/botocore-1.18.16 2020-10-14 16:56:13 +00:00
894e35b4e2 Update schemas.py
minor language
2020-10-14 09:48:40 -07:00
97cf54433b Update models.py
language
2020-10-14 09:45:13 -07:00
82dd663942 Moving default key_type to getDefaults 2020-10-13 19:40:32 -07:00
28381737dc Removed OU from digicert plugin 2020-10-13 19:40:15 -07:00
b677e6e325 Copy subject details for non-CAB-compliant authorities 2020-10-13 19:40:01 -07:00
8b8f9e652b Bump botocore from 1.18.12 to 1.18.16
Bumps [botocore](https://github.com/boto/botocore) from 1.18.12 to 1.18.16.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.18.12...1.18.16)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-13 06:24:35 +00:00
5aa37b48d3 Merge pull request #3181 from hosseinsh/removing-outdated-language
cleaning up outdated language
2020-10-12 10:29:30 -07:00
5db1d31668 Merge branch 'master' into removing-outdated-language 2020-10-12 10:22:53 -07:00
896d1af0f7 Merge pull request #3153 from unic/feature/store-acme-account-details
Store ACME account details
2020-10-12 10:22:16 -07:00
817fc3f0fe Merge branch 'master' into feature/store-acme-account-details 2020-10-11 14:37:31 +02:00
c65386a8a8 Merge pull request #3180 from charhate/ui_changes
Certificate edit: update role and notification with owner change
2020-10-09 18:14:06 -07:00
4c7b429001 Merge branch 'master' into ui_changes 2020-10-09 18:05:33 -07:00
71697bad34 Merge pull request #3175 from hosseinsh/dymanic-digicert-ICAs
Dynamic DigiCert Intermediate CA
2020-10-09 18:05:16 -07:00
770339f94c cleaning up outdated phrases 2020-10-09 18:04:16 -07:00
fb4df8865b Formatting changes and typo 2020-10-09 17:58:03 -07:00
0fc050e17b Merge branch 'master' into dymanic-digicert-ICAs 2020-10-09 17:53:54 -07:00
475833e8e1 Merge branch 'master' into ui_changes 2020-10-09 17:53:43 -07:00
1d18f061f2 Merge pull request #3179 from sirferl/entrust-plugin
Entrust plugin - Additional Test and fixes
2020-10-09 17:53:21 -07:00
198e20ce4f Merge branch 'master' into dymanic-digicert-ICAs 2020-10-09 17:49:33 -07:00
d4819440af Merge branch 'master' into entrust-plugin 2020-10-09 17:47:01 -07:00
d52e0d4e09 Certificate edit: update role and notification with owner change 2020-10-09 16:55:30 -07:00
b59c76d630 Merge pull request #3176 from charhate/ui_changes
Fix disable notify
2020-10-09 16:49:56 -07:00
42e9b8b627 removing the intermediary from being optional 2020-10-09 15:40:25 -07:00
e67fc09bc8 Merge branch 'entrust-plugin' of github.com:sirferl/lemur into entrust-plugin 2020-10-09 12:11:41 +02:00
5a968ffe63 Lint errors 2020-10-09 12:05:57 +02:00
cc02a0adb0 Merge branch 'master' into entrust-plugin 2020-10-09 11:56:47 +02:00
d43e240a2a dded ELIF at determine_end_date, becuase of error. 2020-10-09 11:41:44 +02:00
a6a4f458e0 added Tests and removed problems in test-setup 2020-10-09 11:35:04 +02:00
d5ce38bf71 lint error fix - remove whitespace 2020-10-08 12:50:30 -07:00
8928e04385 Fix disable notify 2020-10-08 11:38:52 -07:00
1a270cd315 switching from static DigiCert ICAs to dynamic ones to support:
https://knowledge.digicert.com/alerts/DigiCert-ICA-Update.html
2020-10-07 20:06:20 -07:00
4f696abb5d adding util method to convert PKCS7 to pem 2020-10-07 20:03:46 -07:00
e078d33103 Merge pull request #3174 from charhate/ui_changes
Authority create: Email added to subject DN
2020-10-07 16:25:00 -07:00
b7d0e62844 Make location optional
Remove form validation and default value in input schema
2020-10-07 13:31:23 -07:00
57534d86cd Disable account saving by default 2020-10-07 12:28:22 +02:00
8353396940 Improve tests 2020-10-07 12:28:22 +02:00
9abd3e97e7 Add test loading acme account from authority 2020-10-07 12:28:22 +02:00
bf66de0bfd Add Test for saving the accound details 2020-10-07 12:28:22 +02:00
e0708410d0 Add store_account value to options in test_setup_acme_client_success 2020-10-07 12:28:22 +02:00
7e6fb740b3 Fix flake8/linting errors 2020-10-07 12:28:22 +02:00
eed628dbab Implement storage of acme account 2020-10-07 12:28:22 +02:00
898b5da661 Add store_account option to acme plugin 2020-10-07 12:28:22 +02:00
e64e2a41d5 Add update_options to authorities service 2020-10-07 12:28:22 +02:00
c72661a87f Removing hardcoded name 2020-10-06 18:50:37 -07:00
6b96aefa21 Authority create: Email added to subject DN for cloudCA 2020-10-06 18:35:28 -07:00
72d28d0af9 Merge pull request #3173 from Netflix/dependabot/pip/boto3-1.15.12 2020-10-06 18:05:56 +00:00
0b667177dd Bump boto3 from 1.15.7 to 1.15.12
Bumps [boto3](https://github.com/boto/boto3) from 1.15.7 to 1.15.12.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.15.7...1.15.12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-06 17:51:12 +00:00
90f17d02d9 Merge pull request #3171 from Netflix/dependabot/pip/botocore-1.18.12 2020-10-06 17:48:19 +00:00
4e6d25d0f9 Bump botocore from 1.18.7 to 1.18.12
Bumps [botocore](https://github.com/boto/botocore) from 1.18.7 to 1.18.12.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.18.7...1.18.12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-06 17:35:06 +00:00
da0970f3c8 Merge pull request #3172 from Netflix/dependabot/pip/faker-4.4.0 2020-10-06 17:32:20 +00:00
16177d751d Bump faker from 4.1.3 to 4.4.0
Bumps [faker](https://github.com/joke2k/faker) from 4.1.3 to 4.4.0.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/faker/compare/v4.1.3...v4.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-06 16:59:28 +00:00
0876d5f911 Merge pull request #3170 from Netflix/dependabot/pip/factory-boy-3.1.0 2020-10-06 16:57:22 +00:00
2586c23efd Bump factory-boy from 3.0.1 to 3.1.0
Bumps [factory-boy](https://github.com/FactoryBoy/factory_boy) from 3.0.1 to 3.1.0.
- [Release notes](https://github.com/FactoryBoy/factory_boy/releases)
- [Changelog](https://github.com/FactoryBoy/factory_boy/blob/master/docs/changelog.rst)
- [Commits](https://github.com/FactoryBoy/factory_boy/compare/3.0.1...3.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-06 16:40:44 +00:00
7a612f36a3 Merge pull request #3169 from Netflix/dependabot/pip/pytest-6.1.1 2020-10-06 16:38:56 +00:00
60d28cf875 Bump pytest from 6.1.0 to 6.1.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.1.0 to 6.1.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.1.0...6.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-06 08:30:51 +00:00
98668b0972 Merge pull request #3168 from charhate/key_type_column
Remove bit length check from last query
2020-09-30 11:11:53 -07:00
010862ebcb Merge branch 'master' of github.com:Netflix/lemur into key_type_column 2020-09-29 16:43:22 -07:00
ea513f465f Remove bit length check from last query 2020-09-29 16:33:10 -07:00
d3b01d6b40 Merge pull request #3159 from charhate/key_type_column
Add key_type to CertificateUploadInputSchema
2020-09-29 10:41:25 -07:00
b9100dbf29 Merge branch 'master' of github.com:Netflix/lemur into key_type_column 2020-09-29 10:25:54 -07:00
c278a6db4d Merge pull request #3166 from unic/bugfix/dns-providers-list
Fix dns-providers type missing from schema
2020-09-29 10:21:14 -07:00
5e10a82621 Merge branch 'master' into bugfix/dns-providers-list 2020-09-29 18:52:11 +02:00
12e8e4891c Merge pull request #3162 from Netflix/dependabot/pip/cryptography-3.1.1 2020-09-29 16:49:13 +00:00
0f0f717520 Bump cryptography from 3.1 to 3.1.1
Bumps [cryptography](https://github.com/pyca/cryptography) from 3.1 to 3.1.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/3.1...3.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-29 16:39:43 +00:00
02ff34f440 Merge pull request #3161 from Netflix/dependabot/pip/boto3-1.15.7 2020-09-29 16:36:47 +00:00
5fc6533443 Bump boto3 from 1.15.2 to 1.15.7
Bumps [boto3](https://github.com/boto/boto3) from 1.15.2 to 1.15.7.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.15.2...1.15.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-29 16:27:01 +00:00
3634b5e423 Merge pull request #3163 from Netflix/dependabot/pip/pytest-6.1.0 2020-09-29 16:24:18 +00:00
23797ec067 Bump pytest from 6.0.2 to 6.1.0
Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.0.2 to 6.1.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.0.2...6.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-29 16:15:06 +00:00
ca357c08c4 Merge pull request #3160 from Netflix/dependabot/pip/botocore-1.18.7 2020-09-29 16:13:12 +00:00
d7fc84f6e9 Fix dns-providers type missing from schema 2020-09-29 14:36:31 +02:00
d3908fa445 Bump botocore from 1.18.2 to 1.18.7
Bumps [botocore](https://github.com/boto/botocore) from 1.18.2 to 1.18.7.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.18.2...1.18.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-29 07:33:30 +00:00
aaff0f7581 Fixing UT for key_type on upload schema 2020-09-28 19:03:21 -07:00
7a226241db Add key_type to CertificateUploadInputSchema
Parse cert body to determine algo
2020-09-28 18:13:00 -07:00
bf7638937a Merge pull request #3144 from hosseinsh/remove-test-secrets
Generate secrets on the fly
2020-09-28 14:49:16 -07:00
ba47e7448d lint 2020-09-28 14:42:03 -07:00
96eada297f lint 2020-09-28 14:40:56 -07:00
0fa136e7a4 Merge branch 'master' into remove-test-secrets 2020-09-25 17:19:39 -07:00
f8705aa730 lint 2020-09-25 17:19:30 -07:00
1e75cf4ab5 Merge pull request #3155 from charhate/key_type_column
Use key_type column for cert get/rotate/reissue/display
2020-09-25 13:01:40 -07:00
8f1c966079 Merge branch 'master' into remove-test-secrets 2020-09-25 12:48:28 -07:00
d49edd886b language 2020-09-25 12:32:33 -07:00
e871c5eb18 Update conf.py 2020-09-25 12:30:37 -07:00
85da1f1c75 Merge pull request #6 from Netflix/master
Entrust plugin : Test
2020-09-24 14:56:50 +02:00
57457bfe78 Merge branch 'master' of github.com:Netflix/lemur into key_type_column 2020-09-23 15:23:45 -07:00
cd13832377 Use key_type column for cert get/rotate/reissue/display
Added unit tests
2020-09-23 15:16:19 -07:00
df8d6e5d7f Merge pull request #3143 from hosseinsh/entrust-revised
Entrust plugin revised
2020-09-23 14:53:18 -07:00
f0aa9d249f Merge branch 'master' into entrust-revised 2020-09-23 13:41:12 -07:00
497afc3b46 Merge pull request #3151 from unic/feature/acme-documentation-improvement
ACME documentation improvement
2020-09-23 13:40:37 -07:00
4e4a7e9cab Merge branch 'master' into entrust-revised 2020-09-23 13:33:24 -07:00
e5961146b9 session hook complains about metadata
+ consistent language.
2020-09-23 14:22:58 -06:00
21c2255c75 Minor spelling improvements 2020-09-23 22:21:49 +02:00
4f1e09e3af Add reference from configuration options, to more detailed explanation 2020-09-23 22:21:49 +02:00
ae1ead6d75 Document ACME plugin specific configurations 2020-09-23 22:21:49 +02:00
1983eb79de Add paragraph about reusing ACME accounts 2020-09-23 22:21:49 +02:00
59bfcec808 Merge pull request #3150 from charhate/key_type_column
Backfill the key_type column
2020-09-23 12:11:20 -07:00
12af0ecb45 UT get_key_type_from_certificate 2020-09-23 11:46:38 -07:00
710290f590 Formatting changes 2020-09-23 11:45:36 -07:00
72be3ba54c Merge branch 'master' into entrust-revised 2020-09-23 10:34:34 -07:00
17218cbf02 Merge branch 'master' into remove-test-secrets 2020-09-23 10:34:19 -07:00
988d23e163 Merge branch 'master' into key_type_column 2020-09-23 10:34:12 -07:00
2c477ae8e7 Merge pull request #3148 from peschmae/bugfix/celery-documentation
Add redis configuration to celery documentation
2020-09-23 10:24:07 -07:00
19b693f636 Update c301c59688d2_.py
language
2020-09-23 10:21:23 -07:00
e3fa072608 Update c301c59688d2_.py
language
2020-09-23 10:17:30 -07:00
d5557c1533 Update index.rst
adding insight about the default db
2020-09-23 09:58:28 -07:00
cad04885a0 Add celery configuration options, to config documentation 2020-09-23 13:17:28 +02:00
f97e880fa6 REDIS_PORT as integer, add hint about multiple redis databases 2020-09-23 11:06:49 +02:00
1ea24e396a Merge branch 'master' of github.com:Netflix/lemur into key_type_column 2020-09-22 19:00:52 -07:00
921e8d8236 Add error message to the logs 2020-09-22 18:46:15 -07:00
9211178e77 Added date-time and modified log file name 2020-09-22 18:31:38 -07:00
8de9842092 Backfill the key_type column: DB Upgrade 2020-09-22 18:22:45 -07:00
9de8e5a28d Merge branch 'master' into entrust-revised 2020-09-22 16:45:46 -07:00
429261fd63 Merge branch 'master' into bugfix/celery-documentation 2020-09-22 10:13:16 -07:00
772894c414 Merge pull request #3093 from peschmae/master
Fix link for WES-entropy-client
2020-09-22 09:38:08 -07:00
c10f4b6483 Merge branch 'master' into master 2020-09-22 09:29:06 -07:00
ebf86c0eb2 Merge pull request #3146 from Netflix/dependabot/pip/boto3-1.15.2 2020-09-22 16:28:02 +00:00
9f66c18e71 Add REDIS_HOST and REDIS_PORT to celery configuration documentation 2020-09-22 14:53:45 +02:00
6dc4b9877b Bump boto3 from 1.14.61 to 1.15.2
Bumps [boto3](https://github.com/boto/boto3) from 1.14.61 to 1.15.2.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.61...1.15.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-22 06:11:00 +00:00
19a678dcc2 removing typo 2020-09-19 08:58:52 -07:00
1632b4b078 making lint happy, running make test-python doesn't run lint 2020-09-18 21:58:53 -07:00
21e9a4508d TypeError: 'float' object cannot be interpreted as an integer 2020-09-18 17:42:28 -07:00
c892cd5ae1 removing anything that remotely looks like a secret in code to set a good example 2020-09-18 17:38:52 -07:00
cc855e2758 modern python style 2020-09-18 17:16:07 -07:00
edab32d9a1 setting the required entrust configs 2020-09-18 17:03:22 -07:00
416f39222a testing 2020-09-18 17:02:19 -07:00
fae3793255 entrrust plugin revised 2020-09-18 11:09:32 -07:00
65795bd139 Merge branch 'master' into master 2020-09-16 10:22:31 -07:00
531e5c0d00 Merge pull request #3135 from charhate/key_type_column
Column addition key_type
2020-09-16 08:38:37 -07:00
5d7ca8520a Add remark that the WES-entropy-client is now linked to a fork 2020-09-16 10:23:04 +02:00
51549ae795 Adding comment for the property to be removed 2020-09-15 17:37:58 -07:00
efd231e0ab Merge branch 'key_type_column' of github.com:charhate/lemur into key_type_column 2020-09-15 15:16:51 -07:00
d8cca855e8 Merge branch 'master' of github.com:Netflix/lemur into key_type_column 2020-09-15 15:16:13 -07:00
5ae65c2c4d Remove unused import 2020-09-15 14:55:04 -07:00
87a85dd3b5 Merge branch 'master' into master 2020-09-15 12:14:13 -07:00
4321ca7cc7 Merge pull request #3142 from hosseinsh/fix-http-proxy-security-alert
Bump http-proxy from 1.16.2 to 1.18.1
2020-09-15 12:13:55 -07:00
2b40d2743c Merge branch 'master' into fix-http-proxy-security-alert 2020-09-15 11:52:31 -07:00
980883cb8d Dependbot failed to merge this PR, so raising it manually
Bump http-proxy from 1.16.2 to 1.18.1
https://github.com/Netflix/lemur/pull/3123#partial-pull-merging
2020-09-15 11:39:29 -07:00
f1e2cc1265 Merge pull request #3140 from Netflix/dependabot/pip/boto3-1.14.61 2020-09-15 17:56:02 +00:00
477f2fa1c2 Merge branch 'master' into key_type_column 2020-09-15 10:51:36 -07:00
f5e71bb431 Bump boto3 from 1.14.56 to 1.14.61
Bumps [boto3](https://github.com/boto/boto3) from 1.14.56 to 1.14.61.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.56...1.14.61)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-15 17:45:52 +00:00
9136a50d51 Merge pull request #3136 from Netflix/dependabot/pip/acme-1.8.0 2020-09-15 17:42:58 +00:00
8022efe32e Bump acme from 1.7.0 to 1.8.0
Bumps [acme](https://github.com/letsencrypt/letsencrypt) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/letsencrypt/letsencrypt/releases)
- [Commits](https://github.com/letsencrypt/letsencrypt/compare/v1.7.0...v1.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-15 17:35:46 +00:00
df3e39cc25 Merge pull request #3134 from Netflix/dependabot/pip/moto-1.3.16 2020-09-15 17:33:24 +00:00
1ceafc593a Bump moto from 1.3.14 to 1.3.16
Bumps [moto](https://github.com/spulec/moto) from 1.3.14 to 1.3.16.
- [Release notes](https://github.com/spulec/moto/releases)
- [Changelog](https://github.com/spulec/moto/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spulec/moto/compare/1.3.14...1.3.16)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-15 17:22:55 +00:00
b147aec53d Merge pull request #3138 from Netflix/dependabot/pip/coverage-5.3 2020-09-15 17:20:53 +00:00
dc675311f0 Bump coverage from 5.2.1 to 5.3
Bumps [coverage](https://github.com/nedbat/coveragepy) from 5.2.1 to 5.3.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/coverage-5.2.1...coverage-5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-15 17:13:55 +00:00
64fffecc8a Merge pull request #3139 from Netflix/dependabot/pip/faker-4.1.3 2020-09-15 17:12:01 +00:00
51fbd6a871 Bump faker from 4.1.2 to 4.1.3
Bumps [faker](https://github.com/joke2k/faker) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/faker/compare/v4.1.2...v4.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-15 17:04:29 +00:00
93057561b3 Merge pull request #3137 from Netflix/dependabot/pip/pytest-6.0.2 2020-09-15 17:01:58 +00:00
f5228407c2 Bump pytest from 6.0.1 to 6.0.2
Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.0.1...6.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-15 16:51:01 +00:00
cff8fb15ae Merge pull request #3133 from sirferl/master
Entrust documentation
2020-09-15 09:49:08 -07:00
305de2217e Merge branch 'master' into master 2020-09-15 09:14:15 -07:00
fd2aff7a16 Merge pull request #3132 from sirferl/entrust-plugin
Entrust plugin
2020-09-15 09:13:46 -07:00
676562ffde Match column type to db schema
No functional change
2020-09-14 18:13:35 -07:00
02d711282d New column key_type
commenting conflicting property for now
2020-09-14 18:12:33 -07:00
02c7a5ca7c another round of lint errors 2020-09-14 16:34:56 +02:00
e011cc9251 added several enhancements following advice from peer 2020-09-14 16:24:53 +02:00
9778eb7b25 fixed lint errors 2020-09-14 15:56:02 +02:00
5bb0143da4 lint errors and removed _path from the API-Cert variables 2020-09-14 15:42:36 +02:00
84496b0f55 fixed a few problems 2020-09-14 15:18:46 +02:00
b8e3162c5f added revoke functionality 2020-09-14 14:20:11 +02:00
b337b27146 added response handler 2020-09-14 12:23:58 +02:00
01678a714f added required vars check 2020-09-14 09:50:55 +02:00
1bdfaa9739 removed merge conflicts 2020-09-14 08:58:31 +02:00
b217a68512 added entrust to setup.py 2020-09-14 08:53:17 +02:00
8adca442e1 Merge branch 'master' into entrust-plugin 2020-09-11 17:11:57 -07:00
5cc6279361 Merge pull request #3115 from charhate/validity
Cert Validity UI Changes
2020-09-11 16:48:50 -07:00
09a2a8fc76 Log message change
PR comments
2020-09-11 15:53:34 -07:00
806aeddd87 Merge branch 'master' into validity 2020-09-11 10:09:01 -07:00
e4ed2a14e7 Merge pull request #3130 from hosseinsh/improved-csr-support
Improved csr support and EC key_type support
2020-09-11 10:08:44 -07:00
6e588f9c7b Merge branch 'master' into validity 2020-09-11 09:06:11 -07:00
1c9c377751 Lint errors 2020-09-11 12:31:15 +02:00
fd52438d61 yet lint errors 2020-09-11 12:30:53 +02:00
de9ad82011 Fixed Lint complaints 2020-09-11 12:24:33 +02:00
3487ecbaa7 Added entrust plugin doc and amended ADCS 2020-09-11 12:04:02 +02:00
aa0a31f90e Added entrust plugin 2020-09-11 11:16:23 +02:00
a99a84b0b2 entrust plugin inital edit 2020-09-10 16:04:31 +02:00
f47f108f43 ientrust plgin - first version 2020-09-10 16:03:29 +02:00
f6c10ef3d8 Merge pull request #5 from Netflix/master
Entrust Plugin
2020-09-10 14:15:55 +02:00
a7be8b6dce adding support for different types of CSR encodings 2020-09-09 19:54:53 -07:00
4923157dc2 expanding key_type to with EC support 2020-09-09 19:54:20 -07:00
aff7ad7ea2 testing 2020-09-09 19:53:59 -07:00
60fd2134ca removing duplicate curves, and marking them in existing mapping 2020-09-09 19:53:35 -07:00
5ab9626cbd overwriting cn and key_type values from CSR, as they take precedence 2020-09-09 19:52:59 -07:00
6fa15c4cb3 methods to extract cn and key_type from csr 2020-09-09 19:48:21 -07:00
de0c38e9ba mapping of curve name to key_type 2020-09-09 19:47:51 -07:00
1eb2df63cc Merge pull request #3127 from Netflix/dependabot/pip/boto3-1.14.56 2020-09-08 17:06:16 +00:00
aeead5363b Bump boto3 from 1.14.54 to 1.14.56
Bumps [boto3](https://github.com/boto/boto3) from 1.14.54 to 1.14.56.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.54...1.14.56)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-08 16:58:54 +00:00
e14dfc8639 Merge pull request #3125 from Netflix/dependabot/pip/psycopg2-2.8.6 2020-09-08 16:56:16 +00:00
6ef6fe40e6 Bump psycopg2 from 2.8.5 to 2.8.6
Bumps [psycopg2](https://github.com/psycopg/psycopg2) from 2.8.5 to 2.8.6.
- [Release notes](https://github.com/psycopg/psycopg2/releases)
- [Changelog](https://github.com/psycopg/psycopg2/blob/master/NEWS)
- [Commits](https://github.com/psycopg/psycopg2/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-08 16:46:51 +00:00
61a0aae96c Merge pull request #3126 from Netflix/dependabot/pip/freezegun-1.0.0 2020-09-08 16:44:48 +00:00
e0ba90d672 Bump freezegun from 0.3.15 to 1.0.0
Bumps [freezegun](https://github.com/spulec/freezegun) from 0.3.15 to 1.0.0.
- [Release notes](https://github.com/spulec/freezegun/releases)
- [Changelog](https://github.com/spulec/freezegun/blob/master/CHANGELOG)
- [Commits](https://github.com/spulec/freezegun/compare/0.3.15...1.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-08 16:38:36 +00:00
5d24cff338 Merge pull request #3124 from Netflix/dependabot/pip/botocore-1.17.56 2020-09-08 16:36:42 +00:00
458b4b062c Bump botocore from 1.17.54 to 1.17.56
Bumps [botocore](https://github.com/boto/botocore) from 1.17.54 to 1.17.56.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.17.54...1.17.56)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-08 06:13:59 +00:00
50f98dd223 Merge pull request #3122 from Netflix/dependabot/pip/boto3-1.14.54 2020-09-03 15:50:03 +00:00
86d37ced17 Bump boto3 from 1.14.48 to 1.14.54
Bumps [boto3](https://github.com/boto/boto3) from 1.14.48 to 1.14.54.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.48...1.14.54)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-03 05:27:42 +00:00
f66d91036b Merge pull request #3110 from Netflix/dependabot/pip/flask-cors-3.0.9 2020-09-03 05:24:55 +00:00
2fd05eed3d Bump flask-cors from 3.0.8 to 3.0.9
Bumps [flask-cors](https://github.com/corydolphin/flask-cors) from 3.0.8 to 3.0.9.
- [Release notes](https://github.com/corydolphin/flask-cors/releases)
- [Changelog](https://github.com/corydolphin/flask-cors/blob/master/CHANGELOG.md)
- [Commits](https://github.com/corydolphin/flask-cors/compare/3.0.8...3.0.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-03 05:13:48 +00:00
ac62cc7767 Merge pull request #3114 from Netflix/dependabot/pip/black-20.8b1 2020-09-03 05:11:24 +00:00
0d95d77a10 Bump black from 19.10b0 to 20.8b1
Bumps [black](https://github.com/psf/black) from 19.10b0 to 20.8b1.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/master/CHANGES.md)
- [Commits](https://github.com/psf/black/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-03 05:04:07 +00:00
cb38415b3c Merge pull request #3113 from Netflix/dependabot/pip/pytest-mock-3.3.1 2020-09-03 05:02:23 +00:00
b9a30a2188 Bump pytest-mock from 3.3.0 to 3.3.1
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.3.0...v3.3.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-03 04:34:55 +00:00
67c57b9b63 Merge pull request #3111 from Netflix/dependabot/pip/cryptography-3.1 2020-09-03 04:32:56 +00:00
26dfe5f654 Bump cryptography from 3.0 to 3.1
Bumps [cryptography](https://github.com/pyca/cryptography) from 3.0 to 3.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/3.0...3.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-03 00:59:57 +00:00
2ca8dba5e4 Merge pull request #3109 from Netflix/dependabot/pip/paramiko-2.7.2 2020-09-03 00:57:05 +00:00
9af887cf95 Bump paramiko from 2.7.1 to 2.7.2
Bumps [paramiko](https://github.com/paramiko/paramiko) from 2.7.1 to 2.7.2.
- [Release notes](https://github.com/paramiko/paramiko/releases)
- [Changelog](https://github.com/paramiko/paramiko/blob/master/NEWS)
- [Commits](https://github.com/paramiko/paramiko/compare/2.7.1...2.7.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-02 17:13:23 +00:00
7cad569114 Merge pull request #3107 from hosseinsh/ecc-support-for-authority-minting
adding the correct signing algorithm, and a missing key Type
2020-09-02 10:11:20 -07:00
6fd27c3372 Merge branch 'master' into ecc-support-for-authority-minting 2020-09-01 17:57:07 -07:00
8e30305596 Merge branch 'master' into validity 2020-09-01 17:56:52 -07:00
9ded7b8571 Merge pull request #3116 from Netflix/fix_distutils
Cleanup setup.py
2020-09-01 17:55:59 -07:00
ef6373cb3a Merge branch 'master' into fix_distutils 2020-09-01 17:42:14 -07:00
5dd6c6636b Merge pull request #3118 from Netflix/fix_travis_build_01
Add SETUPTOOLS_USE_DISTUTILS environment variable to fix travis build issue
2020-09-01 17:41:39 -07:00
af4bb72be3 adding SETUPTOOLS_USE_DISTUTILS environment variable to fix travis build issue 2020-09-01 17:28:42 -07:00
d5e51b3fc3 Remove changes to .travis.yml, moving them to new PR 2020-09-01 17:20:34 -07:00
beba785b09 cleaning up requirements imports and adding comments to change to .travis.yml 2020-09-01 17:16:18 -07:00
4ec0430a61 adding SETUP_TOOLS_USE_DISTUTILS to travis build file 2020-09-01 16:41:09 -07:00
079e8ccf3b removing explicit dependencies on import pip and moving to pkg_resources 2020-09-01 16:35:54 -07:00
77b67f613f removing dependency on distutils from setup.py 2020-09-01 16:07:47 -07:00
0077452e10 fixing import order to fix travis builds 2020-09-01 15:26:23 -07:00
8ad4448c85 Match date format for comparison + expected new lines 2020-09-01 12:44:49 -07:00
db4f68f0ed Logs during cert validity truncate for digicert 2020-08-31 18:20:32 -07:00
9c4fb85dc3 Calculate dates from defaultDays in js 2020-08-31 18:19:32 -07:00
d478def98c removing the custom key Type and doing the conversion in the backend 2020-08-31 16:35:47 -07:00
9a7a632489 using a standard curve for testing 2020-08-28 09:48:35 -07:00
a50c641044 Merge branch 'master' into ecc-support-for-authority-minting 2020-08-27 15:23:46 -07:00
9671b34485 adding support for all type of ECC curves which existing CA plugins might support 2020-08-27 14:15:14 -07:00
91c2976bfc fixing Makefile build issue with @echo 2020-08-27 14:15:14 -07:00
75eaea3aad fixing setup-git so build continues if ./git/hooks does not exist. 2020-08-27 14:15:14 -07:00
1fc2e29ab8 Remove 397 days validation as it causes error in API calls
More to come in future
2020-08-27 14:15:14 -07:00
1577f99567 Bump boto3 from 1.14.33 to 1.14.48
Bumps [boto3](https://github.com/boto/boto3) from 1.14.33 to 1.14.48.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.33...1.14.48)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
9d37f8018a Bump arrow from 0.15.8 to 0.16.0
Bumps [arrow](https://github.com/arrow-py/arrow) from 0.15.8 to 0.16.0.
- [Release notes](https://github.com/arrow-py/arrow/releases)
- [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/arrow-py/arrow/compare/0.15.8...0.16.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
beea47fd09 Bump cloudflare from 2.8.9 to 2.8.13
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.8.9 to 2.8.13.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.8.9...2.8.13)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
4955ec8541 Bump pytest-mock from 3.2.0 to 3.3.0
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.2.0...v3.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
ced9696322 Bump inflection from 0.5.0 to 0.5.1
Bumps [inflection](https://github.com/jpvanhal/inflection) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/jpvanhal/inflection/releases)
- [Commits](https://github.com/jpvanhal/inflection/compare/0.5.0...0.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
50d5c15a69 Bump sphinx from 3.2.0 to 3.2.1
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.2.0...v3.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
54ca1315ca Bump faker from 4.1.1 to 4.1.2
Bumps [faker](https://github.com/joke2k/faker) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/faker/compare/v4.1.1...v4.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
f7abfff51d Bump nodeenv from 1.4.0 to 1.5.0
Bumps [nodeenv](https://github.com/ekalinin/nodeenv) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/ekalinin/nodeenv/releases)
- [Changelog](https://github.com/ekalinin/nodeenv/blob/master/CHANGES)
- [Commits](https://github.com/ekalinin/nodeenv/compare/1.4.0...1.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
d4dfa63cf5 Bump pre-commit from 2.6.0 to 2.7.1
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.6.0 to 2.7.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.6.0...v2.7.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
cbc328d073 Bump factory-boy from 2.12.0 to 3.0.1
Bumps [factory-boy](https://github.com/FactoryBoy/factory_boy) from 2.12.0 to 3.0.1.
- [Release notes](https://github.com/FactoryBoy/factory_boy/releases)
- [Changelog](https://github.com/FactoryBoy/factory_boy/blob/master/docs/changelog.rst)
- [Commits](https://github.com/FactoryBoy/factory_boy/compare/2.12.0...3.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
c5106f5fa4 Bump fakeredis from 1.4.1 to 1.4.3
Bumps [fakeredis](https://github.com/jamesls/fakeredis) from 1.4.1 to 1.4.3.
- [Release notes](https://github.com/jamesls/fakeredis/releases)
- [Commits](https://github.com/jamesls/fakeredis/compare/1.4.1...1.4.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
07f1d751c4 Bump acme from 1.6.0 to 1.7.0
Bumps [acme](https://github.com/letsencrypt/letsencrypt) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/letsencrypt/letsencrypt/releases)
- [Commits](https://github.com/letsencrypt/letsencrypt/compare/v1.6.0...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
ab4cda2298 Extended ADCS_TEMPLATE_ Variable
If there is a config variable ADCS_TEMPLATE_<upper(authority.name)> take the value as Cert template else default to ADCS_TEMPLATE to be compatible with former versions
2020-08-27 14:15:14 -07:00
7a9500eee0 Lint error fix 2020-08-27 14:15:14 -07:00
e79dda3384 doc update DEFAULT_MAX_VALIDITY_DAYS 2020-08-27 14:15:14 -07:00
5ed109e998 Max end date as per start date + default validity 3 years 2020-08-27 14:15:14 -07:00
599a6943e2 Updating LEMUR_DEFAULT_ORGANIZATIONAL_UNIT to empty string 2020-08-27 14:15:14 -07:00
7011a4df8b max date on UI as per max validity configs 2020-08-27 14:15:14 -07:00
4d7c6844e5 Make Organizational Unit optional 2020-08-27 14:15:14 -07:00
2645c4a82d mention 397 for digicert plugin 2020-08-27 14:15:14 -07:00
8d2fffba87 Add new configs to the doc 2020-08-27 14:15:14 -07:00
3cb386cc0f maximum 1 year validity for digicert 2020-08-27 14:15:14 -07:00
e06dea106f Modify unit test test_determine_end_date to match new config 2020-08-27 14:15:14 -07:00
747df683a9 Bump sphinx from 3.1.2 to 3.2.0
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.1.2 to 3.2.0.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.1.2...v3.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
8a1563db54 Updating Lemur docs to capture Digicert validity config changes 2020-08-27 14:15:14 -07:00
d7d483fa9b Renaming PUBLIC_CA to PUBLIC_CA_AUTHORITY_NAMES 2020-08-27 14:15:14 -07:00
25125f3257 Cert validity should not exceed 397 days for publicly trusted issuers 2020-08-27 14:15:14 -07:00
a7082f7332 Bump cloudflare from 2.8.8 to 2.8.9
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.8.8...2.8.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
404d213e8f Modified cert description to have cert id being cloned 2020-08-27 14:15:14 -07:00
e75e472a1a Do not inherit replacement info during cert clone 2020-08-27 14:15:14 -07:00
69b64c63ea Honor selected algorithm during certificate cloning 2020-08-27 14:15:14 -07:00
d07464f3b1 updating documentation for cross-signed ICA 2020-08-27 14:15:14 -07:00
de0e646cf9 Bump boto3 from 1.14.28 to 1.14.33
Bumps [boto3](https://github.com/boto/boto3) from 1.14.28 to 1.14.33.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.28...1.14.33)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
5c5e53b8ec Bump botocore from 1.17.28 to 1.17.33
Bumps [botocore](https://github.com/boto/botocore) from 1.17.28 to 1.17.33.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.17.28...1.17.33)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
85f18afa81 Bump pytest from 5.4.3 to 6.0.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.4.3 to 6.0.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/5.4.3...6.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-27 14:15:14 -07:00
bc8eda2a6b fixing Dockerfile, Lemur builds now 2020-08-27 14:15:14 -07:00
f4bcd1cf30 lack of an empty config file was resulting into this error
```
Traceback (most recent call last):
  File "/home/travis/build/Netflix/lemur/lemur/plugins/lemur_acme/tests/test_acme.py", line 159, in test_request_certificate
    self.acme.request_certificate(mock_acme, [], mock_order)
  File "/home/travis/build/Netflix/lemur/lemur/plugins/lemur_acme/plugin.py", line 211, in request_certificate
    current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA_EXPIRATION_DATE", "17/03/21"), '%d/%m/%y'):
TypeError: strptime() argument 1 must be str, not MagicMock
```
2020-08-27 14:15:14 -07:00
5a6e4e5b43 Let's Encrypt has been using a cross-signed intermediate CA by DST Root CA X3, which is included in any older devices' TrustStore.
https://letsencrypt.org/certificates/

Let's Encrypt is transitioning to use the intermediate CA issued by their own root (ISRG X1) starting from September 29th 2020. This is in preparation of concluding the initial bootstrapping of their CA, by having it cross-signed by an older CA.
https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html

This PR allows Lemur to pin to the cross-signed ICA (same public/private key pair as the ICA signed by ISRG X1). This will prolong support for incompatible systems.
2020-08-27 14:15:14 -07:00
c169ad291e adding the correct signing algorithm, and a missing key Type 2020-08-27 13:29:56 -07:00
3242fc1e13 Validity with radio buttons 2020-08-26 19:30:12 -07:00
9af8d63f11 Merge pull request #3106 from Netflix/dev_branch
fixing setup-git so build continues if ./git/hooks does not exist.
2020-08-26 10:47:04 -07:00
27c5539178 fixing Makefile build issue with @echo 2020-08-26 01:47:17 -07:00
be21d357cb fixing setup-git so build continues if ./git/hooks does not exist. 2020-08-26 01:38:17 -07:00
6aedd3b0d8 Datepicker enhancements 2020-08-25 18:40:36 -07:00
2b64959953 Merge pull request #3105 from charhate/validity
Remove 397 days validation as it causes error in API calls
2020-08-25 16:33:54 -07:00
3efe14c43f Remove 397 days validation as it causes error in API calls
More to come in future
2020-08-25 16:26:20 -07:00
85b47bed05 Merge pull request #3103 from Netflix/dependabot/pip/boto3-1.14.48 2020-08-25 19:50:14 +00:00
0f463d5f13 Bump boto3 from 1.14.33 to 1.14.48
Bumps [boto3](https://github.com/boto/boto3) from 1.14.33 to 1.14.48.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.33...1.14.48)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 19:17:13 +00:00
367c6f0b03 Merge pull request #3096 from Netflix/dependabot/pip/arrow-0.16.0 2020-08-25 19:14:46 +00:00
939c41a70e Bump arrow from 0.15.8 to 0.16.0
Bumps [arrow](https://github.com/arrow-py/arrow) from 0.15.8 to 0.16.0.
- [Release notes](https://github.com/arrow-py/arrow/releases)
- [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/arrow-py/arrow/compare/0.15.8...0.16.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 19:04:47 +00:00
8855e60db7 Merge pull request #3087 from Netflix/dependabot/pip/cloudflare-2.8.13 2020-08-25 19:02:33 +00:00
a4a83dda72 Bump cloudflare from 2.8.9 to 2.8.13
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.8.9 to 2.8.13.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.8.9...2.8.13)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 18:55:27 +00:00
d229ccf654 Merge pull request #3098 from Netflix/dependabot/pip/pytest-mock-3.3.0 2020-08-25 18:53:19 +00:00
ad5416e441 Bump pytest-mock from 3.2.0 to 3.3.0
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.2.0...v3.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 18:42:38 +00:00
41f853afd6 Merge pull request #3100 from Netflix/dependabot/pip/inflection-0.5.1 2020-08-25 18:40:07 +00:00
88a043cfec Bump inflection from 0.5.0 to 0.5.1
Bumps [inflection](https://github.com/jpvanhal/inflection) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/jpvanhal/inflection/releases)
- [Commits](https://github.com/jpvanhal/inflection/compare/0.5.0...0.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 18:28:42 +00:00
605d218385 Merge pull request #3084 from Netflix/dependabot/pip/sphinx-3.2.1 2020-08-25 18:26:21 +00:00
1a8e96ed32 Bump sphinx from 3.2.0 to 3.2.1
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.2.0...v3.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 18:12:28 +00:00
dc4df9b279 Merge pull request #3094 from Netflix/dependabot/pip/faker-4.1.2 2020-08-25 18:10:24 +00:00
61eac2aada Bump faker from 4.1.1 to 4.1.2
Bumps [faker](https://github.com/joke2k/faker) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/faker/compare/v4.1.1...v4.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 18:03:20 +00:00
1cc3051aa2 Merge pull request #3095 from Netflix/dependabot/pip/nodeenv-1.5.0 2020-08-25 18:01:33 +00:00
ad0e469e92 Bump nodeenv from 1.4.0 to 1.5.0
Bumps [nodeenv](https://github.com/ekalinin/nodeenv) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/ekalinin/nodeenv/releases)
- [Changelog](https://github.com/ekalinin/nodeenv/blob/master/CHANGES)
- [Commits](https://github.com/ekalinin/nodeenv/compare/1.4.0...1.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 17:54:14 +00:00
9cb430f92c Merge pull request #3101 from Netflix/dependabot/pip/pre-commit-2.7.1 2020-08-25 17:52:30 +00:00
660a09e4c9 Bump pre-commit from 2.6.0 to 2.7.1
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.6.0 to 2.7.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.6.0...v2.7.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 17:41:18 +00:00
08762d3bbb Merge pull request #3086 from Netflix/dependabot/pip/factory-boy-3.0.1 2020-08-25 17:39:23 +00:00
553b7fab8d Bump factory-boy from 2.12.0 to 3.0.1
Bumps [factory-boy](https://github.com/FactoryBoy/factory_boy) from 2.12.0 to 3.0.1.
- [Release notes](https://github.com/FactoryBoy/factory_boy/releases)
- [Changelog](https://github.com/FactoryBoy/factory_boy/blob/master/docs/changelog.rst)
- [Commits](https://github.com/FactoryBoy/factory_boy/compare/2.12.0...3.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 17:28:39 +00:00
3d160a459c Merge pull request #3099 from Netflix/dependabot/pip/fakeredis-1.4.3 2020-08-25 17:25:59 +00:00
7b2b3911bc Bump fakeredis from 1.4.1 to 1.4.3
Bumps [fakeredis](https://github.com/jamesls/fakeredis) from 1.4.1 to 1.4.3.
- [Release notes](https://github.com/jamesls/fakeredis/releases)
- [Commits](https://github.com/jamesls/fakeredis/compare/1.4.1...1.4.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 17:19:48 +00:00
c899173dc9 Merge pull request #3080 from Netflix/dependabot/pip/acme-1.7.0 2020-08-25 17:17:51 +00:00
3e8ca982a1 Bump acme from 1.6.0 to 1.7.0
Bumps [acme](https://github.com/letsencrypt/letsencrypt) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/letsencrypt/letsencrypt/releases)
- [Commits](https://github.com/letsencrypt/letsencrypt/compare/v1.6.0...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 16:41:17 +00:00
136b8e67d4 Merge pull request #3092 from sirferl/master
Extended  ADCS_TEMPLATE_ Variable
2020-08-25 09:39:12 -07:00
301f099622 Fix link for WES-entropy-client 2020-08-21 09:56:46 +02:00
4f148f3bc3 Merge branch 'master' into master 2020-08-20 11:33:18 +02:00
41b35fb13d Merge pull request #3091 from charhate/pub_issuer
empty OU and date changes on UI
2020-08-19 11:31:46 -07:00
1b73b1d080 Merge branch 'master' into master 2020-08-19 12:29:02 +02:00
c2116df652 Extended ADCS_TEMPLATE_ Variable
If there is a config variable ADCS_TEMPLATE_<upper(authority.name)> take the value as Cert template else default to ADCS_TEMPLATE to be compatible with former versions
2020-08-19 12:25:52 +02:00
5b96b3a032 Lint error fix 2020-08-18 20:03:15 -07:00
d41227327e doc update DEFAULT_MAX_VALIDITY_DAYS 2020-08-18 19:47:38 -07:00
240f0b99c8 Max end date as per start date + default validity 3 years 2020-08-18 19:34:59 -07:00
cab1216cb7 Updating LEMUR_DEFAULT_ORGANIZATIONAL_UNIT to empty string 2020-08-18 15:14:34 -07:00
bc5579e9bf max date on UI as per max validity configs 2020-08-18 14:50:42 -07:00
5b3f40467b Make Organizational Unit optional 2020-08-18 14:50:42 -07:00
14b73b73cf Merge pull request #3081 from charhate/pub_issuer
Cert validity should not exceed 397 days for publicly trusted issuers
2020-08-17 17:41:25 -07:00
d86d599d61 Merge branch 'master' into pub_issuer 2020-08-11 19:28:36 -07:00
6ff8910f87 mention 397 for digicert plugin 2020-08-11 18:53:19 -07:00
acb0463844 Add new configs to the doc 2020-08-11 18:51:41 -07:00
d7ca1570be maximum 1 year validity for digicert 2020-08-11 18:02:42 -07:00
bde2829e72 Modify unit test test_determine_end_date to match new config 2020-08-11 17:10:29 -07:00
226a62d338 Bump sphinx from 3.1.2 to 3.2.0
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.1.2 to 3.2.0.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.1.2...v3.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-11 12:09:22 -07:00
0d1798b0e0 Bump cloudflare from 2.8.8 to 2.8.9
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.8.8...2.8.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-11 12:09:22 -07:00
aa800d79de Merge pull request #3076 from Netflix/dependabot/pip/sphinx-3.2.0
Bump sphinx from 3.1.2 to 3.2.0
2020-08-10 18:49:05 -07:00
9d3b7ac5dd Bump sphinx from 3.1.2 to 3.2.0
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.1.2 to 3.2.0.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.1.2...v3.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-11 01:40:26 +00:00
4c62108c11 Merge pull request #3079 from Netflix/dependabot/pip/cloudflare-2.8.9
Bump cloudflare from 2.8.8 to 2.8.9
2020-08-10 18:38:29 -07:00
682991c022 Updating Lemur docs to capture Digicert validity config changes 2020-08-10 18:07:46 -07:00
18a3514974 Renaming PUBLIC_CA to PUBLIC_CA_AUTHORITY_NAMES 2020-08-10 18:06:45 -07:00
7a83799bcd Cert validity should not exceed 397 days for publicly trusted issuers 2020-08-10 17:30:34 -07:00
914c029138 Bump cloudflare from 2.8.8 to 2.8.9
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.8.8...2.8.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-10 13:43:11 +00:00
574c76ad66 Merge pull request #3074 from charhate/bootswatch-fix
Certificate cloning enhancements/fixes
2020-08-06 10:17:57 -07:00
9bcfcebb3a Merge branch 'master' into bootswatch-fix 2020-08-04 14:09:33 -07:00
817a4c3d90 Modified cert description to have cert id being cloned 2020-08-03 19:24:06 -07:00
c3d8501401 Do not inherit replacement info during cert clone 2020-08-03 19:23:24 -07:00
c15a2c62d1 Honor selected algorithm during certificate cloning 2020-08-03 19:22:13 -07:00
983f9beacb Merge pull request #3057 from hosseinsh/pinning-to-cross-signed-LE-ICA
Pinning to the Cross-signed LE ICA
2020-08-03 17:50:13 -07:00
cbe06bd4d0 Merge branch 'master' into pinning-to-cross-signed-LE-ICA 2020-08-03 17:50:05 -07:00
2a8cf805dd Merge pull request #3070 from Netflix/testbranch
fixing Dockerfile, Lemur builds now
2020-08-03 17:49:15 -07:00
084f9a14f4 updating documentation for cross-signed ICA 2020-08-03 16:14:14 -07:00
2af336f6c2 Merge branch 'master' into pinning-to-cross-signed-LE-ICA 2020-08-03 15:58:59 -07:00
5c4a167cfd Merge branch 'master' into testbranch 2020-08-03 15:58:49 -07:00
b382d0332e Merge pull request #3073 from Netflix/dependabot/pip/boto3-1.14.33 2020-08-03 17:46:19 +00:00
117c0bab04 Bump boto3 from 1.14.28 to 1.14.33
Bumps [boto3](https://github.com/boto/boto3) from 1.14.28 to 1.14.33.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.28...1.14.33)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 17:38:07 +00:00
41a02610ba Merge pull request #3072 from Netflix/dependabot/pip/botocore-1.17.33 2020-08-03 17:35:24 +00:00
c80a3390e7 Bump botocore from 1.17.28 to 1.17.33
Bumps [botocore](https://github.com/boto/botocore) from 1.17.28 to 1.17.33.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.17.28...1.17.33)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 17:27:26 +00:00
88b38f7f0b Merge pull request #3071 from Netflix/dependabot/pip/pytest-6.0.1 2020-08-03 17:25:05 +00:00
229a5fbc9b Merge branch 'master' into testbranch 2020-08-03 09:47:12 -07:00
57e06cf1db Bump pytest from 5.4.3 to 6.0.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.4.3 to 6.0.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/5.4.3...6.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 13:36:46 +00:00
e7c684724a fixing Dockerfile, Lemur builds now 2020-07-31 17:54:18 -07:00
8e1d2b51e0 Merge branch 'master' into pinning-to-cross-signed-LE-ICA 2020-07-27 14:20:00 -07:00
b5e22f5a6b Merge pull request #3063 from Netflix/dependabot/pip/cryptography-3.0
Bump cryptography from 2.9.2 to 3.0
2020-07-27 14:19:43 -07:00
4752e10472 Bump cryptography from 2.9.2 to 3.0
Bumps [cryptography](https://github.com/pyca/cryptography) from 2.9.2 to 3.0.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/2.9.2...3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 18:40:59 +00:00
7ac754a894 Merge pull request #3066 from Netflix/dependabot/pip/boto3-1.14.28 2020-07-27 18:35:15 +00:00
1e90bb2d0b Bump boto3 from 1.14.23 to 1.14.28
Bumps [boto3](https://github.com/boto/boto3) from 1.14.23 to 1.14.28.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.23...1.14.28)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 18:26:11 +00:00
00134a0966 Merge pull request #3068 from Netflix/dependabot/pip/cloudflare-2.8.8 2020-07-27 18:19:51 +00:00
69bfe48cbe Bump cloudflare from 2.8.6 to 2.8.8
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.8.6 to 2.8.8.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.8.6...2.8.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 18:11:16 +00:00
f98d53fda0 Merge pull request #3069 from Netflix/dependabot/pip/arrow-0.15.8 2020-07-27 18:05:41 +00:00
99628aface Bump arrow from 0.15.7 to 0.15.8
Bumps [arrow](https://github.com/arrow-py/arrow) from 0.15.7 to 0.15.8.
- [Release notes](https://github.com/arrow-py/arrow/releases)
- [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/arrow-py/arrow/compare/0.15.7...0.15.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 17:58:40 +00:00
918e1699ea Merge pull request #3065 from Netflix/dependabot/pip/hvac-0.10.5 2020-07-27 17:53:21 +00:00
560894befb Bump hvac from 0.10.4 to 0.10.5
Bumps [hvac](https://github.com/hvac/hvac) from 0.10.4 to 0.10.5.
- [Release notes](https://github.com/hvac/hvac/releases)
- [Changelog](https://github.com/hvac/hvac/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/hvac/hvac/compare/v0.10.4...v0.10.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 17:45:03 +00:00
b5b50d34b2 Merge pull request #3067 from Netflix/dependabot/pip/coverage-5.2.1 2020-07-27 17:39:54 +00:00
276229db4a Bump coverage from 5.2 to 5.2.1
Bumps [coverage](https://github.com/nedbat/coveragepy) from 5.2 to 5.2.1.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/coverage-5.2...coverage-5.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 17:32:39 +00:00
6000d8bb06 Merge pull request #3064 from Netflix/dependabot/pip/botocore-1.17.28 2020-07-27 17:27:42 +00:00
0607520f93 Bump botocore from 1.17.23 to 1.17.28
Bumps [botocore](https://github.com/boto/botocore) from 1.17.23 to 1.17.28.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.17.23...1.17.28)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 13:40:35 +00:00
3c1d6998fb Merge branch 'master' into pinning-to-cross-signed-LE-ICA 2020-07-24 10:25:11 -07:00
456c994d25 Merge pull request #3062 from charhate/bootswatch-fix
Hardcoding bootswatch version to 3.4.1+1
2020-07-24 10:16:34 -07:00
4c3a6112b8 Merge branch 'master' into bootswatch-fix 2020-07-23 17:12:23 -07:00
f6faa856fe Hardcoding bootswatch version to 3.4.1+1
Fixing error : Potentially unhandled rejection [2] variable @path is undefined in file bower_components/bootswatch/sandstone/bootswatch.less line no. 10
2020-07-23 15:40:55 -07:00
91c0432cc2 Merge pull request #2982 from thousandeyes/fix-cryptography-intermediate-ca
Fix intermediate CA creation on cryptography plugin
2020-07-23 14:31:34 -07:00
0fd83d13ae Fix intermediate CA creation on cryptography plugin 2020-07-23 13:58:32 -07:00
4fa0374097 Merge branch 'master' into pinning-to-cross-signed-LE-ICA 2020-07-20 11:18:50 -07:00
a3ae76fac1 Merge pull request #3060 from Netflix/dependabot/pip/boto3-1.14.23 2020-07-20 17:39:43 +00:00
1da7564374 Bump boto3 from 1.14.20 to 1.14.23
Bumps [boto3](https://github.com/boto/boto3) from 1.14.20 to 1.14.23.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.20...1.14.23)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-20 17:33:37 +00:00
e7a0002f1b Merge pull request #3059 from Netflix/dependabot/pip/botocore-1.17.23 2020-07-20 17:31:04 +00:00
0d19986fb1 Bump botocore from 1.17.20 to 1.17.23
Bumps [botocore](https://github.com/boto/botocore) from 1.17.20 to 1.17.23.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.17.20...1.17.23)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-20 17:25:32 +00:00
c0869e0c71 Merge pull request #3061 from Netflix/dependabot/pip/flask-sqlalchemy-2.4.4 2020-07-20 17:23:11 +00:00
863bdc045b Bump flask-sqlalchemy from 2.4.3 to 2.4.4
Bumps [flask-sqlalchemy](https://github.com/pallets/flask-sqlalchemy) from 2.4.3 to 2.4.4.
- [Release notes](https://github.com/pallets/flask-sqlalchemy/releases)
- [Changelog](https://github.com/pallets/flask-sqlalchemy/blob/master/CHANGES.rst)
- [Commits](https://github.com/pallets/flask-sqlalchemy/compare/2.4.3...2.4.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-20 17:15:36 +00:00
8cc57a884e Merge pull request #3058 from Netflix/dependabot/pip/cloudflare-2.8.6 2020-07-20 17:13:19 +00:00
f7fce73e1e Bump cloudflare from 2.8.3 to 2.8.6
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.8.3 to 2.8.6.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.8.3...2.8.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-20 13:36:39 +00:00
2317967802 lack of an empty config file was resulting into this error
```
Traceback (most recent call last):
  File "/home/travis/build/Netflix/lemur/lemur/plugins/lemur_acme/tests/test_acme.py", line 159, in test_request_certificate
    self.acme.request_certificate(mock_acme, [], mock_order)
  File "/home/travis/build/Netflix/lemur/lemur/plugins/lemur_acme/plugin.py", line 211, in request_certificate
    current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA_EXPIRATION_DATE", "17/03/21"), '%d/%m/%y'):
TypeError: strptime() argument 1 must be str, not MagicMock
```
2020-07-15 17:04:49 -07:00
d5ae45a0d0 Let's Encrypt has been using a cross-signed intermediate CA by DST Root CA X3, which is included in any older devices' TrustStore.
https://letsencrypt.org/certificates/

Let's Encrypt is transitioning to use the intermediate CA issued by their own root (ISRG X1) starting from September 29th 2020. This is in preparation of concluding the initial bootstrapping of their CA, by having it cross-signed by an older CA.
https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html

This PR allows Lemur to pin to the cross-signed ICA (same public/private key pair as the ICA signed by ISRG X1). This will prolong support for incompatible systems.
2020-07-14 17:35:13 -07:00
a46991646b Merge pull request #3055 from Netflix/dependabot/pip/boto3-1.14.20 2020-07-13 17:50:03 +00:00
fd2adad49e Bump boto3 from 1.14.16 to 1.14.20
Bumps [boto3](https://github.com/boto/boto3) from 1.14.16 to 1.14.20.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.16...1.14.20)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 17:36:12 +00:00
e3afaccf97 Merge pull request #3053 from Netflix/dependabot/pip/sqlalchemy-utils-0.36.8 2020-07-13 17:33:57 +00:00
05cae2ae8d Bump sqlalchemy-utils from 0.36.7 to 0.36.8
Bumps [sqlalchemy-utils](https://github.com/kvesteri/sqlalchemy-utils) from 0.36.7 to 0.36.8.
- [Release notes](https://github.com/kvesteri/sqlalchemy-utils/releases)
- [Changelog](https://github.com/kvesteri/sqlalchemy-utils/blob/master/CHANGES.rst)
- [Commits](https://github.com/kvesteri/sqlalchemy-utils/compare/0.36.7...0.36.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 17:21:30 +00:00
5bfb98e097 Merge pull request #3054 from Netflix/dependabot/pip/acme-1.6.0 2020-07-13 17:19:25 +00:00
ba9b633c45 Bump acme from 1.5.0 to 1.6.0
Bumps [acme](https://github.com/letsencrypt/letsencrypt) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/letsencrypt/letsencrypt/releases)
- [Commits](https://github.com/letsencrypt/letsencrypt/compare/v1.5.0...v1.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 17:04:17 +00:00
e73c7db16b Merge pull request #3056 from Netflix/dependabot/pip/pytest-mock-3.2.0 2020-07-13 17:02:18 +00:00
0517d01748 Bump pytest-mock from 3.1.1 to 3.2.0
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.1.1...v3.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 16:44:52 +00:00
672f0e10f9 Merge pull request #3052 from Netflix/dependabot/pip/botocore-1.17.20 2020-07-13 16:43:03 +00:00
d02128a093 Bump botocore from 1.17.16 to 1.17.20
Bumps [botocore](https://github.com/boto/botocore) from 1.17.16 to 1.17.20.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.17.16...1.17.20)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 13:38:55 +00:00
d27a1292c5 Merge pull request #3051 from hosseinsh/reverse-package-lock-PR
reversing PR due to internal resolved addresses
2020-07-06 14:14:55 -07:00
f6fed4ddc2 reversing PR due to internal resolved addresses 2020-07-06 14:08:31 -07:00
a58e10f107 Merge pull request #3048 from Netflix/dependabot/pip/boto3-1.14.16 2020-07-06 19:05:47 +00:00
2aea0fbfba Bump boto3 from 1.14.12 to 1.14.16
Bumps [boto3](https://github.com/boto/boto3) from 1.14.12 to 1.14.16.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.12...1.14.16)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 18:58:25 +00:00
0fa9ce7a6d Merge pull request #3047 from Netflix/dependabot/pip/botocore-1.17.16 2020-07-06 18:56:00 +00:00
f863a227ac Bump botocore from 1.17.12 to 1.17.16
Bumps [botocore](https://github.com/boto/botocore) from 1.17.12 to 1.17.16.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.17.12...1.17.16)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 18:48:54 +00:00
31298845f5 Merge pull request #3049 from Netflix/dependabot/pip/sqlalchemy-utils-0.36.7 2020-07-06 18:46:16 +00:00
98c2e5cc31 Bump sqlalchemy-utils from 0.36.6 to 0.36.7
Bumps [sqlalchemy-utils](https://github.com/kvesteri/sqlalchemy-utils) from 0.36.6 to 0.36.7.
- [Release notes](https://github.com/kvesteri/sqlalchemy-utils/releases)
- [Changelog](https://github.com/kvesteri/sqlalchemy-utils/blob/master/CHANGES.rst)
- [Commits](https://github.com/kvesteri/sqlalchemy-utils/compare/0.36.6...0.36.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 18:37:07 +00:00
5e24e3fd91 Merge pull request #3045 from Netflix/dependabot/pip/sphinx-3.1.2 2020-07-06 18:34:31 +00:00
4050111291 Bump sphinx from 3.1.1 to 3.1.2
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.1.1...v3.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 18:25:18 +00:00
343ea09813 Merge pull request #3046 from Netflix/dependabot/pip/python-ldap-3.3.1 2020-07-06 18:23:23 +00:00
c384de7e19 Bump python-ldap from 3.3.0 to 3.3.1
Bumps [python-ldap](https://github.com/python-ldap/python-ldap) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/python-ldap/python-ldap/releases)
- [Commits](https://github.com/python-ldap/python-ldap/compare/python-ldap-3.3.0...python-ldap-3.3.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 18:12:45 +00:00
d8bc7d34c5 Merge pull request #3050 from Netflix/dependabot/pip/pre-commit-2.6.0 2020-07-06 18:10:41 +00:00
b329d1cdb9 Bump pre-commit from 2.5.1 to 2.6.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.5.1 to 2.6.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.5.1...v2.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 18:03:49 +00:00
0f00074500 Merge pull request #3044 from Netflix/dependabot/pip/coverage-5.2 2020-07-06 18:01:58 +00:00
d65198cd2e Bump coverage from 5.1 to 5.2
Bumps [coverage](https://github.com/nedbat/coveragepy) from 5.1 to 5.2.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/coverage-5.1...coverage-5.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 13:32:27 +00:00
75160ae5f3 Merge pull request #3041 from jramosf/patch-1
Fix unmatched field in Authorization
2020-07-02 10:34:04 -07:00
e0c2f4274e Merge branch 'master' into patch-1 2020-07-02 10:16:02 -07:00
4fd1d7d957 Merge pull request #3042 from jramosf/patch-2
Remove f from non-f string
2020-07-02 10:10:59 -07:00
aa11088944 Remove f from non-f string 2020-07-02 16:48:41 +02:00
1f598e3752 Fix unmatched field in Authorization
The field in the formatted string was not matching the args
2020-07-02 16:41:19 +02:00
5870ff4713 Merge pull request #3040 from jramosf/patch-1
Raise ValidationError if CSR contains invalid CN
2020-07-01 14:48:55 -07:00
7a5a5531cc Raise ValidationError if CSR contains invalid CN
If we supply a CSR that contains an empty field in the Subject, Lemur will crash with an error 500 as the ValueError exception is not captured. This change captures the exception and raises a ValidationError which in this case is a 400 sent back to client. Example to reproduce:

    Subject: C=ZZ, ST=Something, L=, O=My_Org, OU=My_Dept, CN=www.booking.com

The empty L= causes a ValueError which needs to be captured.
2020-07-01 15:44:06 +02:00
47946510d4 Merge pull request #3038 from Netflix/dependabot/pip/boto3-1.14.12 2020-06-29 17:20:43 +00:00
9ec7593bc7 Bump boto3 from 1.14.8 to 1.14.12
Bumps [boto3](https://github.com/boto/boto3) from 1.14.8 to 1.14.12.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.8...1.14.12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-29 17:13:05 +00:00
6bb3fa20fb Merge pull request #3037 from Netflix/dependabot/pip/cloudflare-2.8.3 2020-06-29 17:10:32 +00:00
f17ad9aad9 Bump cloudflare from 2.8.2 to 2.8.3
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.8.2 to 2.8.3.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.8.2...2.8.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-29 17:03:09 +00:00
c722df1245 Merge pull request #3039 from Netflix/dependabot/pip/botocore-1.17.12 2020-06-29 17:00:53 +00:00
81457f88c9 Bump botocore from 1.17.8 to 1.17.12
Bumps [botocore](https://github.com/boto/botocore) from 1.17.8 to 1.17.12.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.17.8...1.17.12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-29 16:46:32 +00:00
a669e5b057 Merge pull request #3036 from Netflix/dependabot/pip/twine-3.2.0 2020-06-29 16:44:00 +00:00
80b9f97b39 Bump twine from 3.1.1 to 3.2.0
Bumps [twine](https://github.com/pypa/twine) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/pypa/twine/releases)
- [Changelog](https://github.com/pypa/twine/blob/master/docs/changelog.rst)
- [Commits](https://github.com/pypa/twine/compare/3.1.1...3.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-29 13:38:29 +00:00
9f641c14a9 Merge pull request #3025 from Netflix/dependabot/pip/certifi-2020.6.20 2020-06-23 00:14:12 +00:00
1d9af2eb72 Bump certifi from 2020.4.5.2 to 2020.6.20
Bumps [certifi](https://github.com/certifi/python-certifi) from 2020.4.5.2 to 2020.6.20.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2020.04.05.2...2020.06.20)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-23 00:08:05 +00:00
0003e53f5a Merge pull request #3034 from Netflix/dependabot/pip/boto3-1.14.8 2020-06-23 00:05:24 +00:00
5ab9d7f4e8 Bump boto3 from 1.13.19 to 1.14.8
Bumps [boto3](https://github.com/boto/boto3) from 1.13.19 to 1.14.8.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.19...1.14.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 23:50:41 +00:00
e61ecea0f2 Merge pull request #3029 from Netflix/dependabot/pip/requests-2.24.0 2020-06-22 23:47:59 +00:00
1be0362b11 Bump requests from 2.23.0 to 2.24.0
Bumps [requests](https://github.com/psf/requests) from 2.23.0 to 2.24.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.23.0...v2.24.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 23:39:56 +00:00
7ada40d1b3 Merge pull request #3031 from Netflix/dependabot/pip/arrow-0.15.7 2020-06-22 23:37:15 +00:00
ff6e23f2ea Bump arrow from 0.15.6 to 0.15.7
Bumps [arrow](https://github.com/crsmithdev/arrow) from 0.15.6 to 0.15.7.
- [Release notes](https://github.com/crsmithdev/arrow/releases)
- [Changelog](https://github.com/crsmithdev/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/crsmithdev/arrow/compare/0.15.6...0.15.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 23:29:09 +00:00
738afc4760 Merge pull request #3020 from Netflix/dependabot/pip/flask-replicated-1.4 2020-06-22 23:27:02 +00:00
9c20c0c950 Bump flask-replicated from 1.3 to 1.4
Bumps [flask-replicated](https://github.com/peterdemin/python-flask-replicated) from 1.3 to 1.4.
- [Release notes](https://github.com/peterdemin/python-flask-replicated/releases)
- [Commits](https://github.com/peterdemin/python-flask-replicated/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 23:17:28 +00:00
bf4043d4c2 Merge pull request #3033 from Netflix/dependabot/pip/cloudflare-2.8.2 2020-06-22 23:15:23 +00:00
2a12fd886d Bump cloudflare from 2.7.1 to 2.8.2
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.7.1 to 2.8.2.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.7.1...2.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 23:04:50 +00:00
d350953bcc Merge pull request #3018 from Netflix/dependabot/pip/sphinx-3.1.1 2020-06-22 23:02:45 +00:00
b6b07edcb6 Bump sphinx from 3.1.0 to 3.1.1
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.1.0...v3.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 22:53:26 +00:00
fb39c48530 Merge pull request #3028 from Netflix/dependabot/pip/sphinx-rtd-theme-0.5.0 2020-06-22 22:51:35 +00:00
c8a232f65f Bump sphinx-rtd-theme from 0.4.3 to 0.5.0
Bumps [sphinx-rtd-theme](https://github.com/rtfd/sphinx_rtd_theme) from 0.4.3 to 0.5.0.
- [Release notes](https://github.com/rtfd/sphinx_rtd_theme/releases)
- [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst)
- [Commits](https://github.com/rtfd/sphinx_rtd_theme/compare/0.4.3...0.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 22:43:42 +00:00
125e6d046f Merge pull request #3027 from Netflix/dependabot/pip/python-ldap-3.3.0 2020-06-22 22:41:40 +00:00
17e28748ff Bump python-ldap from 3.2.0 to 3.3.0
Bumps [python-ldap](https://github.com/python-ldap/python-ldap) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/python-ldap/python-ldap/releases)
- [Commits](https://github.com/python-ldap/python-ldap/compare/python-ldap-3.2.0...python-ldap-3.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 22:26:26 +00:00
f532d5c1a5 Merge pull request #3024 from Netflix/dependabot/pip/hvac-0.10.4 2020-06-22 22:24:13 +00:00
8af7a2155d Bump hvac from 0.10.3 to 0.10.4
Bumps [hvac](https://github.com/hvac/hvac) from 0.10.3 to 0.10.4.
- [Release notes](https://github.com/hvac/hvac/releases)
- [Changelog](https://github.com/hvac/hvac/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/hvac/hvac/compare/v0.10.3...v0.10.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 22:13:20 +00:00
a480330c98 Merge pull request #3021 from Netflix/dependabot/pip/pre-commit-2.5.1 2020-06-22 22:11:20 +00:00
59d63b5f40 Bump pre-commit from 2.4.0 to 2.5.1
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.4.0 to 2.5.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.4.0...v2.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 22:01:18 +00:00
51c0342ec4 Merge pull request #3026 from Netflix/dependabot/pip/faker-4.1.1 2020-06-22 21:59:38 +00:00
431c3cc686 Bump faker from 4.1.0 to 4.1.1
Bumps [faker](https://github.com/joke2k/faker) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/faker/compare/v4.1.0...v4.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 21:53:12 +00:00
88ed00c330 Merge pull request #3017 from Netflix/dependabot/pip/botocore-1.16.26 2020-06-22 21:51:22 +00:00
804d3b630a Bump botocore from 1.16.25 to 1.16.26
Bumps [botocore](https://github.com/boto/botocore) from 1.16.25 to 1.16.26.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.16.25...1.16.26)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-15 13:37:15 +00:00
e024fceba2 Merge pull request #3016 from hosseinsh/UnboundLocalError
fixing UnboundLocalError bug
2020-06-11 16:57:48 -07:00
4985744bd8 fixing UnboundLocalError bug 2020-06-11 16:47:37 -07:00
5ea3815c7e Merge pull request #3015 from Netflix/ultra_move_01
moving ultradns tests to separate file
2020-06-11 14:43:23 -07:00
a7a309136f fixing whitespace and imports 2020-06-11 14:15:40 -07:00
f834d10f9a moving ultradns tests to separate file 2020-06-11 14:04:17 -07:00
43bb48c286 Merge pull request #2884 from ilyalabun/ilabun/optimize-certificates-sql
Optimize certificates sql
2020-06-09 17:10:04 -07:00
c40d297735 Merge branch 'master' into ilabun/optimize-certificates-sql 2020-06-09 14:20:31 -07:00
1b05971268 Merge pull request #2985 from hosseinsh/adding-package-lock
freeze dependencies for more reliable builds
2020-06-09 12:17:52 -07:00
472a4654a2 Merge branch 'master' into adding-package-lock 2020-06-09 11:23:36 -07:00
1a0704c43b Merge pull request #2986 from hosseinsh/json-logging-rotate
adding json formatted logging
2020-06-09 11:23:23 -07:00
fd3ea2cf46 Merge branch 'master' into json-logging-rotate 2020-06-09 10:58:53 -07:00
3ec8b7b36e Merge pull request #2842 from hosseinsh/check-revoke-revised
improving check revoked by only considering authorities which do supp…
2020-06-09 10:58:41 -07:00
099ebee409 Merge branch 'master' into check-revoke-revised 2020-06-09 10:47:24 -07:00
64b4437ca7 Merge branch 'master' into adding-package-lock 2020-06-09 10:46:08 -07:00
62469e518f Merge branch 'master' into json-logging-rotate 2020-06-09 10:45:57 -07:00
e5a9debf10 Merge pull request #3013 from Netflix/dependabot/pip/botocore-1.16.25 2020-06-09 16:21:50 +00:00
cd47101069 Bump botocore from 1.16.19 to 1.16.25
Bumps [botocore](https://github.com/boto/botocore) from 1.16.19 to 1.16.25.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.16.19...1.16.25)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 04:09:05 +00:00
b059813c5b Merge pull request #3004 from Netflix/dependabot/pip/certifi-2020.4.5.2 2020-06-09 04:06:48 +00:00
422d9ba050 Bump certifi from 2020.4.5.1 to 2020.4.5.2
Bumps [certifi](https://github.com/certifi/python-certifi) from 2020.4.5.1 to 2020.4.5.2.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2020.04.05.1...2020.04.05.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 03:56:46 +00:00
44be7d2900 Merge pull request #3007 from Netflix/dependabot/pip/redis-3.5.3 2020-06-09 03:54:29 +00:00
34f61db5bc Bump redis from 3.5.2 to 3.5.3
Bumps [redis](https://github.com/andymccurdy/redis-py) from 3.5.2 to 3.5.3.
- [Release notes](https://github.com/andymccurdy/redis-py/releases)
- [Changelog](https://github.com/andymccurdy/redis-py/blob/master/CHANGES)
- [Commits](https://github.com/andymccurdy/redis-py/compare/3.5.2...3.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 03:46:06 +00:00
735db771f8 Merge pull request #3012 from Netflix/dependabot/pip/inflection-0.5.0 2020-06-09 03:43:51 +00:00
f08394ffe2 Bump inflection from 0.4.0 to 0.5.0
Bumps [inflection](https://github.com/jpvanhal/inflection) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/jpvanhal/inflection/releases)
- [Commits](https://github.com/jpvanhal/inflection/compare/0.4.0...0.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 03:35:59 +00:00
354f699135 Merge pull request #3006 from Netflix/dependabot/pip/sphinx-3.1.0 2020-06-09 03:33:55 +00:00
5d00670346 Bump sphinx from 3.0.4 to 3.1.0
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.0.4 to 3.1.0.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.0.4...v3.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 03:28:33 +00:00
a2c56d6686 Merge pull request #2992 from hosseinsh/improve-expiry-email
improved messaging
2020-06-08 20:26:43 -07:00
c3b36d697f clarification 2020-06-08 15:17:45 -07:00
9dccd6f3bd Merge branch 'master' into improve-expiry-email 2020-06-08 15:14:23 -07:00
d9d46dba35 Merge pull request #3005 from Netflix/dependabot/pip/pytest-5.4.3 2020-06-08 17:18:31 +00:00
73347a02b6 Bump pytest from 5.4.2 to 5.4.3
Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.4.2 to 5.4.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/5.4.2...5.4.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-08 17:11:16 +00:00
ad005e0b4c Merge pull request #3011 from Netflix/dependabot/pip/nodeenv-1.4.0 2020-06-08 17:09:29 +00:00
a632f32816 Bump nodeenv from 1.3.5 to 1.4.0
Bumps [nodeenv](https://github.com/ekalinin/nodeenv) from 1.3.5 to 1.4.0.
- [Release notes](https://github.com/ekalinin/nodeenv/releases)
- [Changelog](https://github.com/ekalinin/nodeenv/blob/master/CHANGES)
- [Commits](https://github.com/ekalinin/nodeenv/compare/1.3.5...1.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-08 17:02:49 +00:00
9d097c593e Merge pull request #3010 from Netflix/dependabot/pip/pytest-mock-3.1.1 2020-06-08 17:01:04 +00:00
db45fc678f Bump pytest-mock from 3.1.0 to 3.1.1
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.1.0...v3.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-08 16:51:27 +00:00
4a040c66c5 Merge pull request #3003 from Netflix/dependabot/pip/acme-1.5.0 2020-06-08 16:49:39 +00:00
53601be788 Merge branch 'master' into dependabot/pip/acme-1.5.0 2020-06-08 09:43:39 -07:00
a0c2357d81 Merge pull request #3002 from avoidik/master
docker - add libtool, fix url context path
2020-06-08 09:42:52 -07:00
a2e84441ce Bump acme from 1.4.0 to 1.5.0
Bumps [acme](https://github.com/letsencrypt/letsencrypt) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/letsencrypt/letsencrypt/releases)
- [Commits](https://github.com/letsencrypt/letsencrypt/compare/v1.4.0...v1.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-08 13:40:31 +00:00
7fad7df768 docker - add libtool, fix url context path 2020-06-06 16:23:12 +03:00
5215a71a6d Merge branch 'master' into check-revoke-revised 2020-06-04 15:51:48 -07:00
b487363bda Merge branch 'master' into adding-package-lock 2020-06-04 15:51:33 -07:00
704e61dd53 Merge branch 'master' into json-logging-rotate 2020-06-04 15:51:24 -07:00
e06c3ea192 Merge branch 'master' into improve-expiry-email 2020-06-04 15:51:17 -07:00
8beaeffaec Merge pull request #2997 from Netflix/dependabot/pip/flask-sqlalchemy-2.4.3 2020-06-04 00:34:58 +00:00
7e8bb97920 Merge branch 'master' into dependabot/pip/flask-sqlalchemy-2.4.3 2020-06-03 17:29:26 -07:00
8270ee98e9 Merge pull request #2998 from Netflix/dependabot/pip/boto3-1.13.19 2020-06-04 00:02:21 +00:00
aadca10a12 Merge branch 'master' into dependabot/pip/flask-sqlalchemy-2.4.3 2020-06-03 16:56:28 -07:00
03eb991c89 Merge branch 'master' into dependabot/pip/boto3-1.13.19 2020-06-03 16:56:25 -07:00
6c957be3d8 Merge pull request #3001 from alwaysjolley/valid_domain_fix
fixing domain validation
2020-06-03 16:55:42 -07:00
1bcc9d5d0d allowing for _ in domains 2020-06-03 13:20:23 -04:00
1b8507636b fixing quotes, no escape characters in tests, fixed anchors 2020-06-03 12:49:55 -04:00
3ce7cd6c50 fixing escaped string on domain test 2020-06-03 11:34:14 -04:00
8658ac531e fixing unittests and allowing for single character domains 2020-06-03 08:08:49 -04:00
2a1751ec30 fixing domain validation to account for 2-63 character length and correct character set 2020-06-03 04:56:38 -04:00
860b35e19d Merge branch 'master' into adding-package-lock 2020-06-02 13:54:32 -07:00
aff8500b85 Merge branch 'master' into json-logging-rotate 2020-06-02 13:54:21 -07:00
840408d5d2 Merge branch 'master' into improve-expiry-email 2020-06-02 13:54:09 -07:00
9a5ec72850 Bump boto3 from 1.13.18 to 1.13.19
Bumps [boto3](https://github.com/boto/boto3) from 1.13.18 to 1.13.19.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.18...1.13.19)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-01 17:02:26 +00:00
b68a7f1de2 Bump flask-sqlalchemy from 2.4.1 to 2.4.3
Bumps [flask-sqlalchemy](https://github.com/pallets/flask-sqlalchemy) from 2.4.1 to 2.4.3.
- [Release notes](https://github.com/pallets/flask-sqlalchemy/releases)
- [Changelog](https://github.com/pallets/flask-sqlalchemy/blob/master/CHANGES.rst)
- [Commits](https://github.com/pallets/flask-sqlalchemy/compare/2.4.1...2.4.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-01 17:02:08 +00:00
93225f1ec2 Merge pull request #2999 from Netflix/dependabot/pip/sphinx-3.0.4 2020-06-01 16:59:50 +00:00
01bb9df16d Bump sphinx from 3.0.3 to 3.0.4
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.0.3 to 3.0.4.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.0.3...v3.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-01 16:48:50 +00:00
c42bf89c5e Merge pull request #2996 from Netflix/dependabot/pip/botocore-1.16.19 2020-06-01 16:46:42 +00:00
2e73a4e872 Bump botocore from 1.16.18 to 1.16.19
Bumps [botocore](https://github.com/boto/botocore) from 1.16.18 to 1.16.19.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.16.18...1.16.19)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-01 16:39:45 +00:00
a3af428639 Merge pull request #2995 from Netflix/dependabot/pip/marshmallow-sqlalchemy-0.23.1 2020-06-01 16:37:36 +00:00
e2fefdbe89 Bump marshmallow-sqlalchemy from 0.23.0 to 0.23.1
Bumps [marshmallow-sqlalchemy](https://github.com/marshmallow-code/marshmallow-sqlalchemy) from 0.23.0 to 0.23.1.
- [Release notes](https://github.com/marshmallow-code/marshmallow-sqlalchemy/releases)
- [Changelog](https://github.com/marshmallow-code/marshmallow-sqlalchemy/blob/dev/CHANGELOG.rst)
- [Commits](https://github.com/marshmallow-code/marshmallow-sqlalchemy/compare/0.23.0...0.23.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-01 13:38:37 +00:00
50091cca1d Merge branch 'master' into ilabun/optimize-certificates-sql 2020-05-27 15:29:47 -07:00
d8948a12d3 Merge branch 'master' into check-revoke-revised 2020-05-27 15:29:19 -07:00
277249fff1 Merge branch 'master' into adding-package-lock 2020-05-27 15:29:00 -07:00
86c3771044 Merge branch 'master' into json-logging-rotate 2020-05-27 15:28:48 -07:00
904bc9d8b6 Merge branch 'master' into improve-expiry-email 2020-05-27 15:28:41 -07:00
4bf7e26976 Merge pull request #2994 from Netflix/dependabot/pip/boto3-1.13.18 2020-05-27 22:24:58 +00:00
66a5c3880e Bump boto3 from 1.13.11 to 1.13.18
Bumps [boto3](https://github.com/boto/boto3) from 1.13.11 to 1.13.18.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.11...1.13.18)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-27 22:18:14 +00:00
084672bd07 Merge pull request #2991 from Netflix/dependabot/pip/hvac-0.10.3 2020-05-27 22:15:47 +00:00
3ac2e20e93 Bump hvac from 0.10.1 to 0.10.3
Bumps [hvac](https://github.com/hvac/hvac) from 0.10.1 to 0.10.3.
- [Release notes](https://github.com/hvac/hvac/releases)
- [Changelog](https://github.com/hvac/hvac/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/hvac/hvac/compare/v0.10.1...v0.10.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-27 22:05:24 +00:00
f794ad7e9e Merge pull request #2990 from Netflix/dependabot/pip/sqlalchemy-utils-0.36.6 2020-05-27 22:03:15 +00:00
44eeb2b738 Bump sqlalchemy-utils from 0.36.5 to 0.36.6
Bumps [sqlalchemy-utils](https://github.com/kvesteri/sqlalchemy-utils) from 0.36.5 to 0.36.6.
- [Release notes](https://github.com/kvesteri/sqlalchemy-utils/releases)
- [Changelog](https://github.com/kvesteri/sqlalchemy-utils/blob/master/CHANGES.rst)
- [Commits](https://github.com/kvesteri/sqlalchemy-utils/compare/0.36.5...0.36.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-27 21:56:49 +00:00
22759e7f5c Merge pull request #2988 from Netflix/dependabot/pip/six-1.15.0 2020-05-27 21:55:02 +00:00
760a219e09 Bump six from 1.14.0 to 1.15.0
Bumps [six](https://github.com/benjaminp/six) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/benjaminp/six/releases)
- [Changelog](https://github.com/benjaminp/six/blob/master/CHANGES)
- [Commits](https://github.com/benjaminp/six/compare/1.14.0...1.15.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-27 21:38:46 +00:00
0ed10eafbb Merge pull request #2922 from e11it/master
Docker fixes. Vault dest path and obj_name support vars from cert
2020-05-27 14:36:22 -07:00
d95f02d234 Merge branch 'master' into master 2020-05-27 14:25:07 -07:00
8861cc70cb rewordin 2020-05-26 17:12:47 -07:00
34e3f7c049 improved messaging 2020-05-26 16:38:12 -07:00
4eeab91d73 making lint happy 2020-05-22 18:36:39 -07:00
10dfedee36 making lint happy 2020-05-22 18:33:43 -07:00
5f87b7d2db Merge branch 'master' into adding-package-lock 2020-05-22 18:25:15 -07:00
86310ff02d Merge branch 'master' into check-revoke-revised 2020-05-22 18:25:00 -07:00
87a53557cd Merge branch 'master' into json-logging-rotate 2020-05-22 18:24:53 -07:00
b96657994a Merge pull request #2953 from hosseinsh/cert-rotation-region-by-region
Certificate rotation region by region
2020-05-22 18:24:20 -07:00
8f16688b0a Merge branch 'master' into check-revoke-revised 2020-05-22 17:45:50 -07:00
49a8b80df2 better exception handling when OCSP or CRL or not implemented 2020-05-22 17:36:34 -07:00
c9767b3172 adding logging for revoked certs 2020-05-22 17:32:44 -07:00
49c4a9c3b2 making the revocation to be scoped based on the authority plugin name 2020-05-22 17:29:30 -07:00
4923bbf8a7 adding json formatted logging 2020-05-22 16:22:12 -07:00
09016fd2ee cleaning up the code after more local testing 2020-05-22 16:04:39 -07:00
13cfe7a754 Merge branch 'master' into master 2020-05-22 21:53:05 +03:00
f83e3f764e always assign csr_sans to name 2020-05-22 21:52:43 +03:00
b292c1c3c3 Merge branch 'master' into cert-rotation-region-by-region 2020-05-22 11:32:12 -07:00
0e38870fb5 freeze dependencies for more reliable builds as recommended by
https://docs.npmjs.com/files/package-lock.json
2020-05-22 11:20:26 -07:00
ecf940bb8f Update README.rst
updating python
2020-05-22 10:44:02 -07:00
97145b6dee Merge branch 'master' into ilabun/optimize-certificates-sql 2020-05-22 10:29:28 -07:00
cc4fc66c93 Merge branch 'master' into master 2020-05-22 09:57:46 -07:00
748268ecd5 Merge branch 'master' into cert-rotation-region-by-region 2020-05-22 09:57:06 -07:00
f60ade4d28 Merge pull request #2984 from hosseinsh/improved-logging-enable-auto-rotate
improved logging for enable auto-rotate
2020-05-21 16:12:27 -07:00
3b8f3612a1 Merge branch 'master' into improved-logging-enable-auto-rotate 2020-05-21 15:41:01 -07:00
2582086d39 Merge branch 'master' into ilabun/optimize-certificates-sql 2020-05-21 15:39:58 -07:00
2422717467 Merge pull request #2966 from lumatijev/master
Improve periodic tasks docs
2020-05-21 15:39:10 -07:00
fd444403bb improved logging.
- adding destination name, fixing broken metric.
2020-05-21 15:32:38 -07:00
07cb2f71cb Merge branch 'master' into master 2020-05-18 11:59:46 -07:00
2660beb0bc Merge branch 'master' into cert-rotation-region-by-region 2020-05-18 11:57:19 -07:00
fb3373f789 Merge pull request #2974 from Netflix/dependabot/pip/pre-commit-2.4.0 2020-05-18 18:16:17 +00:00
4d4ff85090 Bump pre-commit from 2.3.0 to 2.4.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.3.0...v2.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 17:44:22 +00:00
fb6979ae6d Merge pull request #2978 from Netflix/dependabot/pip/boto3-1.13.11 2020-05-18 17:42:41 +00:00
2bf03a0bc2 Bump boto3 from 1.13.6 to 1.13.11
Bumps [boto3](https://github.com/boto/boto3) from 1.13.6 to 1.13.11.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.6...1.13.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 17:35:43 +00:00
9d894b0817 Merge pull request #2980 from Netflix/dependabot/pip/redis-3.5.2 2020-05-18 17:32:42 +00:00
d010553d23 Bump redis from 3.4.1 to 3.5.2
Bumps [redis](https://github.com/andymccurdy/redis-py) from 3.4.1 to 3.5.2.
- [Release notes](https://github.com/andymccurdy/redis-py/releases)
- [Changelog](https://github.com/andymccurdy/redis-py/blob/master/CHANGES)
- [Commits](https://github.com/andymccurdy/redis-py/compare/3.4.1...3.5.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 17:23:23 +00:00
25685b4e16 Merge pull request #2979 from Netflix/dependabot/pip/botocore-1.16.11 2020-05-18 17:21:07 +00:00
9bc017c9bf Bump botocore from 1.16.6 to 1.16.11
Bumps [botocore](https://github.com/boto/botocore) from 1.16.6 to 1.16.11.
- [Release notes](https://github.com/boto/botocore/releases)
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.16.6...1.16.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 17:11:39 +00:00
c1902d9232 Merge pull request #2977 from Netflix/dependabot/pip/cloudflare-2.7.1 2020-05-18 17:09:20 +00:00
d3a0fe7491 Bump cloudflare from 2.6.5 to 2.7.1
Bumps [cloudflare](https://github.com/cloudflare/python-cloudflare) from 2.6.5 to 2.7.1.
- [Release notes](https://github.com/cloudflare/python-cloudflare/releases)
- [Changelog](https://github.com/cloudflare/python-cloudflare/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/python-cloudflare/compare/2.6.5...2.7.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 16:58:47 +00:00
ba1bb688b7 Merge pull request #2967 from Netflix/dependabot/pip/marshmallow-sqlalchemy-0.23.0 2020-05-18 16:56:29 +00:00
23cd7d3d62 Bump marshmallow-sqlalchemy from 0.22.3 to 0.23.0
Bumps [marshmallow-sqlalchemy](https://github.com/marshmallow-code/marshmallow-sqlalchemy) from 0.22.3 to 0.23.0.
- [Release notes](https://github.com/marshmallow-code/marshmallow-sqlalchemy/releases)
- [Changelog](https://github.com/marshmallow-code/marshmallow-sqlalchemy/blob/dev/CHANGELOG.rst)
- [Commits](https://github.com/marshmallow-code/marshmallow-sqlalchemy/compare/0.22.3...0.23.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 16:47:18 +00:00
296d4d04a0 Merge pull request #2970 from Netflix/dependabot/pip/pytest-mock-3.1.0 2020-05-18 16:44:53 +00:00
dfd9acb0d7 Bump pytest-mock from 3.0.0 to 3.1.0
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.0.0...v3.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 16:37:21 +00:00
ae75fa8246 Merge pull request #2971 from Netflix/dependabot/pip/pytest-5.4.2 2020-05-18 16:35:41 +00:00
9e1737edc4 Bump pytest from 5.4.1 to 5.4.2
Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.4.1 to 5.4.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/5.4.1...5.4.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 16:25:47 +00:00
c4b42e194f Merge pull request #2968 from Netflix/dependabot/pip/requests-mock-1.8.0 2020-05-18 16:23:59 +00:00
c0829478fd Bump requests-mock from 1.7.0 to 1.8.0
Bumps [requests-mock](https://github.com/jamielennox/requests-mock) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/jamielennox/requests-mock/releases)
- [Commits](https://github.com/jamielennox/requests-mock/compare/1.7.0...1.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 16:14:27 +00:00
477248dd8b Merge pull request #2981 from Netflix/dependabot/pip/faker-4.1.0 2020-05-18 16:12:33 +00:00
5d75204a4f Bump faker from 4.0.3 to 4.1.0
Bumps [faker](https://github.com/joke2k/faker) from 4.0.3 to 4.1.0.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/faker/compare/v4.0.3...v4.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 13:45:08 +00:00
70985f4ff5 revised system arch 2020-05-14 22:37:30 -07:00
201a96ff72 Improve periodic tasks docs 2020-05-11 13:59:07 +02:00
a17f665c52 Merge pull request #2965 from Netflix/dependabot/pip/boto3-1.13.6 2020-05-08 23:02:32 +00:00
5afa32fa22 Bump boto3 from 1.12.39 to 1.13.6
Bumps [boto3](https://github.com/boto/boto3) from 1.12.39 to 1.13.6.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.12.39...1.13.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 22:57:55 +00:00
98e05cf8ea Merge pull request #2961 from Netflix/dependabot/pip/sqlalchemy-utils-0.36.5 2020-05-08 22:55:18 +00:00
5ac65062c9 Bump sqlalchemy-utils from 0.36.3 to 0.36.5
Bumps [sqlalchemy-utils](https://github.com/kvesteri/sqlalchemy-utils) from 0.36.3 to 0.36.5.
- [Release notes](https://github.com/kvesteri/sqlalchemy-utils/releases)
- [Changelog](https://github.com/kvesteri/sqlalchemy-utils/blob/master/CHANGES.rst)
- [Commits](https://github.com/kvesteri/sqlalchemy-utils/compare/0.36.3...0.36.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 22:49:15 +00:00
4534e26f93 Merge pull request #2964 from Netflix/dependabot/pip/acme-1.4.0 2020-05-08 22:47:12 +00:00
6d05af1790 Bump pyjks from 19.0.0 to 20.0.0
Bumps [pyjks](https://github.com/kurtbrose/pyjks) from 19.0.0 to 20.0.0.
- [Release notes](https://github.com/kurtbrose/pyjks/releases)
- [Changelog](https://github.com/kurtbrose/pyjks/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kurtbrose/pyjks/compare/v19.0.0...v20.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 15:42:20 -07:00
6bef8fb9d7 Bump acme from 1.3.0 to 1.4.0
Bumps [acme](https://github.com/letsencrypt/letsencrypt) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/letsencrypt/letsencrypt/releases)
- [Commits](https://github.com/letsencrypt/letsencrypt/compare/v1.3.0...v1.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 22:36:54 +00:00
56308cbd90 Merge pull request #2960 from Netflix/dependabot/pip/pyjks-20.0.0 2020-05-08 22:34:46 +00:00
cdd9137f4e Merge branch 'master' into cert-rotation-region-by-region 2020-05-08 15:32:49 -07:00
dac95313b8 Bump pyjks from 19.0.0 to 20.0.0
Bumps [pyjks](https://github.com/kurtbrose/pyjks) from 19.0.0 to 20.0.0.
- [Release notes](https://github.com/kurtbrose/pyjks/releases)
- [Changelog](https://github.com/kurtbrose/pyjks/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kurtbrose/pyjks/compare/v19.0.0...v20.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 22:29:51 +00:00
f8c6e2338a Merge pull request #2957 from Netflix/dependabot/pip/arrow-0.15.6 2020-05-08 22:27:55 +00:00
5ad9c11716 Bump arrow from 0.15.5 to 0.15.6
Bumps [arrow](https://github.com/crsmithdev/arrow) from 0.15.5 to 0.15.6.
- [Release notes](https://github.com/crsmithdev/arrow/releases)
- [Changelog](https://github.com/crsmithdev/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/crsmithdev/arrow/compare/0.15.5...0.15.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 18:42:41 +00:00
34f6c3a230 Merge pull request #2962 from Netflix/dependabot/pip/pre-commit-2.3.0 2020-05-08 18:40:37 +00:00
8e2226180a Bump pre-commit from 2.2.0 to 2.3.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.2.0...v2.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 18:35:37 +00:00
b993e664a3 Merge pull request #2963 from Netflix/dependabot/pip/sphinx-3.0.3 2020-05-08 18:34:10 +00:00
fa13bda99e Bump sphinx from 3.0.1 to 3.0.3
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 3.0.1 to 3.0.3.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/3.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v3.0.1...v3.0.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 16:45:06 +00:00
53c314cb41 Merge pull request #2959 from hosseinsh/improve-enable-autorotate
improving logging and the possibility of …
2020-05-08 09:43:11 -07:00
0f374c8fd0 Merge branch 'master' into cert-rotation-region-by-region 2020-05-08 09:21:13 -07:00
529ee04ae7 removing duplicate line 2020-05-08 09:16:46 -07:00
65b193a891 Merge branch 'master' into improve-enable-autorotate 2020-05-08 08:50:50 -07:00
daafb5ddd4 Merge pull request #2956 from Netflix/dependabot/pip/cryptography-2.9.2
Bump cryptography from 2.9 to 2.9.2
2020-05-08 08:48:10 -07:00
bffaeb1067 Merge branch 'master' into dependabot/pip/cryptography-2.9.2 2020-05-08 08:39:59 -07:00
746e314a5b Merge pull request #2954 from Netflix/dependabot/pip/fakeredis-1.4.1
Bump fakeredis from 1.4.0 to 1.4.1
2020-05-08 08:38:17 -07:00
f68900d2b3 improving logging and the possibility of defining which Authorities qualify for auto-rotation 2020-05-07 18:28:01 -07:00
7f6eae7213 Bump cryptography from 2.9 to 2.9.2
Bumps [cryptography](https://github.com/pyca/cryptography) from 2.9 to 2.9.2.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/2.9...2.9.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 00:25:47 +00:00
d469700cbf Bump fakeredis from 1.4.0 to 1.4.1
Bumps [fakeredis](https://github.com/jamesls/fakeredis) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/jamesls/fakeredis/releases)
- [Commits](https://github.com/jamesls/fakeredis/compare/1.4.0...1.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 00:23:52 +00:00
843ffad60e removing testing comments 2020-05-07 17:10:50 -07:00
1b6907a404 Certificate rotation region by region
example scheudule:
CELERYBEAT_SCHEDULE = {
    'certificate_rotate': {
        'task': 'lemur.common.celery.certificate_rotate',
        'options': {
            'expires': 180
        },
        'schedule': crontab(minute="*"),
        'kwargs': {'region': 'us-east-1'}
    }
}
2020-05-07 16:28:01 -07:00
54035e8db8 Merge pull request #2952 from Netflix/fix_for_latest_pip
Fix parse_requirements logic for Pip 20.1
2020-04-29 09:42:07 -07:00
ba8184c874 Fix requirement parsing for pip 20.1 2020-04-29 08:51:09 -07:00
4c40e806bc Requirements fix for Pip 20.1 2020-04-29 08:41:42 -07:00
eaaacfad3e Merge pull request #2951 from Netflix/enable_autorotate_attached_certs
Autorotate certs attached to endpoints
2020-04-28 16:33:12 -04:00
7e97d885df Address comments 2020-04-28 13:16:27 -07:00
863af7a3e5 Making CLI command ; Running black 2020-04-28 12:16:46 -07:00
273c3e2793 Celery task to enable autorotate for all certificates attached to endpoints without it enabled 2020-04-28 11:52:43 -07:00
f71a9e0ad2 Merge pull request #2950 from hosseinsh/route53-private-zone-fix
fixing the private DNS zone issue.
2020-04-24 16:08:38 -07:00
8d0007b9c0 fixing the private DNS zone issue.
Private hosted zones will never be visible to third-parties like LetsEncrypt, and Lemur should not consider them as authoritative zones.
This fix, make sure  they are not added to the  dns_provider table.
2020-04-24 15:48:06 -07:00
7b305514a1 Merge pull request #2948 from Netflix/mcandre-fix-cves
Fix Gulp CVEs & Fix broken bootswatch/bootstrap
2020-04-16 09:17:33 -07:00
4fcb050fa8 fixing bootstrap, bootswatch, and updating bower 2020-04-16 01:08:39 -07:00
3eea32a227 Merge branch 'master' into fix-cves 2020-04-15 16:53:02 -07:00
5122e0e375 Merge pull request #2947 from Netflix/qcc_q2_2020
Updated Python requirements and non-breaking node packages.
2020-04-15 16:11:59 -07:00
e33b767d12 updating package.jso based on npm update 2020-04-15 14:31:41 -07:00
f8657998e6 updated python requirements and still works 2020-04-15 14:17:34 -07:00
cee81bd693 updated requirements, fixed unittests, pytest, and distinguidedName ordering 2020-04-09 18:17:05 -07:00
67cb703c83 Merge pull request #2946 from Netflix/enhanced_error_loggin
Improve error logging for a couple of use cases
2020-04-08 15:03:41 -04:00
213b13d3c9 Merge branch 'master' into enhanced_error_loggin 2020-04-08 14:56:51 -04:00
0537d494e7 Merge pull request #2944 from dstipp/oauth2
OAUTH2 fixes
2020-04-08 11:56:42 -07:00
2c8dc24fda Merge branch 'master' into enhanced_error_loggin 2020-04-08 14:51:06 -04:00
1360d846fd Improve error logging for a couple of use cases 2020-04-08 11:50:42 -07:00
3b3cec6f8b Merge branch 'master' into oauth2 2020-04-08 10:12:04 -07:00
6102ec9267 Merge pull request #2943 from hosseinsh/imporved-metrics-sources
emitting the count of certificates on the source
2020-04-08 09:36:59 -07:00
eaeec5d757 Merge branch 'master' into imporved-metrics-sources 2020-04-08 09:23:27 -07:00
7b2d5c9103 Merge pull request #2945 from Netflix/celery_metrics
Add default celery metrics and logging using celery signals
2020-04-08 12:23:04 -04:00
11b15e7e23 Clean up docstrings 2020-04-08 08:41:48 -07:00
eb138fc960 Add default celery metrics and logging using celery signals 2020-04-08 08:38:40 -07:00
45c98a21b3 Merge branch 'master' into imporved-metrics-sources 2020-04-06 16:02:25 -07:00
37f4b4c2a6 Merge pull request #2937 from Netflix/powerdnsplugin_02
Update PowerDNS Plugin to support multiple tokens per domain
2020-04-06 09:32:44 -07:00
bb21f891cb Merge branch 'powerdnsplugin_02' of github.com:Netflix/lemur into powerdnsplugin_02 2020-04-05 21:48:31 -07:00
46e0d1953b Merge branch 'master' of github.com:Netflix/lemur into powerdnsplugin_02 2020-04-05 21:47:24 -07:00
f82ec24dfa updating _get_txt_records return values and docstrings 2020-04-05 21:46:33 -07:00
5c2a2f8ff2 OAUTH2 fixes
* Use OAUTH2 variable instead of PING while using OAUTH
* Some IDPs require a POST instead of a GET to user data
2020-04-04 11:32:23 -04:00
5add647148 # emitting the count of certificates on the source 2020-04-03 16:51:24 -07:00
274e61cffb Merge branch 'master' into powerdnsplugin_02 2020-04-03 12:59:36 -07:00
9ddfd9f3b1 Merge pull request #2942 from Netflix/dependabot/pip/bleach-3.1.4
Bump bleach from 3.1.2 to 3.1.4
2020-04-03 11:34:09 -07:00
2e1b58c70a Bump bleach from 3.1.2 to 3.1.4
Bumps [bleach](https://github.com/mozilla/bleach) from 3.1.2 to 3.1.4.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v3.1.2...v3.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-04-03 18:27:38 +00:00
1dd5d64461 Merge pull request #2935 from Netflix/dependabot/pip/bleach-3.1.2
Bump bleach from 3.1.1 to 3.1.2
2020-04-03 11:24:28 -07:00
c4b1277d3e Merge branch 'master' into dependabot/pip/bleach-3.1.2 2020-04-03 11:09:38 -07:00
e4bba5c6e9 Merge branch 'master' into powerdnsplugin_02 2020-04-01 12:03:18 -07:00
37bdbc0f91 Merge pull request #2940 from Netflix/castrapel-patch-3
No need to retry 25 times on DeleteConflict errors
2020-04-01 14:12:47 -04:00
efb7a33d3e Merge branch 'master' into castrapel-patch-3 2020-04-01 14:03:17 -04:00
577a29db0b Merge pull request #2939 from Netflix/castrapel-patch-2
Bump time limit for clean_source Celery job
2020-04-01 14:03:02 -04:00
77cfa1e55c Merge branch 'master' into dependabot/pip/bleach-3.1.2 2020-04-01 10:55:15 -07:00
b4025e6820 Merge branch 'master' into castrapel-patch-3 2020-04-01 13:55:14 -04:00
9a939e8281 Merge branch 'master' into castrapel-patch-2 2020-04-01 13:54:39 -04:00
2500228d7a Merge pull request #2938 from Netflix/castrapel-patch-1
Remove equivalent destinations when cleaning certificates
2020-04-01 13:54:01 -04:00
d825616ea6 No need to retry 25 times on DeleteConflict errors 2020-04-01 10:53:17 -07:00
e25f97fce7 Bump time limit for clean_source Celery job
For larger accounts, I've hit SoftTimeLimit exceptions before completion of this celery job. Bumping up the time limit on this job.
2020-04-01 10:50:24 -07:00
67d24caef5 Remove equivalent destinations when cleaning certificates
Remove equivalent destinations when cleaning certificates. This will prevent Lemur from attempting to re-upload a certificate after it has been cleaned.
2020-04-01 10:31:12 -07:00
6f3ba23fa0 updating sinlge line of comments 2020-03-30 13:34:24 -07:00
9d9bf9d7ba Merge branch 'powerdnsplugin_02' of github.com:Netflix/lemur into powerdnsplugin_02 2020-03-30 09:02:56 -07:00
d6cc8a8a9a fixing whitespace 2020-03-30 09:01:28 -07:00
66183e6bdd Merge branch 'master' into powerdnsplugin_02 2020-03-27 10:45:15 -07:00
88b14d6ad6 Merge branch 'master' into dependabot/pip/bleach-3.1.2 2020-03-27 10:44:54 -07:00
0c02ddec3c Merge pull request #2936 from hosseinsh/expired-cert
Bugfix:  compare offset-naive and offset-aware datetimes
2020-03-27 10:42:12 -07:00
2b7e60399c Merge branch 'master' into powerdnsplugin_02 2020-03-27 10:27:33 -07:00
0e314d0028 adding documentation and final cleanup 2020-03-27 10:18:38 -07:00
0149f8b0d3 add support for wildcard and naked domains to PowerDNS module 2020-03-26 22:15:10 -07:00
2a2499a929 simplifying code 2020-03-26 20:45:00 -07:00
e4e5bc056b Merge branch 'master' into expired-cert 2020-03-26 19:05:47 -07:00
5206997468 expired is now called for new certs, where the not_after field might be in datetime format, and not comparable to utc 2020-03-26 19:01:07 -07:00
8ba9ae1148 Bump bleach from 3.1.1 to 3.1.2
Bumps [bleach](https://github.com/mozilla/bleach) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v3.1.1...v3.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-24 16:18:43 +00:00
88c40aa93c Merge branch 'master' into master 2020-03-23 20:31:16 -07:00
001ad3b8d9 Merge pull request #2934 from hosseinsh/expired-cert
better handling of destination plugin errors, and also checking cert …
2020-03-23 12:43:49 -07:00
697215f8bc better handling of destination plugin errors, and also checking cert expiration before upload 2020-03-21 20:05:35 -07:00
edf1f9c6ad Merge branch 'master' of github.com:e11it/lemur 2020-03-20 20:53:40 +03:00
7bd5173da4 Merge with Netflix/lemur master 2020-03-20 20:52:33 +03:00
6049e8f117 Merge branch 'master' into master 2020-03-20 20:44:46 +03:00
eb48e2fe75 Merge pull request #2931 from hosseinsh/new_clean_cert_cli
New clean cert cli
2020-03-18 09:02:52 -07:00
1d4da0e3d8 another polish 2020-03-17 16:59:09 -07:00
ecca003ab4 improving the documentation and method naming 2020-03-17 16:55:36 -07:00
9de89ec96a Merge branch 'master' into new_clean_cert_cli 2020-03-17 13:38:32 -07:00
db3920ed25 Merge pull request #2930 from Netflix/powerdnsplugin_01
Fixing authorization lookup code to fix duplicate domain authorizations bug
2020-03-16 11:49:09 -07:00
07dc31bed7 cleaning up whitespace changes 2020-03-16 11:41:05 -07:00
febc1c270b Merge branch 'powerdnsplugin_01' of github.com:Netflix/lemur into powerdnsplugin_01 2020-03-16 11:25:01 -07:00
1a19e250bb updating and cleaning up tests 2020-03-16 11:24:17 -07:00
34d23503de fixing the data bug 2020-03-14 20:41:03 -07:00
b28b4f9a28 adding to new cli commands for cleaning certificates from source:
a) either about to expire in X days and not attached to an endpoint
a) or issued since X days but still not attached to an endpoint
2020-03-14 20:19:26 -07:00
c96695c966 refactor 2020-03-14 20:18:07 -07:00
593c35776c adding new methods for getting pending clean 2020-03-14 20:17:05 -07:00
cf3298d1ad Merge branch 'master' into powerdnsplugin_01 2020-03-13 00:26:41 -07:00
09ce55efcd Merge branch 'powerdnsplugin_01' of github.com:Netflix/lemur into powerdnsplugin_01 2020-03-13 00:23:10 -07:00
921d52b360 fixing get_dns_challenge() logic so duplicate domains (such as wildcard and not wildcard) do not match the wrong authorziations 2020-03-13 00:03:31 -07:00
be722fb1b3 Fix lint 2020-03-11 20:51:10 +03:00
92a8942727 Fix lint 2020-03-11 15:37:11 +03:00
a6c3b85fe1 Fix lint 2020-03-11 15:15:56 +03:00
ba8e315eed Fix typo 2020-03-11 14:22:04 +03:00
729ed3843d Fix bug wth get_options and slash in name 2020-03-11 14:16:29 +03:00
d3cb0b517a Add format support 2020-03-11 02:27:31 +03:00
ad86cf1fd9 Merge remote-tracking branch 'upstream/master' 2020-03-11 00:29:07 +03:00
790367ea5a Update lemur.conf.py
ALLOW_CERT_DELETION from env
2020-03-10 13:46:59 +03:00
cd7adacca3 Merge pull request #2929 from Netflix/powerdnsplugin_01
fixing formatting of POWER_DNS_VERIFY documentation
2020-03-05 17:01:52 -08:00
df596be5cf Merge branch 'master' into powerdnsplugin_01 2020-03-05 16:54:57 -08:00
027580cade Merge branch 'powerdnsplugin_01' of github.com:Netflix/lemur into powerdnsplugin_01 2020-03-05 16:52:38 -08:00
6227e4aa89 fixing formatting of ACME_POWERDNS_VERIFY options 2020-03-05 16:51:21 -08:00
5fc62aff7e Merge pull request #2928 from Netflix/powerdnsplugin_01
Add Support for ACME_POWERDNS_VERIFY Option
2020-03-05 15:46:44 -08:00
e1e7efc96e Merge branch 'master' into powerdnsplugin_01 2020-03-05 15:25:40 -08:00
771e72187a updates based on feedback 2020-03-05 15:24:56 -08:00
b85fe2f2b5 updated documentation language 2020-03-05 15:03:43 -08:00
5dfb6acb17 adding support for ACME_POWERDNS_VERIFY option to support CA Bundles and disabling Server validation 2020-03-05 14:59:21 -08:00
3c24c22890 Merge pull request #2927 from Netflix/validity_gui_update_01
removing 2 year option from Lemur certificate request form
2020-03-04 15:21:47 -08:00
c0004e506e removing 2 year option from Lemur certificate request form 2020-03-04 14:50:44 -08:00
509523b9ee Merge pull request #2926 from Netflix/powerdns_doc_update_01
adding documentation for DIGICERT_MAX_VALIDITY
2020-03-04 11:53:48 -08:00
9ef538305d updating default language for digicert max validity option 2020-03-04 11:46:23 -08:00
72a0552073 Merge branch 'master' into powerdns_doc_update_01 2020-03-04 11:11:32 -08:00
4a4b3b932e Merge branch 'master' into master 2020-03-04 10:32:10 -08:00
a873d69859 adding documentation for DIGICERT_MAX_VALIDITY 2020-03-03 18:24:48 -08:00
38a7f3d43e Merge pull request #2925 from Netflix/renewal_validity_01
adding default/max DigiCert validity dates
2020-03-03 17:36:46 -08:00
1e81d47793 Merge branch 'renewal_validity_01' of github.com:Netflix/lemur into renewal_validity_01 2020-03-03 17:28:58 -08:00
fdc1e20c23 updating config_mock defaults 2020-03-03 17:27:15 -08:00
38b7d6e5e3 Merge branch 'master' into renewal_validity_01 2020-03-03 14:44:33 -08:00
6c46481ffd simplifying return statement for validity years 2020-03-03 14:40:50 -08:00
318292704d fixing default/max DigiCert validity values 2020-03-03 14:29:17 -08:00
6ed70eb9eb Merge pull request #2 from e11it/fix_san_duplicate
Fix: San values #2921
2020-03-03 21:45:55 +03:00
27a86f5c18 Fix: San values #2921
Not sure is it correct solution
2020-03-03 21:45:33 +03:00
462099d906 Merge pull request #1 from e11it/e11it-patch-1
Update plugin.py
2020-03-02 09:18:26 +03:00
fe67ff2146 Update plugin.py
Fix lint
2020-03-02 09:18:02 +03:00
a8c0adaa4d Merge remote-tracking branch 'upstream/master' 2020-02-27 17:08:35 +03:00
5fb3da8bec Add certificate reissue to cron 2020-02-27 11:24:35 +03:00
a584aeb7eb User lemur instead manage.py 2020-02-26 20:12:53 +03:00
ce69d47b8b Fix 2020-02-26 19:43:16 +03:00
4df64a87e5 Merge pull request #2920 from Netflix/dependabot/pip/bleach-3.1.1
Bump bleach from 3.1.0 to 3.1.1
2020-02-24 13:15:11 -08:00
64a7faffd9 Bump bleach from 3.1.0 to 3.1.1
Bumps [bleach](https://github.com/mozilla/bleach) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v3.1.0...v3.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-24 18:30:10 +00:00
2bcb842d93 Merge branch 'master' into fix-cves 2020-02-20 13:42:00 -08:00
1430ac5395 fix 2020-02-18 19:54:41 +03:00
ccb811516c Add dockerfile to build from repo 2020-02-18 19:43:48 +03:00
9612d291ed Add path suffix options 2020-02-18 19:16:27 +03:00
9c56d603ce Merge pull request #2916 from Netflix/le_Log_orderurl
Log certificate issuance success and metrics.
2020-02-17 12:57:00 -08:00
2ee60bcdb6 Merge branch 'master' into le_Log_orderurl 2020-02-17 10:30:58 -08:00
0c95ec3bdf Merge pull request #2918 from sirferl/master
Added VERISIGN_INTERMEDIATE_<authority> parameter
2020-02-17 10:30:43 -08:00
e75df1ddc9 Update plugin.py 2020-02-17 19:04:20 +01:00
d29edabefe Merge branch 'master' into le_Log_orderurl 2020-02-17 09:24:51 -08:00
ed3472d029 Update plugin.py 2020-02-17 15:21:29 +01:00
aa5200b85f Merge branch 'master' into master 2020-02-17 15:04:04 +01:00
3fd0d3e141 Added VERISIGN_INTERMEDIATE_<authority> parameter
When using the VERISIGN_PRODUCT_<authority> Parameter one also has to add this parameter:
VERISIGN_INTERMEDIATE_<authority> = """ <PEM-String of Issuing CA for this certificate Type>""" 
While doing this, I also added code, so the external_id field is filled with data from CA-Answer
2020-02-17 12:40:36 +01:00
0d76690091 Merge pull request #2917 from sirferl/master
New variable VERISIGN_PRODUCT_(authority.name)
2020-02-16 15:38:42 -08:00
1815c89970 Made the change more elegant
As suggested by @hosseinsh. This is of course more elegant.
2020-02-16 09:28:52 +01:00
a70a49e4e9 Update plugin.py 2020-02-15 16:11:58 +01:00
3693bc2d8b removed whitespaces inserted by online editor 2020-02-15 16:09:25 +01:00
bfa953270d Fixed whitespace error 2020-02-15 16:04:44 +01:00
ac95f1cc33 Merge branch 'master' into master 2020-02-15 15:55:28 +01:00
fabcad1e46 New variable VERISIGN_PRODUCT_(authority.name)
If there is a config variable with VERISIGN_PRODUCT_<upper(authority.name)> take the value as Cert product-type
else default to "Server", to be compatoible with former versions.
This enables the use of different Verisign authorities for differnt cert-products eg. EV or Standard Certs
2020-02-15 15:52:24 +01:00
a8e8924e2a Merge branch 'master' into le_Log_orderurl 2020-02-14 17:10:38 -08:00
bde3d1ac66 Merge pull request #2914 from sirferl/master
Fixing kubernetes and verisign plugin
2020-02-14 09:34:34 -08:00
8e3cc93d6a Whitespaces in empty line 113 removed 2020-02-14 07:50:18 +01:00
b521aaf579 Merge branch 'master' into le_Log_orderurl 2020-02-13 16:41:14 -08:00
af21225918 adding logging on sucess and metric submission of URL for certificate issuance 2020-02-13 16:38:33 -08:00
a449cc2b15 Merge branch 'master' into ilabun/optimize-certificates-sql 2020-02-13 16:05:46 -08:00
2b849a6520 Update plugin.py
making lint happy
2020-02-13 15:58:07 -08:00
9db1ea3307 Merge branch 'master' into master 2020-02-13 12:47:06 -08:00
5c4b36fd5f Merge pull request #2915 from Netflix/powerdnsplugin_01
fixed get_domains() to remove duplicate entries, updated usage and tests
2020-02-13 12:45:53 -08:00
571c8bf42d Error when validity_end date is empty #2905
this lines of code (114ff) in threw an error, when the validity_end date was empty:

if options.get("validity_end") > arrow.utcnow().shift(years=2):
raise Exception(
"Verisign issued certificates cannot exceed two years in validity"
)

Actually, they are not needed, because immidiately following is a check for an empty validity_end and for the length of the entered period.
When I commented it out for testing, the error was gone and everything worked as expected.
2020-02-13 07:38:04 +01:00
6c7bb5f9b7 Fixed TLS secret format ( #2913 )
The Plugin handled the TLS secret format wrong: it sent chain certificate instead of requested public certificate #2913
2020-02-13 07:35:35 +01:00
14edd1bd4f Merge pull request #3 from Netflix/master
Fixing kubernetes and verisign plugin
2020-02-13 07:31:08 +01:00
ca8e73286f fixed get_domains() to remove duplicate entries, updated usage and tests 2020-02-12 15:10:24 -08:00
d69c83e5c8 Merge pull request #2912 from Netflix/powerdns_doc_update_01
Add Documentation for PowerDNS Plugin
2020-02-10 11:31:18 -08:00
1e4d4b9413 Merge branch 'master' into fix-cves 2020-02-10 11:23:58 -08:00
2d7284f677 Merge branch 'master' into ilabun/optimize-certificates-sql 2020-02-10 11:23:21 -08:00
c575ed5c79 Merge branch 'master' into powerdns_doc_update_01 2020-02-10 11:22:55 -08:00
7dd2a3c269 Merge pull request #2883 from rohanmehto2/vault-k8s-auth
implemented #2882 k8s auth for vault
2020-02-10 11:22:36 -08:00
98cffb6c79 Merge branch 'master' into fix-cves 2020-02-10 11:15:26 -08:00
c0cf1c02c1 Merge branch 'master' into ilabun/optimize-certificates-sql 2020-02-10 11:14:26 -08:00
b23ae60847 Merge branch 'master' into vault-k8s-auth 2020-02-10 11:12:52 -08:00
77cec4fde9 Fix ssl env. Add issuer and authority 2020-02-05 19:11:36 +03:00
e379e34212 Merge branch 'master' into powerdns_doc_update_01 2020-02-04 18:36:37 -08:00
b9736dfd3f Merge pull request #2911 from Netflix/powerdnsplugin_01
Add PowerDNS ACME Plugin
2020-02-04 18:36:11 -08:00
bcdb3173bd ensuring that "3" is set as an integer instead of a string 2020-02-04 18:23:17 -08:00
5324290234 updating documentation based on feedback 2020-02-04 16:23:53 -08:00
8ea54d7db2 removing exception if domain zone not found. Logging the issue instead 2020-02-04 14:50:56 -08:00
48bccd6f68 moving _check_config() lower in file, near other private methods 2020-02-03 19:08:28 -08:00
c38e651eb0 Merge branch 'powerdnsplugin_01' of github.com:Netflix/lemur into powerdnsplugin_01 2020-02-03 19:04:05 -08:00
53f81fb09f updating based on suggestions in 2911 2020-02-03 18:58:31 -08:00
5e8599540e Merge branch 'master' into ilabun/optimize-certificates-sql 2020-02-03 20:32:41 +01:00
ac0282529e adding basic logging on success 2020-02-03 11:05:20 -08:00
7dac0e1dd8 Update administration.rst 2020-01-31 16:54:25 -08:00
fecb5b6252 Merge branch 'master' into powerdnsplugin_01 2020-01-31 16:37:57 -08:00
fb6d369130 removed unnecessary imports in test_dns_providers.py 2020-01-31 16:18:22 -08:00
fe2b13becb Merge branch 'master' into fix-cves 2020-01-31 16:15:44 -08:00
a4475ad2a3 Merge pull request #2910 from e11it/Fix-Docker
Fixes and enhancements of docker image
2020-01-31 15:30:46 -08:00
e9404aa06b Merge branch 'master' into Fix-Docker 2020-01-31 13:18:43 -08:00
02757359f0 Merge pull request #2907 from Netflix/fix_write_domains_on_failure_01
fixing issue where set_domains() is still called when get_all_zones()…
2020-01-31 13:18:00 -08:00
be7736d350 adding dns tests and assorted exception handling 2020-01-31 13:16:37 -08:00
75d4699c7a Fix nginx ssl. Add env vars. Opt docker 2020-01-31 22:52:59 +03:00
139769edea Merge branch 'master' into fix_write_domains_on_failure_01 2020-01-29 16:03:30 -08:00
30ab0bcdbe Merge pull request #2908 from Netflix/pip_import_fix_01
adding support for pip version >=19.3 by supporting change to PipSess…
2020-01-29 15:56:13 -08:00
48d8e1d235 adding support for pip version >=19.3 by supporting change to PipSession() location in setup.py 2020-01-29 15:30:08 -08:00
969a7107fe fixed PowerDNS Tests 2020-01-29 13:12:09 -08:00
b885244aa7 fixing issue where set_domains() is still called when get_all_zones() throws an exception 2020-01-29 11:26:53 -08:00
ef115ef2b1 moving PowerDNS number_of_attempts to global config variable ACME_POWERDNS_RETRIES 2020-01-29 11:20:39 -08:00
f3039a1210 removed accidently added __init__py file 2020-01-29 11:05:46 -08:00
b91899fe99 created CLI options for testin ACME over dns. Examle: acme dnstest -d _acme-chall.foo.com -t token1 2020-01-28 19:13:28 -08:00
9576fa4b0f Merge pull request #2906 from hosseinsh/logging-get-dns
DNS provider: adding more logging
2020-01-28 16:34:48 -08:00
192ecb3ce0 DNS provider: adding more logging 2020-01-28 16:24:50 -08:00
980360b94a patch gulp-less 2020-01-27 14:09:57 -06:00
7a2ca8969e patch gulp-imagemin 2020-01-27 14:08:22 -06:00
a0a8c155f6 patch gulp-protractor 2020-01-27 13:59:41 -06:00
0129e97dfe address karma CVEs 2020-01-27 13:56:24 -06:00
127f7c1582 Merge pull request #2893 from sirferl/master
Fxing Issue #2831
2020-01-27 09:11:38 -08:00
620f972635 Fixed an error
Found out that I introduced an error when I changed code up for publishig. The certserv.py I use does not return the ID of the certificate created. For now I just leave the field empty. I will create another issue , so that the ID is filled up.
2020-01-27 11:04:49 +01:00
6248cbb902 Merge pull request #2 from Netflix/master
Fxing Issue #2831
2020-01-27 10:59:03 +01:00
5d8eb51ef4 Merge branch 'master' into ilabun/optimize-certificates-sql 2020-01-24 11:28:55 +01:00
c465062673 integrated PowerDNS plugin into dns_providers 2020-01-23 23:53:38 -08:00
21c651a566 Merge pull request #2891 from rajatsharma94/bugfix
fix fatal error in schema validator
2020-01-23 08:49:46 -08:00
9984470b58 fix fatal error in schema validator 2020-01-23 15:27:02 +01:00
bddae6e428 adding PowerDNS delete_txt_record with associated tests 2020-01-22 16:18:52 -08:00
52c7686d58 adding wait_for_dns_change() and tests for PowerDNS ACME plugin 2020-01-21 18:47:21 -08:00
915ec0ba63 added PowerDNS support for create_txt_record and associated tests 2020-01-21 17:08:59 -08:00
99f46c1add Merge pull request #2890 from Gutttlt/patch-1
Fixing "'Role' object has no attribute 'set_third_party'" error on LDAP authentication.
2020-01-21 08:17:30 -08:00
71f43dfcc1 Fixing "'Role' object has no attribute 'set_third_party'" error. 2020-01-21 08:40:54 +01:00
e159e2f3d8 Merge branch 'master' into ilabun/optimize-certificates-sql 2020-01-20 15:41:46 -08:00
3ef2f97967 Merge branch 'master' into vault-k8s-auth 2020-01-20 15:41:38 -08:00
d9990bc075 Merge pull request #2885 from ilyalabun/ilabun/fix-docker-compose-tests
Fix Dockerfile for tests
2020-01-20 15:41:14 -08:00
b10e68df35 Merge branch 'master' into vault-k8s-auth 2020-01-21 01:32:37 +02:00
5293fac125 Merge branch 'master' into ilabun/fix-docker-compose-tests 2020-01-20 15:22:31 -08:00
ca61857dcf Merge pull request #2886 from pmelse/pmelse-workaround-#2857
update for #2857 workaround
2020-01-20 15:19:27 -08:00
acf531ece3 Merge branch 'master' into vault-k8s-auth 2020-01-20 15:18:29 -08:00
6ee856e26d Merge branch 'master' into ilabun/optimize-certificates-sql 2020-01-20 15:15:25 -08:00
2cfe5437d9 Merge branch 'master' into ilabun/fix-docker-compose-tests 2020-01-20 15:15:03 -08:00
6d06f3ca6f Merge branch 'master' into pmelse-workaround-#2857 2020-01-20 15:14:03 -08:00
9b1317f4a4 Merge pull request #2889 from hosseinsh/better-string-formatting
improved string formatting to  avoid dangling white spaces and new lines
2020-01-20 14:53:46 -08:00
3080a9527c adding PowerDNS get_zones functionality and unit tests 2020-01-17 18:29:37 -08:00
7f119b8914 Merge branch 'master' into ilabun/optimize-certificates-sql 2020-01-17 17:18:06 -08:00
cb7507156c Merge branch 'master' into vault-k8s-auth 2020-01-17 17:17:53 -08:00
3b393dec6d Merge branch 'master' into ilabun/fix-docker-compose-tests 2020-01-17 17:17:06 -08:00
d6f41b6a99 improving string formatting to avoid dangling white spaces and new lines 2020-01-16 13:45:13 -08:00
7f73417bda Merge pull request #2887 from hosseinsh/issuer-default-hash-algo
possibility to default to a SIGNING_ALGORITHM for a given profile
2020-01-15 16:37:20 -08:00
1ed6ae539d # possibility to default to a SIGNING_ALGORITHM for a given profile 2020-01-15 16:19:48 -08:00
58d8a145c3 update for #2857 workaround
update for #2857 workaround
2020-01-13 22:13:30 -05:00
cd7d9aee55 fixed lint error 2020-01-13 23:09:58 +02:00
8d957f22af changed file handling 2020-01-13 22:46:34 +02:00
78f9c490dd Fix Dockercompose for tests 2020-01-13 15:26:35 +01:00
bc1a2cf69c Optimize certificates SQL query
Co-authored-by: Javier Ramos <javier.ramos@booking.com>
2020-01-13 14:43:41 +01:00
cc0b2d5439 Added new lowercase indexes for certificates cn, name and domains name
Co-authored-by: Javier Ramos <javier.ramos@booking.com>
2020-01-13 14:40:22 +01:00
cad56c813e fixed lint error 2020-01-12 01:51:48 +02:00
409b499217 added kubernetes auth for vault 2020-01-12 01:25:22 +02:00
a65c4c94a0 Merge pull request #2881 from rohanmehto2/cfssl-key-fix
fix for #2879 cfssl-key TypeError
2020-01-09 10:50:01 -08:00
348682d5ea Merge branch 'master' into cfssl-key-fix 2020-01-09 10:44:02 -08:00
9f35d740d2 Merge pull request #2880 from hosseinsh/improved-expiry-email-notification
Improved messaging to point out to the Auto Rotate option for certifi…
2020-01-09 10:43:33 -08:00
8be8c95b17 handled cfssl-key type error 2020-01-09 15:16:19 +02:00
1537d591a8 Improved messaging to point out to the Auto Rotate option for certificate issuance and renewal. 2020-01-08 14:42:16 -08:00
8e04aa1bd3 Merge pull request #2875 from ilyalabun/master
Eliminate SQL subqueries when showing certificates list
2020-01-06 11:15:31 -08:00
f446fcc1c2 Merge branch 'master' into master 2020-01-03 14:20:02 -08:00
deaac42dba Merge pull request #2878 from hosseinsh/update-reqs-03Jan2019
updating requirements
2020-01-03 14:19:49 -08:00
1ccc15859f updating requirements.
removing this pin, since the issue is now resolved.
kombu<4.6.0 # Bug with inspecting active tasks: https://github.com/celery/kombu/issues/1051
2020-01-03 14:14:01 -08:00
9b9662d470 Merge branch 'master' into master 2020-01-03 13:15:58 -08:00
ebc48ae304 Merge pull request #2876 from pmelse/master
updated permission fix for sftp plugin if certificate is set read-only
2020-01-03 13:14:38 -08:00
45c1207d07 Merge branch 'master' into master 2019-12-27 13:30:56 -05:00
9fb4be1273 remove trailing whitespace 2019-12-27 13:25:03 -05:00
189e8b2725 Eliminate subqueries when showing certificates list 2019-12-20 10:37:47 +01:00
f10121c343 Merge pull request #2874 from nbischof/patch-1
type on quickstart/index.rst
2019-12-02 08:17:56 -08:00
f188aea2c2 type on quickstart/index.rst 2019-12-02 06:22:09 -05:00
805d6942ae Merge pull request #2873 from zarfide/permalinkOptimization_jzarfoss
used fixedName variable to transport db lookup optimization
2019-11-20 10:40:34 -08:00
00a0a27826 used fixedName variable to transport db lookup optimization 2019-11-20 09:44:31 -08:00
93aca891b7 Merge pull request #2872 from zarfide/PS-2522-atlas-redis
Ps 2522 atlas redis
2019-11-06 12:39:19 -08:00
113c9dd657 atlas redis plugin typo cleanup and better exception handling 2019-11-06 10:42:59 -08:00
f803fab413 add plugin to send atlas metric via redis 2019-11-06 10:14:49 -08:00
77fdc8795b Merge pull request #2870 from hosseinsh/cert-sync-endpoint-find-by-hash
missed edge case
2019-10-18 15:53:11 -07:00
0d983bd2b5 missed edge case 2019-10-18 15:39:36 -07:00
f077b19126 Merge branch 'master' into master 2019-10-18 11:32:21 -07:00
da1080e041 Merge pull request #2869 from hosseinsh/cert-sync-endpoint-find-by-hash
Cert sync endpoint find by hash
2019-10-18 11:29:57 -07:00
06f4aed693 keeping track of certs found by hash 2019-10-18 11:21:29 -07:00
11f9920ff9 Merge branch 'master' into cert-sync-endpoint-find-by-hash 2019-10-18 11:08:51 -07:00
2418c3eb0a Merge pull request #2868 from hosseinsh/acme-client-setup-retrial
ACME-clientsetup-retrial
2019-10-18 11:08:31 -07:00
14e13b512e providing a count for conflicts 2019-10-18 11:03:28 -07:00
9037f88430 just in case the path varies 2019-10-18 11:02:41 -07:00
1768aad9e2 capturing no such entity exception. 2019-10-18 10:17:58 -07:00
8aea257e6a optimizing the call to describe cert to only the few certs with the naming issue 2019-10-18 09:24:49 -07:00
f075c5af3d in case no cert match via name-search, search via the cert itself (serial number, hash comparison) 2019-10-18 08:48:11 -07:00
d43e859c34 describing the cert for each endpoint, for better cert search 2019-10-18 08:46:01 -07:00
10b600424e refactoring searching for cert 2019-10-18 08:45:32 -07:00
b5ab87877b adding retry to acme setup client, since it can experience timeouts or other types of Connection Errors 2019-10-17 10:16:33 -07:00
356d3ee9f3 Merge pull request #2866 from Netflix/dependabot/pip/ecdsa-0.13.3
Bump ecdsa from 0.13.2 to 0.13.3
2019-10-15 16:26:39 -07:00
fea37ba101 Merge branch 'master' into dependabot/pip/ecdsa-0.13.3 2019-10-15 15:56:59 -07:00
9d7ab27977 Merge pull request #2865 from hosseinsh/up-dependencies-20191015
updating requirements
2019-10-15 15:56:48 -07:00
a076497cf0 Bump ecdsa from 0.13.2 to 0.13.3
Bumps [ecdsa](https://github.com/warner/python-ecdsa) from 0.13.2 to 0.13.3.
- [Release notes](https://github.com/warner/python-ecdsa/releases)
- [Changelog](https://github.com/warner/python-ecdsa/blob/master/NEWS)
- [Commits](https://github.com/warner/python-ecdsa/compare/python-ecdsa-0.13.2...python-ecdsa-0.13.3)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-15 22:49:30 +00:00
6f96a8f5b0 updating requirements 2019-10-15 15:47:21 -07:00
f0652ca6a9 bug fix for overwriting certificates 2019-10-10 15:49:31 -04:00
2239b68e0e Merge pull request #2854 from hosseinsh/nlb-naming-bug
With NLBs the DNS formatting has changed, which resulted in Lemur not…
2019-09-23 13:23:43 -07:00
477db836f4 lint 2019-09-23 12:52:17 -07:00
86f661a8af With NLBs the DNS formatting has changed, which resulted in Lemur not getting the region correctly parsed 2019-09-23 12:36:08 -07:00
4e7e81aee8 Merge pull request #2853 from hosseinsh/up-dependencies-20Sep2019
updating dependencies, and fixing the deprecated arrow.replaces to shift
2019-09-20 15:34:55 -07:00
96b2149433 removing unintended commit 2019-09-20 15:22:45 -07:00
8c9a1df2cf Merge branch 'master' into up-dependencies-20Sep2019 2019-09-20 15:19:25 -07:00
a13c45e9cc updating dependencies, and fixing the deprecated arrow.replaces to shift 2019-09-20 13:49:38 -07:00
0c70e5c3fd Merge pull request #2852 from hosseinsh/cert-year-validity
removing 3 and 4 years from validity range options
2019-09-20 10:23:47 -07:00
c669cd23f0 Merge branch 'master' into check-revoke-revised 2019-09-20 10:22:04 -07:00
972051a61e removing 3 and 4 years from validity range options 2019-09-20 10:16:23 -07:00
9b2a1bebd1 Merge pull request #2849 from hosseinsh/better-metrics-endpoints
adding account number for better logging, since the endpoint is not a…
2019-08-21 10:13:02 -07:00
d0e8666267 Merge branch 'master' into better-metrics-endpoints 2019-08-21 10:01:00 -07:00
cb966a0204 Merge pull request #2848 from jramosf/patch-1
Show number of found items in pager
2019-08-21 10:00:46 -07:00
db91e48395 adding account number for better logging, since the endpoint is not available in Lemur DB 2019-08-21 09:54:18 -07:00
e5e395f0d9 Show number of found items in pager
This commit does not involve any additional query as the data is already in API calls' responses
2019-08-20 09:29:58 +02:00
18f42d2349 Merge pull request #2847 from hosseinsh/better-metrics-endpoints
metric for missing certificate from an endpoint
2019-08-15 19:20:33 -07:00
9b04d901c4 metric for missing certificate from an endpoint 2019-08-15 19:14:08 -07:00
f09643f350 Merge branch 'master' into check-revoke-revised 2019-08-15 11:15:24 -07:00
24698516fc Merge pull request #2846 from castrapel/better_dns_autodetection
Allow better DNS autodetection for domains that directly match a DNS zone
2019-08-15 11:15:15 -07:00
1c6fee7292 Allow better DNS autodetection for domains that directly match a DNS hosted zone 2019-08-15 10:52:26 -07:00
68abf11be8 Merge branch 'master' into check-revoke-revised 2019-08-13 20:09:27 -07:00
0e31e708e3 Merge pull request #2843 from hosseinsh/soft_time_outs
adding soft time outs for celery
2019-08-13 20:06:12 -07:00
296a315a3e Merge branch 'master' into soft_time_outs 2019-08-13 19:42:22 -07:00
b6486d85ba Merge pull request #2845 from hosseinsh/prevent-duplicate-celery-tasks
preventing celery duplicate tasks
2019-08-13 19:42:11 -07:00
ceb2d3d796 Merge branch 'master' into check-revoke-revised 2019-08-13 14:07:57 -07:00
2de3f287ab standardizing the timeouts to easier monitor any timeouts 2019-08-13 12:21:27 -07:00
6e17d36d76 typos 2019-08-13 12:16:23 -07:00
22c60fedad cosmetics 2019-08-13 12:11:04 -07:00
a3dfc3ef0a consistency 2019-08-13 11:58:58 -07:00
c29f282560 improved the flow for checking if the task is active 2019-08-13 11:52:56 -07:00
4d728738ee handling celery tasks without any arguments 2019-08-13 11:42:43 -07:00
07a9c56fb8 making lint happy 2019-08-13 09:35:57 -07:00
bf47f87c21 preventing celery duplicate tasks 2019-08-12 13:52:01 -07:00
95086e08dc Merge pull request #2839 from kush-bavishi/ultradnsPlugin
Ultradns plugin
2019-08-09 08:59:11 -07:00
5d4413e45c Merge branch 'master' into ultradnsPlugin 2019-08-09 08:48:24 -07:00
0711ba9c04 Merge pull request #2841 from hosseinsh/multi-profile-digicert-plugin
Multi profile digiCert plugin
2019-08-09 07:42:17 -07:00
83159c2417 Merge branch 'master' into multi-profile-digicert-plugin 2019-08-09 07:32:33 -07:00
d2c7330fb7 Merge pull request #2844 from hosseinsh/fixing-metric-bug
fixing metric bug
2019-08-09 00:20:40 -07:00
da9c91afb4 fixing metric bug 2019-08-08 17:56:22 -07:00
3b9b94623f cleaning up 2019-08-07 18:06:59 -07:00
8340e0653b making lint happy 2019-08-07 18:04:28 -07:00
d1519343d1 improving check revoked by only considering authorities which do support revocation and also only including not expired certs 2019-08-07 17:54:10 -07:00
9a02230d63 adding soft time outs for celery 2019-08-07 17:48:06 -07:00
d9aef2da3e Changed dummy nameserver value 2019-08-07 14:38:18 -07:00
a97283f0a4 Fixed indentation 2019-08-07 14:23:09 -07:00
a6bf081bec Remove unused import 2019-08-07 14:08:27 -07:00
43f5c8b34e Fixed indentation 2019-08-07 14:08:06 -07:00
cadf372f7b Removed hardcoded value from function call 2019-08-07 14:02:10 -07:00
b4f4e4dc24 Added extra check for return value to test_create_txt_record 2019-08-07 13:55:02 -07:00
fa7f71d859 Modified paginate response to dummy values 2019-08-07 13:53:10 -07:00
3ff56fc595 Blank line removed 2019-08-07 13:42:11 -07:00
894502644c test_wait_for_dns_change fixed! 2019-08-07 13:39:20 -07:00
37a1b55b08 test_delete_txt_record changed to mock get_zone_name and return the value directly instead of executing the function. 2019-08-07 13:27:21 -07:00
31c2d207a2 test_delete_txt_record fixed. Function call was missing earlier 2019-08-07 13:23:05 -07:00
785c1ca73e test_create_txt_record modified - get_zone_name mocked to return the zone name directly, instead of actually running the function. 2019-08-07 13:20:24 -07:00
f2cbddf9e2 Unit tests for get_zone_name, get_zones 2019-08-07 13:17:16 -07:00
6e84e1fd59 Unit Tests for create_txt_record, delete_txt_record, wait_for_dns_change 2019-08-07 13:04:38 -07:00
ff1f73f985 fixing the plugin test to include authority 2019-08-07 12:05:36 -07:00
bbda9b1d6f making sure to handle when no config file provided, though we do a check for that 2019-08-07 12:05:13 -07:00
e2ea2ca4d1 providing sample config 2019-08-07 11:05:07 -07:00
b885cdf9d0 adding multi profile name support with DigiCert plug.
This requires that the configs are a dict, with multiple entries, where the key is the name of the Authority used to issue certs with.

DIGICERT_CIS_PROFILE_NAMES = {"sha2-rsa-ecc-root": "ssl_plus"}
DIGICERT_CIS_ROOTS = {"root": "ROOT"}
DIGICERT_CIS_INTERMEDIATES = {"inter": "INTERMEDIATE_CA_CERT"}

Hence, in DB one need to add
1) the corresponding authority table, with digicert-cis-issuer. Note the names here are used to mapping in the above config
2) the corresponding intermediary in the certificate table , with root_aurhority_id set to the id of the new authority_id
2019-08-07 10:24:38 -07:00
a7c2b970b0 Unit testing Part 1 2019-08-05 14:00:22 -07:00
ad6c38960a Merge branch 'master' into ultradnsPlugin 2019-07-31 16:05:36 -07:00
2903799b85 Changed string formatting from "{}".format() to f"{}" for consistency 2019-07-31 14:19:49 -07:00
bbc3bf513d Merge pull request #2837 from hosseinsh/moving-cronjobs-to-celery-v2
moving all cron jobs to become celery jobs
2019-07-31 14:11:25 -07:00
e8e4f826ea updating logging format 2019-07-31 13:09:31 -07:00
5a401b2d87 Added the Zone class and Record class to ultradns.py and removed the respective files 2019-07-31 12:04:42 -07:00
fe075dc9f5 Changed function comments to doc strings. 2019-07-31 12:00:31 -07:00
503df999fa Updated metrics.send to send function named, followed by status, separated by a period 2019-07-31 11:32:04 -07:00
11cd095131 Reduced the number of calls to get_public_authoritative_nameserver by using a variable 2019-07-31 11:12:28 -07:00
3ba7fdbd49 Updated logger to log a dictionary instead of a string 2019-07-31 11:11:39 -07:00
0f591e9a3d Merge branch 'master' into moving-cronjobs-to-celery-v2 2019-07-30 14:13:59 -07:00
6bf920e66c Merge branch 'master' into ultradnsPlugin 2019-07-30 14:13:45 -07:00
0be80e156a Merge pull request #2834 from hosseinsh/better-error-handling-dyn
metric tags, to be able to track which domains where failing during t…
2019-07-30 13:38:12 -07:00
7810095796 Merge branch 'master' into better-error-handling-dyn 2019-07-30 13:27:43 -07:00
ff933ba1aa Merge pull request #2833 from hosseinsh/redis-better-error-handling_
better error handling for redis
2019-07-30 13:27:33 -07:00
44bc562e8b Update ultradns.py
Minor logging changes in wait_for_dns_change
2019-07-30 13:08:16 -07:00
30e4f5032b Merge branch 'master' into moving-cronjobs-to-celery-v2 2019-07-30 11:44:24 -07:00
ffdd06ec07 Merge branch 'master' into better-error-handling-dyn 2019-07-30 11:43:56 -07:00
bec4416185 Merge branch 'master' into redis-better-error-handling_ 2019-07-30 11:43:49 -07:00
b17da52ead Merge branch 'master' of https://github.com/kush-bavishi/lemur into ultradnsPlugin 2019-07-30 11:40:20 -07:00
3d48b422b5 Removed TODO 2019-07-30 11:39:35 -07:00
0ec8fe05ea Merge pull request #2838 from hosseinsh/fixing-makeFile
lemur is one level deep than makefile
2019-07-30 10:41:29 -07:00
244aa069f0 lemur is one level deep than makefile 2019-07-30 10:32:09 -07:00
a89cbe9332 moving all cron jobs to become celery jobs 2019-07-30 09:57:15 -07:00
3ad791e1ec Dynamically obtain the authoritative nameserver for the domain 2019-07-29 18:01:28 -07:00
e993194b4f Check ultraDNS authoritative server first. Upon success, check Googles DNS server. 2019-07-29 14:59:28 -07:00
adabe18c90 metric tags, to be able to track which domains where failing during the LetsEncrypt domain validation 2019-07-25 18:56:28 -07:00
429e6a967c better error handling for redis 2019-07-25 18:49:19 -07:00
252410c6e9 Updated TTL from 300 to 5 2019-07-22 16:00:20 -07:00
51f3b7dde0 Added the Record class for UltraDNS 2019-07-22 14:23:40 -07:00
0b52aa8c59 Added Zone class to handle ultradns zones 2019-07-22 11:47:48 -07:00
5c1bc72af8 Merge pull request #2825 from arnydo/docs_adcs
Add ADCS Plugin to Docs
2019-07-18 17:16:53 -07:00
41cf1335d2 Merge branch 'master' into docs_adcs 2019-07-18 17:04:09 -07:00
3beaedc2fa Merge pull request #2832 from hosseinsh/metrics-tags
fixing metrics: source is not dict
2019-07-18 17:03:25 -07:00
004df84c6d Merge branch 'master' into docs_adcs 2019-07-18 16:54:03 -07:00
36ebba6491 source is not dict 2019-07-18 15:16:01 -07:00
e37a7c775e Initial commit for the UltraDNS plugin to support Lets Encrypt 2019-07-18 14:29:54 -07:00
ee151bdc43 Merge pull request #2828 from hosseinsh/improved-logging-alerting-v2
Improved logging alerting v2
2019-07-17 11:33:45 -07:00
a3706594fc Merge branch 'master' into improved-logging-alerting-v2 2019-07-17 11:20:55 -07:00
d199819d66 Merge pull request #2829 from hosseinsh/update-reqs-20190712
updating requirements
2019-07-17 09:54:41 -07:00
09c0fa0f94 updating the function declaration 2019-07-16 17:21:01 -07:00
54ecda4e1a updating fakeredis 2019-07-16 09:09:12 -07:00
0ed00c5011 updating test requirement 2019-07-16 09:01:04 -07:00
cd1aeb15f1 adding testing for redis 2019-07-12 11:50:12 -07:00
ae1633b0f2 updating requirements
has been a while since last update, more testing in deployment needed
2019-07-12 10:38:47 -07:00
1b1bdbb261 spacing 2019-07-12 10:25:37 -07:00
97d74bfa1d fixing the app context issue. we will create an app if no current_app available 2019-07-12 08:47:39 -07:00
2628ed1a82 better alerting 2019-07-11 23:00:35 -07:00
66bff57c04 Add ADCS Plugin Configuration to Docs
Add configuration options based on https://github.com/Netflix/lemur/pull/2255#issue-240136873
2019-07-10 12:10:47 -04:00
41c781318c Add ADCS in Docs
Add info regarding the ADCS plugin created by "https://github.com/sirferl/lemur".
"lemur_adcs" plugin is part of Lemur by default so I added to main plugins section within Docs.
2019-07-10 10:08:14 -04:00
ea8524f035 Merge pull request #2824 from castrapel/doc_image_fix
Fix doc images
2019-07-10 06:54:03 -07:00
424b517914 Fix doc images 2019-07-10 06:53:19 -07:00
7419fd951e Merge pull request #2822 from castrapel/master
Initial LetsEncrypt / Celery docs
2019-07-09 11:29:15 -07:00
f2a2683dcb Merge branch 'master' into master 2019-07-09 11:28:42 -07:00
8eb639e366 Initial LetsEncrypt / Celery docs 2019-07-09 11:13:11 -07:00
2510f25cc0 Merge pull request #2821 from hosseinsh/updating-docs
updating the python version
2019-07-09 10:55:10 -07:00
4f5f55a383 Merge branch 'master' into updating-docs 2019-07-09 10:41:25 -07:00
0b2a5e8646 updating the python version 2019-07-09 09:51:51 -07:00
c7398d9e2f Merge pull request #2820 from castrapel/celery_time_limit_sync
Relax celery time limit for source syncing; Ensure metric tags are string
2019-07-01 11:14:05 -07:00
0c5a8f2039 Relax celery time limit for source syncing; Ensure metric tags are string 2019-07-01 08:35:04 -07:00
ff3016e65e Merge pull request #2818 from Netflix/revert-2807-FasterPermalink
Revert "Faster permalink"
2019-06-26 10:44:44 -07:00
0e037973b2 Revert "Faster permalink" 2019-06-26 10:31:58 -07:00
14d32368c0 Merge pull request #2808 from intgr/restore-manage-shebang
manage.py: Restore shebang line
2019-06-25 09:41:22 -07:00
850620c2a2 Merge branch 'master' into restore-manage-shebang 2019-06-25 09:41:08 -07:00
5df06501f6 Merge pull request #2814 from intgr/expose-cert-hasprivaatekey
Expose new certificate field hasPrivateKey
2019-06-25 09:40:27 -07:00
8fbff00850 Merge branch 'master' into restore-manage-shebang 2019-06-25 09:29:06 -07:00
3434f78e50 Merge pull request #2815 from alwaysjolley/cleanup_chain
Cleanup no chain in Vault destination plugin
2019-06-25 09:28:52 -07:00
404b7a25bc Merge branch 'master' into restore-manage-shebang 2019-06-25 09:27:08 -07:00
86a1fb41ac lint fix 2019-06-25 06:56:37 -04:00
55a96ba790 type none 2019-06-24 15:10:10 -04:00
6699833297 fixing empty chain 2019-06-24 13:10:08 -04:00
2319858586 Expose new certificate field hasPrivateKey
We can also now disable the 'private key' tab when cert doesn't have a
private key.
2019-06-22 15:38:28 +03:00
d96155b6df Merge pull request #2813 from DanielThomas/master
Update SAN text
2019-06-21 13:44:54 -07:00
4565bd7dc6 Update SAN text 2019-06-21 13:33:55 -07:00
e1d8bb83ec Merge pull request #2812 from kush-bavishi/temp-ExpiredToggle-3
Color change for Show Expired button
2019-06-21 11:43:17 -07:00
960064d5c6 Color change for Show Expired button 2019-06-21 11:32:16 -07:00
b564f97a47 Merge pull request #2810 from kush-bavishi/temp-ExpiredToggle-3
Expired Toggle
2019-06-21 09:12:31 -07:00
23caac5576 Merge branch 'master' into temp-ExpiredToggle-3 2019-06-21 08:59:53 -07:00
7bff3a7947 Merge pull request #2811 from hosseinsh/generalizing-api
removing the rotation enabled requirement, to keep the endpoint generic
2019-06-20 16:38:28 -07:00
39d65db7fd Merge branch 'master' into generalizing-api 2019-06-20 16:13:04 -07:00
162a300e53 Merge branch 'master' into temp-ExpiredToggle-3 2019-06-20 16:12:55 -07:00
5b8c600261 Merge pull request #2809 from alwaysjolley/vault_regex
fixing regex to be more flexable
2019-06-20 16:12:03 -07:00
34cdd29a50 removing the rotation enabled requirement, to keep the endpoint generic 2019-06-20 16:06:26 -07:00
de0462e54f Added missing semi-colon and changed double quotes to single quotes 2019-06-20 15:41:32 -07:00
68815b8f44 UI changes - Button to show / hide expired certs. 2019-06-20 15:05:26 -07:00
bbf50cf0b0 updated dest as well as src 2019-06-20 08:26:32 -04:00
02719a1de7 Merge branch 'master' into vault_regex
fixed conflicts:
	lemur/plugins/lemur_vault_dest/plugin.py
2019-06-19 09:53:08 -04:00
56917614a2 fixing regex to be more flexable 2019-06-19 09:46:44 -04:00
8a08edb0f3 manage.py: Restore shebang line
This is an executable file but cannot be executed without the interpreter.

The shebang line was lost in commit 8cbc6b8325
2019-06-18 10:51:11 +03:00
f836c6fff6 API additions for viewing expired certs as well. Default behavior modified to show only valid certs and those which have expired less than 1 month ago. 2019-06-17 14:29:48 -07:00
cdb83c48c5 API additions for viewing expired certs as well. Default behavior modified to show only valid certs and those which have expired less than 1 month ago. 2019-06-17 13:44:50 -07:00
198abcfe65 Merge pull request #2807 from kush-bavishi/FasterPermalink
Faster permalink
2019-06-11 16:16:55 -07:00
c0f8fbb24f Modified Permalink behavior to access a newer, faster API 2019-06-11 15:53:47 -07:00
57016f2f45 Merge branch 'master' of https://github.com/Netflix/lemur into FasterPermalink 2019-06-11 14:33:58 -07:00
491d048948 Modified the behavior of Permalink to access a newer, faster API 2019-06-10 09:47:29 -07:00
29213972ae Merge pull request #2806 from hosseinsh/duplicate-notifications-(alternative)
Duplicate notifications (alternative)
2019-06-06 16:29:55 -07:00
0446aea20e Update messaging.py 2019-06-06 13:35:45 -07:00
1ed41d03ea Merge branch 'master' into duplicate-notifications-(alternative) 2019-06-06 09:10:57 -07:00
28e26a1baf to prevent duplicate emails, we might better remove owner and security email address from the notification recipient 2019-06-05 17:57:11 -07:00
3a512d5bda Merge pull request #2804 from kush-bavishi/CommonNameAutoAdditionAsDNS
Common name auto addition as domain name
2019-06-05 15:29:35 -07:00
24f5ce7170 Merge branch 'master' into CommonNameAutoAdditionAsDNS 2019-06-04 13:59:57 -07:00
f991317301 Merge pull request #2805 from castrapel/docker_37
Upgrade docker to 3.7
2019-06-04 08:32:20 -07:00
aeb32f4853 Upgrade docker to 3.7 2019-06-04 08:21:52 -07:00
45231c2423 Added code to automatically add the common name as a DNS name while creating a certificate. 2019-05-31 14:08:28 -07:00
28b216273d Upgrading Gulp. If this is not necessary, we can remove it later. 2019-05-31 14:07:26 -07:00
ce9b06e4c6 Merge pull request #2802 from castrapel/downgrade_kombu_0530
Downgrade Kombu
2019-05-30 13:37:26 -07:00
e300cf6e1b Downgrade Kombu 2019-05-30 13:34:44 -07:00
7eb9c80fb2 Merge pull request #2798 from castrapel/domains_enhancements
Enhance domains query and sensitive domain checking code
2019-05-30 10:31:24 -07:00
7e31498a4b Merge pull request #2799 from hosseinsh/update-reqs-30052019
Update reqs 30052019
2019-05-30 10:29:56 -07:00
b89dd36771 updating requirements 2019-05-30 10:21:53 -07:00
8b821d0023 Enhance domains query and sensitive domain checking code; Allow creation of opt-out roles via config 2019-05-30 10:21:44 -07:00
071c083eae hiding expired certs after 6 months from the main page 2019-05-30 10:21:03 -07:00
b4d9ab9f0c Merge branch 'master' of github.com:Netflix/lemur into improving-cert-lookup-time 2019-05-30 08:55:49 -07:00
13d46ae42e indexing the not after field in the cert table 2019-05-30 08:55:30 -07:00
8bc23f6deb Merge pull request #2797 from castrapel/get_or_increase_name_simplify
Make get_or_increase_name queries less demanding
2019-05-29 12:50:06 -07:00
6e4306b3bb Merge pull request #2795 from ardichoke/fix_vault_api_v2_append
Fix Certificate Appending With v2 Vault API
2019-05-29 12:49:36 -07:00
5e389f3f48 Add certificate1 to test DB 2019-05-29 12:38:17 -07:00
f81adb1371 Make get_or_increase_name queries less demanding 2019-05-29 12:20:05 -07:00
9da428c7fd Merge pull request #2796 from castrapel/read_replica_support
Read replica support
2019-05-28 12:56:22 -07:00
fd35a26955 Support read replicas 2019-05-28 12:45:39 -07:00
5059cb731a Support read replicas in Lemur for improved performance 2019-05-28 12:38:33 -07:00
09c7076e79 Handle double data field in API v2 2019-05-22 17:12:10 -04:00
de65d363fc Merge pull request #2794 from castrapel/max_retry_iam
Set Max Retries + Add metrics to IAM handler
2019-05-21 13:08:47 -07:00
1423ac0d98 More metrics 2019-05-21 12:55:33 -07:00
34c7e5230b Set a limit on number of retries 2019-05-21 12:52:41 -07:00
3c6799d736 Merge pull request #2793 from castrapel/json_logging
Add support for JSON logging
2019-05-17 08:57:09 -07:00
4fac726cf4 Add support for JSON logging 2019-05-17 08:48:26 -07:00
37e5857406 Merge pull request #2792 from castrapel/black-052019
Black lint all the things
2019-05-16 08:25:12 -07:00
0320c04be2 nosec comment 2019-05-16 08:14:46 -07:00
c5ec5fa41f Add bandit to test and pre-commit 2019-05-16 08:13:42 -07:00
68fd1556b2 Black lint all the things 2019-05-16 07:57:02 -07:00
3680d523d4 Merge pull request #2791 from castrapel/digicert_expose_error
Expose exact response from digicert as error
2019-05-15 13:43:44 -07:00
e3c5490d25 Expose exact response from digicert as error 2019-05-15 13:36:40 -07:00
8c73851708 Merge pull request #2790 from castrapel/resolved_cert
Set resolved cert ID before resolving cert
2019-05-15 11:57:03 -07:00
26d10e8b98 change ordering in more places 2019-05-15 11:47:53 -07:00
7e92edc70a Set resolved cert ID before resolving cert; Ignore sentry exceptions when no records on deletion 2019-05-15 11:43:59 -07:00
ed39e30824 Merge pull request #2788 from hosseinsh/fast-valid-cert-lookup
adding a new API for faster certificate lookup
2019-05-15 10:25:50 -07:00
6eb3836abc Merge branch 'master' into fast-valid-cert-lookup 2019-05-15 10:20:17 -07:00
302219325b Merge pull request #2789 from castrapel/celery-timeouts-LE-validation
Add soft timeouts to celery jobs; Check for PEM in LE order
2019-05-14 14:09:02 -07:00
5d8f71c3e4 nt 2019-05-14 13:02:24 -07:00
565142f985 Add soft timeouts to celery jobs; Check for PEM in LE order 2019-05-14 12:52:30 -07:00
f452a7ce68 adding a new API for faster certificate lookup.
The new API api/1/certificates/valid returns only non-expired (not_after >= today) certs which have auto-rotate enabled:

cn is a required parameter:

http://localhost:8000/api/1/certificates/valid?filter=cn;example.com
cn can also be a database string wildcard ('%'):

http://localhost:8000/api/1/certificates/valid?filter=cn;%
owner is the additional parameter, and must be the email address of the owner:

http://localhost:8000/api/1/certificates/valid?filter=cn;example.com&owner=hossein@example.com
given owner  and a database string wildcard ('%') one can retrieve all certs for that owner, which are still valid, and have auto-rotate enabled:

http://localhost:8000/api/1/certificates/valid?filter=cn;%&owner=hossein@example.com
2019-05-11 18:06:51 -07:00
0f2773c986 Merge pull request #2787 from castrapel/search_by_name
Allow searching for certificates by name via API
2019-05-09 15:08:34 -07:00
3f10b43254 Ignore bandit error 2019-05-09 15:00:09 -07:00
ed18df22db remove permalink change 2019-05-09 14:54:44 -07:00
e33a103ca1 Allow searching for certificates by name via API 2019-05-09 14:36:56 -07:00
02554b427a Merge pull request #2784 from castrapel/add_metrics_reissue_rotate
Gather more metrics on certificate reissue/rotate jobs
2019-05-08 07:57:51 -07:00
c9c782684d Merge branch 'master' into add_metrics_reissue_rotate 2019-05-08 07:48:44 -07:00
87470602fd Gather more metrics on certificate reissue/rotate jobs 2019-05-08 07:48:08 -07:00
9cfdf55d9e Merge pull request #2773 from jplana/jwks_validation_error_control
Better error management in jwks token validation
2019-05-08 07:42:35 -07:00
1de85dbcf4 Merge branch 'master' into jwks_validation_error_control 2019-05-08 07:29:37 -07:00
6a83da2292 Merge pull request #2775 from jplana/re-enable-gulp-backend-proxy
Enable gulp server to proxy backend
2019-05-08 07:22:58 -07:00
34640e009b Merge branch 'master' into re-enable-gulp-backend-proxy 2019-05-08 07:19:51 -07:00
317c84800c Merge branch 'master' into jwks_validation_error_control 2019-05-08 06:50:56 -07:00
3500be39aa Merge pull request #2783 from alwaysjolley/vault_docs
Adding documentation for Vault and CFSSL Plugin changes
2019-05-08 06:50:38 -07:00
029efeb03a fixing syntax 2019-05-08 09:45:13 -04:00
15eb7689ed Adding documentation for Vault and CFSSL Plugin changes 2019-05-08 09:32:46 -04:00
6c66370142 Merge pull request #2782 from castrapel/fix_bearer_token
Converting userinfo authorization to a config var
2019-05-07 15:38:05 -07:00
0eacbd42d7 Converting userinfo authorization to a config var 2019-05-07 15:31:42 -07:00
4e6e7edf27 Rename return variable for better readability 2019-05-07 22:53:01 +02:00
b7ce9ab901 Merge branch 'master' into jwks_validation_error_control 2019-05-07 13:09:02 -07:00
1492890ac7 Merge branch 'master' into re-enable-gulp-backend-proxy 2019-05-07 09:18:01 -07:00
65f594fc29 Merge pull request #2774 from jplana/aid_openid_roles_provider_integration
Don't fail if googleGroups is not found in user profile
2019-05-07 09:17:26 -07:00
0de42fdf0f Merge branch 'master' into re-enable-gulp-backend-proxy 2019-05-07 09:06:24 -07:00
ff583981b1 Merge branch 'master' into aid_openid_roles_provider_integration 2019-05-07 09:06:02 -07:00
e58ff476c9 Merge branch 'master' into jwks_validation_error_control 2019-05-07 09:05:41 -07:00
06ccafeb24 Merge pull request #2772 from jplana/fix_userinfo_authorization
Fixes userinfo using Bearer token
2019-05-07 08:46:18 -07:00
22caaa0c95 Merge branch 'master' into fix_userinfo_authorization 2019-05-07 07:48:47 -07:00
44d4c98a99 Merge pull request #2779 from dciancu/develop
Fix Cloudflare DNS
2019-05-07 07:45:48 -07:00
e65154b48e Merge branch 'master' into develop 2019-05-07 07:36:51 -07:00
adec451613 Merge pull request #2781 from alwaysjolley/lemur_vault_source
adding Vault Source plugin
2019-05-07 07:36:37 -07:00
ef7a8587fe Merge branch 'lemur_vault_source' of github.com:/alwaysjolley/lemur into lemur_vault_source 2019-05-07 10:06:09 -04:00
b0c8901b0a lint cleanup 2019-05-07 10:05:01 -04:00
36ce1cc7ef Merge branch 'master' into lemur_vault_source 2019-05-07 09:41:50 -04:00
fb3f0bd72a adding Vault Source plugin 2019-05-07 09:37:30 -04:00
da237c9c84 Merge branch 'master' into develop 2019-05-06 17:10:15 -07:00
a7af3cf8d2 Fix Cloudflare DNS 2019-05-07 03:05:24 +03:00
298d172536 Merge branch 'master' into jwks_validation_error_control 2019-05-06 16:57:03 -07:00
3be8117310 Merge branch 'master' into fix_userinfo_authorization 2019-05-06 16:56:52 -07:00
852a605de3 Merge branch 'master' into re-enable-gulp-backend-proxy 2019-05-06 16:56:33 -07:00
51bf256723 Merge pull request #2778 from hosseinsh/re-update-05062019
updating requirements
2019-05-06 16:46:41 -07:00
6d5552afd3 updating requirements 2019-05-06 16:31:50 -07:00
47595e2073 Enable gulp server to proxy backend 2019-05-06 12:33:53 +02:00
deed1b9685 Don't fail if googleGroups is not found in user profile 2019-05-06 12:30:25 +02:00
6c99e76c9a Better error management in jwks token validation 2019-05-06 12:27:43 +02:00
2063baefc9 Fixes userinfo using Bearer token 2019-05-06 12:23:24 +02:00
dfadcc52ef Merge pull request #2776 from GaryCarneiro/master
Ubuntu Package name changed
2019-05-03 08:48:49 -07:00
b642c7279d Merge pull request #1 from GaryCarneiro/GaryCarneiro-pypkgnamechange
Package name has changed
2019-05-03 13:49:30 +05:30
8ed6187697 Package name has changed
python-software-properties was renamed to software-properties-common
https://askubuntu.com/questions/422975/e-package-python-software-properties-has-no-installation-candidate
2019-05-03 13:49:02 +05:30
2497f3fd4e Merge pull request #2771 from castrapel/acme_domain_suffix
Updated requirements ; Require internal DNS validation
2019-04-29 14:13:42 -07:00
14cd593a59 Merge branch 'master' into acme_domain_suffix 2019-04-29 13:58:15 -07:00
3a1da72419 nt 2019-04-29 13:57:04 -07:00
6e3f394cff Updated requirements ; Revert change and require DNS validation by provider 2019-04-29 13:55:26 -07:00
cf56300b55 Merge pull request #2770 from castrapel/acme_domain_suffix
Move ACME host validation logic prior to R53 host modification
2019-04-26 17:28:35 -07:00
2e3cd98a3c Merge branch 'master' into acme_domain_suffix 2019-04-26 17:28:29 -07:00
1a90e71884 Move ACME host validation logic prior to R53 host modification 2019-04-26 17:27:44 -07:00
056dfc34e4 Merge pull request #2769 from castrapel/lowercase-acme
Ensure hostname is lowercase when comparing DNS challenges. ACME will…
2019-04-26 15:45:52 -07:00
333ba8030a Ensure hostname is lowercase when comparing DNS challenges. ACME will automatically lowercase the hostname 2019-04-26 15:45:04 -07:00
517a96c6eb Merge pull request #2768 from castrapel/strip_out_self_polling_logic
Strip out self-polling logic and rely on ACME; Enhance ELB logging and retries
2019-04-26 10:30:49 -07:00
1a3ba46873 More retry changes 2019-04-26 10:18:54 -07:00
1e64851d79 Strip out self-polling logic and rely on ACME; Enhance ELB logging and retries 2019-04-26 10:16:18 -07:00
b300a21948 Merge pull request #2767 from castrapel/expose_verisign_exception
Expose verisign exceptions
2019-04-25 19:28:44 -07:00
8eef95b58e Merge branch 'master' into expose_verisign_exception 2019-04-25 19:15:55 -07:00
dcdfb32883 Expose verisign exceptions 2019-04-25 19:14:15 -07:00
4cc2befbfb Merge pull request #2766 from castrapel/improve_dnschallenge_flow
Process DNS Challenges appropriately (1 challenge -> 1 domain)
2019-04-25 15:20:09 -07:00
cbfde3ff94 Merge branch 'master' into improve_dnschallenge_flow 2019-04-25 15:13:16 -07:00
39584f214b Process DNS Challenges appropriately (1 challenge -> 1 domain) 2019-04-25 15:12:52 -07:00
db4c4846b9 Merge pull request #2765 from castrapel/metrics_errors_acme
Metrics / Exception Reporting - LetsEncrypt / Dynect flow, ELB source sync
2019-04-25 14:12:30 -07:00
e2b5962281 Merge branch 'master' into metrics_errors_acme 2019-04-25 13:59:52 -07:00
2bc604e5a9 Better metrics and error reporting 2019-04-25 13:50:41 -07:00
88ed9581b6 Merge pull request #2764 from hosseinsh/update-travis-sudo
Update travis sudo
2019-04-24 18:10:39 -07:00
45da5847f7 Merge branch 'master' of github.com:Netflix/lemur into update-travis-sudo
# Conflicts:
#	.travis.yml
2019-04-24 17:51:07 -07:00
55f35b0f35 removing sudo, since deprecated in Travis
https://github.com/Netflix/lemur/issues/2758
2019-04-24 17:48:18 -07:00
e313b74813 Merge pull request #2763 from castrapel/metrics_errors_acme
Better exception handling, logging, and metrics for ACME flow
2019-04-24 15:33:11 -07:00
272285f64a Better exception handling, logging, and metrics for ACME flow 2019-04-24 15:26:23 -07:00
ccd0bde0bc Merge pull request #2757 from jplana/add-pending-certificate-upload
Allow uploading a signed cert for a pending certificate.
2019-04-24 09:53:50 -07:00
0f9b0f39f7 Merge branch 'master' into add-pending-certificate-upload 2019-04-24 09:34:35 -07:00
21d619e24a Merge pull request #2759 from alwaysjolley/lemur_vault_plugin
Adding SAN filter to Vault Destination Plugin
2019-04-24 09:30:35 -07:00
6804bfb233 Merge branch 'master' into lemur_vault_plugin 2019-04-24 08:53:10 -07:00
4338b4346e Merge branch 'master' into add-pending-certificate-upload 2019-04-24 08:52:58 -07:00
07e8e6a628 Merge pull request #2762 from castrapel/pyjks
Use official pyjks version
2019-04-23 08:06:33 -07:00
b6e09621f8 Use official pyjks version 2019-04-23 08:05:32 -07:00
a801112cf6 Merge branch 'master' into lemur_vault_plugin 2019-04-23 07:07:39 -04:00
85efb6a99e cleanup tmp files 2019-04-23 07:06:52 -04:00
ec7da60d04 Merge pull request #2761 from castrapel/pyjks_pycryptodomex
Pinning pyjks to specific commit with pycryptodomex
2019-04-22 22:00:22 -07:00
83784d7cb8 Pinning pyjks to specific commit with pycryptodomex instead of pycryptodome 2019-04-22 21:50:48 -07:00
9b38761153 Merge branch 'master' into add-pending-certificate-upload 2019-04-22 11:47:02 -07:00
f1a3fdf7b9 Merge pull request #2751 from intgr/rewrite-java-keystore-use-pyjks
Rewrite Java Keystore/Truststore support based on pyjks library
2019-04-22 11:46:26 -07:00
94f3265104 Merge branch 'master' into rewrite-java-keystore-use-pyjks 2019-04-22 09:55:58 -07:00
0bd2c76ed3 Merge pull request #2760 from hosseinsh/re-update-190422
updating requirements, addressing the urllib3 high severity warning
2019-04-22 09:55:47 -07:00
d1e5a40d20 updating requirements, addressing the urllib3 high severity warning 2019-04-22 09:49:03 -07:00
f9dadb2670 fixing validation 2019-04-22 09:38:44 -04:00
8dccaaf544 simpler validation 2019-04-22 07:58:01 -04:00
1667c05742 removed unused functions 2019-04-18 13:57:10 -04:00
b39e2e3f66 Merge branch 'master' into lemur_vault_plugin 2019-04-18 13:55:45 -04:00
fb3b0e8cd7 adding regex filtering 2019-04-18 13:52:40 -04:00
7dd9268ca7 Allow uploading a signed cert for a pending certificate. 2019-04-18 00:46:39 +02:00
8177e12f3f Merge branch 'master' into rewrite-java-keystore-use-pyjks 2019-04-17 10:43:44 -07:00
4c7bf917f2 Merge pull request #2747 from jramosf/master
Parse SubjectAlternativeNames from CSR into Lemur Certificate
2019-04-17 10:43:21 -07:00
52f939658f Merge branch 'master' into rewrite-java-keystore-use-pyjks 2019-04-17 10:31:58 -07:00
f6afcc6d21 Merge branch 'master' into master 2019-04-17 10:28:46 -07:00
2f7342951d Merge pull request #2534 from jplana/upload_certificates_with_csr
Allow uploading csr along with certificates
2019-04-17 10:28:31 -07:00
f76e913689 Merge branch 'master' into upload_certificates_with_csr 2019-04-17 10:20:05 -07:00
135d86c583 Merge branch 'master' into master 2019-04-17 10:19:56 -07:00
c45c2344bc Merge pull request #2756 from castrapel/makefile-checkout-pr
Make command to checkout PR from upstream
2019-04-17 10:19:24 -07:00
eb16afb6e6 Make command to checkout PR from upstream 2019-04-17 10:18:49 -07:00
58dd424de8 Prevent potential NoneType not subscriptable
Fix when data['extensions']['subAltNames']['names'] is none
2019-04-17 18:33:52 +02:00
900ae10764 Merge branch 'master' into master 2019-04-17 09:06:24 -07:00
57874971aa Merge branch 'master' into upload_certificates_with_csr 2019-04-16 15:05:11 -07:00
e45d005b74 Merge branch 'master' into rewrite-java-keystore-use-pyjks 2019-04-16 14:57:16 -07:00
0c078fea57 Merge pull request #2755 from hosseinsh/re-update-190416
updating requirements
2019-04-16 14:55:09 -07:00
8e1f00f17c Merge branch 'master' into master 2019-04-16 08:56:31 -07:00
e24026f3db Merge branch 'master' into rewrite-java-keystore-use-pyjks 2019-04-16 08:51:32 -07:00
5dd2e05972 updating requirements 2019-04-16 08:50:46 -07:00
771f2ebc47 Use SAN_CERT_CSR 2019-04-13 11:01:36 +02:00
770729a72e Allow csr to be empty during upload 2019-04-13 01:17:12 +02:00
2ff811ae71 updating cryptography API call, to create right signing algorithm object. 2019-04-13 00:57:48 +02:00
09796cf7c9 the check_cert_signature() method was attempting to compare RSA and ECC signatures.
If a ec public-key certificate is signed with an RSA key, then it can't be a self-signed certificate, in which case we just raise InvalidSignature.
2019-04-13 00:57:48 +02:00
406753fcde Fix PEP8 2019-04-13 00:49:35 +02:00
a5570d07bc Added some documentation for API users. 2019-04-13 00:48:19 +02:00
c1b02cc8a5 Allow uploading csr along with certificates 2019-04-13 00:48:19 +02:00
cf5b0145bd Merge pull request #2754 from hosseinsh/re-update-190412
Updating requirements
2019-04-12 15:09:44 -07:00
9bdf48c1b9 updating requirements 2019-04-12 14:29:08 -07:00
ad244fc83d Merge branch 'master' of github.com:Netflix/lemur into re-update-190412 2019-04-12 14:26:35 -07:00
da51e7f31d removing the announcement 2019-04-12 14:24:29 -07:00
f8a9ec6e3e simple hardcoded announcement 2019-04-12 14:24:29 -07:00
df8d4e0892 Merge branch 'master' into rewrite-java-keystore-use-pyjks 2019-04-12 09:38:50 -07:00
ceb335f3ab Merge branch 'master' into master 2019-04-12 09:38:41 -07:00
26e3982cb4 Merge pull request #2469 from hosseinsh/hshafagh-src-dst-register
Source and Destination registration
2019-04-12 09:25:25 -07:00
9ecc19c481 adding san filter 2019-04-12 09:53:06 -04:00
6d67ec7e34 removing unused import 2019-04-11 17:34:02 -07:00
512e1a0bdd fixing typos 2019-04-11 17:17:28 -07:00
6ec84a398c checking for None 2019-04-11 17:13:47 -07:00
69c00c4db5 upon creating a new destination, we also add it as source, if the plugin defines this as an option 2019-04-11 17:13:47 -07:00
d7abf2ec18 adding a new util method for setting options 2019-04-11 17:13:47 -07:00
557fac39b5 refactoring the sync job into a service method that we can also call when adding a new destination 2019-04-11 17:13:47 -07:00
d1ead4b79c removing the announcement 2019-04-11 17:13:47 -07:00
5900828051 simple hardcoded announcement 2019-04-11 17:13:47 -07:00
818da6653d removing the announcement 2019-04-11 17:13:47 -07:00
e1a67e9b4e simple hardcoded announcement 2019-04-11 17:13:47 -07:00
84dfdd0600 removing the announcement 2019-04-11 17:13:47 -07:00
ba691a26d4 simple hardcoded announcement 2019-04-11 17:13:47 -07:00
b66fac0494 removing the announcement 2019-04-11 17:13:47 -07:00
1bda246df2 simple hardcoded announcement 2019-04-11 17:13:47 -07:00
9a210c055a Merge branch 'master' into hshafagh-src-dst-register 2019-04-11 15:36:48 -07:00
81e2ff99ff Merge pull request #2468 from hosseinsh/hosseinsh-celeryjob-sync-src-dst
Celery job for synching destinations and sources
2019-04-11 15:17:16 -07:00
2459234147 removing lines 2019-04-11 14:34:26 -07:00
60edab9f6d cleaning up 2019-04-11 14:12:31 -07:00
ec3d2d7316 fixing typo 2019-04-11 13:51:43 -07:00
83d408b238 Merge branch 'master' into hosseinsh-celeryjob-sync-src-dst 2019-04-11 13:30:12 -07:00
266c83367d avoiding hard-coded plugin names 2019-04-11 13:29:37 -07:00
f185df4f1e bringing class AWSDestinationPlugin(DestinationPlugin) after AWSSourcePlugin.slug, such that we can do: sync_as_source_name = AWSSourcePlugin.slug 2019-04-11 13:28:58 -07:00
4dae8851d9 Merge pull request #2753 from castrapel/unpin_dependencies_py37
Update requirements - upgrade to py37
2019-04-10 16:28:19 -07:00
142aadffef Upgrade travis to xenial 2019-04-10 16:18:49 -07:00
d3fbf46f7a Upgrade travis deps 2019-04-10 16:09:55 -07:00
2ff57e932c Update requirements - upgrade to py37 2019-04-10 15:40:48 -07:00
d628e97035 Merge branch 'master' into hosseinsh-celeryjob-sync-src-dst 2019-04-10 09:47:06 -07:00
bc8c7e114a Merge branch 'master' into hshafagh-src-dst-register 2019-04-09 20:52:33 -07:00
f3d0536800 removing hardcoded rules, to give more flexibility into defining new source-destinations 2019-04-09 20:49:07 -07:00
1c09712df0 removing the announcement 2019-04-09 10:09:32 -07:00
0cc98c378f simple hardcoded announcement 2019-04-09 10:09:32 -07:00
6fd7752b29 removing the announcement 2019-04-09 10:08:25 -07:00
bfc4f940da Merge branch 'master' into master 2019-04-09 18:06:09 +02:00
64c6bb2475 Merge branch 'master' into rewrite-java-keystore-use-pyjks 2019-04-09 08:28:05 -07:00
70c8c2b5fc Merge pull request #2750 from ardichoke/vault_kv2
Add support for Vault KV API v2
2019-04-09 08:25:28 -07:00
dbf34a4d48 Rewrite Java Keystore/Truststore support based on pyjks library 2019-04-06 20:24:46 +03:00
2c82708391 simple hardcoded announcement 2019-04-04 12:12:29 -07:00
d80a6bb405 Added tests for CSR parsing into CertificateInputSchema 2019-04-01 08:44:40 +02:00
e10007ef7b Add support for Vault KV API v2
This adds the ability to target KV API v1 or v2.
2019-03-29 10:32:49 -04:00
b86e381e20 Parse SubjectAlternativeNames from CSR into Lemur Certificate 2019-03-27 13:46:33 +01:00
d2e969b836 better synching of source and destinations 2019-03-26 18:20:14 -07:00
a865675537 Merge pull request #2746 from castrapel/authority_validation_LE_errors
Add order URI during LE cert creation failure; Fail properly when inv…
2019-03-25 08:58:12 -07:00
4018c68d49 Merge branch 'master' into authority_validation_LE_errors 2019-03-25 08:34:10 -07:00
c2158ff8fb Add order URI during LE cert creation failure; Fail properly when invalid CA passed; Update reqs 2019-03-25 08:28:23 -07:00
57a680834e Merge pull request #2700 from jramosf/ghjaramos/master
Parse DNSNames from CSR into Lemur Certificate
2019-03-25 08:19:19 -07:00
8a42cfa345 Merge branch 'master' into ghjaramos/master 2019-03-21 08:07:44 -07:00
d920341dab Merge pull request #2617 from alwaysjolley/lemur_vault_plugin
Hashi Vault Destination Plugin
2019-03-21 08:05:54 -07:00
fa4a5122bc fixing file read to trim line endings and cleanup 2019-03-20 14:59:04 -04:00
3a96475a2e Merge branch 'lemur_vault_plugin' of github.com:/alwaysjolley/lemur into lemur_vault_plugin 2019-03-20 13:52:20 -04:00
f99b11d50e refactor url and token to support muiltiple instances of vault 2019-03-20 13:51:06 -04:00
a59eec553a Merge branch 'master' into lemur_vault_plugin 2019-03-18 15:21:18 -07:00
fed6f661c7 Merge branch 'master' into ghjaramos/master 2019-03-18 15:21:10 -07:00
5c11a2941c Merge pull request #2713 from hosseinsh/up-reqs-03182019
updating requirements
2019-03-18 15:20:48 -07:00
dbd948be6e updating requirements 2019-03-18 12:50:18 -07:00
9e5496b484 Update schemas.py 2019-03-15 10:19:25 +01:00
f7452e8379 Parse DNSNames from CSR into Lemur Certificate 2019-03-15 09:29:23 +01:00
157db684c3 Merge branch 'master' into lemur_vault_plugin 2019-03-14 11:09:01 -04:00
cfe0595918 Merge pull request #2689 from castrapel/skip_duplicate_tasks
Skip a task if similar task already active
2019-03-12 15:54:46 -07:00
c445297357 Update celery.py 2019-03-12 15:41:24 -07:00
f38e5b0879 Update celery.py 2019-03-12 15:29:04 -07:00
1a5a91ccc7 Update celery.py 2019-03-12 15:11:13 -07:00
3b3faa66f4 Merge branch 'master' into skip_duplicate_tasks 2019-03-12 14:53:42 -07:00
d220e9326c Skip a task if similar task already active 2019-03-12 14:45:43 -07:00
9ff789ab06 Merge pull request #2336 from intgr/verify-cert-chain
Check that stored certificate chain matches certificate
2019-03-11 18:06:06 -07:00
57d3f3d5a5 Merge branch 'master' into lemur_vault_plugin 2019-03-08 07:08:56 -05:00
f1c09a6f8f fixed comments 2019-03-07 15:58:34 -05:00
93ce259fb2 Merge branch 'master' into verify-cert-chain 2019-03-07 12:46:19 -08:00
bd27932783 Merge pull request #2529 from rmoesbergen/allow-cert-deletion
Implement ALLOW_CERT_DELETION setting
2019-03-07 12:45:53 -08:00
7b0a3cf781 Merge branch 'lemur_vault_plugin' of github.com:/alwaysjolley/lemur into lemur_vault_plugin 2019-03-07 15:42:40 -05:00
752c9a086b fixing error handling and better data formating 2019-03-07 15:41:29 -05:00
92b60b279a Merge branch 'master' into verify-cert-chain 2019-03-06 11:15:32 -08:00
b8d3a4f9aa Update requirements.in 2019-03-06 11:13:34 -08:00
43b1d6217a Merge branch 'master' into allow-cert-deletion 2019-03-06 10:59:33 -08:00
98ece58342 Merge branch 'master' into lemur_vault_plugin 2019-03-06 10:59:03 -08:00
b5d255d7c9 Merge pull request #2646 from Netflix/2636-config-ignore
Ensuring that configs passed via the command line are respected.
2019-03-06 10:58:42 -08:00
f974e29cc9 Merge branch 'master' into verify-cert-chain 2019-03-06 09:36:10 -08:00
45cb0f0513 Merge branch 'master' into allow-cert-deletion 2019-03-06 09:35:10 -08:00
4904e6e223 Merge branch 'master' into lemur_vault_plugin 2019-03-06 12:06:10 -05:00
cc6d53fdeb Ensuring that configs passed via the command line are respected. 2019-03-05 15:39:37 -08:00
721fb8ec70 Merge pull request #2639 from castrapel/really_downgrade_kombu
Downgrade Kombu for real this time
2019-03-05 09:46:36 -08:00
077ae1eedd Downgrade Kombu for real this time 2019-03-05 09:45:59 -08:00
b878c7e2db Merge pull request #2638 from castrapel/downgrade_kombu2
downgrade kombu
2019-03-05 07:50:23 -08:00
20ac4bd3dd downgrade kombu 2019-03-05 07:34:30 -08:00
a1cb8ee266 fixing lint 2019-03-05 07:37:04 -05:00
880eaad6cb Merge branch 'lemur_vault_plugin' of github.com:/alwaysjolley/lemur into lemur_vault_plugin 2019-03-05 07:22:18 -05:00
4a027797e0 fixing linting issues 2019-03-05 07:19:22 -05:00
54ad3ba777 Merge branch 'master' into verify-cert-chain 2019-03-04 17:55:36 -08:00
c9bcd29082 Merge branch 'master' into lemur_vault_plugin 2019-03-04 17:55:00 -08:00
130688252a Merge pull request #2630 from castrapel/relax_search
Relax search; update requirements
2019-03-04 11:14:16 -08:00
dd2900bdbc Relax search;update requirements 2019-03-04 10:04:06 -08:00
10cec063c2 Check that stored certificate chain matches certificate
Similar to how the private key is checked.
2019-03-04 17:10:59 +02:00
20518bc377 Merge branch 'master' into lemur_vault_plugin 2019-03-01 09:58:43 -05:00
5d2f603c84 renamed vault destination plugin to avoid conflict with vault pki plugin 2019-03-01 09:49:52 -05:00
63de8047ce Return 'already deleted' instead of 'not found' when cert has already been deleted 2019-02-27 09:38:25 +01:00
a9735e129c Merge branch 'master' into allow-cert-deletion 2019-02-27 09:28:48 +01:00
930af17802 Merge pull request #2588 from hosseinsh/fixing-signature-verify-ecc
Fixing signature verify ecc
2019-02-26 17:12:21 -08:00
658c58e4b6 clarifying comments 2019-02-26 17:04:43 -08:00
9dbae39604 updating cryptography API call, to create right signing algorithm object. 2019-02-26 16:42:26 -08:00
16a18cc4b7 adding more edge test cases for EC-certs 2019-02-26 16:42:26 -08:00
aec7c7b0bc Merge branch 'master' into fixing-signature-verify-ecc 2019-02-26 09:28:48 -08:00
38827cc66b Merge pull request #2563 from rmoesbergen/fix-blah-as-alias
Fix setting alias to "blah" when exporting without explicit alias
2019-02-26 09:21:59 -08:00
53301728fa Moved url to config file instead of plugin option. One one url can be supported
unless both the token and url are moved to the plugin options.
2019-02-26 09:15:12 -05:00
bc35ae90d0 Merge branch 'master' into allow-cert-deletion 2019-02-25 20:03:47 -08:00
d37943a17f Merge branch 'master' into fix-blah-as-alias 2019-02-25 20:03:27 -08:00
141500f4aa Merge branch 'master' into fixing-signature-verify-ecc 2019-02-25 20:03:18 -08:00
6e4f400d93 Merge pull request #2589 from hosseinsh/update-reqs-022519
updating requirements
2019-02-25 20:03:02 -08:00
65cbe7c77e Merge branch 'master' into allow-cert-deletion 2019-02-25 19:25:31 -08:00
0eefb9e355 Merge branch 'master' into fix-blah-as-alias 2019-02-25 19:25:09 -08:00
de129037e1 Merge branch 'master' into fixing-signature-verify-ecc 2019-02-25 19:24:54 -08:00
9159520716 Merge branch 'master' into update-reqs-022519 2019-02-25 19:24:38 -08:00
e64de7d312 updating requirements 2019-02-25 19:12:20 -08:00
60cd28f516 Merge pull request #2567 from hosseinsh/req-update-022119
updating requirements
2019-02-25 19:10:00 -08:00
40fac02d8b the check_cert_signature() method was attempting to compare RSA and ECC signatures.
If a ec public-key certificate is signed with an RSA key, then it can't be a self-signed certificate, in which case we just raise InvalidSignature.
2019-02-25 19:05:54 -08:00
cd65a36437 - support multiple bundle configuration, nginx, apache, cert only
- update vault destination to support multi cert under one object
- added san list as key value
- read and update object with new keys, keeping other keys, allowing
us to keep an iterable list of keys in an object for deploying multiple
certs to a single node
2019-02-25 09:42:07 -05:00
14d8596b8a updating requirements 2019-02-21 20:19:14 -08:00
ef0c08dfd9 Fix: when no alias is entered when exporting a certificate, the alias is set to 'blah'.
This fix sets it to the common name instead.
2019-02-21 16:33:43 +01:00
a0ca486f0f adding hvac and updating requrements 2019-02-19 15:22:11 -05:00
eaa73998a0 adding lemur_vault destination plugin 2019-02-19 15:03:15 -05:00
29bda6c00d Fix typo's 2019-02-14 11:58:29 +01:00
8abf95063c Implement a ALLOW_CERT_DELETION option (boolean, default False). When enabled, the certificate delete API call will work and the UI
will no longer display deleted certificates. When disabled (the default), the delete API call will not work (405 method not allowed)
 and the UI will show all certificates, regardless of the 'deleted' flag.
2019-02-14 11:57:27 +01:00
c79d9c7051 Merge pull request #2262 from intgr/special-issuer-for-selfsigned-certs
Use special issuer values <selfsigned> and <unknown> in special cases
2019-02-11 16:37:27 -08:00
a390c59e27 Merge branch 'master' into special-issuer-for-selfsigned-certs 2019-02-11 14:09:00 -08:00
e1d13ef965 Merge pull request #2505 from hosseinsh/update-reqs-021119
updating requirements
2019-02-11 14:08:43 -08:00
7c3815e7b5 Merge branch 'master' into update-reqs-021119 2019-02-11 12:05:03 -08:00
e034771e36 Merge branch 'master' into special-issuer-for-selfsigned-certs 2019-02-11 12:04:33 -08:00
f43e3588a2 Merge pull request #1865 from explody/get_by_attributes
Added get_by_attributes to the certificates service, for fetching cer…
2019-02-11 12:04:05 -08:00
793242d3a7 Merge branch 'master' into update-reqs-021119 2019-02-11 10:53:19 -08:00
427025e8f3 Merge branch 'master' into special-issuer-for-selfsigned-certs 2019-02-11 10:27:11 -08:00
ad18ea4bf7 Merge branch 'master' into get_by_attributes 2019-02-11 10:27:02 -08:00
42af082d3a updating requirements 2019-02-11 10:22:59 -08:00
7ea6e2059a Merge pull request #2493 from hosseinsh/update-reqs-020819
pinning pyyaml to ensure only using the patched version
2019-02-08 08:41:31 -08:00
20402ddd2f Merge branch 'master' into update-reqs-020819 2019-02-08 08:28:47 -08:00
73a474bd35 pinning pyyaml to ensure only using the patched version 2019-02-08 08:23:42 -08:00
bd1d29f0a1 Merge pull request #2485 from hosseinsh/update-reqs-020719
updating requirements, pinning pyyaml to patched version.
2019-02-08 08:11:54 -08:00
fd60b16342 updating requirements, pinning pyyaml to patched version. 2019-02-07 17:13:53 -08:00
f29c99bde9 Merge pull request #2482 from hosseinsh/update-reqs-020719
updating requirements
2019-02-07 17:07:51 -08:00
a43c6cf954 Update requirements-docs.txt 2019-02-07 09:57:42 -08:00
198826dd66 Merge branch 'master' into update-reqs-020719 2019-02-07 09:57:12 -08:00
70a70663a2 updating requirements 2019-02-07 09:51:34 -08:00
605663704b Merge branch 'master' into hosseinsh-celeryjob-sync-src-dst 2019-02-05 12:41:33 -08:00
e139b92b24 Merge branch 'master' into hshafagh-src-dst-register 2019-02-05 12:41:26 -08:00
6d1ef933c4 creating a new celery task to sync sources with destinations. This is as a measure to make sure important new destinations are also present as sources. 2019-02-05 10:48:52 -08:00
2107d58050 Merge branch 'master' into get_by_attributes 2019-02-05 10:31:35 -08:00
8d261b4120 Merge branch 'master' into special-issuer-for-selfsigned-certs 2019-02-05 10:29:20 -08:00
eebee9e523 Merge pull request #2467 from castrapel/update-reqs-020519
Updating requirements
2019-02-05 10:21:43 -08:00
d9d12c7603 Merge branch 'master' into update-reqs-020519 2019-02-05 09:48:11 -08:00
35158ed933 Merge pull request #2255 from sirferl/ADCS-plugin
ADCS (MS AD-PKI) plugin
2019-02-05 09:48:02 -08:00
176f9bfea6 Updating requirements 2019-02-05 09:37:04 -08:00
51248c1938 Use special issuer values <selfsigned> and <unknown> in special cases
This way it's easy to find/distinguish selfsigned certificates stored in
Lemur.
2019-02-05 16:56:09 +02:00
1d2771b014 Merge branch 'master' into get_by_attributes 2019-02-04 21:07:09 -08:00
f249a82d71 renaming destination to source. 2019-02-04 16:10:48 -08:00
44a060b159 adding support for creating a source while creating a new dst, while the destination is from AWS 2019-02-04 15:36:39 -08:00
c1cf8d7a92 Merge branch 'master' into ADCS-plugin 2019-02-02 19:21:22 +01:00
1a2712cdf1 Merge pull request #2388 from rmoesbergen/master
Implement certificates 'delete' API call
2019-02-01 17:23:04 -08:00
6c5c9fac01 Merge branch 'master' of https://github.com/rmoesbergen/lemur into rmoesbergen-master 2019-02-01 17:13:59 -08:00
45fbaf159a Merge branch 'master' into master 2019-02-01 16:50:09 -08:00
8e93d007be Merge branch 'master' into get_by_attributes 2019-02-01 16:48:50 -08:00
6705a0e030 Merge branch 'master' into ADCS-plugin 2019-02-01 16:38:39 -08:00
73ac1591e0 Merge pull request #2337 from intgr/enforce-certs-pkeys-are-str
Enforce that PEM strings (certs, keys, CSR) are internally passed as str, not bytes
2019-02-01 16:30:25 -08:00
36ab1c0bec Merge branch 'master' into ADCS-plugin 2019-02-01 19:10:46 +01:00
e24a94d798 Enforce that PEM strings (certs, keys, CSR) are internally passed as str, not bytes
This was already true in most places but not 100%, leading to lots of redundant checks and conversions.
2019-01-30 18:11:24 +02:00
e475d90e2e Merge branch 'master' into master 2019-01-30 07:20:44 -08:00
c60b712523 Merge pull request #2408 from hosseinsh/master
Resolving the 2 years error from UI during cert creation
2019-01-30 07:19:06 -08:00
81e5abd23d Merge branch 'master' of https://github.com/rmoesbergen/lemur into rmoesbergen-master 2019-01-29 17:17:30 -08:00
e5ddf08f48 Merge branch 'master' into master 2019-01-29 16:37:29 -08:00
7f4f4ffded Merge branch 'master' into master 2019-01-29 16:30:15 -08:00
48ad20faca moving the 2 year validity issue to the Verisign plugin, and address it there 2019-01-29 16:17:08 -08:00
0a392b7bac Merge pull request #2436 from castrapel/password_noninteractive
allowing create_user with noninteractive PW;updating reqs
2019-01-29 15:34:14 -08:00
1e708bf1c7 Merge branch 'master' into password_noninteractive 2019-01-29 15:21:34 -08:00
753ae3cbaf Merge pull request #2396 from intgr/expose-cert-distinguished-name
Expose full certificate RFC 4514 Distinguished Name string
2019-01-29 15:21:12 -08:00
d2317acfc5 allowing create_user with noninteractive PW;updating reqs 2019-01-29 15:17:40 -08:00
29638c7f3b Merge branch 'master' into master 2019-01-29 14:59:55 -08:00
93021a5d89 Merge branch 'master' into expose-cert-distinguished-name 2019-01-29 14:56:31 -08:00
7fb97ef4e5 Merge pull request #2432 from alwaysjolley/cfssl_authsign
Adding support for cfssl auth mode signing
2019-01-29 14:56:17 -08:00
c68a9cf80a fixing linting issues 2019-01-29 11:10:56 -05:00
254a3079f2 fix whitespace 2019-01-29 11:01:55 -05:00
b4d1b80e04 Adding support for cfssl auth mode signing 2019-01-29 10:13:44 -05:00
c77ccdf46e Merge branch 'master' into ADCS-plugin 2019-01-28 17:57:46 +01:00
c47fa0f9a2 adjusting the tests to reflect on the new full year convert limit! 2019-01-24 17:52:22 -08:00
a9724e7383 Resolving the 2 years error from UI during cert creation:
Though a CA would accept two year validity, we were getting error for being beyond 2 years.
This is because our current conversion is just current date plus 2 years,
1/25/2019 + 2 years ==> 1/25/2019
This is more strictly seen two years and 1 day extra, violating the 2 year's limit.
2019-01-24 17:23:40 -08:00
4b893ab5b4 Expose full certificate RFC 4514 Distinguished Name string
Using rfc4514_string() method added in cryptography version 2.5.
2019-01-23 10:03:40 +02:00
4c4fbf3e48 Implement certificates delete API call by marking a cert as 'deleted' in the database. Only certificates that have expired can be deleted. 2019-01-21 10:25:28 +01:00
cb35f19d6c Add 'delete_cert' to enum log_type in logs table 2019-01-21 10:22:03 +01:00
4842bb0880 Merge pull request #2376 from castrapel/fix_letsencrypt_sans
Fix letsencrypt sans
2019-01-17 15:21:18 -08:00
d689f5cda3 Fix LetsEncrypt for duplicate CN/SAN 2019-01-17 14:59:57 -08:00
0336d68ee2 Merge remote-tracking branch 'upstream/master' 2019-01-17 14:56:12 -08:00
7f88c24e83 Fix LetsEncrypt Dyn flow for duplicate CN/SAN 2019-01-17 14:56:04 -08:00
3022af4410 Merge pull request #2367 from hosseinsh/master
fixing-lemur-authorities-CN-filtering
2019-01-15 08:15:59 -08:00
d3284a4006 adjusting the query to filter authorities based on matching CN 2019-01-14 17:52:06 -08:00
f9618def0b Merge branch 'master' of github.com:Netflix/lemur 2019-01-14 17:45:35 -08:00
cbfdd0c531 Merge pull request #2360 from Netflix/compare_cert_hashes
Compare certificate hashes
2019-01-14 13:48:43 -08:00
3567a768d5 Compare certificate hashes to determine if Lemur already has a synced certificate 2019-01-14 13:35:55 -08:00
2c545aa4bc Merge pull request #2359 from castrapel/db_cert_filtering_2
Reduce the expense of joins during cert query
2019-01-14 10:11:28 -08:00
757b99e6e5 Merge branch 'master' into db_cert_filtering_2 2019-01-14 09:35:32 -08:00
31a86687e7 Reduce the expense of joins 2019-01-14 09:20:02 -08:00
42ffeda90d Merge pull request #2358 from castrapel/db_cert_filtering
Optimize DB cert filtering
2019-01-14 08:25:50 -08:00
c4e6e7c59b Optimize DB cert filtering 2019-01-14 08:02:27 -08:00
5ebfba26d7 Merge pull request #2338 from castrapel/more_retries
Be more forgiving to throttling
2019-01-11 11:40:08 -08:00
638a8450a3 Merge branch 'master' into more_retries 2019-01-11 11:25:00 -08:00
0e02e6da79 Be more forgiving to throttling 2019-01-11 11:13:43 -08:00
5118681cb5 Merge pull request #2269 from intgr/verify-cert-private-key
Check that stored private keys match certificates
2019-01-10 17:01:34 -08:00
1570cdb5d4 Merge branch 'master' into verify-cert-private-key 2019-01-10 09:50:57 -08:00
7b70719ea7 Merge pull request #2326 from castrapel/upreq-11019
Update requirements
2019-01-10 09:38:20 -08:00
3ee12cc50b Update requirements 2019-01-10 09:26:15 -08:00
cb42685322 Merge branch 'intgr-verify-cert-private-key' 2019-01-09 16:31:56 -08:00
a1ca61d813 changed a too long comment 2019-01-09 09:50:26 +01:00
707affec01 Merge branch 'master' into ADCS-plugin 2019-01-08 10:59:19 -08:00
cdf9a53a40 Merge branch 'master' into verify-cert-private-key 2019-01-08 10:53:28 -08:00
3637410327 Merge pull request #2290 from bby-bishopclark/patch-1
Update index.rst
2019-01-08 10:52:56 -08:00
7bc95ba6ee Merge branch 'master' into patch-1 2019-01-08 10:51:29 -08:00
c5ed0f3f81 Merge pull request #2312 from castrapel/up-reqs-1-2019
Update requirements with Kombu fix
2019-01-08 10:51:16 -08:00
f1e3777947 Merge branch 'verify-cert-private-key' of https://github.com/intgr/lemur into intgr-verify-cert-private-key 2019-01-08 10:49:30 -08:00
b0a2d2a907 Merge branch 'master' into up-reqs-1-2019 2019-01-08 10:22:59 -08:00
61b64d9353 Merge pull request #2280 from lukasmrtvy/master
WIP: Alpine Docker
2019-01-08 10:22:30 -08:00
c95fde7023 Better fix for kombu is to unpin it and modify makefile 2019-01-08 09:55:53 -08:00
faa91ef2a7 Update requirements with Kombu fix 2019-01-08 09:47:46 -08:00
af88ad0f0d changed broken kombu ref. from 4.2.2 to 4.2.1 because travis build fails 2019-01-07 11:35:56 +01:00
a43476bc87 minor errors after lint fix 2019-01-07 11:04:27 +01:00
054685fc38 Merge branch 'master' into ADCS-plugin 2019-01-07 10:23:18 +01:00
c62bcd1456 repaired several lint errors 2019-01-07 10:02:37 +01:00
3ac5361cb2 Update index.rst
Simple English gaffes noticed while perusing docs -- Setup vs set up, it's vs English, etc.
2019-01-03 07:58:42 -08:00
7cbdc09055 Update entrypoint 2019-01-01 12:09:06 +01:00
125a885742 Update Dockerfile 2019-01-01 11:50:48 +01:00
6c1129c946 Rename docker/lemur.conf.py to docker/src/lemur.conf.py 2019-01-01 11:50:14 +01:00
949ebfa285 Update Dockerfile 2019-01-01 11:49:49 +01:00
248c0d226f Rename docker/default.conf to docker/nginx/default.conf 2019-01-01 11:49:36 +01:00
4570fcf7fa Rename docker/default-ssl.conf to docker/nginx/default-ssl.conf 2019-01-01 11:49:24 +01:00
28382ce728 Update default-ssl.conf 2019-01-01 11:48:42 +01:00
bb4b781d24 Update entrypoint 2019-01-01 11:46:56 +01:00
0d0c295f82 Update entrypoint 2019-01-01 11:33:49 +01:00
3cc63c6618 Update entrypoint 2019-01-01 11:05:45 +01:00
ff0dbdcc5a Update entrypoint 2018-12-31 18:36:02 +01:00
918af0873f Update default-ssl.conf 2018-12-31 18:35:17 +01:00
c0f6e5a134 Update default-ssl.conf 2018-12-31 18:03:39 +01:00
628aaf2748 Update entrypoint 2018-12-31 17:36:52 +01:00
809ca0fcfe Update Dockerfile 2018-12-31 17:13:31 +01:00
4faedf3e5b Update entrypoint 2018-12-31 16:58:51 +01:00
728be37de9 Update Dockerfile 2018-12-31 15:37:48 +01:00
7fb0631ff0 Update entrypoint 2018-12-31 15:37:19 +01:00
542e953919 Check that stored private keys match certificates
This is done in two places:
* Certificate import validator -- throws validation errors.
* Certificate model constructor -- to ensure integrity of Lemur's data
  even when issuer plugins or other code paths have bugs.
2018-12-31 16:28:20 +02:00
6b1d2bfb60 Create default-ssl.conf 2018-12-31 14:55:13 +01:00
341756d7c0 Update entrypoint 2018-12-31 14:07:56 +01:00
d6a374130c Update entrypoint 2018-12-31 13:33:58 +01:00
666f180482 Update Dockerfile 2018-12-31 13:21:30 +01:00
c94557f2ed Update entrypoint 2018-12-31 13:21:13 +01:00
ca6f2b782b Update supervisor.conf 2018-12-31 12:52:07 +01:00
239acb5f95 Update supervisor.conf 2018-12-31 12:49:21 +01:00
25c4672845 Update supervisor.conf 2018-12-31 10:41:19 +01:00
aefdead50a Update entrypoint 2018-12-31 00:04:58 +01:00
e488c0ddcf Update Dockerfile 2018-12-30 23:57:14 +01:00
ba20c07420 Update entrypoint 2018-12-30 23:54:31 +01:00
abd29f8462 Update entrypoint 2018-12-30 23:53:39 +01:00
d5d4241501 Update entrypoint 2018-12-30 23:20:29 +01:00
97f6cdccfc Update Dockerfile 2018-12-30 22:58:06 +01:00
7348fd37e8 Update Dockerfile 2018-12-30 22:50:22 +01:00
2ae6c3a714 Update Dockerfile 2018-12-30 22:48:28 +01:00
a4ce379bce Update lemur.conf.py 2018-12-30 22:46:41 +01:00
692671a543 Update entrypoint 2018-12-30 22:43:55 +01:00
60b84a29b5 Update Dockerfile 2018-12-30 22:28:02 +01:00
58296cff5a Update entrypoint 2018-12-30 22:25:11 +01:00
f8008e8614 Update Dockerfile 2018-12-30 22:01:28 +01:00
ce634bfd08 Create default.conf 2018-12-30 21:49:03 +01:00
4edda34e2d Update entrypoint 2018-12-30 21:47:27 +01:00
d8377ffc57 Update supervisor.conf 2018-12-30 21:44:27 +01:00
3901571685 Update Dockerfile 2018-12-30 21:44:05 +01:00
5567bb2eaa Update Dockerfile 2018-12-30 21:43:04 +01:00
6d5782b44c Create lemur.conf.py 2018-12-30 21:38:05 +01:00
c25c703723 Create entrypoint 2018-12-30 21:37:46 +01:00
7eb6617a28 Create supervisor.conf 2018-12-30 21:37:30 +01:00
fc6caecc0b Update Dockerfile 2018-12-30 21:37:09 +01:00
4ec8490c55 Create Dockerfile 2018-12-30 00:04:13 +01:00
d60b0c8805 Merge pull request #2229 from wfhartford/kubernetes-improvment
Improve the Kubernetes Destination plugin
2018-12-21 13:00:46 -08:00
6a31856d0d Update plugin.py 2018-12-21 12:33:47 -08:00
b5d6abb01f Merge branch 'master' into kubernetes-improvment 2018-12-21 12:06:09 -08:00
954c4dfc16 Merge pull request #2261 from intgr/unicode-in-issuer-name
Properly handle Unicode in issuer name sanitization
2018-12-21 08:39:18 -08:00
b7332957e7 Merge branch 'master' into unicode-in-issuer-name 2018-12-21 07:59:20 -08:00
4bfe9bc921 Merge pull request #2219 from wfhartford/kubernetes-fix
Fix Kubernetes Destination Plugin
2018-12-21 07:58:55 -08:00
70381c4c89 Merge branch 'master' into kubernetes-fix 2018-12-21 07:44:11 -08:00
a14fe08a63 Merge branch 'master' into kubernetes-improvment 2018-12-21 07:42:13 -08:00
fb7605e34b Merge branch 'master' into unicode-in-issuer-name 2018-12-21 07:41:08 -08:00
ae2b227943 Merge pull request #2260 from intgr/deduplicate-before-unique-migration
Deduplicate rows before notification associations unique constraint migration
2018-12-21 07:40:24 -08:00
72f6fdb17d Properly handle Unicode in issuer name sanitization
If the point of sanitization is to get rid of all non-alphanumeric
characters then Unicode characters should probably be forbidden too.

We can re-use the same sanitization function as used for cert 'name'
2018-12-21 16:34:12 +02:00
0f2e30cdae Deduplicate rows before notification associations unique constraint migration 2018-12-21 12:11:33 +02:00
f02178c154 added ADCS issuer and source plugin 2018-12-20 11:54:47 +01:00
194e2a43e7 Merge pull request #1 from Netflix/master
Merge fork with updated master again
2018-12-20 09:10:46 +01:00
fbf48316b1 Minor changes for code review suggestions. 2018-12-18 22:43:32 -05:00
073d05ae21 Merge branch 'kubernetes-fix' into kubernetes-improvment 2018-12-18 22:26:03 -05:00
e7313da03e Minor changes for code review suggestions. 2018-12-18 22:24:48 -05:00
0b39d0fa34 Merge pull request #2242 from castrapel/up-reqs-12182018
Update requirements
2018-12-18 12:48:04 -08:00
49723d9aed Merge branch 'master' into up-reqs-12182018 2018-12-18 12:34:41 -08:00
9e8804dddb Merge pull request #2218 from wfhartford/destination-tpl-fix
Fix textarea and validation on destination page
2018-12-18 12:34:26 -08:00
d01e9f21f9 Merge branch 'master' into up-reqs-12182018 2018-12-18 12:29:37 -08:00
b35d494f2d Update requirements 2018-12-18 12:29:12 -08:00
425a07e988 Merge branch 'master' into destination-tpl-fix 2018-12-18 12:27:35 -08:00
388699be7c Merge pull request #2204 from rmoesbergen/master
Bugfix: Prevent 'unserializable' error for unknown SAN types
2018-12-18 12:27:15 -08:00
513e876e2e Merge branch 'master' into master 2018-12-18 12:18:38 -08:00
04681d9e1e Merge pull request #2227 from sirferl/cli-repair-query
updated query to ignore empty parameters
2018-12-18 12:18:08 -08:00
bc621c1468 Improve the Kubernetes Destination plugin
The plugin now supports loading details from local files rather than requiring them to be entered through the UI. This is especially relaent when Lemur is deployed on Kubernetes as the certificate, token, and current namespace will be injected into the pod. The location these details are injected are the defaults if no configuration details are supplied.

The plugin now supports deploying the secret in three different formats:
* Full - matches the formate used by the plugin prior to these changes.
* TLS - creates a secret of type kubernetes.io/tls and includes the certificate chain and private key, this format is used by many kubernetes features.
* Certificate - creates a secret containing only the certificate chain, suitable for use as trust authority where private keys should _NOT_ be deployed.

The deployed secret can now have a name set through the configuration options; the setting allows the insertion of the placeholder '{common_name}' which will be replaced by the certificate's common name value.

Debug level logging has been added.
2018-12-12 13:25:36 -08:00
a50d80992c updated query to ignore empty parameters 2018-12-12 12:45:48 +01:00
060c78fd91 Fix Kubernetes Destination Plugin
The Kubernetes plugin was broken. There were two major issues:
* The server certificate was entered in a string input making it impossible (as far as I know) to enter a valid PEM certificate.
* The base64 encoding calls were passing strings where bytes were expected.

The fix to the first issue depends on #2218 and a change in the options structure. I've also included some improved input validation and logging.
2018-12-10 15:33:04 -08:00
437d918cf7 Fix textarea and validation on destination page
The destination configuration page did not previously support a textarea input as was supported on most other pages. The validation of string inputs was not being performed. This commit addresses both of those issues and corrects the validation expressions for the AWS and S3 destination plugins so that they continue to function. The SFTP destination plugin does not have any string validation. The Kubernetes plugin does not work at all as far as I can tell; there will be another PR in the coming days to address that.
2018-12-10 12:04:16 -08:00
dcf5ce0eec Merge branch 'master' into master 2018-12-07 13:57:59 +01:00
afc7512914 Merge pull request #2200 from castrapel/notification_fix
Fix notification emails
2018-12-06 12:50:01 -08:00
da87135e02 update reqs 2018-12-06 12:29:16 -08:00
27fdce3842 Merge branch 'master' into notification_fix 2018-12-06 12:26:51 -08:00
c32e20b6fc Fix notifications - Ensure that notifcation e-mails are sent appropriately 2018-12-06 12:25:43 -08:00
e0ac749734 When parsing SAN's, ignore unknown san_types, because in some cases they can contain unparsable/serializable values, resulting in a TypeError(repr(o) + " is not JSON serializable") 2018-12-06 16:47:53 +01:00
f944e6aa32 Merge pull request #2177 from castrapel/multiple_dns_providers
Prefer DNS provider with longest matching zone
2018-11-30 12:53:25 -08:00
2a235fb0e2 Prefer DNS provider with longest matching zone 2018-11-30 12:44:52 -08:00
d36a51fabb Merge pull request #2171 from castrapel/letsencrypt_fix
LetsEncrypt Celery Flow
2018-11-29 09:42:36 -08:00
a90154e0ae LetsEncrypt Celery Flow 2018-11-29 09:29:05 -08:00
67b476e6d7 Merge pull request #2158 from castrapel/celery_pending
Add async call to create pending cert when needed
2018-11-28 15:22:31 -08:00
39b76d18dc add countdown to async call 2018-11-28 14:41:56 -08:00
e074a14ee9 unit test 2018-11-28 14:27:03 -08:00
2381d0a4bb Add async call to create pending cert when needed 2018-11-28 11:32:52 -08:00
c66c8f873e Merge pull request #2127 from rmoesbergen/master
Add support for nested group membership in ldap authenticator
2018-11-26 12:09:37 -08:00
5fc5a058b6 Add documentation for the LDAP_IS_ACTIVE_DIRECTORY setting 2018-11-20 10:51:14 +01:00
da10913045 Only search nested group memberships when LDAP_IS_ACTIVE_DIRECTORY is True 2018-11-20 10:37:36 +01:00
61839f4aca Add support for nested group membership in ldap authenticator 2018-11-19 13:42:42 +01:00
661bc9cc13 Merge pull request #2101 from castrapel/left_outer_join
Left outer join on domains tables to avoid missing results
2018-11-13 15:08:59 -08:00
495b3fd844 Merge branch 'master' into left_outer_join 2018-11-13 14:33:51 -08:00
3ce8abe46e Left outer join on domains tables to avoid missing results 2018-11-13 14:33:17 -08:00
d5bf85b3b0 Merge pull request #2099 from castrapel/count_accurate
More accurate db count functionality
2018-11-13 09:26:14 -08:00
92a771f5ed More accurate db count functionality 2018-11-13 09:14:21 -08:00
9d07b6644f Merge pull request #2092 from castrapel/no_csr_reissue
No csr reissue
2018-11-12 10:01:57 -08:00
29be647911 Merge branch 'master' into no_csr_reissue 2018-11-12 09:54:47 -08:00
a7a05e26bc Do not re-use CSR during certificate reissuance; Update requirement; Add more logging to celery handler 2018-11-12 09:52:11 -08:00
6f0005c78e Avoid colliding LetsEncrypt jobs 2018-11-09 10:31:27 -08:00
de1c2fc500 Merge pull request #2062 from castrapel/optimize_filter_queries
Optimize certificate filtering by name
2018-11-08 08:40:01 -08:00
1643650685 Changing essential part of query 2018-11-07 16:02:04 -08:00
08a2a2b0e5 Optimize certificate filtering by name 2018-11-07 15:34:25 -08:00
a2b22a7d09 Merge pull request #2041 from castrapel/unpin_dependencies_fix_moto
Unpin most dependencies, and fix moto
2018-11-05 15:23:59 -08:00
a3f96b96ee Add fixture to failing function 2018-11-05 15:16:09 -08:00
75183ef2f2 Unpin most dependencies, and fix moto 2018-11-05 14:37:52 -08:00
8e3f9a3c5a Merge pull request #2037 from castrapel/db_optimize_select
Add new gin index to optimize ILIKE queries
2018-11-05 13:32:37 -08:00
b9f511ed02 Updat email on travisci 2018-11-05 13:19:22 -08:00
61738dde9e Run query on DB 2018-11-05 13:15:53 -08:00
73e4396edd Enable on all schemas 2018-11-05 12:58:39 -08:00
bb36d0e0fa Add semicolon 2018-11-05 12:47:05 -08:00
0b697b9d53 Adding travis declaration for pg_trgm extension 2018-11-05 12:19:49 -08:00
b6cc8180fe downgrade flake8 2018-11-05 11:20:11 -08:00
52e773230d Add new gin index to optimize ILIKE queries 2018-11-05 10:29:11 -08:00
baa73c7f3e Merge pull request #1946 from castrapel/safer_reissue
safer reissue, fix celery sync job
2018-10-29 14:41:43 -07:00
0277e4dc05 get_or_increase_name fix for pendingcertificates 2018-10-29 13:53:30 -07:00
50761d9d3b safer reissue, fix celery sync job 2018-10-29 13:22:50 -07:00
f824f76d9c Merge pull request #1944 from castrapel/celery_tasks_sync
Celery task for sync job
2018-10-29 12:08:19 -07:00
a3a8a9d6f7 Merge branch 'master' into celery_tasks_sync 2018-10-29 10:28:52 -07:00
3079cfd08c Merge pull request #1945 from castrapel/itsdangerous_up
update itsdangerous
2018-10-29 10:28:41 -07:00
885b674298 Merge branch 'master' into itsdangerous_up 2018-10-29 09:39:54 -07:00
d4880f3e9d update itsdangerous 2018-10-29 09:39:12 -07:00
56ed416cb7 Celery task for sync job 2018-10-29 09:10:43 -07:00
a8b357965e Merge branch 'master' into get_by_attributes 2018-10-29 08:15:42 -07:00
f0e305c20e Merge pull request #1909 from jchuong/upstream-csr
Add CSR to certificiates
2018-10-29 08:13:48 -07:00
2138930102 Merge branch 'master' into get_by_attributes 2018-10-24 07:20:46 -07:00
75069cd52a Add CSR to certificiates
Add csr column to certificates field, as pending certificates have
exposed the CSR already.  This is required as generating CSR from
existing certificate is will not include SANs due to OpenSSL bug:
https://github.com/openssl/openssl/issues/6481

Change-Id: I9ea86c4f87067ee6d791d77dc1cce8f469cb2a22
2018-10-23 17:46:04 -07:00
9d7ad28ca1 Merge pull request #1890 from castrapel/check_last_updated_5_min
Only resolve pending cert if not attempted in last 5 min
2018-10-23 13:27:50 -07:00
31571ba930 Merge branch 'master' into check_last_updated_5_min 2018-10-23 13:14:30 -07:00
b709eed3c3 Only resolve pending cert if not attempted in last 5 min 2018-10-23 13:08:43 -07:00
b0d8454915 Merge pull request #1889 from castrapel/dash_in_aws_name
Prevent dashes from appearing at end of cert name in AWS
2018-10-23 13:02:43 -07:00
054cc64ee8 Prevent dashes from appearing at end of cert name in AWS 2018-10-23 12:49:58 -07:00
e168221bdc Merge pull request #1888 from castrapel/job_to_clean_expired_unattached_certs
Add celery job to clean all expired / unattached certificates from sources
2018-10-22 15:19:03 -07:00
73ed5164cd deps 2018-10-22 14:51:13 -07:00
b058508478 Merge branch 'master' into get_by_attributes 2018-10-22 09:09:55 -07:00
65060d3321 Merge pull request #1885 from castrapel/requests_up
bump requests to resolve compatibility issues w urllib3 v1.24
2018-10-22 09:09:29 -07:00
ac0e9b4b81 bump requests to resolve compatibility issues w urllib3 v1.24 2018-10-19 16:13:26 -07:00
f263be96b8 Merge pull request #1884 from castrapel/unq_sources_label
Add unique constraint to sources table - label column
2018-10-19 15:46:44 -07:00
e83699b6ae Add unique constraint to sources table - label column 2018-10-19 15:34:34 -07:00
81d114092e Merge branch 'github' into get_by_attributes 2018-10-17 12:00:36 -04:00
48017a9d4c Added get_by_attributes to the certificates service, for fetching certs based on arbitrary attributes. Also associated test and extra tests for other service methods 2018-10-17 11:42:09 -04:00
c6b679bb19 Merge pull request #1831 from castrapel/lemur_letsencrypt_resolved_info
Lemur LetsEncrypt Polling Support
2018-10-12 07:33:48 -07:00
a912c3488d python fix to retrigger tests 2018-10-12 07:25:58 -07:00
89a077e54c minor change to pass stuck github check 2018-10-12 07:14:31 -07:00
13ef965666 nit: comments 2018-10-12 05:56:14 -07:00
6073f9e7b6 datetime ref fix 2018-10-12 05:51:30 -07:00
4b3d458dba Celery task to delete old pending certs 2018-10-12 05:47:16 -07:00
3d8c4af543 Merge branch 'master' into lemur_letsencrypt_resolved_info 2018-10-12 05:25:31 -07:00
cc18a68c00 Lemur LetsEncrypt Polling Support 2018-10-11 22:01:05 -07:00
b83c5eca49 Merge pull request #1823 from castrapel/indexes_domains_certs
add indexes to domains and certificates tables to optimize load time
2018-10-11 13:07:23 -07:00
e91d8ec81b add indexes to domains and certificates tables to optimize load time 2018-10-11 11:36:50 -07:00
318527223b Merge pull request #1779 from explody/improved_verify
Improved verify
2018-10-04 11:27:13 -07:00
79033f42b4 Merge branch 'master' into improved_verify 2018-10-02 09:19:24 -04:00
40f4444099 Flake8 fix in test_verify.py 2018-10-01 22:04:31 -04:00
eb7eb6ae88 Merge pull request #1783 from castrapel/verisign_ct_option
Enable optional verisign cloud transparency configuration
2018-10-01 09:32:13 -07:00
56282845fa Enable optional verisign cloud transparency configuration 2018-10-01 09:20:50 -07:00
50919d85a8 Merge remote-tracking branch 'upstream/master' into improved_verify 2018-09-27 11:19:06 -04:00
590fac4aa8 docstring update in verify.py 2018-09-27 10:11:13 -04:00
f19b6382bc Updated verify tests 2018-09-27 10:10:04 -04:00
11f2210894 Merge branch 'improved_verify' of github.com:explody/lemur into improved_verify 2018-09-27 09:28:45 -04:00
652d7f65dd flake8 tweak 2018-09-27 09:28:21 -04:00
c7b57e21a5 Merge pull request #1711 from castrapel/celery_092018
Celery integration
2018-09-17 15:36:06 -07:00
54ba7a053a up reqs 2018-09-17 10:57:03 -07:00
563f0fb9b2 Celery refactoring, celery beat job in configuration 2018-09-17 10:52:12 -07:00
23382b2777 Celery integration 2018-09-13 10:35:54 -07:00
3c521f66a5 Merge pull request #1691 from castrapel/fix_import_v1
Fix certificate import issues
2018-09-10 11:05:56 -07:00
c09d8ae630 Merge branch 'master' into fix_import_v1 2018-09-10 10:35:31 -07:00
7d42e4ce67 Fix certificate import issues 2018-09-10 10:34:47 -07:00
72858e7412 Merge pull request #1690 from castrapel/message-notifications
Add more logging to messaging
2018-09-10 09:57:13 -07:00
f6a130b09d Add more logging to messaging 2018-09-10 09:13:31 -07:00
c9836fbf25 Merge branch 'master' into improved_verify 2018-09-06 07:33:55 -07:00
85b035e6ad Merge pull request #1655 from gesquive/missing_reqs
add missing requirements files for setup
2018-09-05 15:38:56 -07:00
d9aeef363e Merge branch 'master' into missing_reqs 2018-09-05 14:49:48 -07:00
0ab0caa375 Merge pull request #1661 from gesquive/cfssl_error_msg
cfssl plugin error message typo
2018-09-05 14:49:30 -07:00
82e69db0c5 fix error message typo 2018-09-04 10:21:34 -05:00
b503594ac0 add missing requirements files for setup 2018-08-31 13:02:53 -05:00
2815ddf6c8 Moved cert object to be passed to both ocsp/crl methods so we can report in better detail on the certs. Ensured proper returns of False (revoked) True (good) None (unknown) throughout the methods. 2018-08-31 13:34:55 -04:00
34c88494b8 More specific exception catch for cert parsing. line shortening. 2018-08-31 12:19:55 -04:00
7dbca821c3 Reducing the stacked exceptions plus a bit of pep8 2018-08-31 12:01:49 -04:00
b3c4324728 Merge pull request #1630 from castrapel/validate_config_fix
Validate config - fix for issue#1629
2018-08-28 10:08:14 -07:00
d82a615e17 Validate config - fix for issue#1629 2018-08-28 09:15:28 -07:00
cfbb3a205b Merge pull request #1612 from andrewachen/crt-sh-link
Add link to search for certificate in crt.sh.
2018-08-27 10:00:38 -07:00
5797fd4042 Merge pull request #1620 from castrapel/recommit-achen
recommit https://github.com/Netflix/lemur/pull/1612
2018-08-27 10:00:21 -07:00
453bb43157 recommit https://github.com/Netflix/lemur/pull/1612 2018-08-27 09:50:02 -07:00
9d56ae3add Merge branch 'master' into crt-sh-link 2018-08-27 09:23:24 -07:00
dee9bbdfad Merge pull request #1619 from castrapel/up-reqs-828v2
Up reqs 828v2
2018-08-27 09:23:09 -07:00
b8b0f77ff8 Update requirements 2018-08-27 09:00:04 -07:00
7ad24042c1 Add link to search for certificate in crt.sh. 2018-08-24 14:16:25 -07:00
51f05764e0 Merge remote-tracking branch 'upstream/master' 2018-08-23 11:07:46 -07:00
9aac7c2269 Merge pull request #1600 from Netflix/revert-1599-precommit
Revert "Precommit - Fix linty things"
2018-08-22 14:07:06 -07:00
1b77dfa47a Revert "Precommit - Fix linty things" 2018-08-22 13:21:35 -07:00
cdc385ea16 Merge remote-tracking branch 'upstream/master' 2018-08-22 13:18:03 -07:00
bc88d3514c Revert "Merge pull request #1589 from castrapel/issue_1570_ignorecasev2"
This reverts commit a21b71a0e2, reversing
changes made to 075b18e5db.
2018-08-22 13:17:33 -07:00
e21693cb18 Merge pull request #1599 from castrapel/precommit
Precommit - Fix linty things
2018-08-22 10:55:59 -07:00
3e9726d9db Precommit work 2018-08-22 10:38:09 -07:00
a21b71a0e2 Merge pull request #1589 from castrapel/issue_1570_ignorecasev2
Allow case insensitive role matching for cert permissions
2018-08-20 09:03:55 -07:00
6abf274680 Allow case insensitive role matching for cert permissions 2018-08-20 08:55:04 -07:00
075b18e5db Merge pull request #1584 from castrapel/issue_1570_ignorecase
Increase LetsEncrypt Timeout
2018-08-17 17:17:37 -07:00
a0aa78a529 Merge branch 'master' into issue_1570_ignorecase 2018-08-17 16:59:35 -07:00
9f64f0523b Increase timeouts 2018-08-17 15:36:56 -07:00
43ae6c39e3 wait right here 2018-08-17 12:14:02 -07:00
c5fb2422da Merge pull request #1576 from castrapel/issue_1570_ignorecase
force owner into lowercase. Properly decode bytecode private key
2018-08-17 11:26:03 -07:00
7f9a035802 Fix private key bytecode issue 2018-08-17 10:59:01 -07:00
a6b1f33208 Ensure owner names are lowercase for new / updated certificates 2018-08-17 10:41:55 -07:00
cc4cdfcf13 Merge pull request #1575 from castrapel/fix_validity
allow null validity periods
2018-08-17 08:51:53 -07:00
1ad61b1550 allow null validity periods 2018-08-17 07:57:55 -07:00
29556e9f8c Merge pull request #1573 from castrapel/better_dnsprovider_handling
Better dnsprovider handling
2018-08-16 10:33:58 -07:00
be9d683e46 fix merge 2018-08-16 10:15:48 -07:00
da99bcda68 Better zone handling 2018-08-16 10:12:19 -07:00
f3d9513df2 Merge pull request #1561 from castrapel/le_fix
Allow proper detection of zones, fix certificate detection
2018-08-14 15:01:29 -07:00
2c22c9c2f1 Allow proper detection of zones, fix certificate detection 2018-08-14 14:37:45 -07:00
e050177c08 Merge pull request #1553 from castrapel/fix_le_renew
Allow auto-detection of DNS providers / Fix acme renewal flow
2018-08-13 15:22:45 -07:00
1a5abe6550 fix lint 2018-08-13 15:11:57 -07:00
cc836433fb formatting 2018-08-13 15:06:16 -07:00
5829794d82 typo fix 2018-08-13 14:25:54 -07:00
bb026b8b59 Allow LetsEncrypt renewals and requesting certificates without specifying DNS provider 2018-08-13 14:22:59 -07:00
771be58dc5 Merge pull request #1511 from intgr/unittests-use-valid-certs
Fix unit tests certificates to have correct chains and private keys
2018-08-07 10:04:56 -07:00
ab37189022 Merge branch 'master' into unittests-use-valid-certs 2018-08-07 09:42:39 -07:00
3463848cb5 Merge pull request #1509 from intgr/fill-missing-rotation-policy
Fill in missing cert rotation_policy; don't ignore validation errors when re-issuing certs
2018-08-07 09:40:09 -07:00
cf71f88680 Merge branch 'master' into fill-missing-rotation-policy 2018-08-07 08:23:29 -07:00
234533e367 Merge branch 'master' into unittests-use-valid-certs 2018-08-07 08:13:48 -07:00
e0c6d6dd7d Merge pull request #1530 from castrapel/up-reqs8718
Up reqs8718
2018-08-07 08:13:32 -07:00
c82f3bbf0f updating requirements 2018-08-07 07:48:09 -07:00
51d5a897c2 Merge remote-tracking branch 'upstream/master' 2018-08-07 07:47:07 -07:00
7f821abfef Fixed invalid JSON payloads (making API requests fail in particular) (#1522) 2018-08-07 07:46:59 -07:00
1edb964da9 Delete dead code in unit tests (#1510) 2018-08-07 07:46:59 -07:00
400bcaf085 Add .pytest_cache dir to .gitignore (#1512) 2018-08-07 07:46:58 -07:00
583bbee606 remove debug print 2018-08-07 07:46:58 -07:00
7463d47057 Support LetsEncrypt accounts 2018-08-07 07:46:58 -07:00
46cd1a21f7 Proper flask_restful boolean parsing.
This is documented here: https://github.com/flask-restful/flask-restful/issues/488
2018-08-07 07:46:58 -07:00
82c7530b6f fix deletion 2018-08-07 07:46:58 -07:00
6d8217e00f requirements 2018-08-07 07:46:58 -07:00
cc735e9b33 Error logging 2018-08-07 07:46:58 -07:00
19753632a9 Show and send error for pending certs 2018-08-07 07:46:58 -07:00
a3e1d08ba2 Adding pessimistic sqlalchemy disconnection handling 2018-08-07 07:46:58 -07:00
5e34287530 no bare except 2018-08-07 07:46:57 -07:00
dd7c9e3f88 Explicit capture exception during create failure 2018-08-07 07:46:57 -07:00
d8652fad36 Unpinning requests 2018-08-07 07:46:57 -07:00
58ec0bab09 updated requirements 2018-08-07 07:46:57 -07:00
094b2fd5a9 Clean up module imports
Example:
* import lemur.common.utils -> from lemur.common import utils
* import sqlalchemy.types as types -> from sqlalchemy import types
2018-08-07 07:46:57 -07:00
85285b5e62 Cache parsed certificate instead of re-parsing for each field
Use @cached_property decorator to cache the results of parse_certificate().

This significantly cuts down on the number of times certs need to be
parsed for a list view.
2018-08-07 07:46:57 -07:00
16dc7dc2f6 no bare except 2018-08-07 07:46:57 -07:00
f53067ab29 Explicit capture exception during create failure 2018-08-07 07:46:56 -07:00
efd33db69d Unpinning requests 2018-08-07 07:46:56 -07:00
5b13032aec Adds an optional interval variable to notification service's
create_default_expiration_notifications and introduces a new optional
configuration variable, LEMUR_SECURITY_TEAM_EMAIL_INTERVALS, to allow admins
control over the centralized email notification defaults.
2018-08-07 07:46:56 -07:00
177208f9da updated requirements 2018-08-07 07:46:56 -07:00
b8d017418a Clean up module imports
Example:
* import lemur.common.utils -> from lemur.common import utils
* import sqlalchemy.types as types -> from sqlalchemy import types
2018-08-07 07:46:56 -07:00
ce6e64bd17 Cache parsed certificate instead of re-parsing for each field
Use @cached_property decorator to cache the results of parse_certificate().

This significantly cuts down on the number of times certs need to be
parsed for a list view.
2018-08-07 07:46:56 -07:00
6ce044806b initial commit 2018-08-07 07:46:56 -07:00
68203436e0 Sinful Use of $
Using the `$` sign within any block of text already marked as a code block is a grievous sin due to the fact that it makes it 100% pointless for you to have USED THE CODE BLOCK IN THE FIRST PLACE!

The `$` becomes included in the text we're trying to highlight for us to be able to actually use in our own projects.

Why post the info if you don't want us to use it.

Thank you.
2018-08-07 07:46:55 -07:00
f9a7b97839 Merge branch 'master' into unittests-use-valid-certs 2018-08-07 07:45:45 -07:00
2869042f38 Fixed invalid JSON payloads (making API requests fail in particular) (#1522) 2018-08-03 15:26:48 -07:00
82158aece6 Fill in missing cert rotation_policy; don't ignore validation errors when re-issuing certs
CertificateInputSchema requires the rotation_policy field, but
certificates created before the field existed have set to NULL. Thus
saving such certificates failed and probably caused other errors.

Made cert re-issuing (get_certificate_primitives) more strict so such
errors are harder to miss in the future.
2018-08-03 20:06:21 +03:00
1f0f432327 Fix unit tests certificates to have correct chains and private keys
In preparation for certificate integrity-checking: invalid certificate
chains and mismatching private keys will no longer be allowed anywhere
in Lemur code.

The test vector certs were generated using the Lemur "cryptography"
authority plugin.

* Certificates are now more similar to real-world usage: long serial
  numbers, etc.
* Private key is included for all certs, so it's easy to re-generate
  anything if needed.
2018-08-03 19:45:13 +03:00
acd2701fa2 Delete dead code in unit tests (#1510) 2018-08-03 08:21:55 -07:00
bb0c229d7e Add .pytest_cache dir to .gitignore (#1512) 2018-08-03 08:12:04 -07:00
3f9d66bd51 Merge pull request #1497 from castrapel/letsencrypt_account_support
Letsencrypt account support
2018-07-30 15:37:35 -07:00
025d177565 Merge branch 'master' into letsencrypt_account_support 2018-07-30 15:28:29 -07:00
44192d4494 remove debug print 2018-07-30 15:27:23 -07:00
4b5e93cd3c Merge pull request #1491 from mikegrima/booleans
Proper flask_restful boolean parsing
2018-07-30 15:25:52 -07:00
0889076d3b Support LetsEncrypt accounts 2018-07-30 15:25:02 -07:00
d6b482755b Proper flask_restful boolean parsing.
This is documented here: https://github.com/flask-restful/flask-restful/issues/488
2018-07-30 13:49:41 -07:00
b70885595f Merge pull request #1488 from castrapel/fix_delete
fix deletion
2018-07-27 16:44:23 -07:00
caf99d36d6 fix deletion 2018-07-27 15:52:22 -07:00
35341a6828 Merge pull request #1487 from castrapel/cancel_pending_cert_failures
Cancel pending cert failures
2018-07-27 14:26:52 -07:00
2bb00bc666 requirements 2018-07-27 14:20:22 -07:00
e16c1de001 Error logging 2018-07-27 14:17:50 -07:00
2a6dda07eb Show and send error for pending certs 2018-07-27 14:15:14 -07:00
4fa8f9ecc0 Merge pull request #1468 from castrapel/fix_db
http://docs.sqlalchemy.org/en/latest/core/pooling.html#disconnect-handling-pessimistic
2018-07-23 11:07:45 -07:00
9b29f9f819 Adding pessimistic sqlalchemy disconnection handling 2018-07-23 10:57:22 -07:00
faa1779204 Merge remote-tracking branch 'upstream/master' 2018-07-23 10:29:52 -07:00
2f51fea743 no bare except 2018-07-20 13:43:47 -07:00
c78077d8d6 Explicit capture exception during create failure 2018-07-20 13:43:47 -07:00
0bb7a6e125 Unpinning requests 2018-07-20 13:43:47 -07:00
bd9203fcbc Adds an optional interval variable to notification service's
create_default_expiration_notifications and introduces a new optional
configuration variable, LEMUR_SECURITY_TEAM_EMAIL_INTERVALS, to allow admins
control over the centralized email notification defaults.
2018-07-20 13:43:47 -07:00
af8cf2d550 updated requirements 2018-07-20 13:43:47 -07:00
d071d85486 Clean up module imports
Example:
* import lemur.common.utils -> from lemur.common import utils
* import sqlalchemy.types as types -> from sqlalchemy import types
2018-07-20 13:43:47 -07:00
04ee1656ee Cache parsed certificate instead of re-parsing for each field
Use @cached_property decorator to cache the results of parse_certificate().

This significantly cuts down on the number of times certs need to be
parsed for a list view.
2018-07-20 13:43:47 -07:00
56372c55b4 initial commit 2018-07-20 13:43:47 -07:00
7146c4cb71 Sinful Use of $
Using the `$` sign within any block of text already marked as a code block is a grievous sin due to the fact that it makes it 100% pointless for you to have USED THE CODE BLOCK IN THE FIRST PLACE!

The `$` becomes included in the text we're trying to highlight for us to be able to actually use in our own projects. 

Why post the info if you don't want us to use it. 

Thank you.
2018-07-20 13:43:47 -07:00
b0847e2fa1 Merge pull request #1457 from castrapel/better_sentry
Better sentry
2018-07-20 11:16:49 -07:00
f93e938cda no bare except 2018-07-20 10:53:47 -07:00
5a01840784 Explicit capture exception during create failure 2018-07-20 10:47:19 -07:00
0a0992fffe Merge remote-tracking branch 'upstream/master' 2018-07-20 10:08:40 -07:00
2a5f713f97 initial commit 2018-07-20 10:07:32 -07:00
3ac440b6b5 Sinful Use of $
Using the `$` sign within any block of text already marked as a code block is a grievous sin due to the fact that it makes it 100% pointless for you to have USED THE CODE BLOCK IN THE FIRST PLACE!

The `$` becomes included in the text we're trying to highlight for us to be able to actually use in our own projects. 

Why post the info if you don't want us to use it. 

Thank you.
2018-07-20 10:07:32 -07:00
db9891d8cf Merge pull request #1433 from castrapel/update-requests
Unpinning requests
2018-07-17 19:24:57 -07:00
be9be6d3cd Unpinning requests 2018-07-17 18:38:15 -07:00
b02c00bbf1 Merge pull request #1431 from Netflix/security_notifications_config
Adds an optional interval variable to notification service's
2018-07-13 15:51:26 -07:00
7f3454128d Adds an optional interval variable to notification service's
create_default_expiration_notifications and introduces a new optional
configuration variable, LEMUR_SECURITY_TEAM_EMAIL_INTERVALS, to allow admins
control over the centralized email notification defaults.
2018-07-13 14:08:31 -07:00
4922f4dd40 Merge pull request #1426 from jpartain89/patch-1
Sinful Use of `$`
2018-07-12 21:53:17 -07:00
e3de9baaeb Merge branch 'master' into patch-1 2018-07-12 21:37:33 -07:00
5df280e94d Merge pull request #1429 from Brett-Wood/master
Missing file lemur/dns_providers/__init__.py
2018-07-12 21:37:12 -07:00
5afc9ba739 Merge branch 'master' into master 2018-07-12 15:37:44 -07:00
983636c502 Merge pull request #1430 from castrapel/upreqs71218
Update requirements
2018-07-12 15:37:31 -07:00
a19a47dba1 updated requirements 2018-07-12 13:24:44 -07:00
ead374db5f Clean up module imports
Example:
* import lemur.common.utils -> from lemur.common import utils
* import sqlalchemy.types as types -> from sqlalchemy import types
2018-07-12 13:23:38 -07:00
2f32014c75 Cache parsed certificate instead of re-parsing for each field
Use @cached_property decorator to cache the results of parse_certificate().

This significantly cuts down on the number of times certs need to be
parsed for a list view.
2018-07-12 13:23:38 -07:00
4f4be51ac8 Merge remote-tracking branch 'upstream/master' 2018-07-12 11:21:31 -07:00
149caa5602 Clean up module imports
Example:
* import lemur.common.utils -> from lemur.common import utils
* import sqlalchemy.types as types -> from sqlalchemy import types
2018-07-12 11:21:18 -07:00
b472e5e648 Cache parsed certificate instead of re-parsing for each field
Use @cached_property decorator to cache the results of parse_certificate().

This significantly cuts down on the number of times certs need to be
parsed for a list view.
2018-07-12 11:21:18 -07:00
64132ba92b Expose certificate dateCreated via API 2018-07-12 11:21:18 -07:00
9ef356f59d reformat code (noop) 2018-07-12 11:21:17 -07:00
825844107e updates 2018-07-12 11:21:17 -07:00
8dc52b859b initial commit 2018-07-11 11:57:36 -05:00
86082009b9 Sinful Use of $
Using the `$` sign within any block of text already marked as a code block is a grievous sin due to the fact that it makes it 100% pointless for you to have USED THE CODE BLOCK IN THE FIRST PLACE!

The `$` becomes included in the text we're trying to highlight for us to be able to actually use in our own projects. 

Why post the info if you don't want us to use it. 

Thank you.
2018-07-09 23:24:35 -05:00
5b70504144 Merge pull request #1395 from intgr/import-police
Clean up module imports
2018-07-09 10:13:08 -07:00
0398c6e723 Clean up module imports
Example:
* import lemur.common.utils -> from lemur.common import utils
* import sqlalchemy.types as types -> from sqlalchemy import types
2018-07-07 23:56:23 +03:00
c568e41296 Merge pull request #1388 from intgr/cache-parsed-cert
Cache parsed certificate instead of re-parsing for each field
2018-07-06 09:40:40 -07:00
d690ea32bc Cache parsed certificate instead of re-parsing for each field
Use @cached_property decorator to cache the results of parse_certificate().

This significantly cuts down on the number of times certs need to be
parsed for a list view.
2018-07-03 17:31:44 +03:00
48c378127f Merge pull request #1394 from intgr/date-created
Expose certificate dateCreated via API
2018-07-02 13:12:22 -07:00
50846eb682 Expose certificate dateCreated via API 2018-07-02 18:24:18 +03:00
5c0cc69d22 Merge pull request #1409 from castrapel/up-reqs-6292018
reformat code (noop)
2018-06-29 15:39:05 -07:00
74d9fa58be Merge branch 'master' into up-reqs-6292018 2018-06-29 15:28:21 -07:00
1a02740b67 reformat code (noop) 2018-06-29 15:24:31 -07:00
acd473683e Merge pull request #1408 from castrapel/update-reqs
Update reqs
2018-06-29 14:14:02 -07:00
090619151e updates 2018-06-29 14:03:45 -07:00
846027b9e3 Merge remote-tracking branch 'upstream/master' 2018-06-28 13:51:23 -07:00
f9539cfba5 tests 2018-06-28 11:12:55 -07:00
9375862ee2 lint 2018-06-28 11:12:55 -07:00
4818cc4eb9 lint 2018-06-28 11:12:55 -07:00
65461d7418 Update requirements 2018-06-28 11:12:55 -07:00
ad73abced1 Support concurrent issuance in Route53 for LetsEncrypt 2018-06-28 11:12:55 -07:00
261b024bf4 Upgrade dependency boto3 to ==1.7.39 2018-06-28 11:12:54 -07:00
20292275b5 update requirements while we're at it 2018-06-28 11:12:54 -07:00
e912b8e075 Graceful cancellation of pending cert and order details in log for acme failure 2018-06-28 11:12:54 -07:00
4551cc11df update requirements 2018-06-28 11:12:54 -07:00
14b8892cce Limit dns queries to 10 attempts 2018-06-28 11:12:54 -07:00
284e57ad68 boto update. They updated between this and the last change 2018-06-28 11:12:54 -07:00
b46023bb4c lint 2018-06-28 11:12:54 -07:00
3002945d55 Fix unique dyn situation where zone does not match tld, and there's a deeper zone 2018-06-28 11:12:53 -07:00
0d4df75375 update requirements 2018-06-28 11:12:53 -07:00
78c4a86371 remove linuxdst plugin 2018-06-28 11:12:53 -07:00
c0c6ff51e2 Merge pull request #1372 from castrapel/acme_validation_dns_provider_option
R53: Extend only TXT records
2018-06-20 10:50:15 -07:00
4384cbb953 Merge branch 'master' into acme_validation_dns_provider_option 2018-06-20 10:34:38 -07:00
3397fb6560 R53: Extend only TXT records 2018-06-20 10:33:35 -07:00
b231521ff6 Merge pull request #1369 from castrapel/acme_validation_dns_provider_option
Acme validation dns provider option
2018-06-19 21:24:15 -07:00
3efc709e03 tests 2018-06-19 21:16:35 -07:00
dda7f54a16 lint 2018-06-19 20:58:00 -07:00
2d33d3e2b8 lint 2018-06-19 20:35:00 -07:00
d50c9c7748 Merge branch 'master' into acme_validation_dns_provider_option 2018-06-19 16:45:25 -07:00
665a0bcffe Update requirements 2018-06-19 16:38:05 -07:00
a141b8c5ea Support concurrent issuance in Route53 for LetsEncrypt 2018-06-19 16:27:58 -07:00
9d710702a4 Merge pull request #1348 from Netflix/doppins/boto3-equals-1.7.39
[Doppins] Upgrade dependency boto3 to ==1.7.39
2018-06-15 07:48:35 -07:00
e835fa6073 Upgrade dependency boto3 to ==1.7.39 2018-06-14 23:37:07 +00:00
c0d037b9e9 Merge pull request #1347 from castrapel/dyn2
Dyn
2018-06-14 08:16:05 -07:00
b2bc431823 Merge branch 'master' into dyn2 2018-06-14 08:06:31 -07:00
9c5140006b update requirements while we're at it 2018-06-14 08:04:35 -07:00
4e72cb96c9 Graceful cancellation of pending cert and order details in log for acme failure 2018-06-14 08:02:34 -07:00
f88e81ffef Merge pull request #1342 from castrapel/dyn_dns_fix
Limit dns queries to 10 attempts
2018-06-14 07:49:18 -07:00
17861289c8 Merge branch 'master' into dyn_dns_fix 2018-06-13 15:37:08 -07:00
b99aad743b remove linuxdst plugin 2018-06-13 15:15:09 -07:00
b1ce4d630d update requirements 2018-06-13 15:15:09 -07:00
135f2b710c Limit dns queries to 10 attempts 2018-06-13 15:14:48 -07:00
e8c18bd9b6 Merge pull request #1335 from castrapel/dyn_dns_fix
Dyn dns fix
2018-06-13 14:35:42 -07:00
76621e497f boto update. They updated between this and the last change 2018-06-13 14:27:50 -07:00
065e0edc5f lint 2018-06-13 14:22:45 -07:00
d72792ff37 Fix unique dyn situation where zone does not match tld, and there's a deeper zone 2018-06-13 14:08:39 -07:00
f9239b008e Merge remote-tracking branch 'upstream/master' 2018-06-12 08:08:01 -07:00
b31c7357ed update requirements 2018-06-12 08:07:18 -07:00
7c6d6f5297 Merge pull request #1171 from dmitryzykov/linuxdst
Remove linuxdst plugin
2018-06-12 07:56:39 -07:00
e225139011 Merge branch 'master' into linuxdst 2018-06-12 07:45:02 -07:00
37edf80321 Merge pull request #1320 from Netflix/update_reqs
update requirements
2018-06-12 07:44:45 -07:00
038f5dc554 Merge branch 'master' into linuxdst 2018-06-12 07:40:40 -07:00
5e964fad39 update requirements 2018-06-12 07:35:46 -07:00
3800d67d71 Merge pull request #1222 from castrapel/master
LetsEncrypt support . Version bump.
2018-06-12 07:33:17 -07:00
7f5d1a0b6b sync error 2018-06-11 15:40:15 -07:00
92860cffca Default configuration for DNS providers 2018-06-11 13:32:53 -07:00
2aced5c010 Merge branch 'master' into master 2018-06-09 10:28:24 -07:00
4e1879715d Merge pull request #1284 from Netflix/up-reqs-2
Update requirements again
2018-06-05 12:47:04 -07:00
403f70d6db Update requirements again 2018-06-05 09:51:19 -07:00
b33256c809 Merge pull request #1275 from Netflix/up-reqs
Update requirements
2018-06-04 13:41:27 -07:00
e39fac32ec Update requirements 2018-06-04 13:32:51 -07:00
80e3331596 Merge branch 'master' into master 2018-05-30 08:24:00 -07:00
2a3af5214e Merge branch 'master' into linuxdst 2018-05-29 18:54:37 -07:00
4911d713a5 Fix import metrics in notifications/messaging.py (#1254)
`from lemur import metrics` is incorrect for notifications/messaging.py
because that is importing the `metrics` module rather than the
instanciated `lemur.extensions.metrics` object.  This will cause errors
if you import notifications/messaging.py elsewhere, since it can cause
circular dependencies.

Change-Id: Ice28c480373601420fc83bae2d27bb6467cdb752
2018-05-29 18:54:16 -07:00
5e24f685c1 lint error 2018-05-29 10:46:24 -07:00
97d3621705 convert description to TEXT column 2018-05-29 10:23:01 -07:00
544a02ca3f Addressing comments. Updating copyrights. Added function to determine authorative name server 2018-05-29 10:23:01 -07:00
ae26e44cc2 Merge branch 'master' into master 2018-05-25 11:09:23 -07:00
b0f9d33b32 Requirements update 2018-05-25 11:07:26 -07:00
c5e7e5ab68 Merge pull request #1253 from Netflix/quicker_count
Sort and page
2018-05-25 11:04:05 -07:00
5e3add0b81 docstring 2018-05-24 15:21:38 -07:00
9ccd43c29b Merge branch 'master' into quicker_count 2018-05-24 12:57:40 -07:00
9fc6c9aaf7 Sort and page 2018-05-24 12:55:52 -07:00
2d61200a05 Merge pull request #1252 from jchuong/docker-test
Add VIRTUAL_ENV to docker-compose
2018-05-24 12:42:09 -07:00
268d826158 Add VIRTUAL_ENV to docker-compose
This fixes `make test` in the docker-compose, as `up-reqs` checks for
this envvar before installing requirements.  Since this is in a docker
container for testing, just allow pip to install without a virtualenv.

Change-Id: I511efa9fab8d393bf9f2b8e80408fb8cf155bafa
2018-05-23 17:17:23 -07:00
a47b6c330d Use serial_number instead of serial (#1251)
* Add code coverage badge to README

* fixing docs (#1231)

* Change cert.serial to serial_number

This fixes deprecation warning coming from cryptography package about
using cert.serial instead of serial_number.

Change-Id: I252820974c77cc1b80639920a5e8c2e874819dda
2018-05-23 16:04:30 -07:00
de52fa7f48 fix v1 backwards compatibility 2018-05-16 08:00:33 -07:00
680f4966a1 acme v2 support 2018-05-16 07:46:37 -07:00
a9b9b27a0b fix tests 2018-05-10 12:58:04 -07:00
52e7ff9919 Allow specification of dns provider name only 2018-05-10 12:58:04 -07:00
f4a010e505 Merge branch 'master' into master 2018-05-09 07:52:07 -07:00
0bd14488bb Update requirements, handle more lemur_acme exceptions, and remove take a tour button 2018-05-08 15:35:03 -07:00
6500559f8e Fix issue with automatically renewing acme certificates 2018-05-08 14:54:10 -07:00
642dbd4098 Merge branch 'master' into linuxdst 2018-05-08 12:09:05 -07:00
a8187d15c6 quick lint 2018-05-08 11:04:25 -07:00
df5168765b more tests 2018-05-08 11:03:17 -07:00
c26ae16060 fixing docs (#1231) 2018-05-08 10:58:48 -07:00
9ccb8fb838 Alembic simplification 2018-05-07 15:14:32 -07:00
e68b3d2cbd 0.7 release 2018-05-07 09:58:24 -07:00
1be3f8368f dyn support 2018-05-04 15:01:01 -07:00
3e64dd4653 Additional work 2018-05-04 15:01:01 -07:00
48dde287d8 Merge branch 'master' into master 2018-05-01 12:36:32 -07:00
4da2f33892 Merge pull request #1229 from seils/bugfix-1228
Fix URL for Latest Docs image
2018-04-30 10:16:49 -07:00
74ca13861c Merge branch 'master' into master 2018-04-27 11:19:23 -07:00
532872b3c6 dns_provider ui 2018-04-27 11:18:51 -07:00
d37f730ee8 Merge branch 'master' into bugfix-1228 2018-04-27 08:54:08 -07:00
5e744c4c52 Merge pull request #1230 from seils/coverage-badge
Add code coverage badge to README
2018-04-27 08:53:19 -07:00
858c4eb808 Add code coverage badge to README 2018-04-27 08:46:11 -04:00
3ffeb8ab00 Fix URL for Latest Docs image 2018-04-26 19:45:00 -04:00
0579b2935c Print variable value instead of name (#1227)
* Print variable value instead of name

* Fixed ordering and variable name for stdout string
2018-04-26 09:39:42 -07:00
c5cb01bd33 Merge branch 'master' into master 2018-04-26 09:16:31 -07:00
efd5836e43 fix test 2018-04-26 09:04:13 -07:00
f0f2092fb4 Some unit tests 2018-04-25 11:19:34 -07:00
e09b7eb978 Selectively enable CORS. (#1220) 2018-04-24 17:10:38 -07:00
3e5db9eedb Check for default rotation policy before updating db (#1223) 2018-04-24 16:55:26 -07:00
91500d1022 Minor comment & stdout corrections (#1225) 2018-04-24 16:53:51 -07:00
51d2990eb9 Merge branch 'master' of github.com:castrapel/lemur 2018-04-24 09:48:33 -07:00
38b8df4a07 lint 2018-04-24 09:48:14 -07:00
211027919f Merge branch 'master' into master 2018-04-24 09:43:20 -07:00
38c33395c8 Merge branch 'castrapel-hackday' 2018-04-24 09:41:26 -07:00
7704f51441 Working acme flow. Pending DNS providers UI 2018-04-24 09:38:57 -07:00
ae63808678 Update administration.rst (#1221) 2018-04-23 12:15:56 -07:00
81e349e07d Merge branch 'master' into hackday 2018-04-23 10:11:49 -07:00
49cdf1c7cf Merge pull request #1219 from castrapel/forcible_remove_python-ldap
reqs update
2018-04-23 09:34:39 -07:00
7e36b0e8fd comment 2018-04-23 09:26:36 -07:00
552c07e932 reqs update 2018-04-23 09:23:23 -07:00
44e3b33aaa More stuff. Will prioritize this more next week 2018-04-20 14:49:54 -07:00
a8ce219016 Readthedocs doesn't have the necessary c header files to build python-ldap. (#1215) 2018-04-19 13:57:35 -07:00
b9e93065f7 Removing the need for a separate requirements txt (#1214) 2018-04-19 13:26:49 -07:00
78f9ceb995 Merge pull request #1212 from titouanc/docs-influxdb
[add] Reference lemur-influxdb as 3rd party plugin
2018-04-16 15:18:24 -07:00
1904a187e0 Merge branch 'master' into docs-influxdb 2018-04-16 15:12:09 -07:00
0320a9aece Merge pull request #1211 from titouanc/fix-pip10
[fix] Pip imports for pip 10
2018-04-16 15:11:42 -07:00
4e94e51218 [add] Reference lemur-influxdb as 3rd party plugin 2018-04-16 20:15:25 +02:00
4392657a71 [fix] Pip imports for pip 10 2018-04-16 19:41:28 +02:00
fbce1ef7c7 temp digicert fix 2018-04-13 15:50:55 -07:00
309d10c4e2 stuff 2018-04-13 15:50:55 -07:00
f43100a247 py3 2018-04-13 15:50:55 -07:00
4d05a09a20 fix_changes 2018-04-13 15:50:55 -07:00
3538f1a629 fix_errors 2018-04-13 15:50:55 -07:00
993958c356 up-reqs 2018-04-13 15:50:55 -07:00
2d6d2357b5 DNS Providers list returned 2018-04-13 15:50:55 -07:00
a66d85b63d clean up a bit 2018-04-13 15:50:55 -07:00
b0bd0435c4 more stuff 2018-04-13 15:50:54 -07:00
b2e6938815 WIP: Add support for Acme/LetsEncrypt with DNS Provider integration 2018-04-13 15:50:54 -07:00
d66dd543bf actually update deps 2018-04-13 15:50:53 -07:00
de7a5a30d1 unpin flask in requirements.in 2018-04-13 15:50:53 -07:00
40c35dc77b Update more dependencies. Remove hashes 2018-04-13 15:50:53 -07:00
5dd03098e5 actually update deps 2018-04-13 15:50:53 -07:00
672a28bb28 Update auth keys, change python version to satisfy tests 2018-04-13 15:50:53 -07:00
8ea2f5253a unpin flask in requirements.in 2018-04-13 15:50:53 -07:00
1e0146a453 Merge pull request #1208 from castrapel/fixdate
Fix date
2018-04-13 15:39:42 -07:00
c03133622f Correct validities 2018-04-13 15:18:17 -07:00
8303cfbd2b Fix datetime 2018-04-13 14:53:45 -07:00
3ef550f738 Merge branch 'master' into hackday 2018-04-12 12:49:52 -07:00
c8767e23bf Merge pull request #1204 from castrapel/up-reqs
Up reqs
2018-04-12 12:49:00 -07:00
f302408712 py3 2018-04-12 12:34:08 -07:00
c88c0b0127 fix_changes 2018-04-12 08:59:06 -07:00
acb1eab24e fix_errors 2018-04-12 08:58:04 -07:00
6cd2205f1f up-reqs 2018-04-12 08:52:47 -07:00
f6fd262618 DNS Providers list returned 2018-04-11 15:56:00 -07:00
5125990c4c clean up a bit 2018-04-11 07:48:04 -07:00
52cb145333 ecc: add the support for ECC (#1191)
* ecc: add the support for ECC

update generate_private_key to support ECC.  Move key types to constant.  Update UI for the new key types

* ecc: Remove extra line to fix linting

* ecc: Fix flake8 lint problems

* Update options.tpl.html
2018-04-10 16:54:17 -07:00
c6bd93fe85 PostgreSQL is required, not optional due to JSON column usage, so link to quickstart instructions and add create_config statement. (#1198) 2018-04-10 16:54:02 -07:00
6a762d463f Documenting connection pool config settings (#1197) 2018-04-10 16:50:58 -07:00
5beb319b27 more stuff 2018-04-10 16:04:07 -07:00
12622d5847 Adding metrics for request timings. (#1190) 2018-04-10 15:55:02 -07:00
a9baaf4da4 add(plugins): Added a statsd plugin for lemur (#1189) 2018-04-10 15:15:03 -07:00
f61098b874 WIP: Add support for Acme/LetsEncrypt with DNS Provider integration 2018-04-10 14:28:53 -07:00
8ca4f730e8 lemur_digicert: Do not truncate valid_to anymore (#1187)
* lemur_digicert: Do not truncate valid_to anymore

The valid_to field for Digicert supports YYYY-MM-DDTHH:MM:SSZ so we should stop truncating

* lemur_digicert: Update unit tests for valid_to
2018-04-10 13:23:09 -07:00
0b5f85469c Merge pull request #1164 from intgr/fix-boolean-filter
Fix filtering on boolean columns, broken with SQLAlchemy 1.2 upgrade
2018-04-09 09:36:08 -07:00
8e2b2123f1 Fix filtering on boolean columns, broken with SQLAlchemy 1.2 upgrade
SQLAlchemy 1.2 does not allow comparing string values to boolean
columns. This caused errors like:

    sqlalchemy.exc.StatementError: (builtins.TypeError) Not a boolean value: 'true'

For more details see http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#boolean-datatype-now-enforces-strict-true-false-none-values
2018-04-09 18:59:23 +03:00
b4b9a913b3 Merge pull request #1177 from castrapel/up-reqs-4-9-2018
update requirements
2018-04-09 07:57:41 -07:00
2dc6478c34 update requirements 2018-04-09 07:46:08 -07:00
28614b5793 remove linuxdst plugin 2018-04-04 14:49:25 +03:00
4a0103a88d SFTP destination plugin (#1170)
* add sftp destination plugin
2018-04-03 10:30:19 -07:00
fb494bc32a Merge pull request #1158 from castrapel/up_reqs_3292018
Updating requirements and add makefile function to automate req updates
2018-03-29 09:30:51 -07:00
de9c00b293 Merge branch 'master' into up_reqs_3292018 2018-03-29 09:11:23 -07:00
3e5cbb40ce Updating requirements 2018-03-29 09:10:41 -07:00
47793635b2 Merge pull request #1150 from castrapel/issue_1089
Allow quotes for exact match
2018-03-29 09:07:26 -07:00
259800ce35 Merge branch 'master' into issue_1089 2018-03-29 08:48:52 -07:00
a38f286fb9 Merge pull request #1152 from castrapel/issue_1151_remove_get_pending_certificates
Remove get_pending_certificates from verisign issuer
2018-03-29 08:48:37 -07:00
b6ffbfa40e Merge branch 'master' into issue_1151_remove_get_pending_certificates 2018-03-28 10:04:37 -07:00
b814a4f009 Remove get_pending_certificates from verisign issuer 2018-03-28 08:56:28 -07:00
4ed6b7727a Merge branch 'master' into issue_1089 2018-03-28 08:35:32 -07:00
c3a2781507 Allow quotes for exact match 2018-03-28 08:33:43 -07:00
ffba1d2b85 Merge pull request #1149 from castrapel/up-deps
dep upgrades
2018-03-28 07:59:48 -07:00
248409e43f dep upgrades 2018-03-28 07:43:25 -07:00
a316cbba73 [add] Docs and default config for metric plugins (#1148) 2018-03-27 15:51:32 -07:00
12135c445b Merge pull request #1138 from castrapel/useractive
check if user active properly
2018-03-26 13:31:13 -07:00
844202f36b check if user active properly 2018-03-26 13:14:22 -07:00
9b4a124c08 Merge pull request #1137 from castrapel/req_up
Upodate reqs
2018-03-26 10:41:53 -07:00
ab1b31604c Upodate reqs 2018-03-26 10:08:23 -07:00
a8b18480aa Merge pull request #1124 from Netflix/doppins/alembic-equals-0.9.9
[Doppins] Upgrade dependency alembic to ==0.9.9
2018-03-26 09:42:10 -07:00
b5e4df5c16 Merge branch 'master' into doppins/alembic-equals-0.9.9 2018-03-26 09:08:45 -07:00
2dbcc7a297 Merge pull request #1128 from Netflix/doppins/moto-equals-1.3.1
[Doppins] Upgrade dependency moto to ==1.3.1
2018-03-26 09:08:30 -07:00
1730b3bacc Merge branch 'master' into doppins/alembic-equals-0.9.9 2018-03-26 09:00:20 -07:00
d730ffbc72 Merge branch 'master' into doppins/moto-equals-1.3.1 2018-03-26 08:59:46 -07:00
d36fececd6 Merge pull request #1135 from Netflix/doppins/python-dateutil-equals-2.7.2
[Doppins] Upgrade dependency python-dateutil to ==2.7.2
2018-03-26 08:59:12 -07:00
0caafea777 Merge branch 'master' into doppins/python-dateutil-equals-2.7.2 2018-03-26 08:33:44 -07:00
c847339b0e Merge pull request #1129 from Netflix/doppins/pytest-equals-3.5.0
[Doppins] Upgrade dependency pytest to ==3.5.0
2018-03-26 08:28:35 -07:00
58bb08b604 Upgrade dependency python-dateutil to ==2.7.2 2018-03-26 15:10:21 +00:00
98d303c6c0 Merge branch 'master' into doppins/pytest-equals-3.5.0 2018-03-26 08:07:15 -07:00
9514edafba Merge pull request #1133 from Netflix/doppins/python-dateutil-equals-2.7.1
[Doppins] Upgrade dependency python-dateutil to ==2.7.1
2018-03-26 08:06:18 -07:00
adb9149413 Upgrade dependency python-dateutil to ==2.7.1 2018-03-24 19:23:33 +00:00
c51fed5307 allowing null basic contraints (#1131) 2018-03-23 11:38:47 -07:00
db746f1296 Adds support for CDLDistributionPoints. (#1130) 2018-03-23 08:51:18 -07:00
62046aed59 Upgrade dependency pytest to ==3.5.0 2018-03-22 23:49:52 +00:00
5e0e8804c0 Upgrade dependency moto to ==1.3.1 2018-03-22 18:29:22 +00:00
416791d4c5 Merge pull request #1127 from castrapel/fixrequests2
Pin requests and pyopenssl
2018-03-22 09:33:52 -07:00
5ee11ed4e0 Merge branch 'master' into fixrequests2 2018-03-22 09:28:10 -07:00
3b2ef95798 pin requests and pyopenssl 2018-03-22 09:23:23 -07:00
827e4c65a7 pin requests and pyopenssl 2018-03-22 09:06:23 -07:00
fef89feb62 Upgrade dependency alembic to ==0.9.9 2018-03-22 14:44:00 +00:00
42f92306a5 Update more dependencies. Remove hashes 2018-03-21 15:03:28 -07:00
44b8fd6ef5 actually update deps 2018-03-21 15:03:28 -07:00
5a86ebe318 Update auth keys, change python version to satisfy tests 2018-03-21 15:03:28 -07:00
1e3df62993 [fix] No internal server error when trying to Google Auth an unregistered user (#1109) 2018-03-21 15:03:28 -07:00
662eaf4933 Remove non-ASCII character (#1104) 2018-03-21 15:03:28 -07:00
3fd82e51bd unpin flask in requirements.in 2018-03-21 15:03:28 -07:00
154e38b42e Merge pull request #1115 from castrapel/up-some-deps2
Update more dependencies. Remove hashes
2018-03-21 14:54:57 -07:00
915cdeb426 Merge branch 'master' into up-some-deps2 2018-03-21 14:49:39 -07:00
e15836e9ca Update more dependencies. Remove hashes 2018-03-21 14:48:51 -07:00
8e1eae9a45 Merge pull request #1114 from castrapel/up-some-deps
Unpin some dependencies in requirement in files, update requirements.txt
2018-03-21 13:39:39 -07:00
d67542d7f5 actually update deps 2018-03-21 12:46:30 -07:00
a202d082e8 Merge branch 'master' into up-some-deps 2018-03-21 12:43:33 -07:00
4087f1c03b Update auth keys, change python version to satisfy tests 2018-03-21 11:57:19 -07:00
bbacb7e210 [fix] No internal server error when trying to Google Auth an unregistered user (#1109) 2018-03-21 11:57:19 -07:00
19cf8f6bdd Remove non-ASCII character (#1104) 2018-03-21 11:57:19 -07:00
f05d1750ee unpin flask in requirements.in 2018-03-21 11:57:19 -07:00
fa696b56c2 Merge pull request #1103 from castrapel/flask-up
unpin flask in requirements.in
2018-03-21 10:41:03 -07:00
3f52cd9c2b Merge branch 'master' into flask-up 2018-03-21 10:31:07 -07:00
d44a1934fe Update auth keys, change python version to satisfy tests 2018-03-21 10:29:08 -07:00
08f66df860 [fix] No internal server error when trying to Google Auth an unregistered user (#1109) 2018-03-21 08:14:54 -07:00
48d9a3ec8a Remove non-ASCII character (#1104) 2018-03-20 16:54:30 -07:00
de0b4ddc99 unpin flask in requirements.in 2018-03-20 15:43:25 -07:00
6e1bb0c49c Merge pull request #1088 from castrapel/requirements3
Requirements for dev, docs, and tests
2018-03-19 11:41:38 -07:00
d4597b6bb6 typo fix 2018-03-19 11:03:47 -07:00
52f5930744 requirements for dev, docs, and tests 2018-03-19 11:02:46 -07:00
9504ad3b80 Merge pull request #1087 from castrapel/requirements2
requirements.txt
2018-03-19 10:46:37 -07:00
d2c7f8a963 Addition of requirements.txt and requirements.in with pinned versions for all dependencies, sub-dependencies, along with hashes 2018-03-19 09:39:25 -07:00
ff05deaa1f Merge pull request #1084 from Netflix/doppins/pyldap-equals-3.0.0
[Doppins] Upgrade dependency pyldap to ==3.0.0
2018-03-19 07:33:16 -07:00
b233f567ce Merge branch 'master' into doppins/pyldap-equals-3.0.0 2018-03-19 07:21:57 -07:00
b30d2c9536 Merge pull request #1083 from Netflix/doppins/paramiko-equals-2.4.1
[Doppins] Upgrade dependency paramiko to ==2.4.1
2018-03-19 07:21:09 -07:00
5dd37ea696 Merge branch 'master' into doppins/pyldap-equals-3.0.0 2018-03-19 07:10:01 -07:00
49393070e0 Merge branch 'master' into doppins/paramiko-equals-2.4.1 2018-03-19 07:09:56 -07:00
fdb6dd4077 Merge pull request #1086 from Netflix/revert_req_logging
Revert req logging
2018-03-16 14:26:15 -07:00
74a516cde0 nt 2018-03-16 14:15:03 -07:00
58da68d72f Revert "Requirements and Elasticsearch logging configuration"
This reverts commit c08d3dd82f.
2018-03-16 14:10:12 -07:00
918250ce78 Merge pull request #1085 from castrapel/requirements_logging
Requirements and logging
2018-03-16 12:14:10 -07:00
c7ca3949f6 info level, and new variable name 2018-03-16 11:55:53 -07:00
bbf5e95186 fix unusued import 2018-03-16 10:07:47 -07:00
462e757f92 Merge branch 'master' into requirements_logging 2018-03-16 08:51:25 -07:00
58798f1513 undo gitignore change 2018-03-16 08:41:12 -07:00
087490e26a add newlines 2018-03-16 08:38:59 -07:00
c08d3dd82f Requirements and Elasticsearch logging configuration 2018-03-16 08:36:10 -07:00
430cb5ea1b Upgrade dependency pyldap to ==3.0.0 2018-03-14 12:32:14 +00:00
9b1c279fd5 Upgrade dependency paramiko to ==2.4.1 2018-03-13 01:40:29 +00:00
17be8b626d EntryPoints digicert_cis_source missing comma (#1082) 2018-03-10 09:44:51 -08:00
412757b178 Merge pull request #1071 from Netflix/notif-fix
Fix cloned notifications
2018-02-27 13:14:58 -08:00
18c64fafe4 address comment 2018-02-27 12:34:18 -08:00
77a1600c13 Fix cloned notifications 2018-02-27 10:57:43 -08:00
59ce586ea4 Merge pull request #1069 from Netflix/pending_fix
comments on alembic changes. resolve invalid usage of log_service.create
2018-02-26 12:44:21 -08:00
5fe28f6503 Description modification 2018-02-26 12:37:31 -08:00
1f641c0ba6 Description modification 2018-02-26 12:36:40 -08:00
cca3797669 comments on alembic changes. resolve invalid usage of log_service.create 2018-02-26 12:08:31 -08:00
c9cb5800ec Merge pull request #1068 from Netflix/pending_fix
fix pending cert db changes
2018-02-26 09:52:07 -08:00
a28fdac242 fix pending cert db changes 2018-02-26 09:43:08 -08:00
0724fcffeb Merge pull request #1067 from Netflix/unq-const
unq constraint
2018-02-26 08:34:46 -08:00
7032abf2e7 Merge branch 'master' into unq-const 2018-02-26 08:03:31 -08:00
9e8fa5827d unq constraint 2018-02-24 23:15:39 -08:00
5d18838868 Use Cloudflare as DNS provider for LE certs (#945)
* Use Cloudflare as DNS provider for LE certs

* Better handle dns_provider plugins
2018-02-22 08:17:28 -08:00
2578970f7d Async Certificate Issuing using Pending Certificates (#1037)
* Add PendingCertificate model

This change creates a DB table called pending_certificates and
associated mapping relationship tables from pending certificate to
roles, rotation policy, destination, sources, etc.

The table is generated on initialization of Lemur. A pending
certificate holds most of the information of a Certificate, while it has
not be issued so that it can later backfill the information when the CA
has issued the certificate.

Change-Id: I277c16b776a71fe5edaf0fa0e76bbedc88924db0
Tickets: PBL-36499

* Create a PendingCertificate if cert is empty

IssuePlugins should return empty cert bodies if the request failed to
complete immediately (such as Digicert).  This way, we can immediately
return the certificate, or if not just place into PendingCertificates
for later processing.

+ Fix relation from Certificate to Pending Certificate, as view only.
There is no real need for anything more than that since Pending cert
only needs to know the cert to replace when it is issued later.

+ Made PendingCertificate private key be empty: UI does not allow
private key on 'Create' but only on 'Import'.  For Instart, we require
the private key but upstream does not necessarily need it.  Thus, if
someone at Instart wants to create a CSR / key combo, they should
manually issue the cert themselves and import later.  Otherwise you
should let Lemur generate that.  This keeps the workflow transparent for
upstream Lemur users.

Change-Id: Ib74722a5ed5792d4b10ca702659422739c95ae26
Tickets: PBL-36343

* Fix empty private_key when create Pending Cert

On creation of a certificate with a CSR, there is no option for private
key.  In this case, we actually have a dictionary with private_key as
key, but the value is None.  This fixes the strip() called on NoneType.

Change-Id: I7b265564d8095bfc83d9d4cd14ae13fea3c03199
Tickets: PBL-36499

* Source sync finds and uses pending certificate

When a source syncs certificates, it will check for a pending
certificate.  If that is found via external_id (given by digicert as
order_id) then it will use the found Pending Certificate's fields to
create a new certificate.  Then the pending certificate is deleted.

Tickets: PBL-36343
Change-Id: I4f7959da29275ebc47a3996741f7e98d3e2d29d9

* Add Lemur static files and views for pending certs

This adds the basic static files to view pending certificates in a
table.

Tickets: PBL-36343
Change-Id: Ia4362e6664ec730d05d280c5ef5c815a6feda0d9

* Add CLI and plugin based pending fetch

This change uses the adds a new function to issuer plugins to fetch
certificates like source, but for one order.  This way, we can control
which pending certificates to try and populate instead of getting all
certificates from source.

Tickets: PBL-36343
Change-Id: Ifc1747ccdc2cba09a81f298b31ddddebfee1b1d6

* Revert source using Pending Certificate

Tickets: PBL-36343
Change-Id: I05121bc951e0530d804070afdb9c9e09baa0bc51

* Fix PendingCertificate init getting authority id

Should get authority id from authority.id instead of the authority_id
key in kwargs.

Change-Id: Ie56df1a5fb0ab2729e91050f3ad1a831853e0623
Tickets: n/a

* Add fixtures and basic test for PendingCertificate

Change-Id: I4cca34105544d40dac1cc50a87bba93d8af9ab34
Tickets: PBL-36343

* Add User to create_certificate parameters

create_certificate now takes a User, which will be used to populate the
'creator' field in certificates.service.upload().  This allows the UI
populate with the current user if the owner does not exist in Lemur.

+ Fix chain being replaced with version from pending certificate, which
may be empty (depends on plugin implementation).

Change-Id: I516027b36bc643c4978b9c4890060569e03f3049
Tickets: n/a

* Fix permalink and filters to pending certs

Fixes the permalink button to get a single pending certificate
Add argument filter parsing for the pending certificate API
Fix comment on API usage
Added get_by_name for pending_certificate (currently unused, but useful
for CLI, instead of using IDs)

Change-Id: Iaa48909c45606bec65dfb193c13d6bd0e816f6db
Tickets: PBL-36910

* Update displayed fields for Pending Certificates

There are a number of unused / unpopulated fields from Certificate UI
that does apply to Pending Certificates.  Those ones were removed, and
added other useful fields:
Owner, number of attempts to fetch and date created

Change-Id: I3010a715f0357ba149cf539a19fdb5974c5ce08b
Tickets: PBL-36910

* Add common name (cn) to Pending Certificate model

Fixes the UI missing the CN for Pending Certificate, as it was
originally being parsed from the generated certificate.  In the case of
pending certificate, the CN from the user generates the request, which
means a pending cert can trust the original user putting in the CN
instead of having to parse the not-yet-generated certificate.  There is
no real possibility to return a certificate from a pending certificate
where the CN has changed since it was initially ordered.

Change-Id: I88a4fa28116d5d8d293e58970d9777ce73fbb2ab
Tickets: PBL-36910

* Fix missing imports for service filter

+ Removed duplicate get_by_name function from old merge

Change-Id: I04ae6852533aa42988433338de74390e2868d69b
Tickets: PBL-36910

* Add private key viewing to Pending Certificates

Add private key API for Pending Certificates, with the same
authorization as Certificates (only owner, creator or owner-roles can
view private key).

Change-Id: Ie5175154a10fe0007cc0e9f35b80c0a01ed48d5b
Tickets: PBL-36910

* Add edit capability to pending certificates

Like editing certificates, we should be able to modify some parts of a
pending certificate so the resulting certificate has the right
references, owner, etc.

+ Added API to update pending certificate
+ Fix UI to use pending certificate scope instead of reusing Certificate
+ Change pending_certificate.replaces to non-passive association, so
that updates do affect it (similar to roles/notifications/etc)

Tickets: PBL-36910
Change-Id: Ibbcb166a33f0337e1b14f426472261222f790ce6

* Add common_name parsing instead using kwargs

To fix tests where common name may not be passed in, use the CSR
generated to find the official common name.

Change-Id: I09f9258fa92c2762d095798676ce210c5d7a3da4
Tickets: PBL-36343

* Add Cancel to pending certificates and plugins

This allows pending certificates to be cancelled, which will be handled
by the issuer plugin.

Change-Id: Ibd6b5627c3977e33aca7860690cfb7f677236ca9
Tickets: PBL-36910

* Add API for Cancelling Pending Certificate

Added the DELETE handler for pending_certificates, which will cancel and
delete the pending certificate from the pending certs table on
successful cancellation via Issuer Plugin.

+ Add UT for testing cancel API

Change-Id: I11b1d87872e4284f6e4f9c366a15da4ddba38bc4
Tickets: PBL-36910

* Remove Export from Pending Certificates

Pending Certificates doesn't need an export since it should just be
fetched by Lemur via plugins, and the CSR is viewable via the UI.

Change-Id: I9a3e65ea11ac5a85316f6428e7f526c3c09178ae
Tickets: PBL-36910

* Add cancel button functionality to UI

This adds the Cancel option to the dropdown of pending certificates.

+ Adds modal window for Note (may not be required for all issuers, just
Digicert)
+ Add schema for cancel input
+ Fix Digitcert plugin for non-existant orders

When an order is actually issued, then attempting to cancel will return
a 403 from Digicert.  This is a case where it should only be done once
we know the pending cert has been sitting for too long.

Change-Id: I256c81ecd142dd51dcf8e38802d2c202829887b0
Tickets: PBL-36910

* Fix test_pending_cancel UT

This change creates and injects a pending cert, which will then be used
for the ID so it can be canceled by the unit test.

Change-Id: I686e7e0fafd68cdaeb26438fb8504d79de77c346
Tickets: PBL-36343

* Fix test_digicert on non-existent order

cancelling a non-existent order is fine since we're cancelling it

Change-Id: I70c0e82ba2f4b8723a7f65b113c19e6eeff7e68c
Tickets: PBL-36343

* Add migrations for PendingCertificates

Added revision for Pending Certificates table and foreign key mapping
tables.

Change-Id: Ife8202cef1e6b99db377851264639ba540b749db
Tickets: n/a

* Fix relationship copy from Pending to Certificate

When a Pending Certificate is changed to a full Certificate, the
relationship fields are not copied via vars() function, as it's not a
column but mapped via association table.  This adds an explicit copy for
these relations.  Which will properly copy them to the new Certificate,
and thus also update destinations.

Change-Id: I322032ce4a9e3e67773f7cf39ee4971054c92685
Tickets: PBL-36343

* Fix renaming of certificates and unit tests

The rename flag was not used to rename certificates on creation as
expected.

Fixed unit test, instead of expunging the session, just copy the
pending_certificate so we don't have a weird reference to the object
that can't be copied via vars() function.

Change-Id: I962943272ed92386ab6eab2af4ed6d074d4cffa0
Tickets: PBL-36343

* Updated developer docs for async certs

Added blurb for implementing new issuer functions.

Change-Id: I1caed6e914bcd73214eae2d241e4784e1b8a0c4c
Tickets: n/a
2018-02-22 08:13:16 -08:00
f44fe81573 fix for https://github.com/Netflix/lemur/issues/1045 (#1056) 2018-02-20 08:28:11 -08:00
aa5d97f49b Update setup.py (#1055) 2018-01-26 10:38:30 -08:00
f262c93912 Option to suppress SSL errors (#1044) 2018-01-17 09:17:03 -08:00
763c5e8356 Add DIGICERT_ORDER_TYPE to Digicert plugin (#1025)
* Add DIGICERT_ORDER_TYPE to Digicert plugin

This allows lemur.conf.py to control which kind of certificate to
order.  User defined options are not currently supported in the the UI,
so we cannot create multiple Digicert authorities at runtime for
separate certificate types.

Change-Id: I06c216ec3c476e0001b240530626a86464be999e

* Fix Mock URL for Digicert test

Change-Id: Ida7c0ed1bd120c9024bea091c03b7d1ecfa66498

* Add documentation for DIGICERT_ORDER_TYPE

Change-Id: I0bc347883b628416eb7f13a7c60c937dcb6ae0c2
2018-01-13 18:06:17 -08:00
050295ea20 Fix DigiCert issuer plugin revoke URL (#1041)
The URL for revoking DigiCert certificates was incorrect.

Change-Id: I39fb7d290a2a649ab08a47e7dcbe18a8c0bd8a59
2018-01-11 17:12:21 -08:00
77044f56fc Upgrade dependency pytest to ==3.3.2 (#1038) 2018-01-04 17:06:24 -08:00
eea413a90f Modifying the way we report metrics. Relying on metric tags instead of the the metric name for additional dimensions. (#1036) 2018-01-02 15:26:31 -08:00
8cad2f9f56 Version bump. (#1034) 2018-01-02 14:08:56 -08:00
64ac32f683 6.0 release. (#1033) 2018-01-02 14:03:38 -08:00
1287c3dc4a CRL verify: handle "Remove from CRL" status as not revoked (#1028)
Per RFC 5280 section 6.3.3 (k):
https://tools.ietf.org/html/rfc5280#section-6.3.3
2018-01-02 13:39:02 -08:00
9d7fc9db8c [Doppins] Upgrade dependency boto3 to ==1.5.7 (#1024) 2018-01-02 13:11:44 -08:00
99b10c436a CRL verify: skip unknown URI schemes like ldap:// and add unit tests (#1027) 2018-01-02 13:11:17 -08:00
bb54085c20 Downgrading library again. (#1032) 2018-01-02 12:36:45 -08:00
9a0ada75fa Upgrading satellizer library. (#1031) 2018-01-02 09:12:06 -08:00
848ce8c978 Refactoring authentincation to support GET and POST requests. Closes #990. (#1030) 2018-01-01 19:11:29 -08:00
7b8df16c9e Fix typo in default SSH key path. (#1026) 2017-12-20 09:09:56 -08:00
7a84f38db9 Don't write files from the test suite (#1020)
The lemur_email.tests.test_render test would fail when running unittests
from a read-only source tree.
2017-12-12 10:14:39 -08:00
ba4de07ad8 Improve certificate details view, make information more concise (#1021)
The "Description" field can now display multi-line text content.

The "Authority" field now displays the authority name in Lemur (if
known) as well as issuer's name. For imported certs, "Imported" is
displayed.
2017-12-12 09:49:30 -08:00
b2d87940d6 Allow sorting and filtering by camelCase field names (#1019)
The API exposes camelCase field names everywhere, but only accepted
underscore_field_names in 'filter' or 'sort' GET attributes. Now both
are allowed.
2017-12-12 09:44:53 -08:00
6edc5180c7 fix roles assigned in the ui for sso (#1017)
This commit fixes the ability to assign roles to people in the ui
when the user is SSO. The idea is if a role is ever assigned via
SSO it becomes a "SSO Role" or a "Third Party" Role. by setting
third_party to true on the role object.

Once a role is marked as third party it can no longer be controlled
through the ui for SSO Users. (for ui users this poses no functional
change). It must be controlled via SSO.
2017-12-11 13:51:45 -08:00
f0c895a008 Upgrade dependency acme to ==0.20.0 (#1016) 2017-12-07 09:12:21 -08:00
6d6716b8a2 Upgrade dependency requests-mock to ==1.4.0 (#1013) 2017-12-07 09:12:08 -08:00
d64a010c39 Upgrade dependency pytest to ==3.3.1 (#1014) 2017-12-07 09:11:51 -08:00
e1f241bd55 Don't send notifications that are marked inactive (#1015)
Apparently previously Lemur ignored the "active" flag of notifications.
2017-12-06 08:32:24 -08:00
ad88637f22 Adding some niceties around the way users are associated with tokens. (#1012)
* Adding some niceties around the way users are associated with tokens.

- Includes user typeahead
- Tooltips
- User information displayed in table
- Default to current user when no user is passed
2017-12-05 10:57:17 -08:00
a756a74b49 Ensures we can get multiple endpoints with the same name but different ports. (#1011) 2017-12-04 13:13:02 -08:00
c311c0a221 Pinning JS versions (#1010) 2017-12-04 11:29:02 -08:00
ecc0934657 Adding cli command to clear out pending symantec certificates. (#1009) 2017-12-04 10:04:12 -08:00
c402f1ff87 add per user api keys to the backend (#995)
Adds in per user api keys to the backend of lemur.
the basics are:
  - API Keys are really just JWTs with custom second length TTLs.
  - API Keys are provided in the exact same ways JWTs are now.
  - API Keys can be revoked/unrevoked at any time by their creator
    as well as have their TTL Change at anytime.
  - Users can create/view/list their own API Keys at will, and
    an admin role has permission to modify all api keys in the
    instance.

Adds in support for lemur api keys to the frontend of lemur.
doing this required a few changes to the backend as well, but it is
now all working (maybe not the best way though, review will determine
that).

  - fixes inconsistency in moduleauthor name I inputted during the
    first commit.
  - Allows the revoke schema to optionally allow a full api_key object.
  - Adds `/users/:user_id/api_keys/:api_key` and `/users/:user_id/api_keys`
    endpoints.
  - normalizes use of `userId` vs `userId`
  - makes `put` call respond with a JWT so the frontend can show
    the token on updating.
  - adds in the API Key views for clicking "API Keys" on the main nav.
  - adds in the API Key views for clicking into a users edit page.
  - adds tests for the API Key backend views I added.
2017-12-04 08:50:31 -08:00
eb810f1bf0 Upgrade dependency jinja2 to ==2.10 (#992) 2017-12-03 10:08:13 -08:00
c067573193 Upgrade dependency paramiko to ==2.4.0 (#994) 2017-12-03 10:07:30 -08:00
553c119356 Upgrade dependency SQLAlchemy-Utils to ==0.32.21 (#991) 2017-12-03 10:06:32 -08:00
e62cb1b6b8 Upgrade dependency boto3 to ==1.4.8 (#1002) 2017-12-03 10:06:10 -08:00
4da243a59e Upgrade dependency moto to ==1.1.25 (#997) 2017-12-03 10:05:44 -08:00
622192e75e [Doppins] Upgrade dependency pytest to ==3.3.0 (#993)
* Upgrade dependency pytest to ==3.3.0
2017-12-03 10:05:31 -08:00
81a6ec644a Upgrade dependency tabulate to ==0.8.2 (#1006) 2017-12-03 10:05:05 -08:00
58100cda8b Upgrade dependency arrow to ==0.12.0 (#1005) 2017-12-03 10:04:51 -08:00
734ab5f3cd Upgrade dependency pyldap to ==2.4.45 (#998) 2017-12-03 10:02:28 -08:00
d855f752c8 Upgrade dependency marshmallow to ==2.15.0 (#1008) 2017-12-03 09:33:18 -08:00
5ac3ecb85e Added revoke support to cfssl plugin (#1007)
* Added revoke support to cfssl plugin
2017-11-29 14:33:22 -08:00
dfb9e3a0c8 Add nodejs-legacy to provide the 'node' command (#1004)
Affecting Ubuntu 16.04.3 LTS:

Following the directions of http://lemur.readthedocs.io/en/latest/quickstart/index.html, the make release command fails as the command 'node' cannot be found.

Adding nodejs-legacy solves the issue and allows the build to complete.

(lemur) lemur@lemur1:/www/lemur$ make release
--> Installing dependencies
npm install
npm WARN deprecated gulp-minify-css@1.2.4: Please use gulp-clean-css
npm WARN deprecated bower@1.8.2: ...psst! Your project can stop working at any moment because its dependencies can change. Prevent this by migrating to Yarn                                                                                 : https://bower.io/blog/2017/how-to-migrate-away-from-bower/
npm WARN deprecated gulp-foreach@0.1.0: Either use gulp-tap or gulp-flatmap, depending on your needs
npm WARN deprecated express@2.5.11: express 2.x series is deprecated
npm WARN deprecated connect@1.9.2: connect 1.x series is deprecated
npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated minimatch@0.2.14: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated graceful-fs@1.2.3: graceful-fs v3.0.0 and before will fail on node releases >= v7.0. Please update to graceful-fs@^4.0.0 as soon as poss                                                                                 ible. Use 'npm ls graceful-fs' to find it in the tree.
npm WARN deprecated node-uuid@1.4.8: Use uuid module instead
npm WARN deprecated minimatch@0.3.0: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN prefer global marked@0.3.6 should be installed with -g

> optipng-bin@3.1.4 postinstall /www/lemur/node_modules/optipng-bin
> node lib/install.js

sh: 1: node: not found
npm WARN install:optipng-bin@3.1.4 optipng-bin@3.1.4 postinstall: `node lib/install.js`
npm WARN install:optipng-bin@3.1.4 spawn ENOENT

> jpegtran-bin@3.2.0 postinstall /www/lemur/node_modules/jpegtran-bin
> node lib/install.js

sh: 1: node: not found
npm WARN install:jpegtran-bin@3.2.0 jpegtran-bin@3.2.0 postinstall: `node lib/install.js`
npm WARN install:jpegtran-bin@3.2.0 spawn ENOENT

> gifsicle@3.0.4 postinstall /www/lemur/node_modules/gifsicle
> node lib/install.js

sh: 1: node: not found
npm WARN install:gifsicle@3.0.4 gifsicle@3.0.4 postinstall: `node lib/install.js`
npm WARN install:gifsicle@3.0.4 spawn ENOENT

> Lemur@ postinstall /www/lemur
> bower install --allow-root --config.interactive=false

/usr/bin/env: ‘node’: No such file or directory


Makefile:24: recipe for target 'release' failed
make: *** [release] Error 1
(lemur) lemur@lemur1:/www/lemur$ which node
(lemur) lemur@lemur1:/www/lemur$

Installing the package to solve the issue.

vsnine@lemur1:~$ sudo apt-get install nodejs-legacy
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  nodejs-legacy
0 upgraded, 1 newly installed, 0 to remove and 79 not upgraded.
Need to get 27.7 kB of archives.
After this operation, 81.9 kB of additional disk space will be used.
Get:1 http://ca.archive.ubuntu.com/ubuntu xenial-updates/universe amd64 nodejs-legacy all 4.2.6~dfsg-1ubuntu4.1 [27.7 kB]
Fetched 27.7 kB in 0s (52.4 kB/s)
Selecting previously unselected package nodejs-legacy.
(Reading database ... 73230 files and directories currently installed.)
Preparing to unpack .../nodejs-legacy_4.2.6~dfsg-1ubuntu4.1_all.deb ...
Unpacking nodejs-legacy (4.2.6~dfsg-1ubuntu4.1) ...
Processing triggers for man-db (2.7.5-1) ...
Setting up nodejs-legacy (4.2.6~dfsg-1ubuntu4.1) ...
vsnine@lemur1:~$ which node
/usr/bin/node
vsnine@lemur1:~$
2017-11-27 09:37:14 -08:00
c2b2ce1f11 Allowing the export of CAs that don't have a chain. (#1000) 2017-11-21 11:42:23 -08:00
cecfe47540 Adding the ability to revoke enmasse (#999) 2017-11-21 09:36:10 -08:00
4b544ae207 CSR Export Plugin (#988)
This plugin allows a certificate to be exported as a CSR via OpenSSL
x509.  The workflow will be:
* Create self-signed cert via Cryptography authority
* Export CSR via this plugin
* Sign your own cert outside of Lemur
* Import new cert with private key

Change-Id: Id3f7db2506bd959236cd3a6df622841058abda5a
2017-11-14 10:11:06 -08:00
e30e17038b Removing unused import. (#989) 2017-11-14 09:24:26 -08:00
7e2c16ee38 Fixes for using ACME with Route53 (#986)
* Changes required for functional Route53 operations

* Changes required for functional ACME operations with Route53

* Changes required for functional ACME operations with Route53, need external ID
2017-11-13 10:19:54 -08:00
041f3a22fa Added ability to set custom roles for users logging in via oauth provider (#985) 2017-11-10 08:38:33 -08:00
f990ef27cf Adding sentry tracking to issued with certificate deployment. (#978) 2017-10-26 15:21:13 -07:00
bef762e0d6 Upgrade dependency acme to ==0.19.0 (#957) 2017-10-25 13:44:01 -07:00
0d001b358e [Doppins] Upgrade dependency tabulate to ==0.8.1 (#953)
* Upgrade dependency tabulate to ==0.8.0

* Upgrade dependency tabulate to ==0.8.1
2017-10-25 11:19:25 -07:00
c1cd5c71e0 [Doppins] Upgrade dependency moto to ==1.1.19 (#946)
* Upgrade dependency moto to ==1.1.18

* Upgrade dependency moto to ==1.1.19
2017-10-25 11:06:36 -07:00
d4209510c2 Adding some additional exception capturing during certificate parsing. (#976) 2017-10-25 08:19:07 -07:00
620e279453 Caa (#975)
* Adding verisign error code for a CAA failure.

* Tweaking error msg.
2017-10-24 14:46:33 -07:00
bbf73c48a3 Adding health exception tracking. (#977) 2017-10-24 14:04:51 -07:00
9319dda0ec Added ability to ignore cert for oauth2 provider (#971)
* Added ability to ignore cert for oauth2 provider

This is useful for development environments where the OAuth provider
doesn't have a valid cert!

* Setting default for OAUTH2_VERIFY_CERT to true
2017-10-20 16:36:14 -07:00
14f5340802 During higher loads, retrying the connection attempt is often required for the CIS api. (#972) 2017-10-12 10:37:58 -07:00
0152985e64 Adding serial numbers when certificates with the same name are encoun… (#970)
* Adding serial numbers when certificates with the same name are encountered.
2017-10-11 13:20:19 -07:00
e43268f585 Source plugin (#965)
* Ensure that None values aren't passed.
2017-10-09 10:37:44 -07:00
7ef788752e Source plugin (#964)
* Another minor fix.
2017-10-06 17:39:31 -07:00
b66d7ce1fd Source plugin (#963)
* Ensuring that we have default options for source plugins.

* Handle duplicate serials. Serials are not unique across issuers.

* Minor fix.
2017-10-06 13:22:03 -07:00
dc34652efd Source plugin (#962)
* Ensuring that we have default options for source plugins.

* Handle duplicate serials. Serials are not unique across issuers.
2017-10-06 08:49:05 -07:00
e0d2fb0de1 Ensuring that we have default options for source plugins. (#961) 2017-10-05 17:27:45 -07:00
e0d9443141 Ensuring existing users are also given the default role. (#960) 2017-10-05 16:47:52 -07:00
a6305a5cae Adding Digicert CIS Sourceplugin (#959)
* Adding necessary features to complete backfill

* Fixing pagination logic.
2017-10-04 16:56:01 -07:00
9e2578be1e Adding necessary features to complete backfill (#958) 2017-10-04 14:57:57 -07:00
09b8f532a7 Adding cli to mass revoke certificates. (#955) 2017-10-03 10:51:53 -07:00
e0939a2856 Adding some default data to put. (#950) 2017-09-29 14:49:07 -07:00
90f4b458e3 Adding the lemur identity to be able to re-issue certificates. (#949) 2017-09-29 14:07:40 -07:00
f5213deb67 Removing revocation comments for now. (#947) 2017-09-29 10:53:15 -07:00
bb08b1e637 Initial work allowing certificates to be revoked. (#941)
* Initial work allowing for certificates to be revoked.
2017-09-28 18:27:56 -07:00
ea6f5c920b Update index.rst (#942)
Fixed typo for libsasl3-dev (was libsas13-dev).
2017-09-27 09:44:03 -07:00
54ff4cddbf Disallow issuing certificates from inactive authority (#936) 2017-09-25 15:34:49 -07:00
645641f4bd Avoid redundant key_view log entries (#937)
Don't re-request private key when it's already loaded in frontend.
2017-09-25 15:34:07 -07:00
97d83890e0 Various minor cleanups and fixes (#938)
* Documentation fixes

* Various docstring and help string fixes

* Minor code cleanups

* Removed redundant .gitignore entry, ignored package-lock.json.
* 'return' statement in certificates.service.render was redundant
* Split up too long line
* Non-matching tags in templates
2017-09-25 15:33:42 -07:00
ec5dec4a16 Add option to disable owner email address in CSR subject (#939) 2017-09-25 15:32:08 -07:00
4cfb621423 Upgrade dependency moto to ==1.1.14 (#940) 2017-09-25 15:31:39 -07:00
c381331c10 Upgrade dependency pyjwt to ==1.5.3 (#901) 2017-09-25 09:19:54 -07:00
a7923f2a06 Upgrade dependency six to ==1.11.0 (#926) 2017-09-25 09:19:40 -07:00
e5f7172c97 [Doppins] Upgrade dependency paramiko to ==2.3.1 (#927)
* Upgrade dependency paramiko to ==2.3.0

* Upgrade dependency paramiko to ==2.3.1
2017-09-25 09:19:24 -07:00
43fff0450b Upgrade dependency acme to ==0.18.2 (#928) 2017-09-25 09:19:08 -07:00
107fd3fce1 [Doppins] Upgrade dependency raven to ==6.2.1 (#933)
* Upgrade dependency raven to ==6.2.0

* Upgrade dependency raven to ==6.2.1
2017-09-25 09:18:57 -07:00
1a9b6dec26 [Doppins] Upgrade dependency moto to ==1.1.13 (#931)
* Upgrade dependency moto to ==1.1.12

* Upgrade dependency moto to ==1.1.13
2017-09-25 09:18:40 -07:00
444be5bb7f Updated Quikstart (#934)
Got some failures doing a clean install on Ubuntu 17.04 Zesty Zapus (Final) from virtualboxes.org
2017-09-22 12:35:25 -07:00
5ebfa018ee [Doppins] Upgrade dependency moto to ==1.1.11 (#922)
* Upgrade dependency moto to ==1.1.7

* Upgrade dependency moto to ==1.1.8

* Upgrade dependency moto to ==1.1.9

* Upgrade dependency moto to ==1.1.10

* Upgrade dependency moto to ==1.1.11
2017-09-21 10:31:45 -07:00
a6dab5e1ee a bit more ldap documentaion (#930) 2017-09-21 06:00:26 -07:00
f766871824 Create default rotation policy with name (#924) 2017-09-18 09:09:59 -07:00
ba29bbe3be Upgrade dependency pyOpenSSL to ==17.2.0 (#918) 2017-09-13 20:54:54 -07:00
d711031ce9 Upgrade dependency moto to ==1.1.6 (#919) 2017-09-13 20:54:43 -07:00
af5c19cc52 Solving conflicts 2017-09-13 09:41:19 -07:00
359fbd2d73 Pinning version of PyOpenSSL #873 2017-09-13 09:39:52 -07:00
e8b9853367 Fixes 873 by explicitly declaring pyopenssl version. (#917) 2017-09-13 09:30:20 -07:00
376b2b8051 Upgrade dependency moto to ==1.1.5 (#916) 2017-09-12 16:01:24 -07:00
e8d0af87e4 Upgrade dependency SQLAlchemy-Utils to ==0.32.16 (#895) 2017-09-12 09:59:49 -07:00
a4267320b0 Upgrade dependency Flask-Script to ==2.0.6 (#900) 2017-09-12 09:59:23 -07:00
52dd42701a Upgrade dependency moto to ==1.1.4 (#915) 2017-09-12 09:58:38 -07:00
fc9b1e5b12 server_default from "False" to sa.false() (#913) 2017-09-11 09:19:19 -07:00
2ecfaa41cf Add pyldap mock for readthedocs (#912) 2017-09-11 09:18:03 -07:00
7106c4fdcf Sync docs requirements.txt (#910) 2017-09-10 10:41:46 -07:00
9420ca9949 Upgrade dependency acme to ==0.18.1 (#908) 2017-09-08 16:59:49 -07:00
956a1851a2 Upgrade dependency moto to ==1.1.3 (#909) 2017-09-08 16:59:39 -07:00
dafed86179 Improve certificate name normalization: remove Unicode characters, etc. (#906)
* Accented characters are replaced with non-accented version (ä -> a)
* Spaces are replaced with '-' (previously they were removed)
* Multiple non-alphanumeric characters are collapsed into one '-'
2017-09-08 10:52:22 -07:00
e72efce071 Upgrade dependency acme to ==0.18.0 (#902) 2017-09-07 18:09:52 -07:00
77b9658dba Upgrade dependency pyldap to ==2.4.37 (#903) 2017-09-07 18:09:37 -07:00
090c984ca3 Upgrade dependency pytest to ==3.2.2 (#904) 2017-09-07 18:09:15 -07:00
2ff25b656f Upgrade dependency moto to ==1.1.2 (#905) 2017-09-07 18:09:07 -07:00
ff4d1edd63 remove duplicated ldap_bind_uri description (#898) 2017-09-04 10:12:40 -07:00
79d12578c7 basic ldap support (#842) 2017-09-03 20:41:43 -07:00
c0784b40e0 Upgrade dependency Flask-Migrate to ==2.1.1 (#892) 2017-08-29 20:20:39 -07:00
ff87c487c8 It's too expensive to attempt to load all certificates associated with a given notification. Some queries such as default are associated with a large number of certificates. We have little control over when these objects are loaded, but when marshalled they are lazyloaded via SQLAlachemy. If a user needs to get all the certificates associated with a certificate they should use the /notifications/<id>/certificates endpoints that support pagination. (#891) 2017-08-28 17:57:39 -07:00
82b43b5a9d Create signal hooks and handler for dumping CSR and certificate details (#882) 2017-08-28 17:35:56 -07:00
4b4e159a8e [Doppins] Upgrade dependency moto to ==1.1.1 (#888)
* Upgrade dependency moto to ==1.1.0

* Upgrade dependency moto to ==1.1.1
2017-08-28 17:35:12 -07:00
bb1c339655 Fix ability to remove all roles from authority (#880) 2017-08-28 17:35:01 -07:00
aca6d6346f Removing legacy requirement for nodejs. Closes #866 (#887) 2017-08-25 10:12:56 -07:00
e7efaf4365 Prevent creation of empty SubjAltNames extension in CSR (#883) 2017-08-18 09:10:56 -07:00
c6d76f580e Disable unused Flask Principal sessions (#881)
Lemur uses its own auth token for authentication; logging out doesn't
properly dispose of the Flask Principal session.
2017-08-17 09:24:35 -07:00
941df0366d Fix roles display on user screen and fix removing user roles (#879) 2017-08-17 09:24:10 -07:00
7762d6ed52 Reworked sensitive domain name and restriction logic (#878)
* This is a fix for a potential security issue; the old code had edge
  cases with unexpected behavior.
* LEMUR_RESTRICTED_DOMAINS is no more, instead LEMUR_WHITELISTED_DOMAINS
  is a list of *allowed* domain name patterns. Per discussion in PR #600
* Domain restrictions are now checked everywhere: in domain name-like
  CN (common name) values and SAN DNSNames, including raw CSR requests.
* Common name values that contain a space are exempt, since they cannot
  be valid domain names.
2017-08-16 19:24:49 -07:00
466df367e6 Upgrade dependency boto3 to ==1.4.6 (#874) 2017-08-16 09:56:22 -07:00
b0c8787cfa Upgrade dependency marshmallow to ==2.13.6 (#877) 2017-08-16 09:56:08 -07:00
cf805f530f Prevent unintended access to sensitive fields (passwords, private keys) (#876)
Make sure that fields specified in filter, sortBy, etc. are model fields
and may be accessed. This is fixes a potential security issue.

The filter() function allowed guessing the content of password hashes
one character at a time.

The sort() function allowed the user to call an arbitrary method of an
arbitrary model attribute, for example sortBy=id&sortDir=distinct would
produce an unexpected error.
2017-08-16 09:38:42 -07:00
b40c6a1c67 Upgrade dependency pem to ==17.1.0 (#872) 2017-08-10 15:08:11 -07:00
3a62010445 Upgrade dependency pytest to ==3.2.1 (#871) 2017-08-09 15:00:15 -07:00
3b4e7d9169 Fixed typo (#870) 2017-08-09 08:40:22 -07:00
4245ba0d15 Upgrade dependency acme to ==0.17.0 (#866) 2017-08-06 11:19:10 -07:00
95e4c23db1 Upgrade dependency factory-boy to ==2.9.2 (#868) 2017-08-06 11:19:00 -07:00
f5e120ad2e Update readme.txt (#869) 2017-08-04 12:42:27 -07:00
fab146b328 [Doppins] Upgrade dependency factory-boy to ==2.9.1 (#863)
* Upgrade dependency factory-boy to ==2.9.0

* Upgrade dependency factory-boy to ==2.9.1
2017-08-02 09:17:25 -07:00
5aeadf8f98 [Doppins] Upgrade dependency psycopg2 to ==2.7.3 (#858)
* Upgrade dependency psycopg2 to ==2.7.2

* Upgrade dependency psycopg2 to ==2.7.3
2017-08-02 09:16:38 -07:00
5f9c655594 Upgrade dependency Flask-Migrate to ==2.1.0 (#861) 2017-08-02 09:16:21 -07:00
dd18cac702 Upgrade dependency boto3 to ==1.4.5 (#862) 2017-08-02 09:16:01 -07:00
b76ab902e5 Upgrade dependency pytest to ==3.2.0 (#865) 2017-08-02 09:15:42 -07:00
f5082e2d3a Starting transition away from not_before and not_after. (#854) 2017-07-14 09:24:59 -07:00
61c493fc91 Adding additional failure conditions to sentry tracking. (#853)
* Adding additional failure conditions to sentry tracking.

* Removing sentry extension as a circular import.
2017-07-13 14:49:04 -07:00
6779e19ac9 Adding enum migration. (#852) 2017-07-13 13:12:53 -07:00
443eb43d1f Adding the ability to specify a per-certificate rotation policy. (#851) 2017-07-12 16:46:11 -07:00
560bd5a872 Upgrade dependency acme to ==0.16.0 (#850) 2017-07-12 15:53:32 -07:00
8f35a64faf Upgrade dependency pyjwt to ==1.5.2 (#846) 2017-07-12 15:52:50 -07:00
7507f6be50 Updating documentation (#849) 2017-07-05 20:17:19 -07:00
ac3b441456 Upgrade dependency pytest to ==3.1.3 (#847) 2017-07-05 19:02:59 -07:00
53113e5eeb Add auditing for creating or updating a cert. (#845) 2017-07-04 06:39:16 -07:00
9d5db3ec12 This should not have been upgraded as it breaks mTLS (#844) 2017-06-29 16:29:26 -07:00
169dcb86e2 supporting the ability to push exceptions to sentry (#843) 2017-06-29 14:12:38 -07:00
e4f5224f42 set ses email content type to utf-8 instead of string (#841) 2017-06-28 09:44:19 -07:00
98907e66e9 Minor fixes to S3.put signature (#840) 2017-06-27 16:18:34 -07:00
c05343d58e Adds the ability for destination plugins to be sub-classed from Expor… (#839)
* Adds the ability for destination plugins to be sub-classed from ExportDestination. These plugins have the extra option of specifying an export plugin before the destination receives the data. Closes #807.

* fixing tests
2017-06-26 12:03:24 -07:00
541fbc9a6d Use named kwargs rather than args when calling s3 put (#830) 2017-06-20 11:28:19 -07:00
ef08e02333 [Doppins] Upgrade dependency paramiko to ==2.2.1 (#833)
* Upgrade dependency paramiko to ==2.1.3

* Upgrade dependency paramiko to ==2.2.0

* Upgrade dependency paramiko to ==2.2.1
2017-06-14 09:20:35 -07:00
35cc7ef8d7 Adding support for private DigiCert certificates (#835) 2017-06-14 09:20:24 -07:00
e77382864b Fixing KeyError on error handling (#834) 2017-06-14 09:07:27 -07:00
b5fd802005 Upgrade dependency acme to ==0.15.0 (#831) 2017-06-09 09:03:07 -07:00
98897f3c98 Upgrade dependency pytest to ==3.1.2 (#832) 2017-06-09 09:02:55 -07:00
d49bb8a6ca Upgrade dependency Flask-RESTful to ==0.3.6 (#828) 2017-06-03 20:25:11 -07:00
05f2d3b2d9 Upgrade dependency moto to ==1.0.1 (#829) 2017-06-03 20:24:51 -07:00
d4d6d832b1 Fixing audit filtering and sorting. (#827) 2017-06-02 09:07:22 -07:00
9c92138f2d Fixing autorotation failures. (#825)
* Fixing issue with auto rotation failing due to a change in the way certificate data is serialized.
2017-06-02 08:59:42 -07:00
5a4806bc43 Allowing description to be optional. (#826) 2017-06-01 17:09:04 -07:00
54105e221e Upgrade dependency Flask-Migrate to ==2.0.4 (#822) 2017-05-31 08:58:54 -07:00
adfc76aa79 Upgrade dependency pytest to ==3.1.1 (#823) 2017-05-31 08:58:38 -07:00
3e3f7af796 Upgrade dependency cryptography to ==1.9 (#821) 2017-05-30 09:03:46 -07:00
07969f7e10 Ensuring IPAddresses and IPNetworks are correctly serialized. (#818) 2017-05-26 10:48:26 -07:00
249ab23df4 Upgrade dependency acme to ==0.14.2 (#817) 2017-05-25 17:40:55 -07:00
3141b47fba Catch OAuth providers that want the params sent as data (#800) 2017-05-25 10:21:29 -07:00
31f4cf0253 adding url context path to html templates (#814) 2017-05-25 10:20:32 -07:00
21d48b32c9 Fixing an issue with uploading to cloudfront. (#815) 2017-05-25 10:10:12 -07:00
11bd42af82 Correct status code for basic-auth (#813)
* ensuring those using basic auth recieve a correct status code when their password is incorrect

* Fixing oauth status codes
2017-05-23 09:48:31 -07:00
feac9cb3a3 Upgrade dependency pytest to ==3.1.0 (#811) 2017-05-23 09:31:18 -07:00
f6b5012f56 Add Check of DB connections on healthcheck URL (#812) 2017-05-22 17:15:41 -07:00
f9b388c658 Modifying the was s3 uploading works. (#810)
* Modiying the was s3 uploading works.

* Fixing pep8
2017-05-20 12:07:44 -07:00
4093f4669a Switching remaining uses of boto to boto3. (#809) 2017-05-20 11:09:55 -07:00
9594f2cd8d Upgrading moto and fixing test that break due to deprecation. (#808)
* Upgrading moto and fixing test that break due to deprecation.

* Adding region.
2017-05-20 10:40:22 -07:00
380203eb53 Adding the ability to upload to cloudfront via the 'path' parameter. Cloudfront destinations must be created separately. (#805)
Closes #277
2017-05-18 13:49:17 -07:00
307a73c752 Fixing some confusion between 401 vs 403 error code. 401 indicates that the user should attempt to authenticate again. Where as 403 indicates the user is authenticated but not allowed to complete an action. (#804)
Closes #767
2017-05-18 13:20:17 -07:00
7ad471a810 Upgrade dependency acme to ==0.14.1 (#801) 2017-05-16 13:33:21 -07:00
1184f9d070 Upgrade dependency freezegun to ==0.3.9 (#803) 2017-05-16 13:32:20 -07:00
3050aca3e6 Minor fixes to the domains UI. (#798)
* Fixes checkbox input.

* Fixes notification message.
2017-05-15 19:14:12 -07:00
8c41c6785d Fixes issue where domains without any associated certificates are not searchable. (#797) 2017-05-15 19:07:32 -07:00
092ce0f9d8 Closes #792. (#796) 2017-05-15 19:07:16 -07:00
97dceb5623 fixed typo in supervisord example config (#790) 2017-05-12 09:18:32 -07:00
23b6df536f Fix Minor Typo in index.rst (#793)
Changed LEMUR_DEFAUTL_ORGANIZATION to LEMUR_DEFAULT_ORGANIZATION
2017-05-12 09:17:52 -07:00
95b4206986 Removing tests folder from coverage report. (#788) 2017-05-11 19:42:53 -07:00
914de78576 Adds migration to fix keys on unique index. Closes #743. (#785) 2017-05-10 12:13:42 -07:00
ecf00fe9d6 Splitting out the default date issuance logic for CIS and CC. CIS assumes years is converted to validity_end while CC prefers validity_years over validity_end. (#784) 2017-05-10 12:05:03 -07:00
7257e791ff Upgrade dependency acme to ==0.14.0 (#777) 2017-05-08 19:27:33 -07:00
c71b3a319d Log the audit logs (#781) 2017-05-08 09:43:26 -07:00
767147aef1 Check for unknown as status is no longer represented as a boolean (#780) 2017-05-08 09:43:19 -07:00
ce5a45037a Fix for status representation in the view (#778) 2017-05-05 11:04:40 -07:00
9c9ca37586 Enabling hex serial numbers without breaking backward compatibility. (#779)
* Enabling hex serial numbers without breaking backward compatibility.

* Fixing tests.
2017-05-05 11:04:09 -07:00
381cd2e1ff Updated apache config (#776)
You guys asked for one that worked... It took me a little while to tweak, esp. since I'm not a guru with python.  The comment about needing mod_wsgi isn't true, unless you want to run lemur as a cgi program... I suspect that's from an older version that ran as cgi and not as a standalone webserver.
2017-05-04 08:45:55 -07:00
2a2d5a5583 Adding an example digicert url. Closes #700. (#775) 2017-05-01 10:59:49 -07:00
5c41dafc97 fix unit and interval transposition in schemas.py (#752) (#774) 2017-04-30 12:23:34 -07:00
6367a98134 Creating a user named 'lemur' in postgres (#773)
Creating a user named 'lemur' in postgres
2017-04-28 15:31:08 -07:00
0bbe2b0331 config LEMUR_MAIL to LEMUR_EMAIL (#772)
I referenced https://github.com/Netflix/lemur/blob/master/lemur/plugins/lemur_email/plugin.py and it appears this configuration option should be "LEMUR_EMAIL"
2017-04-28 15:01:21 -07:00
6a77d511e8 Upgrade dependency xmltodict to ==0.11.0 (#769) 2017-04-28 15:00:41 -07:00
989e3733a2 Add docker setup for running tests on a docker enabled dev environment. (#771) 2017-04-28 09:28:06 -07:00
fbc24ea400 There is an issue when iterating over extensions where certificates might not have been issued in adherence with basic constraints. Here we log these errors instead of failing out right. (#770) 2017-04-27 17:45:34 -07:00
2b8c2f612e Upgrade dependency pyjwt to ==1.5.0 (#768) 2017-04-27 12:16:36 -07:00
4905020e77 ensuring stdout has a default log level (#766) 2017-04-27 10:11:47 -07:00
75787d20bc ensuring that lemur's default user has a valid email (#765) 2017-04-27 09:53:35 -07:00
ca9f120988 fixing some pep8 issues (#764) 2017-04-27 09:44:39 -07:00
5fb6753445 Upgrade dependency marshmallow to ==2.13.5 (#753) 2017-04-27 09:20:03 -07:00
e86954e8ea Destination Plugin/Lemur_linuxdst (#736)
* Added lemur_linuxdst

* Revert "Added lemur_linuxdst"

This reverts commit 010c19bd1937320189ee5a0660f9e356221121f3.

* added plugin\lemur_linuxdst

Destination plugin for a target linux host

* Update remote_host.py

* Update plugin.py

* Update remote_host.py

* Update plugin.py

* Update plugin.py

* chaning var and funct names

* Write data with local temp

* .

* .

* typo

* tested plugin successfully

* Update plugin.py

* Update remote_host.py

* removed whitespace

* set permissions on exported keys to 600

sftp.chmod(dst_dir_cn + '/' + dst_file, (stat.S_IRUSR))

* Update plugin.py

* Update remote_host.py

* Update plugin.py

* added 'paramiko==2.1.2'

required for lemur_linuxdst plugin

* data stored in clear text at rest

* Update plugin.py

* Update plugin.py

* Update remote_host.py
2017-04-27 09:19:49 -07:00
604cd60dbe Return correct intermediate certificate on digicert creation. (#762)
This commit also removes the unused DIGICERT_INTERMEDIATE env
var as it is not used.
2017-04-27 09:14:20 -07:00
05f4ae8e58 Hexify cert serial (#763)
* Hexify serial at the serialization layer

* Fix for flakey test. Change test to test for uppercased string
2017-04-27 09:13:04 -07:00
88ac783fd2 PEP8 Fixes (#760) 2017-04-25 09:23:18 -07:00
bc66ede9aa Fixing Bandit findings and adding travis Bandit job (#759)
* Fixes for Bandit

This commit fixes a couple of issues so that Bandit can run
cleanly using medium+ severity and confidence filtering.

* Adding Lemur Bandit job to TravisCI
2017-04-24 18:37:03 -07:00
1c295896e6 Add test for when there are no notifications on a certificate (#757) 2017-04-24 09:04:49 -07:00
f90076abe9 Update index.rst (#754)
Seems the api for these actions have changed. Thought I would update the documentation around this. Let me know if I've misunderstood something.
2017-04-19 16:06:32 -07:00
01aa372e59 Version bump. (#751) 2017-04-08 13:23:48 -07:00
479ac81aa9 0.5 Release (#750) 2017-04-08 13:17:24 -07:00
9c69c6d129 [Doppins] Upgrade dependency marshmallow-sqlalchemy to ==0.13.1 (#719)
* Upgrade dependency marshmallow-sqlalchemy to ==0.13.0

* Upgrade dependency marshmallow-sqlalchemy to ==0.13.1
2017-04-08 12:43:51 -07:00
ea1e9cb4c6 Upgrade dependency psycopg2 to ==2.7.1 (#721) 2017-04-08 12:34:17 -07:00
dac7a77afb Upgrade dependency gunicorn to ==19.7.1 (#733) 2017-04-08 12:33:57 -07:00
9b21197fec Upgrade dependency SQLAlchemy-Utils to ==0.32.14 (#745) 2017-04-08 12:33:46 -07:00
e4255649c0 Upgrade dependency acme to ==0.13.0 (#746) 2017-04-08 12:33:28 -07:00
81aff42e03 Removing this exception handling, that error should be caught above. (#749) 2017-04-07 16:01:40 -07:00
221851abc1 supervisor ; cause services not to start (#744)
the ; in the supervisor/conf.d/app.conf file cause the service not to start.
2017-04-06 09:21:13 -07:00
7f019583f2 Don’t set ‘custom_expiration_date’ if validity years is set in the UI. (#742)
* Don’t set ‘custom_expiration_date’ if validity years is set in the UI.

* Use single quotes instead of double quotes.
2017-04-04 17:11:17 -07:00
e18a188723 Spell fixes in docs (#740) 2017-03-30 21:09:30 -07:00
f91ae5b319 Fixes bug where authority status was not set correctly. (#739) 2017-03-29 10:10:51 -07:00
dd39b9ebe8 adding url context path to build, adding documentation on url contextpath (#737) 2017-03-28 15:21:13 -07:00
15896a3b11 Fix spelling error in LEMUR_DEFAULT_COUNTRY (#734) 2017-03-22 15:49:16 -07:00
e092606181 Upgrade dependency marshmallow to ==2.13.4 (#732) 2017-03-20 09:08:26 -07:00
a4707c5fc9 added a few steps (#731)
Added a few steps that are needed during the install on a fresh Ubuntu image
2017-03-18 21:36:26 -07:00
f0dde845db Adding ability to exclude certificates from expiration (#730)
* adding ability to exclude certificates from expiration

* fixing tests
2017-03-15 11:25:19 -07:00
b0ea027769 Underscores should not be in hostnames (#728) 2017-03-15 08:41:06 -07:00
d9f2faa462 Upgrade dependency pytest to ==3.0.7 (#727) 2017-03-14 15:06:54 -07:00
7b4d31d4f6 added steps for loading custom plugin (#725)
* added steps for loading custom plugin

added steps for loading a custom plugin into Lemur once the files have been put into place (/www/lemur/lemur/plugins/) and the setup.py file (/www/lemur/setup.py) has been modified.

* updated __init__.py section


except Exception as e:
2017-03-14 09:30:22 -07:00
522e182694 added python3-dev to dependencies (#724)
make release fails without it
2017-03-13 15:45:10 -07:00
6c8a6620d2 specify python3 when creating virtualenv (#723)
Lemur is developed against Python3.5. If you do not specify the Python version it is possible the virtualenv will be built on a different version.
2017-03-13 13:58:44 -07:00
d68b2b22e0 Update bower.json (#722)
Angular angular-sanitize is pulling in an incompatible version of angular knocking out the webUI by breaking chart.js.
2017-03-13 12:28:08 -07:00
a4068001a3 Updating docs to align with normal deployment. (#718) 2017-03-12 15:01:21 -07:00
574fed2618 Upgrade dependency marshmallow to ==2.13.3 (#717) 2017-03-11 11:07:17 -08:00
8762e1c5ae Issue #703 bugfix (#711)
* Ensures that both AKI serial/issue _and_ keyid won't be included.
Validation issues crop up if both types of AKI fields are present.

* Ensure that SAN extension includes the certificate's common name

* Fix scenario where subAltNames are getting dropped when applying a template

* Ensure that SAN includes the CN

* Ensuring that getting here without a SAN extension won't break things.

* New cleaner approach

* Some bits of handling the extensions are a bit hacky, requiring access to attributes inside the objects in x509.
I think this is pretty clean though.

* lintian check

* Fixing tests
2017-03-10 09:09:18 -08:00
d94e3113ff Upgrade dependency marshmallow to ==2.13.2 (#716) 2017-03-10 09:08:34 -08:00
3c5b2618c0 Rely on the lemur generating the correct name for rotated certificates. (#714)
* Rely on the lemur generating the correct name for rotated certificates.

* Fixing tests.
2017-03-09 13:09:20 -08:00
602c5580d3 Only validates values if present in options. Fixing authority test to parse plugin information. (#713) 2017-03-06 20:38:04 -08:00
038beafb5e Upgrade dependency gunicorn to ==19.7.0 (#709) 2017-03-04 18:28:35 -08:00
14923f8c07 Upgrade dependency marshmallow to ==2.13.1 (#710) 2017-03-04 18:28:24 -08:00
b715687617 Ensuring that we don't fail cleaning if it doesn't exist. (#708) 2017-03-03 16:03:52 -08:00
c46fa5d69c Ensures the rotation has a value during migration. (#707) 2017-03-03 15:16:25 -08:00
310e1d4501 Adds support for filtering by UI. Closes #702. (#706) 2017-03-03 15:07:26 -08:00
fc957b63ff Source syncing tweaks. (#705)
* Allow owner to be specified when syncing certs.

* Ensuring non-endpoint plugins don't fail to complete syncing.

* Adding in some additional error handling.
2017-03-03 14:53:56 -08:00
d53f64890c Adding max notification constraint. (#704)
* Adds additional constraints to the max notification time. With an increasing number of certificates we need to limit the max notification time to reduce the number of certificates that need to be analyzed for notification eligibility.
2017-03-03 12:59:16 -08:00
5f5583e2cb UI adjustments for mutually exclusive (radio button version) encipher/decipher-only Key Usage #664 (#692)
* UI adjustments to make Key Agreement, Encipher Only, and Decipher Only relationship more user-friendly

* whitespace typo

* Issue #663 switching Encipher/Decipher Only options to be mutually exclusive and un-checkable radio buttons.

* Found a bug in the fields schema that was dropping Key Agreement bit if encipher/decipher only weren't checked
2017-02-16 13:26:56 -08:00
4c11ac9a42 [Doppins] Upgrade dependency acme to ==0.11.1 (#647)
* Upgrade dependency acme to ==0.10.0

* Upgrade dependency acme to ==0.10.1

* Upgrade dependency acme to ==0.10.2

* Upgrade dependency acme to ==0.11.0

* Upgrade dependency acme to ==0.11.1
2017-02-16 13:24:28 -08:00
cf6ad94509 Adjusting the way that certificates are requested. (#643)
* Adjusting the way that certificates are requested.

* Fixing tests.
2017-02-16 13:24:05 -08:00
08bb9c73a0 allow attributes to be excluded from a cert subject (#690)
* allow more flexibility in cert subject name

* clean up logic/remove unnecessary code
2017-02-16 13:21:52 -08:00
8e49194764 Issue 688 cert templates (#689)
* subAltNames were getting wiped out every time a template was selected

* isCritical variables aren't presented in the UI, nor is this information used in determining to use them.
2017-02-10 12:43:41 -08:00
8afcb50a39 Fixing the re-issuance process. Ensuring that certificates that are r… (#686)
* Fixing the re-issuance process. Ensuring that certificates that are re-issued go through the normal schema validation.

* Fixing tests.
2017-02-03 11:21:53 -08:00
0326e1031f adding generic OAuth2 provider (#685)
* adding support for Okta Oauth2

* renaming to OAuth2

* adding documentation of options

* fixing flake8 problems
2017-02-03 10:36:49 -08:00
117009c0a2 Lemur cryptography refactor and updates (#668)
* Renaming the function so it sounds less root-specific

* Refactoring lemur_cryptography
* Adding to the certificate interface an easy way to request the subject and public_key of a certificate
* Turning the create authority functionality into a wrapper of creating a CSR in the certificate codebase and issueing that certificate in this plugin. (Dependent on https://github.com/Netflix/lemur/pull/666 changes first)
* Ensuring that intermediate certificates and signed certificates retain their chain cert data

* Handling extensions that are the responsibility of the CA
Implementing authority_key_identifier for lemur_cryptography signatures and including skeletons of handling the certificate_info_access and crl_distribution_points

* Fixing errors found with linter

* Updating plugin unit tests

* Changing this for Python3. Underlying cryptography library expects these to be bytes now.

* Updating tests to match new function names/interfaces

* Another naming update in the plugin tests

* Appears that create_csr won't like this input without an owner.

* Undoing last commit and putting it into the right place this time.

* create_csr should be good now with these options, and chain certs will be blank in tests

* This won't be blank in issue_certificate, like it will in creating an authority.

* Much cleaner

* unnecessary import
2017-02-01 10:34:24 -08:00
b7833d8e09 Upgrade dependency Flask-Migrate to ==2.0.3 (#682) 2017-01-31 09:15:52 -08:00
3fd39fb823 Upgrade dependency marshmallow to ==2.12.2 (#683) 2017-01-31 09:15:40 -08:00
317b7cabb3 Ensuring usage matched OIDs. (#681) 2017-01-28 23:22:20 -08:00
a59bc1f436 Fixes (#680)
* Adding some additional logging.
2017-01-28 16:40:37 -08:00
c24810b876 Modifying variable to fit epextions. (#679) 2017-01-28 14:07:12 -08:00
bc94353850 Closes #648, also fixes several issues #666. (#678) 2017-01-27 21:05:25 -08:00
f13a3505f3 X509 extensions issue#646 (#666)
* Allowing that create_csr can be called with an additional flag in the csr_config to adjust the BasicConstraints for a CA.

* If there are no SANs, skip adding a blank list of SANs.

* Adding handling for all the extended key usage, key usage, and subject key identifier extensions.

* Fixing lint checks. I was overly verbose.

* This implements marshalling of the certificate extensions into x509 ExtensionType objects in the schema validation code.

* Will create x509 ExtensionType objects in the schema validation stage
* Allows errors parsing incoming options to bubble up to the requestor as ValidationErrors.
* Cleans up create_csr a lot in the certificates/service.py
* Makes BasicConstraints _just another extension_, rather than a hard-coded one
* Adds BasicConstraints option for path_length to the UI for creating an authority
* Removes SAN types which cannot be handled from the UI for authorities and certificates.
* Fixes Certificate() object model so that it doesn't just hard-code only SAN records in the extensions property and actually returns the extensions how you expect to see them. Since Lemur is focused on using these data in the "CSR" phase of things, extensions that don't get populated until signing will be in dict() form.* Trying out schema validation of extensions
2017-01-27 12:31:29 -08:00
4af871f408 Added migration to cover what seem to be missing fields. (#676) 2017-01-27 09:07:20 -08:00
162d5ccb62 Gracefully handle importing certificates with missing data (#674)
* fixing index out of range issue

* catching exceptions is common values aren't set

* fixing lint errors

* fixing unrelated lint/import error
2017-01-24 13:48:53 -08:00
b1723b4985 [Doppins] Upgrade dependency marshmallow to ==2.12.1 (#672)
* Upgrade dependency marshmallow to ==2.12.0

* Upgrade dependency marshmallow to ==2.12.1
2017-01-24 13:46:37 -08:00
6bf7d56d51 Upgrade dependency moto to ==0.4.31 (#673) 2017-01-24 13:46:14 -08:00
9751cbbf83 Upgrade dependency pytest to ==3.0.6 (#671) 2017-01-22 18:03:22 -08:00
8fa5ffa007 Upgrade dependency boto3 to ==1.4.4 (#670) 2017-01-20 13:10:01 -08:00
f353956353 Many fixes to authority/certificate extensions pages (#659)
* Aligning certificate creation between authority and certificate workflows
* Correctly missing and mis-named fields in schemas
* Re-ordering KeyUsage and ExtendedKeyUsage for consistency and clarity
* Adding client authentication to the authority options.

* Missing blank lines for pyflakes linting

* Updating tests for new fields/names/typos
2017-01-18 14:31:17 -08:00
02cfb2d877 Stealing this code form the attachSubAltName function in the certificates workflow. (#655)
The function was wiping out any extensions that weren't SAN names from the authority UI.
2017-01-18 14:24:15 -08:00
1b6f88f6fd Fixing handling of adding custom OIDs in UI (#653)
* is_critical wasn't in the schema, so was getting dropped.
* isCritical in the Javascript wasn't getting assigned if it was unchecked. Now, it will be assumed false if missing.
* The display of critical or not in the list of added custom OIDs was unclear when it was just true/false with no heading. Now it will be displayed as critical or nothing instead.
* The namespace for the checkbox for isCritical was wrong, and didn't get processed with the oid/type/value variables.
2017-01-18 14:20:44 -08:00
9f6ad08c50 Updating hooks. (#660) 2017-01-18 14:16:31 -08:00
25340fd744 Combining Authority Key Identifier extension options in the schema. (#651)
* Combining Authority Key Identifier extension options in the schema.
This makes processing them in the cert/csr generation stage make more sense because they are two options in the same x.509 extension. They were already in the same part of the schema for authorities, but this makes the certificates follow the same pattern, and it allows them to share the same schema/validation layout.

* Updating schema tests to match changes

* Fixing an idiot typo

* I promise to stop using Travis as a typo-corrector soon.
2017-01-18 14:16:19 -08:00
7f2b44db04 Correcting grammar for subca ValidationError message for clarity (#657) 2017-01-18 12:34:16 -08:00
d67b6c6120 Chains are not always a given. (#645) 2017-01-08 17:27:50 -08:00
4cfb5752b2 Upgrade dependency marshmallow to ==2.11.1 (#644) 2017-01-08 14:52:28 -08:00
0d7b2d9f44 Upgrade dependency Flask to ==0.12 (#639) 2017-01-08 10:53:02 -08:00
08ebc4cd59 Upgrade dependency marshmallow-sqlalchemy to ==0.12.1 (#640) 2017-01-08 10:50:37 -08:00
85ae9712e3 Upgrade dependency marshmallow to ==2.11.0 (#642) 2017-01-08 10:49:41 -08:00
83128f3019 Fixing elb sync issues. (#641)
* Fixing elb sync issues.

* Fixing de-duplications of names.
2017-01-05 16:06:34 -08:00
7aa5ba9c6b Fixing an IAM syncing issue. Were duplicates were not properly sync'd… (#638)
* Fixing an IAM syncing issue. Were duplicates were not properly sync'd with Lemur. This resulted in a visibility gap. Even 'duplicates' need to sync'd to Lemur such that we can track rotation correctly. Failing on duplicates lead to missing those certificates and the endpoints onto which they were deployed. This commit removes the duplicate handling altogether.

* Fixing tests.
2017-01-04 17:46:47 -08:00
e5dee2d7e6 Adding additional metrics for when destinations fail to upload. (#637) 2016-12-28 09:52:23 -08:00
b0232b804e Removing cloned date defaults. (#636) 2016-12-27 11:35:53 -08:00
de7cec35c6 Clean refactor (#635)
* Adding rotation to the UI.

* Removing spinkit dependency.

* refactoring source cleaning
2016-12-27 10:31:33 -08:00
700c57b807 Rotation ui (#633)
* Adding rotation to the UI.

* Removing spinkit dependency.
2016-12-26 15:55:11 -08:00
ce75bba2c3 Replacement refactor. (#631)
* Deprecating replacement keyword.

* Def renaming.
2016-12-26 11:09:50 -08:00
46f8ebd136 Modifying the way rotation works. (#629)
* Modifying the way rotation works.

* Adding docs.

* Fixing tests.
2016-12-23 13:18:42 -08:00
f8279d6972 Fixes a bug where pagination was incorrect. (#628) 2016-12-21 18:39:21 -08:00
072ca4da4f Adding some additional output to rotation command. (#627) 2016-12-21 13:34:14 -08:00
8c5c30dfd4 Adding some additional output to expiration command. (#626) 2016-12-21 11:01:21 -08:00
edc0116a3a urllib3 still failing. (#625) 2016-12-21 11:01:09 -08:00
c1b2c3689c [Doppins] Upgrade dependency requests to ==2.12.4 (#543)
* Upgrade dependency requests to ==2.12.2

* Upgrade dependency requests to ==2.12.3

* Upgrade dependency requests to ==2.12.4
2016-12-21 10:06:30 -08:00
6746cc33a0 Upgrade dependency factory-boy to ==2.8.1 (#616) 2016-12-21 10:01:46 -08:00
74723d1a1f Adding ability to modify ELBv2 endpoints. (#624) 2016-12-21 08:23:14 -08:00
fccb8148d5 Upgrade dependency marshmallow to ==2.10.5 (#615) 2016-12-21 07:19:32 -08:00
3a4ebbf92c Upgrade dependency SQLAlchemy-Utils to ==0.32.12 (#614) 2016-12-21 07:19:10 -08:00
48735e685c Upgrade dependency boto3 to ==1.4.3 (#623) 2016-12-20 18:28:07 -08:00
cdcae4efb0 Closes #594 (#621) 2016-12-20 14:26:39 -08:00
f7c795c7f6 Closes #577. (#622) 2016-12-20 14:26:29 -08:00
beba2ba092 Adding additional reporting and refactoring existing setup. (#620) 2016-12-20 12:48:14 -08:00
9ac10a97ce Fix acme tests (#619)
* Ensures that in-active users are not allowed to login.

* Ensuring acme issuer loads correctly.
2016-12-19 22:59:23 -08:00
2f5f82d797 Ensures that in-active users are not allowed to login. (#618) 2016-12-19 22:58:57 -08:00
c7fdb2acd7 adding required variables (#611) 2016-12-18 18:21:22 -08:00
51c7216b70 Fixing configuration value. (#610)
* Fixing and configuration value.

* Pinning fake factory.
2016-12-18 18:21:12 -08:00
0f3ffaade0 Fall back to CN for CA name when organization is not available (#607)
In-house CAs may not have the organization field filled out.
2016-12-16 16:27:25 -08:00
156b98f7f0 Ensuring that rotation only happens for certificates with endpoints to rotate. (#606) 2016-12-15 15:20:21 -08:00
a09faac9a7 Endpoint sync fixes (#604) 2016-12-15 10:26:59 -08:00
d20c552248 Fixing issues with rotation. (#603)
* Fixing issues with rotation.

* Fixing tests
2016-12-14 17:30:13 -08:00
f7fdf7902d Upgrade dependency boto to ==2.45.0 (#601) 2016-12-14 16:53:47 -08:00
b327963925 Plugin base classes: update method signatures & fix raise (#598)
This way IDEs can verify method overrides in subclasses, otherwise these
are flagged as erroneous.

Changed base classes to properly raise NotImplementedError; previously
they would cause "TypeError: exceptions must derive from BaseException"

Also fixed exception handling in sources.service.clean().
2016-12-14 13:42:29 -08:00
1eb3d563c6 Fix error reporting for certs without private key (#599) 2016-12-14 13:25:56 -08:00
02991c70a9 Allow Lemur "start" to use the global config. (#596)
* allowing our runserver to use the config specified by -c

* Maintaining config for gunicorn
2016-12-14 13:23:50 -08:00
71ddbb409c Minor documentation fixes/tweaks (#597)
Mostly typos, grammar errors and inconsistent indentation in code
examples.

Some errors detected using Topy (https://github.com/intgr/topy), all
changes verified by hand.
2016-12-14 09:29:04 -08:00
fbcedc2fa0 Specifying a recommended postgres version (#592) 2016-12-13 11:22:10 -08:00
3dad818af2 ensuring our index gets created (#591) 2016-12-13 11:13:44 -08:00
5dc0fa91e8 Upgrade dependency boto3 to ==1.4.2 (#550) 2016-12-13 09:53:49 -08:00
565c9ae98d adding missing init (#587) 2016-12-13 09:21:31 -08:00
2d6aa620b4 Attempting to upgrade to node LTS (#585)
* Attempting to upgrade to node LTS

* Updating travis config to node
2016-12-13 08:50:12 -08:00
03d5a6cfe1 Refactors how notifications are generated. (#584) 2016-12-12 11:22:49 -08:00
a5c47e4fdc Upgrade dependency Flask-Migrate to ==2.0.2 (#582) 2016-12-12 10:42:57 -08:00
9581278481 Upgrade dependency cryptography to ==1.7 (#583) 2016-12-12 10:42:45 -08:00
1c3ac21291 Ensuring the digicert session is handled correctly (#579) 2016-12-11 08:38:59 -08:00
25faf05807 Upgrade dependency boto to ==2.44.0 (#578) 2016-12-08 17:31:53 -08:00
968dd52f6f Fixes (#576)
* Fixing email notification

* Adding endpoint expiration

* Fixing endpoint type for ELBs

* Allowing verisign to include additional SANs
2016-12-08 15:52:27 -08:00
a4b32b0d31 Fixing up notification testing (#575) 2016-12-08 11:33:40 -08:00
be1415fbd4 Ensuring new cli is available (#574) 2016-12-08 09:11:19 -08:00
b5901a1570 adding needed migration files (#573) 2016-12-07 17:31:59 -08:00
bdc6dc8683 Fixing a bug were extensions got a default value (#572) 2016-12-07 17:28:18 -08:00
5087fa67dc skipping a few tests that aren't ready yet (#571) 2016-12-07 16:52:00 -08:00
fc205713c8 Certificate rotation enhancements (#570) 2016-12-07 16:24:59 -08:00
9adc5ad59e Adding last updated time (#569) 2016-12-07 15:43:57 -08:00
f63ccd033d Ensuring that endpoints without output_schema work as expected (#568) 2016-12-07 15:40:29 -08:00
d7c0e2ec35 Ensuring that certificates returned from digicert are in the proper format (#564) 2016-12-06 12:25:52 -08:00
00da52f32e Ensuring that CSRs are correctly validated under python3 (#565) 2016-12-06 12:25:43 -08:00
287c684866 Ensuring that certificates returned from digicert are in the proper format (#564) 2016-12-06 12:10:39 -08:00
e94cf6ddc9 Ensuring that certificates returned from digicert are in the proper format (#564) 2016-12-06 12:05:18 -08:00
81272a2f7a Moving validation to server start. (#563) 2016-12-05 16:43:38 -08:00
e622a49b72 Adding better error handling around certificate rotation (#562) 2016-12-05 15:12:55 -08:00
9030aed8a4 Ensuring that our syncing process can find duplicate certifcates that do no need to be sync'd (#560) 2016-12-05 11:08:29 -08:00
eee534a161 Upgrade dependency pytest to ==3.0.5 (#559) 2016-12-05 10:54:54 -08:00
344abbda66 fixing signature (#556) 2016-12-02 13:48:50 -08:00
834814f867 adding additional status code metrics (#555) 2016-12-02 13:02:59 -08:00
7f823a04cd Ensuring that acme and cryptography respect different key types (#554) 2016-12-02 10:54:18 -08:00
0f5e925a1a Ensuring that default-issuer is set (#553) 2016-12-02 09:54:16 -08:00
e0c79389ca Allowing tar to be installed without git or other development tools (#552) 2016-12-01 16:20:46 -08:00
a40bc65fd4 Default authority. (#549)
* Enabling the specification of a default authority, if no default is found then the first available authority is selected

* PEP8

* Skipping tests relying on keytool
2016-12-01 15:42:03 -08:00
81bf98c746 Enabling RSA2048 and RSA4096 as available key types (#551)
* Enabling RSA2048 and RSA4096 as available key types

* Fixing re-issuance
2016-12-01 15:41:53 -08:00
41b59c5445 adding required variables to digicert issuer (#546) 2016-12-01 10:50:25 -08:00
e1bbf9d80c Improving endpoint rotation logic (#545) 2016-11-30 15:11:17 -08:00
bd2abdf45f Upgrade dependency arrow to ==0.10.0 (#541) 2016-11-30 15:07:36 -08:00
abb91fbb65 fixing a few minor issue with cloning (#544) 2016-11-30 10:54:53 -08:00
f9b16a2110 csr as string (#542) 2016-11-29 18:50:20 -08:00
588ac1d6a6 Digicert cis fixes (#540) 2016-11-29 17:15:39 -08:00
058d2938fb migrating off of openssl (#539) 2016-11-29 11:30:44 -08:00
3db3214cbe installing the digicert CIS plugin (#537) 2016-11-29 10:02:40 -08:00
bfc80f982c minor fixes and downgrading requests (#535) 2016-11-28 16:50:26 -08:00
727bc87ede Log fixes (#534)
* tying up some loose ends with event logging

* Ensuring creators can access
2016-11-28 14:13:16 -08:00
e2143d3ee8 tweaking the way data is returned (#532) 2016-11-28 12:29:03 -08:00
b46ff4158a Initial workon the digicert high issuance api. (#531) 2016-11-28 10:50:58 -08:00
734233257c Upgrade dependency arrow to ==0.9.0 (#529) 2016-11-27 15:27:12 -08:00
250558baf3 Ensuring that authority owners can access certificates issued by that… (#526)
* Ensuring that authority owners can access certificates issued by that authority
2016-11-25 20:35:07 -08:00
8e5323e2d7 migrating flask imports (#525) 2016-11-22 21:11:20 -08:00
06a920502c Updating readme with supported python verisions (#524) 2016-11-22 17:09:21 -08:00
d5d036b412 adding a work around for new gunicorn (#523) 2016-11-22 16:47:29 -08:00
9d03e75d9b tweaking a few things to support the new marshmallow (#522) 2016-11-22 15:14:19 -08:00
0158807847 Upgrade dependency cryptography to ==1.6 (#521) 2016-11-21 21:38:42 -08:00
06a3f3ea0d version bump (#520) 2016-11-21 15:29:31 -08:00
12ae0a587d teaking the way exceptions are handled (#519) 2016-11-21 15:26:17 -08:00
b3aa057d58 Upgrade deps. (#517) 2016-11-21 14:29:20 -08:00
dd6d332166 Removing python2 compatibility. (#518) 2016-11-21 14:03:04 -08:00
6eca2eb147 Re-working the way audit logs work.
* Adding more checks.
2016-11-21 11:28:11 -08:00
744e204817 Initial work on #74. (#514)
* Initial work on #74.

* Fixing tests.

* Adding migration script.

* Excluding migrations from coverage report.
2016-11-21 09:19:14 -08:00
d45e7d6b85 [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.
2016-11-18 11:27:46 -08:00
6fd47edbe3 Adds the ability to clone existing certificates. (#513) 2016-11-17 16:19:52 -08:00
a616310eb7 Fixing an issue were aws certificates plugins might not have a chain. (#512) 2016-11-17 14:47:10 -08:00
2130029f90 Adding new notification templates. (#511) 2016-11-17 14:16:59 -08:00
d11f254476 Closes: #469 (#510) 2016-11-17 12:16:30 -08:00
d54a11ad11 Ensuring coverage is run. (#509) 2016-11-17 11:11:09 -08:00
a9361fe428 Endpoints should be visible to all. (#508) 2016-11-17 10:45:26 -08:00
5345170a4f Ensuring that the passed in configuration has precedence over the environment config. (#507) 2016-11-17 09:31:37 -08:00
d0ccd85afe Adding coverage. (#506)
* Adding coverage.

* Attempting to adding coverage.

* Adding coveragerc.
2016-11-16 16:44:51 -08:00
520404c215 fix string -> byte conversion on python2 (#472) 2016-11-16 16:03:38 -08:00
9ac1756011 removing new 'active' logic for the time being (#505) 2016-11-16 15:56:24 -08:00
851d74da3d Ensuring that private key is in string format before it gets stored (#504)
* Ensuring that private key is in string format before it gets stored

* Fixing failing test.
2016-11-16 15:05:25 -08:00
3f2691c5d4 Minor fixes. (#502) 2016-11-16 13:23:35 -08:00
eaf34b1c8b Disabling the protect active flag (#498) 2016-11-16 09:31:02 -08:00
e9219adfb5 Ensuring model's have a basic __repr__. (#499) 2016-11-16 09:30:54 -08:00
9eddaf66cb adding human readable string (#500) 2016-11-16 09:30:46 -08:00
0a29a3fa2a Adding release notes. (#459) 2016-11-15 16:44:40 -08:00
9bb0787410 Ensuring that duplicates are migrated correctly. (#496)
* Ensuring that duplicates are migrated correctly.

* fixing typo
2016-11-15 16:43:45 -08:00
dd14fd202d clean out ADMINS references (#495)
* add variables to the documentation forwq oauth2

* remove old reference to ADMINS to get rid of any confusion
2016-11-15 16:43:28 -08:00
114deba06e Adding the ability to silence notifications on creation. (#490) 2016-11-12 09:29:42 -08:00
0334f1094d fixing documentation typo (#489) 2016-11-11 13:35:24 -08:00
7af68c3cc0 Adding additional metric gathering for failed sync operations. (#488) 2016-11-11 13:28:01 -08:00
953d3a08e7 Adding example request to documentation. (#487) 2016-11-11 12:54:12 -08:00
f141ae78f3 Typo. (#485) 2016-11-10 14:40:59 -08:00
94d619cfa6 Minor errors. (#484) 2016-11-10 14:34:45 -08:00
89470a0ce0 Adding default validity and retry logic. (#483) 2016-11-10 11:23:37 -08:00
e6b291d034 Time (#482)
* adding python 3.5 as a target

* adding env flag

* Aligning on arrow dates.
2016-11-09 10:56:22 -08:00
b0eef03c73 adding python 3.5 as a target (#481)
* adding python 3.5 as a target

* adding env flag
2016-11-08 15:22:50 -08:00
25a6c722b6 Adding digicert documentation. (#480) 2016-11-08 14:56:05 -08:00
67a5993926 fixing type in ciphers (#479) 2016-11-08 12:23:21 -08:00
aa979e31fd Digicert plugin (#478)
* Initial work on digicert plugin.

* Adding certificate pickup, to digicert plugin.

* Removing and rotating test api key.
2016-11-07 14:40:00 -08:00
b74df2b3e4 Minor changes for python3. (#477) 2016-11-07 14:33:07 -08:00
4afedaf537 Fixes (#476)
* Ensures that Vault can accept bytes and strings.

* Make restricted domains optional.

* Fixing notify flag.
2016-11-04 09:16:41 -07:00
2b79474060 Trying this to fix defaulting org to Netflix (#475) 2016-11-02 09:12:47 -07:00
a6360ebfe5 Adding pending certificate metric. (#473) 2016-11-01 14:24:45 -07:00
d99681904e Fixing test to take python3 into account. (#460)
* Fixing test to take python3 into account.
2016-10-31 17:02:08 -07:00
1ac1a44e83 San alt name (#468) 2016-10-31 11:00:15 -07:00
f990f92977 Fixing typo in documentation for LEMUR_DEFAULT_ORGANIZATIONAL_UNIT spelling (#467) 2016-10-27 20:26:28 -07:00
490d5b6e6c python2.x .base64url_decode has a single parameter and incoming data is utf-8.. need to convert so string (#463) 2016-10-26 00:50:00 -07:00
4b7fc8551c fix(web): send JSON for all errors (#464)
Configure werkzeug to output JSON error messages for the benefit of
downstream clients. This also allows for metrics collection in all cases
where werkzeug is outputting an exception.
2016-10-26 00:46:43 -07:00
cd9c112218 Implement a CFSSL issuer plugin (#452)
* Implement CFSSL issuer plugin

Implement a Lemur plugin for generating certificates from the open
source certificate authority CFSSL
(https://github.com/cloudflare/cfssl). The plugin interacts with CFSSL
through the CFSSL REST API. The CFSSL configuration is defined in the
lemur.conf.py property file using property names prefixed with "CFSSL_".

* Update documentation to include CFSSL plugin
2016-10-22 00:52:18 -07:00
a8f44944b1 Closes #415 2016-10-17 23:23:14 -07:00
d31c9b19ce Closes #412. Allows 'name' be a valid attribute to specify a role. (#457) 2016-10-16 03:56:13 -07:00
fb178866f4 Fixes an issue with the source tests failing. (#456) 2016-10-16 03:55:37 -07:00
f921b67fff Removing the ability to use spaces in custom names. (#455) 2016-10-15 04:56:25 -07:00
c367e4f73f Prevents the silencing of notifications that are actively deployed. (#454)
* Renaming 'active' to 'notify' as this is clearer and more aligned to what this value is actually controlling. 'active' is now a property that depends on whether any endpoints were found to be using the certificate. Also added logic for issue #405 disallowing for a certificates' notifications to be silenced when it is actively deployed on an endpoint.

* Adding migration script to alter 'active' column.
2016-10-15 00:12:11 -07:00
dcb18a57c4 Adds option to restrict certificate expiration dates to weekdays. (#453)
* Adding ability to restrict certificate creation to weekdays.

* Ensuring that we test for weekends.
2016-10-15 00:04:35 -07:00
1b861baf0a Updating gulp-protractor dependency (#451)
Following the quickstart instructions, I ran into issues at `make develop` because
several dependencies couldn't be resolved. Several errors like this in the output
of `npm install`:

```
npm ERR! TypeError: Cannot read property 'latest' of undefined
npm ERR!     at next (/usr/share/npm/lib/cache.js:687:35)
npm ERR!     at /usr/share/npm/lib/cache.js:675:5
npm ERR!     at saved (/usr/share/npm/node_modules/npm-registry-client/lib/get.js:142:7)
npm ERR!     at /usr/lib/nodejs/graceful-fs/polyfills.js:133:7
npm ERR!     at Object.oncomplete (fs.js:107:15)
npm ERR! If you need help, you may report this log at:
npm ERR!     <http://github.com/isaacs/npm/issues>
npm ERR! or email it to:
npm ERR!     <npm-@googlegroups.com>

npm ERR! System Linux 3.13.0-92-generic
npm ERR! command "/usr/bin/nodejs" "/usr/bin/npm" "install"
npm ERR! cwd /home/lemur/lemur
npm ERR! node -v v0.10.25
npm ERR! npm -v 1.3.10
npm ERR! type non_object_property_load
```

`npm list` yielded this output at the bottom:

```
npm ERR! missing: @types/jasmine@^2.2.31, required by protractor@4.0.9
npm ERR! missing: @types/node@^6.0.35, required by protractor@4.0.9
npm ERR! missing: @types/q@^0.0.30, required by protractor@4.0.9
npm ERR! missing: @types/selenium-webdriver@~2.53.30, required by protractor@4.0.9
npm ERR! missing: adm-zip@0.4.7, required by protractor@4.0.9
npm ERR! missing: chalk@^1.1.3, required by protractor@4.0.9
npm ERR! missing: glob@^7.0.3, required by protractor@4.0.9
npm ERR! missing: jasmine@2.5.2, required by protractor@4.0.9
npm ERR! missing: jasminewd2@0.0.10, required by protractor@4.0.9
npm ERR! missing: optimist@~0.6.0, required by protractor@4.0.9
npm ERR! missing: q@1.4.1, required by protractor@4.0.9
npm ERR! missing: saucelabs@~1.3.0, required by protractor@4.0.9
npm ERR! missing: selenium-webdriver@2.53.3, required by protractor@4.0.9
npm ERR! missing: source-map-support@~0.4.0, required by protractor@4.0.9
npm ERR! missing: webdriver-manager@^10.2.2, required by protractor@4.0.9
npm ERR! not ok code 0
```

lemur depends explicitly on gulp-protractor 0.0.11 explicitly
gulp-protractor 0.0.11 depends on protractor at _any_ version (*)
The latest versions of protractor (@4) require much newer versions of nodejs
according to the Compatibility section of
https://www.npmjs.com/package/protractor.

gulp-protractor 0.0.12 fixes and constrains some of these dependencies better
and adds a debug option, and fixes a few typos in comments and metadata.
https://github.com/mllrsohn/gulp-protractor/compare/0.0.11...0.0.12
2016-10-14 14:06:13 -07:00
10d833e598 Added Symantec plugin error checking for invalid domain suffix (#449) 2016-10-13 15:23:56 -07:00
708d85abeb Fixes a bug where certificates discovered by lemur's source plugins were not given the appropriate default notifications. (#447) 2016-10-11 21:08:13 -07:00
ee028382df Show only roles that the user is a member of, in list view, for other views show all roles such that certificates and authorities can be shared across teams/groups. (#446) 2016-10-11 17:56:38 -07:00
c05a49f8c9 Fixes an issuer where a member of a role is not able to add new users to said role. (#445) 2016-10-11 17:24:15 -07:00
35cfb50955 add variables to the documentation forwq oauth2 (#444) 2016-10-11 17:23:25 -07:00
f179e74a4a Fix Java export default password generator (#441)
When exporting a certificate, the password is an optional parameter.
When a password is not supplied by the caller, a default password is
generated by the method. The generation library creates the random
password as a bytes object. The bytes object raises an error in the
'keytool' command used to export the certificate. The keytool is
expecting the password to be a str object.

The fix is to decode the generated password from a bytes object to a str
object.

The associated Java plugin tests have been updated to verify the export
method returns the password as a str object. In addition, the tests have
been updated to correctly test the export methods response object. The
original tests treated the response as a single object. The current
export methods return a tuple of data (type, password, data).

In order to make the tests compatible with both Python2 and Python3, the
'six' library was used to test the password is in fact a string.
2016-10-10 22:43:23 -07:00
9065aa3750 Update the private key regex validation (#435)
* Update the private key regex validation

Private keys provided by the Let's Encrypt certificate authority as part
of their certificate bundle fail the import/upload certificate private
key validation. The validation is looking for a specific character
sequence at the begin of the certificate. In order to support valid
Let's Encrypt private keys, the regex has been updated to check for both
the existing sequence and the Let's Encrypt character sequence.

Example Let's Encrypt private key:

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvsiwV8A5+r0tQ
QzUAJO0DfoEb9tMWvoFi0DLs9tx88IwMqItPPl9+RNzQnv9qqZR1h4W97sxP8aWY
...
AeS667IJO/2DMKaGiEldaVZtgqdUhCL8Rm4XUFVb1GjLa03E4VRU6W7eQ4hgT2a7
cHDAR8MiovNyfT0fm8Xz3ac=
-----END PRIVATE KEY-----

* Add private key regex for footer

Update the import/upload private key validation regex to verify both the
header and footer are matching.
2016-10-10 22:42:09 -07:00
96e42c793e Refactors the default notification option. Also ensures that notifications and destinations are easier to test. (#437) 2016-10-09 00:06:53 -07:00
72a390c563 Ensure the openssl and cryptography work under python3. (#438) 2016-10-09 00:06:15 -07:00
a19c918c68 Closes #411 (#439) 2016-10-09 00:06:03 -07:00
c45c23ae6f Detect root (#440)
* Closes #430
2016-10-09 00:05:50 -07:00
5cbf5365c5 Active S3 destination plugin (#433)
* Activate the AWS S3 destination plugin

Add the AWS S3 destination plugin to the list of available Lemur
plugins.

Update the S3 destination plugin's "accountNumber" option to be of type
'str' to handle account numbers starting with zeros.

Update Lemur's utils for parsing certificates to correctly encode the
X509 certificates before loading for python3.

* Add S3 destination plugin test

Added simple test to verify S3 destination plugin is available.
2016-10-08 17:06:20 -07:00
3ad7a37f95 Fix import certificate private key encoding (#434)
When importing a certificate, the private key is passed to the
import/upload process from the UI as a str object. In Python3 this
raises two issues when processing the private key - the private key
validation fails and database insert of the certificate fails.

The fix in both cases is to correctly encode the private key as a bytes
object.
2016-10-08 17:04:54 -07:00
6cac2838e3 Fix for missing profile pic. (#429) 2016-09-27 13:02:01 -07:00
fbbf7f90f6 Fix test certificates module hanging issue (#427)
* Fix test certificates module hanging issue

When executing the lemur/tests/test_certificates.py module's tests, all
tests are executed, but the test process appears to hang and never
completes with the display of the results for the tests.

The hanging issue is traced to the two test methods:
test_import(logged_in_user) and test_upload(logged_in_user). The issue
has to do with the test methods' using the logged_in_user(app) fixture from
the conftest.py module as the method parameter.

The test methods at issue require the session, db, and app fixtures to
be initialized for the tests to complete successfully. The
logged_in_user() fixture only initializes the app fixture. Updating the
test_import() and test_upload() methods parameters to be the "session"
fixture fixes the hanging issue and the tests complete successfully.

This is the command being used to execute the tests...
$ py.test -s -v lemur/tests/test_certificates.py

* Update fix for test certificates hanging issue

Based on feedback from the original pull request for this fix, added the
session fixture to the logged_in_user fixture and reverted the
test_import() and test_upload() methods to use the logged_in_user
(instead of the session fixture).
2016-09-27 13:01:37 -07:00
1ea75a5d2d fix(certificates): import re module (#428) 2016-09-21 22:54:46 -07:00
3ce87c8a6b Fix code reference to older version of flake8 (#426)
The pre-commit module contained a reference to the variable
DEFAULT_CONFIG which does not exist in the version of flake8 packaged
with the project. The DEFAULT_CONFIG defines the path to the current
user's local flake8 config file.

The flake8 2.6.2 version packaged with project has been refactored to
set the local flake8 config file in the flake8/main.py module.
2016-09-18 11:05:29 -07:00
39645a1a84 feat(certificates): add support for restricted domains (#424)
Lemur's documentation already mentions LEMUR_RESTRICTED_DOMAINS, a list
of regular expressions matching domains only administrators can issue
certificates for. An option to mark domains as sensitive existed in the
API, however the configuration option was not implemented.

Now both ways of sensitivity are checked in the same place.
2016-09-12 16:59:14 -07:00
a60e372c5a Ensuring that password hashes are compared correctly under python3 2016-09-07 13:25:51 -07:00
76cece7b90 Ensuring that private keys are retrieved correctly under python3. (#422) 2016-09-07 12:34:50 -07:00
ca2944d566 Ensuring the inactive certificates are not alerted on. (#418) 2016-08-29 15:46:35 -07:00
53d0636574 Python3 (#417)
* Fixing tests.

* Fixing issue where decrypted credentials were not returning valid strings.

* Fixing issues with python3 authentication.
2016-08-29 08:58:53 -07:00
7e6278684c Python3 (#416)
* Fixing issue where decrypted credentials were not returning valid strings.
2016-08-26 16:02:23 -07:00
2d7a6ccf3c Owner email (#414)
* Ensuring python2 works with unicode strings.

* adding in owner DN

* fixing tests

* Upgrading requests.

* Fixing tests.
2016-08-25 10:09:46 -07:00
18b99c0de4 Fixing an issue where openssl can't find the certificates to create PKCS12 files (#408) 2016-08-17 10:33:59 -07:00
96674571a5 Fix a typo. UI -> API (#407) 2016-07-29 18:29:44 -07:00
29a330b1f4 Orphaned certificates (#406)
* Fixing whitespace.

* Fixing syncing.

* Fixing tests
2016-07-28 13:08:24 -07:00
a644f45625 Adding some simplified reporting. (#403)
* Adding issuance report.

* Fixing whitespace.
2016-07-27 12:41:32 -07:00
3db669b24d Ensuring that the temporary certificate is created correctly (#400) 2016-07-12 18:07:11 -07:00
f38868a97f Fixing various problems with the syncing of endpoints, throttling sta… (#398)
* Fixing various problems with the syncing of endpoints, throttling stale endpoints etc.
2016-07-12 08:40:49 -07:00
4f3dc5422c Allowing the role-user associated to be updated. (#396)
* Allowing the role-user associated to be updated.

* Fixing tests

* Fixing tests, for real.
2016-07-07 13:03:10 -07:00
1ba7181067 Fixed an issue were default notifications were added even when updati… (#395)
* Fixed an issue were default notifications were added even when updating a certificate, resulting in duplicate notifications.

* Ensuring imported certificates get the same treatment.
2016-07-07 11:44:11 -07:00
74bf54cb8f Slack spruce up (#394)
* Formatting slack message.

* Tweaking tests.
2016-07-06 10:27:13 -07:00
d4732d3ab0 Closes #335. (#392) 2016-07-04 16:08:16 -07:00
cb9631b122 Closes #356. (#391) 2016-07-04 15:38:51 -07:00
4077893d08 Ensuring that destinations require private keys by default. (#390)
* Ensuring that destinations require private keys by default.
2016-07-04 15:30:20 -07:00
4ee1c21144 Closes #372 (#389)
* Closes #372
2016-07-04 14:32:46 -07:00
c8eca56690 Closes #366 (#387) 2016-07-04 13:03:46 -07:00
300e2d0b7d Adding plugin tests. (#385)
* Adding plugin tests.

* Fixing some python 2/3 incompatibilities.
2016-07-01 11:32:19 -07:00
a8040777b3 Upgrading plugin docs with better example. (#386) 2016-07-01 10:50:18 -07:00
e34de921b6 Target Individuals for Certificates (#384)
* Allowing individual users to be targeted for a role.

* Ensuring that even new users get a per user-role
2016-07-01 09:04:39 -07:00
a04f707f63 Fixing readme badges (#382) 2016-06-30 09:06:14 -07:00
9aec899bfd Fixing a few errors.
* Fixing organizational_unit and common name

* FIxing organization name and allow creaters to view CA.
2016-06-29 16:16:37 -07:00
afb66df1a4 Adding plugin information to docs. (#379)
* Adding documentation about the installed plugins.

* Adding new default option.
2016-06-29 10:08:54 -07:00
54b888bb08 Adding a toy certificate authority. (#378) 2016-06-29 09:05:39 -07:00
eefff8497a Adding a new default issuer. 2016-06-28 17:46:26 -07:00
ecbab64c35 Adding endpoint migration script. (#376) 2016-06-28 16:12:56 -07:00
c8447dea3d Fixing a few issues with startup. (#374) 2016-06-28 14:28:05 -07:00
5021e8ba91 Adding ACME Support (#178) 2016-06-27 15:57:53 -07:00
f846d78778 S3 destination (#371) 2016-06-27 15:11:46 -07:00
fe9703dd94 Closes #284 (#336) 2016-06-27 14:40:46 -07:00
b44a7c73d8 Kubernetes desination plugin (#357)
* Kubernetes desination plugin

* fixing build warnings

* fixing build warnings
2016-06-27 14:40:01 -07:00
9ae27f1415 Merge pull request #368 from kevgliss/367-role-permission
Fixes #367
2016-06-23 13:44:46 -07:00
19b928d663 Fixes #367 2016-06-23 13:29:59 -07:00
5193342b3a Merge pull request #365 from kevgliss/docs
Updating flake8 ignore
2016-06-23 09:59:08 -07:00
109fb4bb45 Updating flake8 ignore 2016-06-23 09:40:55 -07:00
d6ccd812c2 Merge pull request #364 from kevgliss/docs
Updating requirements.txt
2016-06-23 09:20:56 -07:00
81a6228028 Updating requirements.txt 2016-06-23 09:20:35 -07:00
eeb216b75e Merge pull request #362 from kevgliss/docs
Fixing documentation requirement.
2016-06-22 14:05:13 -07:00
6714595fee Fixing documentation requirement. 2016-06-22 14:04:41 -07:00
025924c4f7 Merge pull request #361 from kevgliss/docs
Aadding an httpdomain version
2016-06-22 14:02:50 -07:00
7c10c8dac7 adding an httpdomain version 2016-06-22 13:59:32 -07:00
daea8f6ae4 Bug fixes (#355)
* we should not require password to update users

* Fixing an issue were roles would not be added.
2016-06-13 17:22:45 -07:00
41d1fe9191 Using UTC time in JWT token creation (#354)
As stated in PyJWT's documentation [1] and JWT specification [2][3], UTC
times must be used. This commit fixes JWT decoding in servers not using
UTC time.

[1] https://pypi.python.org/pypi/PyJWT/1.4.0
[2] https://tools.ietf.org/html/rfc7519#section-4.1.6
[3] https://tools.ietf.org/html/rfc7519#section-2
2016-06-13 11:18:07 -07:00
7d50e4d65f Merge pull request #353 from mikegrima/issue352
Fix for Issue #352.
2016-06-09 15:13:37 -07:00
9a653403ae Fix for Issue #352. 2016-06-08 16:41:31 -07:00
77f13c9edb Fixing issue were, after a user changes their mind validity years wil… (#349) 2016-06-06 12:11:40 -07:00
d95b1a0a41 release bump (#348) 2016-06-06 09:01:19 -07:00
d9cc4980e8 Fixing destination upload. (#347)
* Fixing an issue where uploaded certificates would have a name of 'None'

* Clarifying comment.

* Improving order.
2016-06-03 18:45:58 -07:00
5e987fa8b6 Adding additional data migrations. (#346) 2016-06-03 17:56:32 -07:00
42001be9ec Fixing the way filters were toggled. (#345) 2016-06-03 09:24:17 -07:00
dc198fec8c Docs (#344)
* Adding release info.

* adding some fields

* Adding Source Plugin change.

* Updating docs
2016-06-03 08:28:09 -07:00
acd47d5ec9 Fixing an issue were authorities were not related to their roles (#342) 2016-06-02 09:07:17 -07:00
72e3fb5bfe Fixing several small issues. (#341)
* Fixing several small issues.

* Fixing tests.
2016-06-01 11:18:00 -07:00
b2539b843b Fixing and error causing duplicate roles to be created. (#339)
* Fixing and error causing duplicate roles to be created.

* Fixing python3

* Fixing python2 and python3
2016-05-31 15:44:54 -07:00
be5dff8472 Adding a visualization for authorities. (#338)
* Adding a visualization for authorities.

* Fixing some lint.

* Fixing some lint.
2016-05-30 21:52:34 -07:00
76037e8b3a Fixing certificate names. (#337) 2016-05-27 12:00:10 -07:00
11f4bd503b Fixes (#332)
* Ensuring domains are returned correctly.

* Ensuring certificates receive owner role
2016-05-24 17:10:19 -07:00
6688b279e7 Fixing some bad renaming. (#331) 2016-05-24 10:43:40 -07:00
1ca38015bc Fixes (#329)
* Modifying the way roles are assigned.

* Adding migration scripts.

* Adding endpoints field for future use.

* Fixing dropdowns.
2016-05-23 18:38:04 -07:00
656269ff17 Closes #147 (#328)
* Closes #147

* Fixing tests

* Ensuring we can validate max dates.
2016-05-23 11:28:25 -07:00
bd727b825d Making roles more apparent for certificates and authorities. (#327) 2016-05-20 12:48:12 -07:00
e04c1e7dc9 Fixing a few things, adding tests. (#326) 2016-05-20 09:03:34 -07:00
615df76dd5 Closes 262 (#324)
Moves the authority -> role relationship from a 1 -> many to a many -> many. This will allow one role to control and have access to many authorities.
2016-05-19 13:37:05 -07:00
112c6252d6 Adding password reset command to the cli. (#325) 2016-05-19 10:07:15 -07:00
b13370bf0d Making dropdowns look a bit better. (#322)
* Making dropdowns look a bit better.

* Pleasing Lint.
2016-05-19 09:04:50 -07:00
88aa5d3fdb Making nested notifications less verbose (#321) 2016-05-19 08:48:55 -07:00
b187d8f836 Adding a better comparison. (#320) 2016-05-16 19:03:10 -07:00
1763a1a717 254 duplication certificate name (#319) 2016-05-16 15:59:40 -07:00
62b61ed980 Fixing various issues. (#318)
* Fixing various issues.

* Fixing tests
2016-05-16 11:09:50 -07:00
c11034b9bc Fixes various issues. (#317) 2016-05-16 09:23:48 -07:00
58e8fe0bd0 Fixes various issues. (#316) 2016-05-13 14:35:38 -07:00
a0c8765588 Various bug fixes. (#314) 2016-05-12 12:38:44 -07:00
9022059dc6 Marshmallowing roles (#313) 2016-05-10 14:22:22 -07:00
7f790be1e4 Marsmallowing users (#312) 2016-05-10 14:19:24 -07:00
93791c999d Marsmallowing destinations (#311) 2016-05-10 13:43:26 -07:00
5e9f1437ad Marsmallowing sources (#310) 2016-05-10 13:16:33 -07:00
f9655213b3 Marshmallowing notifications. (#308) 2016-05-10 11:27:57 -07:00
008d608ec4 Fixing error in notifications. (#307) 2016-05-09 17:35:18 -07:00
78c8d12ad8 Cleaning up the way authorities are selected and upgrading uib dependencies. 2016-05-09 17:17:00 -07:00
df0ad4d875 Authorities marshmallow addition (#303) 2016-05-09 11:00:16 -07:00
776e0fcd11 Slack plugin for notifications (#305) 2016-05-08 09:07:16 -07:00
6ec3bad49a Closes #278 (#298)
* Closes #278
2016-05-05 15:28:17 -07:00
52f44c3ea6 Closes #278 and #199, Starting transition to marshmallow (#299)
* Closes #278  and #199, Starting transition to marshmallow
2016-05-05 12:52:08 -07:00
941d36ebfe Merge pull request #302 from kevgliss/301-p12-no-chain
Closes #301
2016-05-04 17:07:42 -07:00
db8243b4b4 Closes #301 2016-05-04 16:56:05 -07:00
f919b7360e Merge pull request #294 from kevgliss/regex
Regex
2016-04-25 17:20:52 -07:00
8e1b7c0036 Removing validation because regex is hard 2016-04-25 16:13:33 -07:00
9b0e0fa9c2 removing validtion from openssl 2016-04-25 16:11:37 -07:00
565d7afa92 Merge pull request #293 from kevgliss/devdocs
Fixes #291
2016-04-25 12:30:54 -07:00
c914ba946f Merge pull request #292 from kevgliss/docs
Fixes #285 Renames sync_sources function to sync to align documentation.
2016-04-25 12:16:47 -07:00
6f9280f64a Adding gulp path 2016-04-25 12:16:33 -07:00
8fe460e401 Fixes #291 2016-04-25 11:34:05 -07:00
b9fe359d23 Fixes #285 Renames sync_sources function to sync to align documentation. 2016-04-25 11:21:25 -07:00
2c6d494c32 Merge pull request #290 from kevgliss/289-java-export-intermediates
Fixes #289 and #275
2016-04-21 16:46:11 -07:00
dbd1279226 Fixes #289 and #275 2016-04-21 16:22:19 -07:00
b463fcf61b Merge pull request #280 from kevgliss/SAN-hotfix
Fixes an issue where custom OIDs would clear out san extensions
2016-04-11 12:04:24 -07:00
82b4f5125d Fixes an issue where custom OIDs would clear out san extensions 2016-04-11 11:17:18 -07:00
3f89d6d009 Merge pull request #271 from kevgliss/195
Closes #195
2016-04-08 12:01:10 -07:00
676f843c92 Merge pull request #276 from kevgliss/san-hotfix
Fixes an issue where custom OIDs would clear out san extensions
2016-04-07 10:30:12 -07:00
c2387dc120 Fixes an issue where custom OIDs would clear out san extensions 2016-04-07 10:29:08 -07:00
9a8e1534c0 Merge pull request #274 from kevgliss/metric_fix
Fixing an issue were metrics would not be sent
2016-04-05 10:50:46 -07:00
dbc4964e94 Fixing an issue were metrics would not be sent 2016-04-05 10:23:33 -07:00
00b263f345 Merge pull request #273 from kevgliss/216
Closes #216
2016-04-01 16:59:49 -07:00
62d03b0d41 Closes #216 2016-04-01 16:54:33 -07:00
b5a4b293a9 Merge pull request #270 from kevgliss/248
Closes #248
2016-04-01 14:28:52 -07:00
bfcfdb83a7 Closes #195 2016-04-01 14:27:57 -07:00
4ccbfa8164 Closes #248 2016-04-01 13:29:08 -07:00
675d10c8a6 Merge pull request #269 from kevgliss/263
Closes #263
2016-04-01 13:08:13 -07:00
2cde7336dc Closes #263 2016-04-01 13:01:56 -07:00
169490dbec Merge pull request #268 from kevgliss/252
Closes #252
2016-04-01 10:16:10 -07:00
3ceb297276 Merge pull request #267 from kevgliss/261
Closes #261
2016-04-01 10:12:10 -07:00
12633bfed6 Merge pull request #266 from kevgliss/tox
removing testing support for py33
2016-04-01 10:11:59 -07:00
5958bac2a2 Merge pull request #265 from kevgliss/257
Closes #257
2016-04-01 10:11:32 -07:00
37f2d5b8b0 Closes #252 2016-04-01 10:09:28 -07:00
47891d2953 Closes #261 2016-04-01 09:58:19 -07:00
af68571f4e removing testing support for py33 2016-04-01 09:52:19 -07:00
d0ec925ca3 Merge pull request #264 from kevgliss/246
Closes #246
2016-04-01 09:51:10 -07:00
939194158a Closes #257 2016-04-01 09:49:44 -07:00
576265e09c Closes #246 2016-04-01 09:19:36 -07:00
dfaf45344c Merge pull request #250 from lfaraone/patch-1
Remove duplicate `install` in Quickstart
2016-03-01 09:21:04 -08:00
6c378957e9 Remove duplicate install in Quickstart 2016-03-01 04:12:10 +00:00
e8f9bc80a0 Merge pull request #249 from kevgliss/master
Updating docs
2016-02-29 12:51:47 -08:00
a30b8b21e4 updating postgres login 2016-02-29 08:53:35 -08:00
12204852aa changeing the default port to 8000 2016-02-29 08:48:27 -08:00
edba980b56 Merge pull request #245 from mikegrima/issue243
Removed deprecated auth api endpoint.
2016-02-16 17:11:41 -08:00
ba666ddbfa Removed deprecated auth api endpoint. 2016-02-16 15:04:53 -08:00
35f9f59c57 Merge pull request #242 from kevgliss/version_bump
version bump
2016-02-05 13:13:01 -08:00
ac1f493338 version bump 2016-02-05 13:12:21 -08:00
1c3c70d460 Merge pull request #241 from kevgliss/0.2.2.release
adding changelog
2016-02-05 13:01:35 -08:00
e8e7bdf9e0 adding changelog 2016-02-05 13:00:59 -08:00
d263e0e60c Merge pull request #240 from kevgliss/234-truststore-permission
Adding a new flag to export plugins 'requires_key' that specifies whe…
2016-01-29 12:55:41 -08:00
028d86c0bb Adding a new flag to export plugins 'requires_key' that specifies whether the export plugin needs access to the private key. Defaults to True. 2016-01-29 12:45:18 -08:00
f8b6830013 Merge pull request #239 from kevgliss/228-filter-values
Fixing documentation for filter format
2016-01-29 11:54:13 -08:00
49a40c50e8 Merge pull request #238 from kevgliss/231-authority-owner
associating new authorities with the owner roles
2016-01-29 11:47:56 -08:00
2ba48995fe Fixing documentation for filter format 2016-01-29 11:47:16 -08:00
3cc8ade6d8 associating new authorities with the owner roles 2016-01-29 10:59:04 -08:00
39c9a0a299 Merge pull request #237 from kevgliss/218_password_regex
relaxing keystore password validation
2016-01-29 10:37:49 -08:00
3ad317fb6d Merge pull request #236 from kevgliss/migration_script_fixups
Removing per 2.0 migration scripts
2016-01-29 10:30:41 -08:00
bd46440d12 relaxing keystore password validation 2016-01-29 10:29:04 -08:00
f3a28814ae Merge pull request #235 from kevgliss/226_replaces
Makes 'replacements' a non-required attribute for importing. Closes #226
2016-01-29 09:42:42 -08:00
9f8f64b9ec removing pre 2.0 migration scripts, and adding documentation for correct path during init 2016-01-29 09:22:12 -08:00
1e524a49c0 making 'replacements' a non-require attribute for importing. Closes #226 2016-01-29 09:02:51 -08:00
467c276fca Merge pull request #227 from AlexCline/fix_postinstall_for_224
Use the local bower instead of the global one.
2016-01-20 16:35:00 -08:00
f610e39418 Use the local bower instead of the global one.
This change updates package.json's postinstall command to use the
locally installed bower, rather than the global bower which might
not exist or might not be in the current user's PATH.
2016-01-20 17:10:41 -05:00
27d977b2fa Merge pull request #214 from ebgcdev/master
Minor spelling fix
2016-01-13 09:21:36 -08:00
b36e72bfcc Minor spelling fix
Using the possessive “Your” rather than “You’re” in “Your passphrase
is:”
2016-01-12 22:04:42 -08:00
e49701228d Merge pull request #212 from kevgliss/rolling
Adding a rolling metric count
2016-01-11 15:34:20 -08:00
48f8b33d7d Adding a rolling metric count 2016-01-11 15:26:32 -08:00
d87ace8c89 Merge pull request #211 from kevgliss/hotfix
fixing an issue were urllib does not like unicode
2016-01-11 10:38:45 -08:00
b1326d4145 fixing an issue were urllib does not like unicode 2016-01-11 10:31:58 -08:00
7c2862c958 Merge pull request #210 from kevgliss/hotfix
Fixes an assumption that 'subAltNames' are always passed to the API.
2016-01-11 09:08:38 -08:00
0a4f5ad64d Fixing an assumption that 'subAltNames' are always passed to the API. 2016-01-10 17:33:19 -08:00
c617a11c55 Merge pull request #209 from kevgliss/migrate_chain
Adding command to transparently rotate the chain on an ELB
2016-01-10 14:37:29 -08:00
053167965a Adding command to transparently rotate the chain on an ELB 2016-01-10 14:20:36 -08:00
a7ac45b937 Merge pull request #206 from kevgliss/syncing
Fixing issue where we were seeing AWS API errors due to certificates …
2016-01-08 16:39:51 -08:00
5482bbf4bd Fixing issue where we were seeing AWS API errors due to certificates not having private keys and could not be uploaded or 'synced' 2016-01-07 13:42:46 -08:00
0a58e106b5 Merge pull request #205 from rpicard/rpicard/fixgooglesso
Fix how the provider settings are passed to Satellizer
2016-01-05 17:31:35 -08:00
a1395a5808 Fix how the provider settings are passed to Satellizer 2016-01-05 17:26:09 -08:00
a0d50ef03a Merge pull request #203 from kevgliss/ssoHostfix
fixing typo
2016-01-05 09:41:12 -08:00
685e2c8b6d fixing typo 2016-01-05 09:40:53 -08:00
c6d9a20fe5 Merge pull request #202 from kevgliss/hotfix
reverting depedency
2016-01-04 13:58:36 -08:00
4a952d867b reverting depedency 2016-01-04 13:58:12 -08:00
cb4cf43fcf Merge pull request #201 from kevgliss/hotfix
Fixing setup.py
2016-01-04 11:48:19 -08:00
1bce7a832b Fixing setup.py 2016-01-04 11:46:07 -08:00
574234f70f Merge pull request #200 from kevgliss/requirements
updating dependencies
2016-01-04 10:41:24 -08:00
42e5470dd0 updating dependencies 2016-01-04 10:36:39 -08:00
8199365324 Merge pull request #194 from CameronNemo/patch-1
docs/quickstart: fix port number
2015-12-31 14:27:18 -08:00
86c92eb31e docs/quickstart: fix port number 2015-12-31 12:57:18 -08:00
d9fd952c03 Merge pull request #193 from kevgliss/docs
Improving documentation layout
2015-12-31 11:21:48 -08:00
967c7ded8d Improving documentation layout 2015-12-31 11:12:56 -08:00
a4bf847b56 Merge pull request #192 from kevgliss/sensitive-domains
Adds ability for domains to be marked as sensitive and only be allowe…
2015-12-30 15:36:31 -08:00
d6917155e8 Fixing tests 2015-12-30 15:32:01 -08:00
3f024c1ef4 Adds ability for domains to be marked as sensitive and only be allowed to be issued by an admin closes #5 2015-12-30 15:11:08 -08:00
96d253f0f9 Merge pull request #191 from kevgliss/bump
version bump
2015-12-30 09:15:30 -08:00
9b166fb9a9 version bump 2015-12-30 09:15:11 -08:00
b8ae8cd452 Merge pull request #190 from kevgliss/0.2.1
0.2.1 release info
2015-12-30 09:11:46 -08:00
ca82b227b9 0.2.1 release info 2015-12-30 09:11:19 -08:00
862496495f Merge pull request #189 from m4c3/patch-1
Define ACTIVE_PROVIDERS in default config
2015-12-30 08:50:05 -08:00
8bb9a8c5d1 Define ACTIVE_PROVIDERS in default config
The configuration item ACTIVE_PROVIDERS must be initialized

Workaround for this error:
2015-12-30 13:58:48,073 ERROR: Internal Error [in /www/lemur/local/lib/python2.7/site-packages/flask_restful/__init__.py:299]
Traceback (most recent call last):
  File "/www/lemur/local/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/www/lemur/local/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/www/lemur/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 462, in wrapper
    resp = resource(*args, **kwargs)
  File "/www/lemur/local/lib/python2.7/site-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/www/lemur/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 572, in dispatch_request
    resp = meth(*args, **kwargs)
  File "/www/lemur/lemur/auth/views.py", line 276, in get
    for provider in current_app.config.get("ACTIVE_PROVIDERS"):
TypeError: 'NoneType' object is not iterable
2015-12-30 14:56:59 +01:00
00cb66484b Merge pull request #188 from kevgliss/csr
Adding the ability to submit a third party CSR
2015-12-29 12:11:11 -08:00
cabe2ae18d Adding the ability to issue third party created CSRs 2015-12-29 10:49:33 -08:00
665a3f3180 Merge pull request #187 from kevgliss/sso
Fixing some issues with dynamically supporting multiple SSO providers
2015-12-27 22:06:31 -05:00
3b5d7eaab6 More Linting 2015-12-27 18:08:17 -05:00
aa2358aa03 Fixing linting 2015-12-27 18:02:38 -05:00
a7decc1948 Fixing some issues with dynamically supporting multiple SSO providers 2015-12-27 17:54:11 -05:00
38b48604f3 Merge pull request #186 from rpicard/master
Add Google SSO
2015-12-27 15:30:52 -05:00
60856cb7b9 Add an endpoint to return active authentication providers
This endpoint can be used by Angular to figure out what authentication
options to display to the user. It returns a dictionary of configuration
details that the front-end needs for each provider.
2015-12-22 18:03:56 -05:00
350d013043 Add Google SSO
This pull request adds Google SSO support. There are two main changes:

1. Add the Google auth view resource
2. Make passwords optional when creating a new user. This allows an admin
to create a user without a password so that they can only login via Google.
2015-12-22 13:44:30 -05:00
70c92fea15 Merge pull request #183 from kevgliss/rotate
Adding rotate command
2015-12-18 12:05:52 -05:00
6211b126a9 Fixing py3 syntax error 2015-12-18 11:01:08 -05:00
54c3fcc72a Adding rotate command 2015-12-17 23:17:27 -05:00
27c9088ddb Merge pull request #182 from kevgliss/176-p12
Closes #176
2015-12-17 15:01:54 -08:00
b8c2d42cad Closes #176 2015-12-17 14:52:20 -08:00
1f5ddd9530 Merge pull request #181 from kevgliss/172-export
Closes #172
2015-12-17 14:35:55 -08:00
2896ce0dad Closes #172 2015-12-16 08:18:01 -08:00
29bcde145c 0.2.1 release 2015-12-14 10:42:51 -08:00
11db429bcc adding OSSMETADATA for NetflixOSS tracking 2015-12-11 15:57:28 -08:00
75aea9f885 Merge pull request #179 from rpicard/master
Update example supervisor configuration file
2015-12-10 18:31:02 -08:00
c80559005f Update example supervisor configuration file
supervisord should run as root and spawn the lemur process as the lemur
user. I also added the LEMUR_CONF environment variable because it was
not reading the configuration file in by default.
2015-12-10 17:39:49 -08:00
9b927cfcc2 Merge pull request #177 from kevgliss/docs
clarifying upgrade process
2015-12-09 17:29:13 -08:00
4db7931aa0 clarifying upgrade process 2015-12-09 17:18:01 -08:00
1e67329c64 Merge pull request #175 from kevgliss/notifications
Fixing templates
2015-12-04 09:57:42 -08:00
6d17e4d538 Fixing templates 2015-12-04 09:51:38 -08:00
350f58ec9d Merge pull request #174 from kevgliss/binding
Disabling one-time binding
2015-12-03 17:16:19 -08:00
de9478a992 Disabling one-time binding 2015-12-03 16:57:37 -08:00
70a2c985cf Merge pull request #171 from kevgliss/packaging
Fixing the startup port
2015-12-02 17:14:24 -08:00
78037dc9ec Fixing the startup port 2015-12-02 17:13:52 -08:00
9b11efd1e5 Merge pull request #170 from kevgliss/export
Adding export plugin docs
2015-12-02 16:05:36 -08:00
3c2ee8fbb3 Adding export plugin docs 2015-12-02 16:04:40 -08:00
163cc3f795 Merge pull request #169 from kevgliss/bump
Version bump
2015-12-02 14:54:17 -08:00
041382b02f Version bump 2015-12-02 14:53:46 -08:00
837bfc3aa5 Merge pull request #167 from forkd/master
minor changes in quickstart guide
2015-12-02 14:51:07 -08:00
5ba1176f14 Merge pull request #168 from kevgliss/changelog
Changelog
2015-12-02 14:50:42 -08:00
f08649b02d Updating change log 2015-12-02 14:50:14 -08:00
edbe5a254b minor changes in quickstart guide 2015-12-02 14:34:22 +00:00
cfedb30628 Merge pull request #166 from kevgliss/template
Making the notification email template cleaner
2015-12-01 18:16:54 -08:00
aa18b88a61 Making the notification email template cleaner 2015-12-01 17:13:43 -08:00
05962e71e3 Merge branch 'forkd-master' 2015-12-01 13:03:23 -08:00
bafc3d0082 minor adjustments 2015-12-01 13:03:08 -08:00
308f1b44c3 Merge branch 'master' of git://github.com/forkd/lemur into forkd-master 2015-12-01 13:01:54 -08:00
cd17789529 Removing unneeded import 2015-12-01 11:51:39 -08:00
bf988d89c4 updated quickstart guide 2015-12-01 19:03:17 +00:00
b1e842ae47 Merge pull request #162 from kevgliss/160-startup
Closes #160
2015-12-01 10:08:03 -08:00
fcc3c35ae2 Merge pull request #163 from kevgliss/docs
Updating docs
2015-12-01 10:07:37 -08:00
e2524e43cf adding exports 2015-12-01 09:44:41 -08:00
6aac2d62be Closes #160 2015-12-01 09:40:27 -08:00
95e2636f23 Updating docs 2015-12-01 09:15:53 -08:00
7565492bb9 Merge pull request #161 from kevgliss/docs
adding version.py
2015-12-01 08:34:25 -08:00
89f7f12f92 adding version.py 2015-12-01 08:33:37 -08:00
cdd15ca818 Merge pull request #159 from kevgliss/migrations
Adding current migration files.
2015-11-30 15:44:14 -08:00
11f2d88b16 Adding current migration files. 2015-11-30 15:43:38 -08:00
8066d540e0 Merge pull request #158 from kevgliss/fix
Fix
2015-11-30 14:27:23 -08:00
c3091a7346 Adding missing files. 2015-11-30 14:08:17 -08:00
9cadebcd50 adding example requests 2015-11-30 13:51:27 -08:00
3e54eb7520 adding closed 2015-11-30 12:51:28 -08:00
068f19c895 Merge pull request #156 from kevgliss/bump
adding automatic versioning
2015-11-30 12:09:33 -08:00
3651cce542 adding automatic versioning 2015-11-30 10:43:41 -08:00
9e0b9d9dda Merge pull request #154 from kevgliss/125-output-plugins
Initial work on #125
2015-11-30 10:31:25 -08:00
f194e2a1be Linting 2015-11-30 10:24:53 -08:00
f56c6f2836 Downgrading req to pass tests. 2015-11-30 10:10:50 -08:00
ec896461a7 Adding final touches to #125 2015-11-30 09:47:36 -08:00
8eeed821d3 Adding UI elements 2015-11-27 13:27:14 -08:00
80c1689b24 Merge pull request #155 from Joe8Bit/master
Fix requires.io badge
2015-11-27 10:21:33 -08:00
7c29b566be Update README.rst
Update README.rst to point to correct requires.io badge
2015-11-27 14:37:23 +00:00
920d595c12 Initial work on #125 2015-11-25 14:54:08 -08:00
5d7174b2a7 Merge pull request #153 from Netflix/requires-io-master
[requires.io] dependency update on master branch
2015-11-25 14:44:31 -08:00
3c60f47e3f [requires.io] dependency update 2015-11-25 14:18:01 -08:00
c4abc59673 [requires.io] dependency update 2015-11-25 14:18:00 -08:00
ff4cdd82ee Merge pull request #152 from kevgliss/144-search
Closes #144
2015-11-24 16:13:05 -08:00
1c6e9caa40 Closes #144 2015-11-24 16:07:44 -08:00
07ec04ddc6 Merge pull request #151 from kevgliss/122-replacement
Closes #122
2015-11-24 15:01:39 -08:00
d6b3f5af81 Closes #122 2015-11-24 14:53:22 -08:00
ce1fe9321c Merge pull request #150 from kevgliss/121-validation
121 validation
2015-11-23 16:48:40 -08:00
2c88e4e3ba fixing conflict 2015-11-23 16:42:14 -08:00
fed37c9dc0 Fixes badge 2015-11-23 16:41:31 -08:00
e14eefdc31 Added the ability to find an authority even if a user only types the name in and does not select it. 2015-11-23 16:41:31 -08:00
2525d369d4 Merge pull request #149 from kevgliss/requirements2
Updating requirements
2015-11-23 15:59:58 -08:00
0600481a67 Updating requirements 2015-11-23 15:41:11 -08:00
f0324e4755 Merge pull request #148 from kevgliss/120-error-length
Closes #120
2015-11-23 15:25:30 -08:00
00f0f957c0 Lint again 2015-11-23 15:13:18 -08:00
9c652d784d Merge pull request #143 from kevgliss/requirements
Updating requirements
2015-11-23 14:59:31 -08:00
eb2fa74661 Fixing test 2015-11-23 14:49:05 -08:00
146c599deb Lint cleanup 2015-11-23 14:47:34 -08:00
574c4033ab Closes #120 2015-11-23 14:30:23 -08:00
9f122eec18 Merge pull request #145 from kevgliss/140-permalink
Closes #140
2015-11-23 12:02:49 -08:00
eb0f6a04d8 Closes #140 2015-11-23 10:43:07 -08:00
c7230befe4 Merge pull request #142 from kevgliss/139-description
Closes #139
2015-11-23 10:27:04 -08:00
9a316ae1a9 Updating requirements 2015-11-23 10:23:23 -08:00
df4364714e Closes #139 2015-11-23 09:53:55 -08:00
a1cd2b39eb Merge pull request #135 from mikegrima/travis-tweak
Removed un-needed build step.
2015-10-29 13:34:50 -07:00
8a7a15a361 Merge pull request #132 from cloughrm/master
Use american english for consistency
2015-10-29 13:34:31 -07:00
2cdeecb6e0 Merge pull request #134 from Netflix/monkeysecurity-patch-1
Removing hyphen from in-active.
2015-10-29 13:33:49 -07:00
638e4a5ac1 Removed un-needed build step.
I hope they have size 'M'.
2015-10-29 13:02:58 -07:00
93b4ef5f17 Removing hyphen from in-active.
`inactive` is a word.  in-active is ... something else.
2015-10-29 11:54:00 -07:00
26d490f74a Merge pull request #133 from belladzaster/grammer
Fixing grammar
2015-10-28 21:53:19 -07:00
01a1190524 Fixing grammer 2015-10-28 19:55:08 -07:00
2073090628 Use american english for consistency 2015-10-28 19:39:10 -07:00
6d00cb208d Merge pull request #131 from belladzaster/master
Fixing Typos
2015-10-28 19:32:08 -07:00
13b9bf687d Fixing Typos 2015-10-28 18:24:31 -07:00
461 changed files with 42363 additions and 8778 deletions

5
.coveragerc Normal file
View File

@ -0,0 +1,5 @@
[report]
include = lemur/*.py
omit = lemur/migrations/*
lemur/tests/*

3
.gitattributes vendored
View File

@ -1 +1,2 @@
* text=auto
* text=auto
version.py export-subst

15
.gitignore vendored
View File

@ -1,3 +1,4 @@
/.cache
.coverage
.tox
.DS_Store
@ -9,9 +10,11 @@
*.db
*.pid
*.enc
*.env
MANIFEST
test.conf
pip-log.txt
package-lock.json
/htmlcov
/cover
/build
@ -23,8 +26,16 @@ pip-log.txt
/lemur/static/dist/
/lemur/static/app/vendor/
/wheelhouse
/lemur/lib
/lemur/bin
/lemur/lib64
/lemur/include
docs/_build
.editorconfig
.idea
test.conf
lemur/tests/tmp
.pytest_cache
lemur/tests/tmp
/lemur/plugins/lemur_email/tests/expiration-rendered.html
/lemur/plugins/lemur_email/tests/rotation-rendered.html

View File

@ -8,7 +8,7 @@
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"latedef": false,
"newcap": false,
"noarg": true,
"quotmark": "single",
@ -22,6 +22,8 @@
"angular": false,
"moment": false,
"toaster": false,
"d3": false,
"self": false,
"_": false
}
}

24
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,24 @@
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: v0.9.1
hooks:
- id: trailing-whitespace
- id: flake8
- id: check-merge-conflict
- repo: git://github.com/pre-commit/mirrors-jshint
sha: v2.9.5
hooks:
- id: jshint
- repo: https://github.com/ambv/black
rev: stable
hooks:
- id: black
language_version: python3.7
- repo: local
hooks:
- id: python-bandit-vulnerability-check
name: bandit
entry: bandit
args: ['--ini', 'tox.ini', '-r', 'consoleme']
language: system
pass_filenames: false

View File

@ -1,18 +1,16 @@
sudo: false
language: python
dist: bionic
node_js:
- "6.2.0"
addons:
postgresql: "9.4"
matrix:
include:
- python: "2.7"
env: TOXENV=py27
- python: "3.3"
env: TOXENV=py33
- python: "3.4"
env: TOXENV=py34
- python: "3.7"
env: TOXENV=py37
cache:
directories:
@ -22,18 +20,34 @@ cache:
env:
global:
- PIP_DOWNLOAD_CACHE=".pip_download_cache"
install:
- make dev-postgres
# The following line is a temporary workaround for this issue: https://github.com/pypa/setuptools/issues/2230
- SETUPTOOLS_USE_DISTUTILS=stdlib
# do not load /etc/boto.cfg with Python 3 incompatible plugin
# https://github.com/travis-ci/travis-ci/issues/5246#issuecomment-166460882
- BOTO_CONFIG=/doesnotexist
before_script:
- psql -c "create database lemur;" -U postgres
- psql -c "create user lemur with password 'lemur;'" -U postgres
- psql lemur -c "create extension IF NOT EXISTS pg_trgm;" -U postgres
- npm config set registry https://registry.npmjs.org
- npm install -g bower
- pip install --upgrade setuptools
install:
- pip install coveralls
- pip install bandit
script:
- make test
- bandit -r . -ll -ii -x lemur/tests/,docs
after_success:
- coveralls
notifications:
email:
kglisson@netflix.com
recipients:
- lemur@netflix.com
on_success: never
on_failure: always

View File

@ -1,15 +1,319 @@
Changelog
=========
0.2.0 - `master` _
~~~~~~~~~~~~~~~~~~~
0.8.0 - `2020-11-13`
~~~~~~~~~~~~~~
.. note:: This version not yet released and is under active development
This release comes after more than two years and contains many interesting new features and improvements.
In addition to multiple new plugins, such as ACME-http01, ADCS, PowerDNS, UltraDNS, Entrust, SNS, many of Lemur's existing
flows have improved.
In the future, we plan to do frequent releases.
Summary of notable changes:
- AWS S3 plugin: added delete, get methods, and support for uploading/deleting acme tokens
- ACME plugin:
- revamp of the plugin
- support for http01 domain validation, via S3 and SFTP as destination for the acme token
- support for CNAME delegated domain validation
- store-acme-account-details
- PowerDNS plugin
- UltraDNS plugin
- ADCS plugin
- SNS plugin
- Entrust plugin
- Rotation:
- respecting keyType and extensions
- region-by-region rotation option
- default to auto-rotate when cert attached to endpoint
- default to 1y validity during rotation for multi-year browser-trusted certs
- Certificate: search_by_name, and important performance improvements
- UI
- reducing the EC curve options to the relevant ones
- edit option for notifications, destinations and sources
- showing 13 month validity as default
- option to hide certs expired since 3month
- faster Permalink (no search involved)
- commonName Auto Added as DNS in the UI
- improved search and cert lookup
- celery tasks instead of crone, for better logging and monitoring
- countless bugfixes
- group-lookup-fix-referral
- url_context_path
- duplicate notification
- digicert-time-bug-fix
- improved-csr-support
- fix-cryptography-intermediate-ca
- enhanced logging
- vault-k8s-auth
- cfssl-key-fix
- cert-sync-endpoint-find-by-hash
- nlb-naming-bug
- fix_vault_api_v2_append
- aid_openid_roles_provider_integration
- rewrite-java-keystore-use-pyjks
- vault_kv2
To see the full list of changes, you can run
$ git log --merges --first-parent master --pretty=format:"%h %<(10,trunc)%aN %C(white)%<(15)%ar%Creset %C(red bold)%<(15)%D%Creset %s" | grep -v "depend"
Special thanks to all who contributed to this release, notably:
- `peschmae <https://github.com/peschmae>`_
- `sirferl <https://github.com/sirferl>`_
- `lukasmrtvy <https://github.com/lukasmrtvy>`_
- `intgr <https://github.com/intgr>`_
- `kush-bavishi <https://github.com/kush-bavishi>`_
- `alwaysjolley <https://github.com/alwaysjolley>`_
- `jplana <https://github.com/jplana>`_
- `explody <https://github.com/explody>`_
- `titouanc <https://github.com/titouanc>`_
- `jramosf <https://github.com/jramosf>`_
Upgrading
---------
.. note:: This release will need a migration change. Please follow the `documentation <https://lemur.readthedocs.io/en/latest/administration.html#upgrading-lemur>`_ to upgrade Lemur.
0.7 - `2018-05-07`
~~~~~~~~~~~~~~
This release adds LetsEncrypt support with DNS providers Dyn, Route53, and Cloudflare, and expands on the pending certificate functionality.
The linux_dst plugin will also be deprecated and removed.
The pending_dns_authorizations and dns_providers tables were created. New columns
were added to the certificates and pending_certificates tables, (For the DNS provider ID), and authorities (For options).
Please run a database migration when upgrading.
The Let's Encrypt flow will run asynchronously. When a certificate is requested through the acme-issuer, a pending certificate
will be created. A cron needs to be defined to run `lemur pending_certs fetch_all_acme`. This command will iterate through all of the pending
certificates, request a DNS challenge token from Let's Encrypt, and set the appropriate _acme-challenge TXT entry. It will
then iterate through and resolve the challenges before requesting a certificate for each pending certificate. If a certificate
is successfully obtained, the pending_certificate will be moved to the certificates table with the appropriate properties.
Special thanks to all who helped with this release, notably:
- The folks at Cloudflare
- dmitryzykov
- jchuong
- seils
- titouanc
Upgrading
---------
.. note:: This release will need a migration change. Please follow the `documentation <https://lemur.readthedocs.io/en/latest/administration.html#upgrading-lemur>`_ to upgrade Lemur.
0.6 - `2018-01-02`
~~~~~~~~~~~~~~~~~~
Happy Holidays! This is a big release with lots of bug fixes and features. Below are the highlights and are not exhaustive.
Features:
* Per-certificate rotation policies, requires a database migration. The default rotation policy for all certificates.
is 30 days. Every certificate will gain a policy regardless of if auto-rotation is used.
* Adds per-user API Keys, allows users to issue multiple long-lived API tokens with the same permission as the user creating them.
* Adds the ability to revoke certificates from the Lemur UI/API, this is currently only supported for the digicert CIS and cfssl plugins.
* Allow destinations to support an export function. Useful for file system destinations e.g. S3 to specify the export plugin you wish to run before being sent to the destination.
* Adds support for uploading certificates to Cloudfront.
* Re-worked certificate metadata pane for improved readability.
* Adds support for LDAP user authentication
Bugs:
* Closed `#767 <https://github.com/Netflix/lemur/issues/767>`_ - Fixed issue with login redirect loop.
* Closed `#792 <https://github.com/Netflix/lemur/issues/792>`_ - Fixed an issue with a unique constraint was violated when replacing certificates.
* Closed `#752 <https://github.com/Netflix/lemur/issues/752>`_ - Fixed an internal server error when validating notification units.
* Closed `#684 <https://github.com/Netflix/lemur/issues/684>`_ - Fixed migration failure when null values encountered.
* Closes `#661 <https://github.com/Netflix/lemur/issues/661>`_ - Fixed an issue where default values were missing during clone operations.
Special thanks to all who helped with this release, notably:
- intgr
- SecurityInsanity
- johanneslange
- RickB17
- pr8kerl
- bunjiboys
See the full list of issues closed in `0.6 <https://github.com/Netflix/lemur/milestone/5>`_.
Upgrading
---------
.. note:: This release will need a migration change. Please follow the `documentation <https://lemur.readthedocs.io/en/latest/administration.html#upgrading-lemur>`_ to upgrade Lemur.
0.5 - `2016-04-08`
~~~~~~~~~~~~~~~~~~
This release is most notable for dropping support for python2.7. All Lemur versions >0.4 will now support python3.5 only.
Big thanks to neilschelly for quite a lot of improvements to the `lemur-cryptography` plugin.
Other Highlights:
* Closed `#501 <https://github.com/Netflix/lemur/issues/501>`_ - Endpoint resource as now kept in sync via an
expiration mechanism. Such that non-existant endpoints gracefully fall out of Lemur. Certificates are never
removed from Lemur.
* Closed `#551 <https://github.com/Netflix/lemur/pull/551>`_ - Added the ability to create a 4096 bit key during certificate
creation. Closed `#528 <https://github.com/Netflix/lemur/pull/528>`_ to ensure that issuer plugins supported the new 4096 bit keys.
* Closed `#566 <https://github.com/Netflix/lemur/issues/566>`_ - Fixed an issue changing the notification status for certificates
without private keys.
* Closed `#594 <https://github.com/Netflix/lemur/issues/594>`_ - Added `replaced` field indicating if a certificate has been superseded.
* Closed `#602 <https://github.com/Netflix/lemur/issues/602>`_ - AWS plugin added support for ALBs for endpoint tracking.
Special thanks to all who helped with this release, notably:
- RcRonco
- harmw
- jeremyguarini
See the full list of issues closed in `0.5 <https://github.com/Netflix/lemur/milestone/4>`_.
Upgrading
---------
.. note:: This release will need a slight migration change. Please follow the `documentation <https://lemur.readthedocs.io/en/latest/administration.html#upgrading-lemur>`_ to upgrade Lemur.
0.4 - `2016-11-17`
~~~~~~~~~~~~~~~~~~
There have been quite a few issues closed in this release. Some notables:
* Closed `#284 <https://github.com/Netflix/lemur/issues/284>`_ - Created new models for `Endpoints` created associated
AWS ELB endpoint tracking code. This was the major stated goal of this milestone and should serve as the basis for
future enhancements of Lemur's certificate 'deployment' capabilities.
* Closed `#334 <https://github.com/Netflix/lemur/issues/334>`_ - Lemur not has the ability
to restrict certificate expiration dates to weekdays.
Several fixes/tweaks to Lemurs python3 support (thanks chadhendrie!)
This will most likely be the last release to support python2.7 moving Lemur to target python3 exclusively. Please comment
on issue #340 if this negatively affects your usage of Lemur.
See the full list of issues closed in `0.4 <https://github.com/Netflix/lemur/milestone/3>`_.
Upgrading
---------
.. note:: This release will need a slight migration change. Please follow the `documentation <https://lemur.readthedocs.io/en/latest/administration.html#upgrading-lemur>`_ to upgrade Lemur.
0.3.0 - `2016-06-06`
~~~~~~~~~~~~~~~~~~~~
This is quite a large upgrade, it is highly advised you backup your database before attempting to upgrade as this release
requires the migration of database structure as well as data.
Upgrading
---------
Please follow the `documentation <https://lemur.readthedocs.io/en/latest/administration.html#upgrading-lemur>`_ to upgrade Lemur.
Source Plugin Owners
--------------------
The dictionary returned from a source plugin has changed keys from `public_certificate` to `body` and `intermediate_certificate` to chain.
Issuer Plugin Owners
--------------------
This release may break your plugins, the keys in `issuer_options` have been changed from `camelCase` to `under_score`.
This change was made to break an undue reliance on downstream options maintains a more pythonic naming convention. Renaming
these keys should be fairly trivial, additionally pull requests have been submitted to affected plugins to help ease the transition.
.. note:: This change only affects issuer plugins and does not affect any other types of plugins.
* Closed `#63 <https://github.com/Netflix/lemur/issues/63>`_ - Validates all endpoints with Marshmallow schemas, this allows for
stricter input validation and better error messages when validation fails.
* Closed `#146 <https://github.com/Netflix/lemur/issues/146>`_ - Moved authority type to first pane of authority creation wizard.
* Closed `#147 <https://github.com/Netflix/lemur/issues/147>`_ - Added and refactored the relationship between authorities and their
root certificates. Displays the certificates (and chains) next to the authority in question.
* Closed `#199 <https://github.com/Netflix/lemur/issues/199>`_ - Ensures that the dates submitted to Lemur during authority and
certificate creation are actually dates.
* Closed `#230 <https://github.com/Netflix/lemur/issues/230>`_ - Migrated authority dropdown to an ui-select based dropdown, this
should be easier to determine what authorities are available and when an authority has actually been selected.
* Closed `#254 <https://github.com/Netflix/lemur/issues/254>`_ - Forces certificate names to be generally unique. If a certificate name
(generated or otherwise) is found to be a duplicate we increment by appending a counter.
* Closed `#254 <https://github.com/Netflix/lemur/issues/275>`_ - Switched to using Fernet generated passphrases for exported items.
These are more sounds that pseudo random passphrases generated before and have the nice property of being in base64.
* Closed `#278 <https://github.com/Netflix/lemur/issues/278>`_ - Added ability to specify a custom name to certificate creation, previously
this was only available in the certificate import wizard.
* Closed `#281 <https://github.com/Netflix/lemur/issues/281>`_ - Fixed an issue where notifications could not be removed from a certificate
via the UI.
* Closed `#289 <https://github.com/Netflix/lemur/issues/289>`_ - Fixed and issue where intermediates were not being properly exported.
* Closed `#315 <https://github.com/Netflix/lemur/issues/315>`_ - Made how roles are associated with certificates and authorities much more
explicit, including adding the ability to add roles directly to certificates and authorities on creation.
0.2.2 - 2016-02-05
~~~~~~~~~~~~~~~~~~
* Closed `#234 <https://github.com/Netflix/lemur/issues/234>`_ - Allows export plugins to define whether they need
private key material (default is True)
* Closed `#231 <https://github.com/Netflix/lemur/issues/231>`_ - Authorities were not respecting 'owning' roles and their
users
* Closed `#228 <https://github.com/Netflix/lemur/issues/228>`_ - Fixed documentation with correct filter values
* Closed `#226 <https://github.com/Netflix/lemur/issues/226>`_ - Fixes issue were `import_certificate` was requiring
replacement certificates to be specified
* Closed `#224 <https://github.com/Netflix/lemur/issues/224>`_ - Fixed an issue where NPM might not be globally available (thanks AlexClineBB!)
* Closed `#221 <https://github.com/Netflix/lemur/issues/234>`_ - Fixes several reported issues where older migration scripts were
missing tables, this change removes pre 0.2 migration scripts
* Closed `#218 <https://github.com/Netflix/lemur/issues/234>`_ - Fixed an issue where export passphrases would not validate
0.2.1 - 2015-12-14
~~~~~~~~~~~~~~~~~~
* Fixed bug with search not refreshing values
* Cleaned up documentation, including working supervisor example (thanks rpicard!)
* Closed #165 - Fixed an issue with email templates
* Closed #188 - Added ability to submit third party CSR
* Closed #176 - Java-export should allow user to specify truststore/keystore
* Closed #176 - Extended support for exporting certificate in P12 format
0.2.0 - 2015-12-02
~~~~~~~~~~~~~~~~~~
* Closed #120 - Error messages not displaying long enough
* Closed #121 - Certificate create form should not be valid until a Certificate Authority object is available
* Closed #122 - Certificate API should allow for the specification of preceding certificates
You can now target a certificate(s) for replacement. When specified the replaced certificate will be marked as
'inactive'. This means that there will be no notifications for that certificate.
* Closed #139 - SubCA autogenerated descriptions for their certs are incorrect
* Closed #140 - Permalink does not change with filtering
* Closed #144 - Should be able to search certificates by domains covered, included wildcards
* Closed #165 - Cleaned up expiration notification template
* Closed #160 - Cleaned up quickstart documentation (thanks forkd!)
* Closed #144 - Now able to search by all domains in a given certificate, not just by common name
0.1.5 - 2015-10-26
~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~
* **SECURITY ISSUE**: Switched from use a AES static key to Fernet encryption.
* **SECURITY ISSUE**: Switched from use an AES static key to Fernet encryption.
Affects all versions prior to 0.1.5. If upgrading this will require a data migration.
see: `Upgrading Lemur <https://lemur.readthedocs.com/adminstration#UpgradingLemur>`_
see: `Upgrading Lemur <https://lemur.readthedocs.io/administration#UpgradingLemur>`_

14
Dockerfile Normal file
View File

@ -0,0 +1,14 @@
FROM python:3.7
RUN apt-get update
RUN apt-get install -y make software-properties-common curl
RUN curl -sL https://deb.nodesource.com/setup_7.x | bash -
RUN apt-get update
RUN apt-get install -y npm libldap2-dev libsasl2-dev libldap2-dev libssl-dev
RUN pip install pip==20.0.2
RUN pip install -U setuptools
RUN pip install coveralls bandit
WORKDIR /app
COPY . /app/
RUN pip install -e .
RUN pip install "file://`pwd`#egg=lemur[dev]"
RUN pip install "file://`pwd`#egg=lemur[tests]"

View File

@ -1,4 +1,3 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -187,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014 Netflix, Inc.
Copyright 2018 Netflix, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
include setup.py package.json bower.json gulpfile.js README.rst MANIFEST.in LICENSE AUTHORS
include setup.py version.py package.json bower.json gulpfile.js README.rst MANIFEST.in LICENSE AUTHORS requirements*.txt
recursive-include lemur/plugins/lemur_email/templates *
recursive-include lemur/static *
global-exclude *~

View File

@ -1,33 +1,59 @@
NPM_ROOT = ./node_modules
STATIC_DIR = src/lemur/static/app
SHELL=/bin/bash
USER := $(shell whoami)
develop: update-submodules setup-git
@echo "--> Installing dependencies"
ifeq ($(USER), root)
@echo "WARNING: It looks like you are installing Lemur as root. This is not generally advised."
npm install --unsafe-perm
else
npm install
endif
pip install "setuptools>=0.9.8"
# order matters here, base package must install first
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
node_modules/.bin/gulp package --urlContextPath=$(urlContextPath)
@echo ""
release:
@echo "--> Installing dependencies"
ifeq ($(USER), root)
@echo "WARNING: It looks like you are installing Lemur as root. This is not generally advised."
npm install --unsafe-perm
else
npm install
endif
pip install "setuptools>=0.9.8"
# order matters here, base package must install first
pip install -e .
node_modules/.bin/gulp build
node_modules/.bin/gulp package --urlContextPath=$(urlContextPath)
@echo ""
dev-docs:
pip install -r docs/requirements.txt
pip install -r requirements-docs.txt
reset-db:
@echo "--> Dropping existing 'lemur' database"
dropdb lemur || true
@echo "--> Creating 'lemur' database"
createdb -E utf-8 lemur
@echo "--> Enabling pg_trgm extension"
psql lemur -c "create extension IF NOT EXISTS pg_trgm;"
@echo "--> Applying migrations"
lemur db upgrade
cd lemur && lemur db upgrade
setup-git:
@echo "--> Installing git hooks"
git config branch.autosetuprebase always
cd .git/hooks && ln -sf ../../hooks/* ./
if [ -d .git/hooks ]; then \
git config branch.autosetuprebase always; \
cd .git/hooks && ln -sf ../../hooks/* ./; \
fi
@echo ""
clean:
@ -41,7 +67,7 @@ test: develop lint test-python
testloop: develop
pip install pytest-xdist
py.test tests -f
coverage run --source lemur -m py.test
test-cli:
@echo "--> Testing CLI"
@ -60,7 +86,7 @@ test-js:
test-python:
@echo "--> Running Python tests"
py.test lemur/tests || exit 1
coverage run --source lemur -m py.test
@echo ""
lint: lint-python lint-js
@ -82,4 +108,28 @@ coverage: develop
publish:
python setup.py sdist bdist_wheel upload
.PHONY: develop dev-postgres dev-docs setup-git build clean update-submodules test testloop test-cli test-js test-python lint lint-python lint-js coverage publish
up-reqs:
ifndef VIRTUAL_ENV
$(error Please activate virtualenv first)
endif
@echo "--> Updating Python requirements"
pip install --upgrade pip
pip install --upgrade pip-tools
pip-compile --output-file requirements.txt requirements.in -U --no-index
pip-compile --output-file requirements-docs.txt requirements-docs.in -U --no-index
pip-compile --output-file requirements-dev.txt requirements-dev.in -U --no-index
pip-compile --output-file requirements-tests.txt requirements-tests.in -U --no-index
@echo "--> Done updating Python requirements"
@echo "--> Removing python-ldap from requirements-docs.txt"
grep -v "python-ldap" requirements-docs.txt > tempreqs && mv tempreqs requirements-docs.txt
@echo "--> Installing new dependencies"
pip install -e .
@echo "--> Done installing new dependencies"
@echo ""
# Execute with make checkout-pr pr=<pr number>
checkout-pr:
git fetch upstream pull/$(pr)/head:pr-$(pr)
.PHONY: develop dev-postgres dev-docs setup-git build clean update-submodules test testloop test-cli test-js test-python lint lint-python lint-js coverage publish release

1
OSSMETADATA Normal file
View File

@ -0,0 +1 @@
osslifecycle=active

View File

@ -5,32 +5,31 @@ Lemur
:alt: Join the chat at https://gitter.im/Netflix/lemur
:target: https://gitter.im/Netflix/lemur?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. image:: https://img.shields.io/pypi/v/lemur.svg
:target: https://pypi.python.org/pypi/lemur/
:alt: Latest Version
.. image:: https://readthedocs.org/projects/lemur/badge/?version=latest
:target: https://lemur.readthedocs.org
:target: https://lemur.readthedocs.io
:alt: Latest Docs
.. image:: https://img.shields.io/badge/NetflixOSS-active-brightgreen.svg
.. image:: https://travis-ci.org/Netflix/lemur.svg
:target: https://travis-ci.org/Netflix/lemur
.. image:: https://badge.waffle.io/Netflix/lemur.png?label=ready&title=Ready
:target: https://waffle.io/Netflix/lemur
:alt: 'Stories in Ready'
.. image:: https://coveralls.io/repos/github/Netflix/lemur/badge.svg?branch=master
:target: https://coveralls.io/github/Netflix/lemur?branch=master
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.
It works on CPython 2.7, 3.3, 3.4. We deploy on Ubuntu and develop on OS X.
It works on Python 3.7. 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/>`_
- `Documentation <http://lemur.readthedocs.io/>`_
- `Source code <https://github.com/netflix/lemur>`_
- `Issue tracker <https://github.com/netflix/lemur/issues>`_
- `Docker <https://github.com/Netflix/lemur-docker>`_

View File

@ -6,42 +6,45 @@
},
"private": true,
"dependencies": {
"angular": "1.3",
"json3": "~3.3",
"es5-shim": "~4.0",
"jquery": "~2.1",
"angular-resource": "1.2.15",
"angular-cookies": "1.2.15",
"angular-sanitize": "1.2.15",
"angular-route": "1.2.15",
"angular-strap": "~2.0.2",
"restangular": "~1.4.0",
"ng-table": "~0.5.4",
"ngAnimate": "*",
"moment": "~2.6.0",
"angular-animate": "~1.4.0",
"angular-loading-bar": "~0.6.0",
"fontawesome": "~4.2.0",
"jquery": "~2.2.0",
"angular-wizard": "~0.4.0",
"bootswatch": "3.3.1+2",
"angular-spinkit": "~0.3.3",
"angular-bootstrap": "~0.12.0",
"angular-ui-switch": "~0.1.0",
"angular-chart.js": "~0.7.1",
"satellizer": "~0.9.4",
"angularjs-toaster": "~0.4.14",
"ngletteravatar": "~3.0.1",
"angular": "1.4.9",
"json3": "~3.3",
"es5-shim": "~4.5.0",
"angular-bootstrap": "~1.1.1",
"angular-animate": "~1.4.9",
"restangular": "~1.5.1",
"ng-table": "~0.8.3",
"moment": "~2.11.1",
"bootstrap": "~3.4.1",
"angular-loading-bar": "~0.8.0",
"angular-moment": "~0.10.3",
"moment-range": "~2.1.0",
"angular-clipboard": "~1.3.0",
"angularjs-toaster": "~1.0.0",
"angular-chart.js": "~0.8.8",
"ngletteravatar": "~4.0.0",
"bootswatch": "3.4.1+1",
"fontawesome": "~4.5.0",
"satellizer": "~0.13.4",
"angular-ui-router": "~0.2.15",
"angular-clipboard": "~1.1.1"
},
"devDependencies": {
"angular-mocks": "~1.3",
"angular-scenario": "~1.3",
"ngletteravatar": "~3.0.1"
"font-awesome": "~4.5.0",
"lodash": "~4.0.1",
"underscore": "~1.8.3",
"angular-smart-table": "2.1.8",
"angular-strap": ">= 2.2.2",
"angular-underscore": "^0.5.0",
"angular-translate": "^2.9.0",
"angular-ui-switch": "~0.1.0",
"angular-sanitize": "~1.5.0",
"angular-file-saver": "~1.0.1",
"angular-ui-select": "~0.17.1",
"d3": "^3.5.17"
},
"resolutions": {
"bootstrap": "~3.3.1",
"angular": "1.3"
"moment": ">=2.8.0 <2.11.0",
"lodash": ">=1.3.0 <2.5.0",
"angular": "1.4.9"
},
"ignore": [
"**/.*",

27
docker-compose.yml Normal file
View File

@ -0,0 +1,27 @@
---
version: '2.0'
services:
test:
build: .
volumes:
- ".:/app"
links:
- postgres
command: make test
environment:
SQLALCHEMY_DATABASE_URI: postgresql://lemur:lemur@postgres:5432/lemur
VIRTUAL_ENV: 'true'
postgres:
image: postgres
restart: always
environment:
POSTGRES_USER: lemur
POSTGRES_PASSWORD: lemur
ports:
- "5432:5432"
redis:
image: "redis:alpine"
ports:
- "6379:6379"

3
docker/.dockerignore Normal file
View File

@ -0,0 +1,3 @@
*-env
docker-compose.yml
Dockerfile

67
docker/Dockerfile Normal file
View File

@ -0,0 +1,67 @@
FROM alpine:3.8
ARG VERSION
ENV VERSION master
ARG URLCONTEXT
ENV uid 1337
ENV gid 1337
ENV user lemur
ENV group lemur
RUN addgroup -S ${group} -g ${gid} && \
adduser -D -S ${user} -G ${group} -u ${uid} && \
apk --update add python3 libldap postgresql-client nginx supervisor curl tzdata openssl bash && \
apk --update add --virtual build-dependencies \
git \
tar \
curl \
python3-dev \
npm \
bash \
musl-dev \
gcc \
autoconf \
automake \
libtool \
make \
nasm \
zlib-dev \
postgresql-dev \
libressl-dev \
libffi-dev \
cyrus-sasl-dev \
openldap-dev && \
mkdir -p /opt/lemur /home/lemur/.lemur/ && \
curl -sSL https://github.com/Netflix/lemur/archive/$VERSION.tar.gz | tar xz -C /opt/lemur --strip-components=1 && \
pip3 install --upgrade pip && \
pip3 install --upgrade setuptools && \
mkdir -p /run/nginx/ /etc/nginx/ssl/ && \
chown -R $user:$group /opt/lemur/ /home/lemur/.lemur/
WORKDIR /opt/lemur
RUN npm install --unsafe-perm && \
pip3 install -e . && \
node_modules/.bin/gulp build && \
node_modules/.bin/gulp package --urlContextPath=${URLCONTEXT} && \
apk del build-dependencies
COPY entrypoint /
COPY src/lemur.conf.py /home/lemur/.lemur/lemur.conf.py
COPY supervisor.conf /
COPY nginx/default.conf /etc/nginx/conf.d/
COPY nginx/default-ssl.conf /etc/nginx/conf.d/
RUN chmod +x /entrypoint
WORKDIR /
HEALTHCHECK --interval=12s --timeout=12s --start-period=30s \
CMD curl --fail http://localhost:80/api/1/healthcheck | grep -q ok || exit 1
USER root
ENTRYPOINT ["/entrypoint"]
CMD ["/usr/bin/supervisord","-c","supervisor.conf"]

67
docker/Dockerfile-src Normal file
View File

@ -0,0 +1,67 @@
FROM alpine:3.8
ARG VERSION
ENV VERSION master
ARG URLCONTEXT
ENV uid 1337
ENV gid 1337
ENV user lemur
ENV group lemur
RUN addgroup -S ${group} -g ${gid} && \
adduser -D -S ${user} -G ${group} -u ${uid} && \
apk --update add python3 libldap postgresql-client nginx supervisor curl tzdata openssl bash && \
apk --update add --virtual build-dependencies \
git \
tar \
curl \
python3-dev \
npm \
bash \
musl-dev \
gcc \
autoconf \
automake \
libtool \
make \
nasm \
zlib-dev \
postgresql-dev \
libressl-dev \
libffi-dev \
cyrus-sasl-dev \
openldap-dev && \
pip3 install --upgrade pip && \
pip3 install --upgrade setuptools && \
mkdir -p /home/lemur/.lemur/ && \
mkdir -p /run/nginx/ /etc/nginx/ssl/
COPY ./ /opt/lemur
WORKDIR /opt/lemur
RUN chown -R $user:$group /opt/lemur/ /home/lemur/.lemur/ && \
npm install --unsafe-perm && \
pip3 install -e . && \
node_modules/.bin/gulp build && \
node_modules/.bin/gulp package --urlContextPath=${URLCONTEXT} && \
apk del build-dependencies
COPY docker/entrypoint /
COPY docker/src/lemur.conf.py /home/lemur/.lemur/lemur.conf.py
COPY docker/supervisor.conf /
COPY docker/nginx/default.conf /etc/nginx/conf.d/
COPY docker/nginx/default-ssl.conf /etc/nginx/conf.d/
RUN chmod +x /entrypoint
WORKDIR /
HEALTHCHECK --interval=12s --timeout=12s --start-period=30s \
CMD curl --fail http://localhost:80/api/1/healthcheck | grep -q ok || exit 1
USER root
ENTRYPOINT ["/entrypoint"]
CMD ["/usr/bin/supervisord","-c","supervisor.conf"]

29
docker/docker-compose.yml Normal file
View File

@ -0,0 +1,29 @@
version: '3'
services:
postgres:
image: "postgres:10"
restart: always
volumes:
- pg_data:/var/lib/postgresql/data
env_file:
- pgsql-env
lemur:
# image: "netlix-lemur:latest"
build: .
depends_on:
- postgres
- redis
env_file:
- lemur-env
- pgsql-env
ports:
- 80:80
- 443:443
redis:
image: "redis:alpine"
volumes:
pg_data: {}

59
docker/entrypoint Normal file
View File

@ -0,0 +1,59 @@
#!/bin/bash
set -eo pipefail
if [ -z "${POSTGRES_USER}" ] || [ -z "${POSTGRES_PASSWORD}" ] || [ -z "${POSTGRES_HOST}" ] || [ -z "${POSTGRES_DB}" ];then
echo "Database vars not set"
exit 1
fi
export POSTGRES_PORT="${POSTGRES_PORT:-5432}"
export LEMUR_ADMIN_PASSWORD="${LEMUR_ADMIN_PASSWORD:-admin}"
export SQLALCHEMY_DATABASE_URI="postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB"
PGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB --command 'select 1;'
echo " # Create Postgres trgm extension"
PGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB --command 'CREATE EXTENSION IF NOT EXISTS pg_trgm;'
echo " # Done"
if [ -z "${SKIP_SSL}" ]; then
if [ ! -f /etc/nginx/ssl/server.crt ] && [ ! -f /etc/nginx/ssl/server.key ]; then
openssl req -x509 -newkey rsa:4096 -nodes -keyout /etc/nginx/ssl/server.key -out /etc/nginx/ssl/server.crt -days 365 -subj "/C=US/ST=FAKE/L=FAKE/O=FAKE/OU=FAKE/CN=FAKE"
fi
[ -f "/etc/nginx/conf.d/default-ssl.conf.a" ] && mv /etc/nginx/conf.d/default-ssl.conf.a /etc/nginx/conf.d/default-ssl.conf
[ -f "/etc/nginx/conf.d/default.conf" ] && mv -f /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.a
fi
# if [ ! -f /home/lemur/.lemur/lemur.conf.py ]; then
# echo "Creating config"
# https://github.com/Netflix/lemur/issues/2257
# python3 /opt/lemur/lemur/manage.py create_config
# echo "Done"
# fi
echo " # Running init"
su lemur -s /bin/bash -c "cd /opt/lemur/lemur; lemur init -p ${LEMUR_ADMIN_PASSWORD}"
echo " # Done"
# echo "Creating user"
# https://github.com/Netflix/lemur/issues/
# echo "something that will create user" | python3 /opt/lemur/lemur/manage.py shell
# echo "Done"
cron_notify="${CRON_NOTIFY:-"0 22 * * *"}"
cron_sync="${CRON_SYNC:-"*/15 * * * *"}"
cron_revoked="${CRON_CHECK_REVOKED:-"0 22 * * *"}"
cron_reissue="${CRON_REISSUE:-"0 23 * * *"}"
echo " # Populating crontab"
echo "${cron_notify} lemur notify expirations" > /etc/crontabs/lemur
echo "${cron_sync} lemur source sync -s all" >> /etc/crontabs/lemur
echo "${cron_revoked} lemur certificate check_revoked" >> /etc/crontabs/lemur
echo "${cron_reissue} lemur certificate reissue -c" >> /etc/crontabs/lemur
echo " # Done"
exec "$@"

25
docker/lemur-env Normal file
View File

@ -0,0 +1,25 @@
# SKIP_SSL=1
# LEMUR_TOKEN_SECRET=
# LEMUR_DEFAULT_COUNTRY=
# LEMUR_DEFAULT_STATE=
# LEMUR_DEFAULT_LOCATION=
# LEMUR_DEFAULT_ORGANIZATION=
# LEMUR_DEFAULT_ORGANIZATIONAL_UNIT=
# LEMUR_DEFAULT_ISSUER_PLUGIN=cryptography-issuer
# LEMUR_DEFAULT_AUTHORITY=cryptography
# MAIL_SERVER=mail.example.com
# MAIL_PORT=25
# LEMUR_EMAIL=lemur@example.com
# LEMUR_SECURITY_TEAM_EMAIL=['team@example.com']
# LEMUR_TOKEN_SECRET=
# LEMUR_ENCRYPTION_KEYS=['']
# DEBUG=True
# LDAP_DEBUG=True
# LDAP_AUTH=True
# LDAP_BIND_URI=ldap://example.com
# LDAP_BASE_DN=DC=example,DC=com
# LDAP_EMAIL_DOMAIN=example.com
# LDAP_USE_TLS=False
# LDAP_REQUIRED_GROUP=certificate-management-admins
# LDAP_GROUPS_TO_ROLES={'certificate-management-admins': 'admin', 'Team': 'team@example.com'}
# LDAP_IS_ACTIVE_DIRECTORY=False

View File

@ -0,0 +1,37 @@
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name _;
access_log /dev/stdout;
error_log /dev/stderr;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location /api {
proxy_pass http://127.0.0.1:8000;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
root /opt/lemur/lemur/static/dist;
include mime.types;
index index.html;
}
}

26
docker/nginx/default.conf Normal file
View File

@ -0,0 +1,26 @@
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
server {
listen 80;
access_log /dev/stdout;
error_log /dev/stderr;
location /api {
proxy_pass http://127.0.0.1:8000;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
root /opt/lemur/lemur/static/dist;
include mime.types;
index index.html;
}
}

4
docker/pgsql-env Normal file
View File

@ -0,0 +1,4 @@
POSTGRES_USER=lemur
POSTGRES_PASSWORD=12345
POSTGRES_DB=lemur
POSTGRES_HOST=postgres

60
docker/src/lemur.conf.py Normal file
View File

@ -0,0 +1,60 @@
import os
import random
import string
import base64
from ast import literal_eval
_basedir = os.path.abspath(os.path.dirname(__file__))
CORS = os.environ.get("CORS") == "True"
debug = os.environ.get("DEBUG") == "True"
def get_random_secret(length):
secret_key = ''.join(random.choice(string.ascii_uppercase) for x in range(round(length / 4)))
secret_key = secret_key + ''.join(random.choice("~!@#$%^&*()_+") for x in range(round(length / 4)))
secret_key = secret_key + ''.join(random.choice(string.ascii_lowercase) for x in range(round(length / 4)))
return secret_key + ''.join(random.choice(string.digits) for x in range(round(length / 4)))
SECRET_KEY = repr(os.environ.get('SECRET_KEY', get_random_secret(32).encode('utf8')))
LEMUR_TOKEN_SECRET = repr(os.environ.get('LEMUR_TOKEN_SECRET',
base64.b64encode(get_random_secret(32).encode('utf8'))))
LEMUR_ENCRYPTION_KEYS = repr(os.environ.get('LEMUR_ENCRYPTION_KEYS',
base64.b64encode(get_random_secret(32).encode('utf8'))))
LEMUR_ALLOWED_DOMAINS = []
LEMUR_EMAIL = ''
LEMUR_SECURITY_TEAM_EMAIL = []
ALLOW_CERT_DELETION = os.environ.get('ALLOW_CERT_DELETION') == "True"
LEMUR_DEFAULT_COUNTRY = str(os.environ.get('LEMUR_DEFAULT_COUNTRY',''))
LEMUR_DEFAULT_STATE = str(os.environ.get('LEMUR_DEFAULT_STATE',''))
LEMUR_DEFAULT_LOCATION = str(os.environ.get('LEMUR_DEFAULT_LOCATION',''))
LEMUR_DEFAULT_ORGANIZATION = str(os.environ.get('LEMUR_DEFAULT_ORGANIZATION',''))
LEMUR_DEFAULT_ORGANIZATIONAL_UNIT = str(os.environ.get('LEMUR_DEFAULT_ORGANIZATIONAL_UNIT',''))
LEMUR_DEFAULT_ISSUER_PLUGIN = str(os.environ.get('LEMUR_DEFAULT_ISSUER_PLUGIN',''))
LEMUR_DEFAULT_AUTHORITY = str(os.environ.get('LEMUR_DEFAULT_AUTHORITY',''))
ACTIVE_PROVIDERS = []
METRIC_PROVIDERS = []
LOG_LEVEL = str(os.environ.get('LOG_LEVEL','DEBUG'))
LOG_FILE = str(os.environ.get('LOG_FILE','/home/lemur/.lemur/lemur.log'))
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI','postgresql://lemur:lemur@localhost:5432/lemur')
LDAP_DEBUG = os.environ.get('LDAP_DEBUG') == "True"
LDAP_AUTH = os.environ.get('LDAP_AUTH') == "True"
LDAP_IS_ACTIVE_DIRECTORY = os.environ.get('LDAP_IS_ACTIVE_DIRECTORY') == "True"
LDAP_BIND_URI = str(os.environ.get('LDAP_BIND_URI',''))
LDAP_BASE_DN = str(os.environ.get('LDAP_BASE_DN',''))
LDAP_EMAIL_DOMAIN = str(os.environ.get('LDAP_EMAIL_DOMAIN',''))
LDAP_USE_TLS = str(os.environ.get('LDAP_USE_TLS',''))
LDAP_REQUIRED_GROUP = str(os.environ.get('LDAP_REQUIRED_GROUP',''))
LDAP_GROUPS_TO_ROLES = literal_eval(os.environ.get('LDAP_GROUPS_TO_ROLES') or "{}")

33
docker/supervisor.conf Normal file
View File

@ -0,0 +1,33 @@
[supervisord]
nodaemon=true
user=root
logfile=/dev/stdout
logfile_maxbytes=0
pidfile = /tmp/supervisord.pid
[program:lemur]
environment=LEMUR_CONF=/home/lemur/.lemur/lemur.conf.py
command=lemur start -b 0.0.0.0:8000
user=lemur
directory=/opt/lemur/lemur
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
user=root
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:cron]
environment=LEMUR_CONF=/home/lemur/.lemur/lemur.conf.py
command=/usr/sbin/crond -f
user=root
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

1656
docs/administration.rst Normal file
View File

@ -0,0 +1,1656 @@
Configuration
=============
.. warning::
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.
.. note::
All configuration values are python strings unless otherwise noted.
Basic Configuration
-------------------
.. data:: LOG_LEVEL
:noindex:
::
LOG_LEVEL = "DEBUG"
.. data:: LOG_FILE
:noindex:
::
LOG_FILE = "/logs/lemur/lemur-test.log"
.. data:: LOG_UPGRADE_FILE
:noindex:
::
LOG_UPGRADE_FILE = "/logs/lemur/db_upgrade.log"
.. data:: DEBUG
:noindex:
Sets the flask debug flag to true (if supported by the webserver)
::
DEBUG = False
.. warning::
This should never be used in a production environment as it exposes Lemur to
remote code execution through the debug console.
.. data:: CORS
:noindex:
Allows for cross domain requests, this is most commonly used for development but could
be use in production if you decided to host the webUI on a different domain than the server.
Use this cautiously, if you're not sure. Set it to `False`
::
CORS = False
.. data:: SQLALCHEMY_DATABASE_URI
:noindex:
If you have ever used sqlalchemy before this is the standard connection string used. Lemur uses a postgres database and the connection string would look something like:
::
SQLALCHEMY_DATABASE_URI = 'postgresql://<user>:<password>@<hostname>:5432/lemur'
.. data:: SQLALCHEMY_POOL_SIZE
:noindex:
The default connection pool size is 5 for sqlalchemy managed connections. Depending on the number of Lemur instances,
please specify per instance connection pool size. Below is an example to set connection pool size to 10.
::
SQLALCHEMY_POOL_SIZE = 10
.. warning::
This is an optional setting but important to review and set for optimal database connection usage and for overall database performance.
.. data:: SQLALCHEMY_MAX_OVERFLOW
:noindex:
This setting allows to create connections in addition to specified number of connections in pool size. By default, sqlalchemy
allows 10 connections to create in addition to the pool size. This is also an optional setting. If `SQLALCHEMY_POOL_SIZE` and
`SQLALCHEMY_MAX_OVERFLOW` are not speficied then each Lemur instance may create maximum of 15 connections.
::
SQLALCHECK_MAX_OVERFLOW = 0
.. note::
Specifying the `SQLALCHEMY_MAX_OVERFLOW` to 0 will enforce limit to not create connections above specified pool size.
.. data:: LEMUR_ALLOW_WEEKEND_EXPIRATION
:noindex:
Specifies whether to allow certificates created by Lemur to expire on weekends. Default is True.
.. data:: LEMUR_ALLOWED_DOMAINS
:noindex:
List of regular expressions for domain restrictions; if the list is not empty, normal users can only issue
certificates for domain names matching at least one pattern on this list. Administrators are exempt from this
restriction.
Cerificate common name is matched against these rules *if* it does not contain a space. SubjectAltName DNS names
are always matched against these rules.
Take care to write patterns in such way to not allow the `*` wildcard character inadvertently. To match a `.`
character, it must be escaped (as `\.`).
.. data:: LEMUR_OWNER_EMAIL_IN_SUBJECT
:noindex:
By default, Lemur will add the certificate owner's email address to certificate subject (for CAs that allow it).
Set this to `False` to disable this.
.. data:: LEMUR_TOKEN_SECRET
:noindex:
The TOKEN_SECRET is the secret used to create JWT tokens that are given out to users. This should be securely generated and kept private.
::
LEMUR_TOKEN_SECRET = 'supersecret'
An example of how you might generate a random string:
>>> import random
>>> secret_key = ''.join(random.choice(string.ascii_uppercase) for x in range(6))
>>> secret_key = secret_key + ''.join(random.choice("~!@#$%^&*()_+") for x in range(6))
>>> secret_key = secret_key + ''.join(random.choice(string.ascii_lowercase) for x in range(6))
>>> secret_key = secret_key + ''.join(random.choice(string.digits) for x in range(6))
.. data:: LEMUR_ENCRYPTION_KEYS
:noindex:
The LEMUR_ENCRYPTION_KEYS is used to encrypt data at rest within Lemur's database. Without a key Lemur will refuse
to start. Multiple keys can be provided to facilitate key rotation. The first key in the list is used for
encryption and all keys are tried for decryption until one works. Each key must be 32 URL safe base-64 encoded bytes.
Running lemur create_config will securely generate a key for your configuration file.
If you would like to generate your own, we recommend the following method:
>>> import os
>>> import base64
>>> base64.urlsafe_b64encode(os.urandom(32))
::
LEMUR_ENCRYPTION_KEYS = ['1YeftooSbxCiX2zo8m1lXtpvQjy27smZcUUaGmffhMY=', 'LAfQt6yrkLqOK5lwpvQcT4jf2zdeTQJV1uYeh9coT5s=']
.. data:: PUBLIC_CA_MAX_VALIDITY_DAYS
:noindex:
Use this config to override the limit of 397 days of validity for certificates issued by CA/Browser compliant authorities.
The authorities with cab_compliant option set to true will use this config. The example below overrides the default validity
of 397 days and sets it to 365 days.
::
PUBLIC_CA_MAX_VALIDITY_DAYS = 365
.. data:: DEFAULT_VALIDITY_DAYS
:noindex:
Use this config to override the default validity of 365 days for certificates offered through Lemur UI. Any CA which
is not CA/Browser Forum compliant will be using this value as default validity to be displayed on UI. Please
note that this config is used for cert issuance only through Lemur UI. The example below overrides the default validity
of 365 days and sets it to 1095 days (3 years).
::
DEFAULT_VALIDITY_DAYS = 1095
.. data:: DEBUG_DUMP
:noindex:
Dump all imported or generated CSR and certificate details to stdout using OpenSSL. (default: `False`)
.. data:: ALLOW_CERT_DELETION
:noindex:
When set to True, certificates can be marked as deleted via the API and deleted certificates will not be displayed
in the UI. When set to False (the default), the certificate delete API will always return "405 method not allowed"
and deleted certificates will always be visible in the UI. (default: `False`)
Certificate Default Options
---------------------------
Lemur allows you to fine tune your certificates to your organization. The following defaults are presented in the UI
and are used when Lemur creates the CSR for your certificates.
.. data:: LEMUR_DEFAULT_COUNTRY
:noindex:
::
LEMUR_DEFAULT_COUNTRY = "US"
.. data:: LEMUR_DEFAULT_STATE
:noindex:
::
LEMUR_DEFAULT_STATE = "California"
.. data:: LEMUR_DEFAULT_LOCATION
:noindex:
::
LEMUR_DEFAULT_LOCATION = "Los Gatos"
.. data:: LEMUR_DEFAULT_ORGANIZATION
:noindex:
::
LEMUR_DEFAULT_ORGANIZATION = "Netflix"
.. data:: LEMUR_DEFAULT_ORGANIZATIONAL_UNIT
:noindex:
::
LEMUR_DEFAULT_ORGANIZATIONAL_UNIT = ""
.. data:: LEMUR_DEFAULT_ISSUER_PLUGIN
:noindex:
::
LEMUR_DEFAULT_ISSUER_PLUGIN = "verisign-issuer"
.. data:: LEMUR_DEFAULT_AUTHORITY
:noindex:
::
LEMUR_DEFAULT_AUTHORITY = "verisign"
Notification Options
--------------------
Lemur currently has very basic support for notifications. Currently only expiration notifications are supported. Actual notification
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.
Notifications are sent to the certificate creator, owner and security team as specified by the `LEMUR_SECURITY_TEAM_EMAIL` configuration parameter.
Certificates marked as inactive will **not** be notified of upcoming expiration. This enables a user to essentially
silence the expiration. If a certificate is active and is expiring the above will be notified according to the `LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS` or
30, 15, 2 days before expiration if no intervals are set.
Lemur supports sending certificate expiration notifications through SES and SMTP.
.. data:: LEMUR_EMAIL_SENDER
:noindex:
Specifies which service will be delivering notification emails. Valid values are `SMTP` or `SES`
.. note::
If using SMTP as your provider you will need to define additional configuration options as specified by Flask-Mail.
See: `Flask-Mail <https://pythonhosted.org/Flask-Mail>`_
If you are using SES the email specified by the `LEMUR_MAIL` configuration will need to be verified by AWS before
you can send any mail. See: `Verifying Email Address in Amazon SES <http://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-email-addresses.html>`_
.. data:: LEMUR_SES_SOURCE_ARN
:noindex:
Specifies an ARN to use as the SourceArn when sending emails via SES.
.. note::
This parameter is only required if you're using a sending authorization with SES.
See: `Using sending authorization with Amazon SES <https://docs.aws.amazon.com/ses/latest/DeveloperGuide/sending-authorization.html>`_
.. data:: LEMUR_SES_REGION
:noindex:
Specifies a region for sending emails via SES.
.. note::
This parameter defaults to us-east-1 and is only required if you wish to use a different region.
.. data:: LEMUR_EMAIL
:noindex:
Lemur sender's email
::
LEMUR_EMAIL = 'lemur.example.com'
.. data:: LEMUR_SECURITY_TEAM_EMAIL
:noindex:
This is an email or list of emails that should be notified when a certificate is expiring. It is also the contact email address for any discovered certificate.
::
LEMUR_SECURITY_TEAM_EMAIL = ['security@example.com']
.. data:: LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS
:noindex:
Lemur notification intervals
::
LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS = [30, 15, 2]
.. data:: LEMUR_SECURITY_TEAM_EMAIL_INTERVALS
:noindex:
Alternate notification interval set for security team notifications. Use this if you would like the default security team notification interval for new certificates to differ from the global default as specified in LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS. If unspecified, the value of LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS is used. Security team default notifications for new certificates can effectively be disabled by setting this value to an empty array.
::
LEMUR_SECURITY_TEAM_EMAIL_INTERVALS = [15, 2]
Celery Options
---------------
To make use of automated tasks within lemur (e.g. syncing source/destinations, or reissuing ACME certificates), you
need to configure celery. See :ref:`Periodic Tasks <PeriodicTasks>` for more in depth documentation.
.. data:: CELERY_RESULT_BACKEND
:noindex:
The url to your redis backend (needs to be in the format `redis://<host>:<port>/<database>`)
.. data:: CELERY_BROKER_URL
:noindex:
The url to your redis broker (needs to be in the format `redis://<host>:<port>/<database>`)
.. data:: CELERY_IMPORTS
:noindex:
The module that celery needs to import, in our case thats `lemur.common.celery`
.. data:: CELERY_TIMEZONE
:noindex:
The timezone for celery to work with
.. data:: CELERYBEAT_SCHEDULE
:noindex:
This defines the schedule, with which the celery beat makes the worker run the specified tasks.
Since the celery module, relies on the RedisHandler, the following options also need to be set.
.. data:: REDIS_HOST
:noindex:
Hostname of your redis instance
.. data:: REDIS_PORT
:noindex:
Port on which redis is running (default: 6379)
.. data:: REDIS_DB
:noindex:
Which redis database to be used, by default redis offers databases 0-15 (default: 0)
Authentication Options
----------------------
Lemur currently supports Basic Authentication, LDAP Authentication, Ping OAuth2, and Google out of the box. Additional flows can be added relatively easily.
LDAP Options
~~~~~~~~~~~~
Lemur supports the use of an LDAP server in conjunction with Basic Authentication. Lemur local users can still be defined and take precedence over LDAP users. If a local user does not exist, LDAP will be queried for authentication. Only simple ldap binding with or without TLS is supported.
LDAP support requires the pyldap python library, which also depends on the following openldap packages.
.. code-block:: bash
$ sudo apt-get update
$ sudo apt-get install libldap2-dev libsasl2-dev libldap2-dev libssl-dev
To configure the use of an LDAP server, a number of settings need to be configured in `lemur.conf.py`.
Here is an example LDAP configuration stanza you can add to your config. Adjust to suit your environment of course.
.. code-block:: python
LDAP_AUTH = True
LDAP_BIND_URI='ldaps://secure.evilcorp.net'
LDAP_BASE_DN='DC=users,DC=evilcorp,DC=net'
LDAP_EMAIL_DOMAIN='evilcorp.net'
LDAP_USE_TLS = True
LDAP_CACERT_FILE = '/opt/lemur/trusted.pem'
LDAP_REQUIRED_GROUP = 'certificate-management-access'
LDAP_GROUPS_TO_ROLES = {'certificate-management-admin': 'admin', 'certificate-management-read-only': 'read-only'}
LDAP_IS_ACTIVE_DIRECTORY = True
The lemur ldap module uses the `user principal name` (upn) of the authenticating user to bind. This is done once for each user at login time. The UPN is effectively the email address in AD/LDAP of the user. If the user doesn't provide the email address, it constructs one based on the username supplied (which should normally match the samAccountName) and the value provided by the config LDAP_EMAIL_DOMAIN.
The config LDAP_BASE_DN tells lemur where to search within the AD/LDAP tree for the given UPN (user). If the bind with those credentials is successful - there is a valid user in AD with correct password.
Each of the LDAP options are described below.
.. data:: LDAP_AUTH
:noindex:
This enables the use of LDAP
::
LDAP_AUTH = True
.. data:: LDAP_BIND_URI
:noindex:
Specifies the LDAP server connection string
::
LDAP_BIND_URI = 'ldaps://hostname'
.. data:: LDAP_BASE_DN
:noindex:
Specifies the LDAP distinguished name location to search for users
::
LDAP_BASE_DN = 'DC=Users,DC=Evilcorp,DC=com'
.. data:: LDAP_EMAIL_DOMAIN
:noindex:
The email domain used by users in your directory. This is used to build the userPrincipalName to search with.
::
LDAP_EMAIL_DOMAIN = 'evilcorp.com'
The following LDAP options are not required, however TLS is always recommended.
.. data:: LDAP_USE_TLS
:noindex:
Enables the use of TLS when connecting to the LDAP server. Ensure the LDAP_BIND_URI is using ldaps scheme.
::
LDAP_USE_TLS = True
.. data:: LDAP_CACERT_FILE
:noindex:
Specify a Certificate Authority file containing PEM encoded trusted issuer certificates. This can be used if your LDAP server is using certificates issued by a private CA.
::
LDAP_CACERT_FILE = '/path/to/cacert/file'
.. data:: LDAP_REQUIRED_GROUP
:noindex:
Lemur has pretty open permissions. You can define an LDAP group to specify who can access Lemur. Only members of this group will be able to login.
::
LDAP_REQUIRED_GROUP = 'Lemur LDAP Group Name'
.. data:: LDAP_GROUPS_TO_ROLES
:noindex:
You can also define a dictionary of ldap groups mapped to lemur roles. This allows you to use ldap groups to manage access to owner/creator roles in Lemur
::
LDAP_GROUPS_TO_ROLES = {'lemur_admins': 'admin', 'Lemur Team DL Group': 'team@example.com'}
.. data:: LDAP_IS_ACTIVE_DIRECTORY
:noindex:
When set to True, nested group memberships are supported, by searching for groups with the member:1.2.840.113556.1.4.1941 attribute set to the user DN.
When set to False, the list of groups will be determined by the 'memberof' attribute of the LDAP user logging in.
::
LDAP_IS_ACTIVE_DIRECTORY = False
Authentication Providers
~~~~~~~~~~~~~~~~~~~~~~~~
If you are not using an authentication provider 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>`_
.. data:: ACTIVE_PROVIDERS
:noindex:
::
ACTIVE_PROVIDERS = ["ping", "google", "oauth2"]
.. data:: PING_SECRET
:noindex:
::
PING_SECRET = 'somethingsecret'
.. data:: PING_ACCESS_TOKEN_URL
:noindex:
::
PING_ACCESS_TOKEN_URL = "https://<yourpingserver>/as/token.oauth2"
.. data:: PING_USER_API_URL
:noindex:
::
PING_USER_API_URL = "https://<yourpingserver>/idp/userinfo.openid"
.. data:: PING_JWKS_URL
:noindex:
::
PING_JWKS_URL = "https://<yourpingserver>/pf/JWKS"
.. data:: PING_NAME
:noindex:
::
PING_NAME = "Example Oauth2 Provider"
.. data:: PING_CLIENT_ID
:noindex:
::
PING_CLIENT_ID = "client-id"
.. data:: PING_REDIRECT_URI
:noindex:
::
PING_REDIRECT_URI = "https://<yourlemurserver>/api/1/auth/ping"
.. data:: PING_AUTH_ENDPOINT
:noindex:
::
PING_AUTH_ENDPOINT = "https://<yourpingserver>/oauth2/authorize"
.. data:: OAUTH2_SECRET
:noindex:
::
OAUTH2_SECRET = 'somethingsecret'
.. data:: OAUTH2_ACCESS_TOKEN_URL
:noindex:
::
OAUTH2_ACCESS_TOKEN_URL = "https://<youroauthserver> /oauth2/v1/authorize"
.. data:: OAUTH2_USER_API_URL
:noindex:
::
OAUTH2_USER_API_URL = "https://<youroauthserver>/oauth2/v1/userinfo"
.. data:: OAUTH2_JWKS_URL
:noindex:
::
OAUTH2_JWKS_URL = "https://<youroauthserver>/oauth2/v1/keys"
.. data:: OAUTH2_NAME
:noindex:
::
OAUTH2_NAME = "Example Oauth2 Provider"
.. data:: OAUTH2_CLIENT_ID
:noindex:
::
OAUTH2_CLIENT_ID = "client-id"
.. data:: OAUTH2_REDIRECT_URI
:noindex:
::
OAUTH2_REDIRECT_URI = "https://<yourlemurserver>/api/1/auth/oauth2"
.. data:: OAUTH2_AUTH_ENDPOINT
:noindex:
::
OAUTH2_AUTH_ENDPOINT = "https://<youroauthserver>/oauth2/v1/authorize"
.. data:: OAUTH2_VERIFY_CERT
:noindex:
::
OAUTH2_VERIFY_CERT = True
.. data:: GOOGLE_CLIENT_ID
:noindex:
::
GOOGLE_CLIENT_ID = "client-id"
.. data:: GOOGLE_SECRET
:noindex:
::
GOOGLE_SECRET = "somethingsecret"
Metric Providers
~~~~~~~~~~~~~~~~
If you are not using a metric provider you do not need to configure any of these options.
.. data:: ACTIVE_PROVIDERS
:noindex:
A list of metric plugins slugs to be ativated.
::
METRIC_PROVIDERS = ['atlas-metric']
Plugin Specific Options
-----------------------
ACME Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. data:: ACME_DNS_PROVIDER_TYPES
:noindex:
Dictionary of ACME DNS Providers and their requirements.
.. data:: ACME_ENABLE_DELEGATED_CNAME
:noindex:
Enables delegated DNS domain validation using CNAMES. When enabled, Lemur will attempt to follow CNAME records to authoritative DNS servers when creating DNS-01 challenges.
Active Directory Certificate Services Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. data:: ADCS_SERVER
:noindex:
FQDN of your ADCS Server
.. data:: ADCS_AUTH_METHOD
:noindex:
The chosen authentication method. Either basic (the default), ntlm or cert (SSL client certificate). The next 2 variables are interpreted differently for different methods.
.. data:: ADCS_USER
:noindex:
The username (basic) or the path to the public cert (cert) of the user accessing PKI
.. data:: ADCS_PWD
:noindex:
The passwd (basic) or the path to the private key (cert) of the user accessing PKI
.. data:: ADCS_TEMPLATE
:noindex:
Template to be used for certificate issuing. Usually display name w/o spaces
.. data:: ADCS_TEMPLATE_<upper(authority.name)>
:noindex:
If there is a config variable ADCS_TEMPLATE_<upper(authority.name)> take the value as Cert template else default to ADCS_TEMPLATE to be compatible with former versions. Template to be used for certificate issuing. Usually display name w/o spaces
.. data:: ADCS_START
:noindex:
Used in ADCS-Sourceplugin. Minimum id of the first certificate to be returned. ID is increased by one until ADCS_STOP. Missing cert-IDs are ignored
.. data:: ADCS_STOP
:noindex:
Used for ADCS-Sourceplugin. Maximum id of the certificates returned.
.. data:: ADCS_ISSUING
:noindex:
Contains the issuing cert of the CA
.. data:: ADCS_ROOT
:noindex:
Contains the root cert of the CA
Entrust Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Enables the creation of Entrust certificates. You need to set the API access up with Entrust support. Check the information in the Entrust Portal as well.
Certificates are created as "SERVER_AND_CLIENT_AUTH".
Caution: Sometimes the entrust API does not respond in a timely manner. This error is handled and reported by the plugin. Should this happen you just have to hit the create button again after to create a valid certificate.
The following parameters have to be set in the configuration files.
.. data:: ENTRUST_URL
:noindex:
This is the url for the Entrust API. Refer to the API documentation.
.. data:: ENTRUST_API_CERT
:noindex:
Path to the certificate file in PEM format. This certificate is created in the onboarding process. Refer to the API documentation.
.. data:: ENTRUST_API_KEY
:noindex:
Path to the key file in RSA format. This certificate is created in the onboarding process. Refer to the API documentation. Caution: the request library cannot handle encrypted keys. The keyfile therefore has to contain the unencrypted key. Please put this in a secure location on the server.
.. data:: ENTRUST_API_USER
:noindex:
String with the API user. This user is created in the onboarding process. Refer to the API documentation.
.. data:: ENTRUST_API_PASS
:noindex:
String with the password for the API user. This password is created in the onboarding process. Refer to the API documentation.
.. data:: ENTRUST_NAME
:noindex:
String with the name that should appear as certificate owner in the Entrust portal. Refer to the API documentation.
.. data:: ENTRUST_EMAIL
:noindex:
String with the email address that should appear as certificate contact email in the Entrust portal. Refer to the API documentation.
.. data:: ENTRUST_PHONE
:noindex:
String with the phone number that should appear as certificate contact in the Entrust portal. Refer to the API documentation.
.. data:: ENTRUST_ISSUING
:noindex:
Contains the issuing cert of the CA
.. data:: ENTRUST_ROOT
:noindex:
Contains the root cert of the CA
.. data:: ENTRUST_PRODUCT_<upper(authority.name)>
:noindex:
If there is a config variable ENTRUST_PRODUCT_<upper(authority.name)> take the value as cert product name else default to "STANDARD_SSL". Refer to the API documentation for valid products names.
Verisign Issuer Plugin
~~~~~~~~~~~~~~~~~~~~~~
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 plugin's own documentation
for those plugins.
.. data:: VERISIGN_URL
:noindex:
This is the url for the Verisign API
.. data:: VERISIGN_PEM_PATH
:noindex:
This is the path to the mutual TLS certificate used for communicating with Verisign
.. data:: VERISIGN_FIRST_NAME
:noindex:
This is the first name to be used when requesting the certificate
.. data:: VERISIGN_LAST_NAME
:noindex:
This is the last name to be used when requesting the certificate
.. data:: VERISIGN_EMAIL
:noindex:
This is the email to be used when requesting the certificate
.. data:: VERISIGN_INTERMEDIATE
:noindex:
This is the intermediate to be used for your CA chain
.. data:: VERISIGN_ROOT
:noindex:
This is the root to be used for your CA chain
Digicert Issuer Plugin
~~~~~~~~~~~~~~~~~~~~~~
The following configuration properties are required to use the Digicert issuer plugin.
.. data:: DIGICERT_URL
:noindex:
This is the url for the Digicert API (e.g. https://www.digicert.com)
.. data:: DIGICERT_ORDER_TYPE
:noindex:
This is the type of certificate to order. (e.g. ssl_plus, ssl_ev_plus see: https://www.digicert.com/services/v2/documentation/order/overview-submit)
.. data:: DIGICERT_API_KEY
:noindex:
This is the Digicert API key
.. data:: DIGICERT_ORG_ID
:noindex:
This is the Digicert organization ID tied to your API key
.. data:: DIGICERT_ROOT
:noindex:
This is the root to be used for your CA chain
.. data:: DIGICERT_DEFAULT_VALIDITY_DAYS
:noindex:
This is the default validity (in days), if no end date is specified. (Default: 397)
.. data:: DIGICERT_MAX_VALIDITY_DAYS
:noindex:
This is the maximum validity (in days). (Default: value of DIGICERT_DEFAULT_VALIDITY_DAYS)
.. data:: DIGICERT_PRIVATE
:noindex:
This is whether or not to issue a private certificate. (Default: False)
CFSSL Issuer Plugin
~~~~~~~~~~~~~~~~~~~
The following configuration properties are required to use the CFSSL issuer plugin.
.. data:: CFSSL_URL
:noindex:
This is the URL for the CFSSL API
.. data:: CFSSL_ROOT
:noindex:
This is the root to be used for your CA chain
.. data:: CFSSL_INTERMEDIATE
:noindex:
This is the intermediate to be used for your CA chain
.. data:: CFSSL_KEY
:noindex:
This is the hmac key to authenticate to the CFSSL service. (Optional)
Hashicorp Vault Source/Destination Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Lemur can import and export certificate data to and from a Hashicorp Vault secrets store. Lemur can connect to a different Vault service per source/destination.
.. note:: This plugin does not supersede or overlap the 3rd party Vault Issuer plugin.
.. note:: Vault does not have any configuration properties however it does read from a file on disk for a vault access token. The Lemur service account needs read access to this file.
Vault Source
""""""""""""
The Vault Source Plugin will read from one Vault object location per source defined. There is expected to be one or more certificates defined in each object in Vault.
Vault Destination
"""""""""""""""""
A Vault destination can be one object in Vault or a directory where all certificates will be stored as their own object by CN.
Vault Destination supports a regex filter to prevent certificates with SAN that do not match the regex filter from being deployed. This is an optional feature per destination defined.
AWS Source/Destination Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order for Lemur to manage its 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 TLS certificates in AWS. But is not required to do so.
Setting up IAM roles
""""""""""""""""""""
Lemur's AWS plugin uses boto heavily to talk to all the AWS resources it manages. By default it uses the on-instance credentials to make the necessary calls.
In order to limit the permissions, we will create two new IAM roles for Lemur. You can name them whatever you would like but for example sake we will be calling them LemurInstanceProfile and Lemur.
Lemur uses to STS to talk to different accounts. For managing one account this isn't necessary but we will still use it so that we can easily add new accounts.
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 are example policies for the LemurInstanceProfile:
SES-SendEmail
.. code-block:: python
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendEmail"
],
"Resource": "*"
}
]
}
STS-AssumeRole
.. code-block:: python
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action":
"sts:AssumeRole",
"Resource": "*"
}
]
}
Next we will create the Lemur IAM role.
.. 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.
Here is an example policy for Lemur:
IAM-ServerCertificate
.. code-block:: python
{
"Statement": [
{
"Action": [
"iam:ListServerCertificates",
"iam:UpdateServerCertificate",
"iam:GetServerCertificate",
"iam:UploadServerCertificate"
],
"Resource": [
"*"
],
"Effect": "Allow",
"Sid": "Stmt1404836868000"
}
]
}
.. code-block:: python
{
"Statement": [
{
"Action": [
"elasticloadbalancing:DescribeInstanceHealth",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeLoadBalancerPolicyTypes",
"elasticloadbalancing:DescribeLoadBalancerPolicies",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DeleteLoadBalancerListeners",
"elasticloadbalancing:CreateLoadBalancerListeners"
],
"Resource": [
"*"
],
"Effect": "Allow",
"Sid": "Stmt1404841912000"
}
]
}
Setting up STS access
"""""""""""""""""""""
Once we have setup our accounts we need to ensure that we create a trust relationship so that LemurInstanceProfile can assume the Lemur role.
In the AWS console select the Lemur IAM role and select the Trust Relationships tab and click Edit Trust Relationship
Below is an example policy:
.. code-block:: python
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<awsaccountnumber>:role/LemurInstanceProfile",
]
},
"Action": "sts:AssumeRole"
}
]
}
Adding N+1 accounts
"""""""""""""""""""
To add another account we go to the new account and create a new Lemur IAM role with the same policy as above.
Then we would go to the account that Lemur is running is and edit the trust relationship policy.
An example policy:
.. code-block:: python
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<awsaccountnumber>:role/LemurInstanceProfile",
"arn:aws:iam::<awsaccountnumber1>:role/LemurInstanceProfile",
]
},
"Action": "sts:AssumeRole"
}
]
}
Setting up SES
""""""""""""""
Lemur has built in support for sending it's certificate notifications via Amazon's simple email service (SES). To force
Lemur to use SES ensure you are the running as the IAM role defined above and that you have followed the steps outlined
in Amazon's documentation `Setting up Amazon SES <http://docs.aws.amazon.com/ses/latest/DeveloperGuide/setting-up-ses.html>`_
The configuration::
LEMUR_MAIL = 'lemur.example.com'
Will be the sender of all notifications, so ensure that it is verified with AWS.
SES if the default notification gateway and will be used unless SMTP settings are configured in the application configuration
settings.
PowerDNS ACME Plugin
~~~~~~~~~~~~~~~~~~~~~~
The following configuration properties are required to use the PowerDNS ACME Plugin for domain validation.
.. data:: ACME_POWERDNS_DOMAIN
:noindex:
This is the FQDN for the PowerDNS API (without path)
.. data:: ACME_POWERDNS_SERVERID
:noindex:
This is the ServerID attribute of the PowerDNS API Server (i.e. "localhost")
.. data:: ACME_POWERDNS_APIKEYNAME
:noindex:
This is the Key name to use for authentication (i.e. "X-API-Key")
.. data:: ACME_POWERDNS_APIKEY
:noindex:
This is the API Key to use for authentication (i.e. "Password")
.. data:: ACME_POWERDNS_RETRIES
:noindex:
This is the number of times DNS Verification should be attempted (i.e. 20)
.. data:: ACME_POWERDNS_VERIFY
:noindex:
This configures how TLS certificates on the PowerDNS API target are validated. The PowerDNS Plugin depends on the PyPi requests library, which supports the following options for the verify parameter:
True: Verifies the TLS certificate was issued by a known publicly-trusted CA. (Default)
False: Disables certificate validation (Not Recommended)
File/Dir path to CA Bundle: Verifies the TLS certificate was issued by a Certificate Authority in the provided CA bundle.
ACME Plugin
~~~~~~~~~~~~
The following configration properties are optional for the ACME plugin to use. They allow reusing an existing ACME
account. See :ref:`Using a pre-existing ACME account <AcmeAccountReuse>` for more details.
.. data:: ACME_PRIVATE_KEY
:noindex:
This is the private key, the account was registered with (in JWK format)
.. data:: ACME_REGR
:noindex:
This is the registration for the ACME account, the most important part is the uri attribute (in JSON)
.. _CommandLineInterface:
Command Line Interface
======================
Lemur installs a command line script under the name ``lemur``. This will allow you to
perform most required operations that are unachievable within the web UI.
If you're using a non-standard configuration location, you'll need to prefix every command with
--config (excluding create_config, which is a special case). For example::
lemur --config=/etc/lemur.conf.py help
For a list of commands, you can also use ``lemur help``, or ``lemur [command] --help``
for help on a specific command.
.. note:: The script is powered by a library called `Flask-Script <https://github.com/smurfix/flask-script>`_
Builtin Commands
----------------
All commands default to `~/.lemur/lemur.conf.py` if a configuration is not specified.
.. data:: create_config
Creates a default configuration file for Lemur.
Path defaults to ``~/.lemur/lemur.config.py``
::
lemur create_config .
.. note::
This command is a special case and does not depend on the configuration file
being set.
.. data:: init
Initializes the configuration file for Lemur.
::
lemur -c /etc/lemur.conf.py init
.. data:: start
Starts a Lemur service. You can also pass any flag that Gunicorn uses to specify the webserver configuration.
::
lemur start -w 6 -b 127.0.0.1:8080
.. data:: db upgrade
Performs any needed database migrations.
::
lemur db upgrade
.. data:: check_revoked
Traverses every certificate that Lemur is aware of and attempts to understand its validity.
It utilizes both OCSP and CRL. If Lemur is unable to come to a conclusion about a certificates
validity its status is marked 'unknown'.
.. data:: sync
Sync attempts to discover certificates in the environment that were not created by Lemur. If you wish to only sync
a few sources you can pass a comma delimited list of sources to sync.
::
lemur sync -s source1,source2
Additionally you can also list the available sources that Lemur can sync.
::
lemur sync
.. data:: notify
Will traverse all current notifications and see if any of them need to be triggered.
::
lemur notify
.. data:: acme
Handles all ACME related tasks, like ACME plugin testing.
::
lemur acme
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
Upgrading Lemur
===============
To upgrade Lemur to the newest release you will need to ensure you have the latest code and have run any needed
database migrations.
To get the latest code from github run
::
cd <lemur-source-directory>
git pull -t <version>
python setup.py develop
.. note::
It's important to grab the latest release by specifying the release tag. This tags denote stable versions of Lemur.
If you want to try the bleeding edge version of Lemur you can by using the master branch.
After you have the latest version of the Lemur code base you must run any needed database migrations. To run migrations
::
cd <lemur-source-directory>/lemur
lemur db upgrade
This will ensure that any needed tables or columns are created or destroyed.
.. note::
Internally, this uses `Alembic <http://alembic.zzzcomputing.com/en/latest/>`_ to manage database migrations.
.. note::
By default Alembic looks for the `migrations` folder in the current working directory.The migrations folder is
located under `<LEMUR_HOME>/lemur/migrations` if you are running the lemur command from any location besides
`<LEMUR_HOME>/lemur` you will need to pass the `-d` flag to specify the absolute file path to the `migrations` folder.
Plugins
=======
There are several interfaces currently available to extend Lemur. These are a work in
progress and the API is not frozen.
Lemur includes several plugins by default. Including extensive support for AWS, VeriSign/Symantec.
Verisign/Symantec
-----------------
:Authors:
Kevin Glisson <kglisson@netflix.com>,
Curtis Castrapel <ccastrapel@netflix.com>,
Hossein Shafagh <hshafagh@netflix.com>
:Type:
Issuer
:Description:
Basic support for the VICE 2.0 API
Cryptography
------------
:Authors:
Kevin Glisson <kglisson@netflix.com>,
Mikhail Khodorovskiy <mikhail.khodorovskiy@jivesoftware.com>
:Type:
Issuer
:Description:
Toy certificate authority that creates self-signed certificate authorities.
Allows for the creation of arbitrary authorities and end-entity certificates.
This is *not* recommended for production use.
Acme
----
:Authors:
Kevin Glisson <kglisson@netflix.com>,
Curtis Castrapel <ccastrapel@netflix.com>,
Hossein Shafagh <hshafagh@netflix.com>,
Mikhail Khodorovskiy <mikhail.khodorovskiy@jivesoftware.com>,
Chad Sine <csine@netflix.com>
:Type:
Issuer
:Description:
Adds support for the ACME protocol (including LetsEncrypt) with domain validation using several providers.
Atlas
-----
:Authors:
Kevin Glisson <kglisson@netflix.com>,
Curtis Castrapel <ccastrapel@netflix.com>,
Hossein Shafagh <hshafagh@netflix.com>
:Type:
Metric
:Description:
Adds basic support for the `Atlas <https://github.com/Netflix/atlas/wiki>`_ telemetry system.
Email
-----
:Authors:
Kevin Glisson <kglisson@netflix.com>,
Curtis Castrapel <ccastrapel@netflix.com>,
Hossein Shafagh <hshafagh@netflix.com>
:Type:
Notification
:Description:
Adds support for basic email notifications via SES.
Slack
-----
:Authors:
Harm Weites <harm@weites.com>
:Type:
Notification
:Description:
Adds support for slack notifications.
AWS (Source)
----
:Authors:
Kevin Glisson <kglisson@netflix.com>,
Curtis Castrapel <ccastrapel@netflix.com>,
Hossein Shafagh <hshafagh@netflix.com>
:Type:
Source
:Description:
Uses AWS IAM as a source of certificates to manage. Supports a multi-account deployment.
AWS (Destination)
----
:Authors:
Kevin Glisson <kglisson@netflix.com>,
Curtis Castrapel <ccastrapel@netflix.com>,
Hossein Shafagh <hshafagh@netflix.com>
:Type:
Destination
:Description:
Uses AWS IAM as a destination for Lemur generated certificates. Support a multi-account deployment.
AWS (SNS Notification)
-----
:Authors:
Jasmine Schladen <jschladen@netflix.com>
:Type:
Notification
:Description:
Adds support for SNS notifications. SNS notifications (like other notification plugins) are currently only supported
for certificate expiration. Configuration requires a region, account number, and SNS topic name; these elements
are then combined to build the topic ARN. Lemur must have access to publish messages to the specified SNS topic.
Kubernetes
----------
:Authors:
Mikhail Khodorovskiy <mikhail.khodorovskiy@jivesoftware.com>
:Type:
Destination
:Description:
Allows Lemur to upload generated certificates to the Kubernetes certificate store.
Java
----
:Authors:
Kevin Glisson <kglisson@netflix.com>
:Type:
Export
:Description:
Generates java compatible .jks keystores and truststores from Lemur managed certificates.
Openssl
-------
:Authors:
Kevin Glisson <kglisson@netflix.com>
:Type:
Export
:Description:
Leverages Openssl to support additional export formats (pkcs12)
CFSSL
-----
:Authors:
Charles Hendrie <chad.hendrie@thomsonreuters.com>
:Type:
Issuer
:Description:
Basic support for generating certificates from the private certificate authority CFSSL
Vault
-----
:Authors:
Christopher Jolley <chris@alwaysjolley.com>
:Type:
Source
:Description:
Source plugin imports certificates from Hashicorp Vault secret store.
Vault
-----
:Authors:
Christopher Jolley <chris@alwaysjolley.com>
:Type:
Destination
:Description:
Destination plugin to deploy certificates to Hashicorp Vault secret store.
3rd Party Plugins
=================
The following plugins are available and maintained by members of the Lemur community:
Digicert
--------
:Authors:
Chris Dorros
:Type:
Issuer
:Description:
Adds support for basic Digicert
:Links:
https://github.com/opendns/lemur-digicert
InfluxDB
--------
:Authors:
Titouan Christophe
:Type:
Metric
:Description:
Sends key metrics to InfluxDB
:Links:
https://github.com/titouanc/lemur-influxdb
Hashicorp Vault
---------------
:Authors:
Ron Cohen
:Type:
Issuer
:Description:
Adds support for basic Vault PKI secret backend.
:Links:
https://github.com/RcRonco/lemur_vault
Have an extension that should be listed here? Submit a `pull request <https://github.com/netflix/lemur>`_ and we'll
get it added.
Want to create your own extension? See :doc:`../developer/plugins/index` to get started.
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 they can be assigned one or more roles. These roles are typically dynamically created
depending on an external identity provider (Google, LDAP, etc.), or are hardcoded within Lemur and associated with special
meaning.
Within Lemur there are three main permissions: AdminPermission, CreatorPermission, OwnerPermission. Sub-permissions such
as ViewPrivateKeyPermission are compositions of these three main Permissions.
Lets take a look at how these permissions are used:
Each `Authority` has a set of roles associated with it. If a user is also associated with the same roles
that the `Authority` is associated with, Lemur allows that user to user/view/update that `Authority`.
This RBAC is also used when determining which users can access which certificate private key. Lemur's current permission
structure is setup such that if the user is a `Creator` or `Owner` of a given certificate they are allow to view that
private key. Owners can also be a role name, such that any user with the same role as owner will be allowed to view the
private key information.
These permissions are applied to the user upon login and refreshed on every request.
.. seealso::
`Flask-Principal <https://pythonhosted.org/Flask-Principal>`_

View File

@ -1,660 +0,0 @@
Configuration
=============
.. warning::
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.
Basic Configuration
-------------------
.. data:: LOG_LEVEL
:noindex:
::
LOG_LEVEL = "DEBUG"
.. data:: LOG_FILE
:noindex:
::
LOG_FILE = "/logs/lemur/lemur-test.log"
.. data:: debug
:noindex:
Sets the flask debug flag to true (if supported by the webserver)
::
debug = False
.. warning::
This should never be used in a production environment as it exposes Lemur to
remote code execution through the debug console.
.. data:: CORS
:noindex:
Allows for cross domain requests, this is most commonly used for development but could
be use in production if you decided to host the webUI on a different domain than the server.
Use this cautiously, if you're not sure. Set it to `False`
::
CORS = False
.. data:: SQLACHEMY_DATABASE_URI
:noindex:
If you have ever used sqlalchemy before this is the standard connection string used. Lemur uses a postgres database and the connection string would look something like:
::
SQLALCHEMY_DATABASE_URI = 'postgresql://<user>:<password>@<hostname>:5432/lemur'
.. data:: LEMUR_RESTRICTED_DOMAINS
:noindex:
This allows the administrator to mark a subset of domains or domains matching a particular regex as
*restricted*. This means that only an administrator is allows to issue the domains in question.
.. data:: LEMUR_TOKEN_SECRET
:noindex:
The TOKEN_SECRET is the secret used to create JWT tokens that are given out to users. This should be securely generated and kept private.
::
LEMUR_TOKEN_SECRET = 'supersecret'
An example of how you might generate a random string:
>>> import random
>>> secret_key = ''.join(random.choice(string.ascii_uppercase) for x in range(6))
>>> secret_key = secret_key + ''.join(random.choice("~!@#$%^&*()_+") for x in range(6))
>>> secret_key = secret_key + ''.join(random.choice(string.ascii_lowercase) for x in range(6))
>>> secret_key = secret_key + ''.join(random.choice(string.digits) for x in range(6))
.. data:: LEMUR_ENCRYPTION_KEYS
:noindex:
The LEMUR_ENCRYPTION_KEYS is used to encrypt data at rest within Lemur's database. Without a key Lemur will refuse
to start. Multiple keys can be provided to facilitate key rotation. The first key in the list is used for
encryption and all keys are tried for decryption until one works. Each key must be 32 URL safe base-64 encoded bytes.
Running lemur create_config will securely generate a key for your configuration file.
If you would like to generate your own, we recommend the following method:
>>> import os
>>> import base64
>>> base64.urlsafe_b64encode(os.urandom(32))
::
LEMUR_ENCRYPTION_KEYS = ['1YeftooSbxCiX2zo8m1lXtpvQjy27smZcUUaGmffhMY=', 'LAfQt6yrkLqOK5lwpvQcT4jf2zdeTQJV1uYeh9coT5s=']
Certificate Default Options
---------------------------
Lemur allows you to find tune your certificates to your organization. The following defaults are presented in the UI
and are used when Lemur creates the CSR for your certificates.
.. data:: LEMUR_DEFAULT_COUNTRY
:noindex:
::
LEMUR_DEFAULT_COUNTRY = "US"
.. data:: LEMUR_DEFAULT_STATE
:noindex:
::
LEMUR_DEFAULT_STATE = "California"
.. data:: LEMUR_DEFAULT_LOCATION
:noindex:
::
LEMUR_DEFAULT_LOCATION = "Los Gatos"
.. data:: LEMUR_DEFAULT_ORGANIZATION
:noindex:
::
LEMUR_DEFAULT_ORGANIZATION = "Netflix"
.. data:: LEMUR_DEFAULT_ORGANIZATION_UNIT
:noindex:
::
LEMUR_DEFAULT_ORGANIZATIONAL_UNIT = "Operations"
Notification Options
--------------------
Lemur currently has very basic support for notifications. Currently only expiration notifications are supported. Actual notification
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.
Notifications are sent to the certificate creator, owner and security team as specified by the `LEMUR_SECURITY_TEAM_EMAIL` configuration parameter.
Certificates marked as in-active will **not** be notified of upcoming expiration. This enables a user to essentially
silence the expiration. If a certificate is active and is expiring the above will be notified according to the `LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS` or
30, 15, 2 days before expiration if no intervals are set.
Lemur supports sending certification expiration notifications through SES and SMTP.
.. data:: LEMUR_EMAIL_SENDER
:noindex:
Specifies which service will be delivering notification emails. Valid values are `SMTP` or `SES`
.. note::
If using STMP as your provider you will need to define additional configuration options as specified by Flask-Mail.
See: `Flask-Mail <https://pythonhosted.org/Flask-Mail>`_
If you are using SES the email specified by the `LEMUR_MAIL` configuration will need to be verified by AWS before
you can send any mail. See: `Verifying Email Address in Amazon SES <http://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-email-addresses.html>`_
.. data:: LEMUR_MAIL
:noindex:
Lemur sender's email
::
LEMUR_MAIL = 'lemur.example.com'
.. data:: LEMUR_SECURITY_TEAM_EMAIL
:noindex:
This is an email or list of emails that should be notified when a certificate is expiring. It is also the contact email address for any discovered certificate.
::
LEMUR_SECURITY_TEAM_EMAIL = ['security@example.com']
.. data:: LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS
:noindex:
Lemur notification intervals
::
LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS = [30, 15, 2]
Authority Options
-----------------
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 plugin's own documentation
for those plugins.
.. data:: VERISIGN_URL
:noindex:
This is the url for the Verisign API
.. data:: VERISIGN_PEM_PATH
:noindex:
This is the path to the mutual TLS certificate used for communicating with Verisign
.. data:: VERISIGN_FIRST_NAME
:noindex:
This is the first name to be used when requesting the certificate
.. data:: VERISIGN_LAST_NAME
:noindex:
This is the last name to be used when requesting the certificate
.. data:: VERISIGN_EMAIL
:noindex:
This is the email to be used when requesting the certificate
.. data:: VERISIGN_INTERMEDIATE
:noindex:
This is the intermediate to be used for your CA chain
.. data:: VERISIGN_ROOT
:noindex:
This is the root to be used for your CA chain
Authentication
--------------
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>`_
.. data:: PING_SECRET
:noindex:
::
PING_SECRET = 'somethingsecret'
.. data:: PING_ACCESS_TOKEN_URL
:noindex:
::
PING_ACCESS_TOKEN_URL = "https://<yourpingserver>/as/token.oauth2"
.. data:: PING_USER_API_URL
:noindex:
::
PING_USER_API_URL = "https://<yourpingserver>/idp/userinfo.openid"
.. data:: PING_JWKS_URL
:noindex:
::
PING_JWKS_URL = "https://<yourpingserver>/pf/JWKS"
AWS Plugin Configuration
========================
In order for Lemur to manage its 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 TLS certificates in AWS. But is not required to do so.
Setting up IAM roles
--------------------
Lemur's AWS plugin uses boto heavily to talk to all the AWS resources it manages. By default it uses the on-instance credentials to make the necessary calls.
In order to limit the permissions, we will create two new IAM roles for Lemur. You can name them whatever you would like but for example sake we will be calling them LemurInstanceProfile and Lemur.
Lemur uses to STS to talk to different accounts. For managing one account this isn't necessary but we will still use it so that we can easily add new accounts.
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 are example policies for the LemurInstanceProfile:
SES-SendEmail
.. code-block:: python
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendEmail"
],
"Resource": "*"
}
]
}
STS-AssumeRole
.. code-block:: python
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action":
"sts:AssumeRole",
"Resource": "*"
}
]
}
Next we will create the the Lemur IAM role.
.. 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.
Here is an example policy for Lemur:
IAM-ServerCertificate
.. code-block:: python
{
"Statement": [
{
"Action": [
"iam:ListServerCertificates",
"iam:UpdateServerCertificate",
"iam:GetServerCertificate",
"iam:UploadServerCertificate"
],
"Resource": [
"*"
],
"Effect": "Allow",
"Sid": "Stmt1404836868000"
}
]
}
.. code-block:: python
{
"Statement": [
{
"Action": [
"elasticloadbalancing:DescribeInstanceHealth",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeLoadBalancerPolicyTypes",
"elasticloadbalancing:DescribeLoadBalancerPolicies",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DeleteLoadBalancerListeners",
"elasticloadbalancing:CreateLoadBalancerListeners"
],
"Resource": [
"*"
],
"Effect": "Allow",
"Sid": "Stmt1404841912000"
}
]
}
Setting up STS access
---------------------
Once we have setup our accounts we need to ensure that we create a trust relationship so that LemurInstanceProfile can assume the Lemur role.
In the AWS console select the Lemur IAM role and select the Trust Relationships tab and click Edit Trust Relationship
Below is an example policy:
.. code-block:: python
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<awsaccountnumber>:role/LemurInstanceProfile",
]
},
"Action": "sts:AssumeRole"
}
]
}
Adding N+1 accounts
-------------------
To add another account we go to the new account and create a new Lemur IAM role with the same policy as above.
Then we would go to the account that Lemur is running is and edit the trust relationship policy.
An example policy:
.. code-block:: python
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<awsaccountnumber>:role/LemurInstanceProfile",
"arn:aws:iam::<awsaccountnumber1>:role/LemurInstanceProfile",
]
},
"Action": "sts:AssumeRole"
}
]
}
Setting up SES
--------------
Lemur has built in support for sending it's certificate notifications via Amazon's simple email service (SES). To force
Lemur to use SES ensure you are the running as the IAM role defined above and that you have followed the steps outlined
in Amazon's documentation `Setting up Amazon SES <http://docs.aws.amazon.com/ses/latest/DeveloperGuide/setting-up-ses.html>`_
The configuration::
LEMUR_MAIL = 'lemur.example.com'
Will be the sender of all notifications, so ensure that it is verified with AWS.
SES if the default notification gateway and will be used unless SMTP settings are configured in the application configuration
settings.
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 changes with the following command.
.. code-block:: bash
$ lemur db upgrade
.. note:: Internally, this uses `Alembic <https://alembic.readthedocs.org/en/latest/>`_ to manage database migrations.
.. _CommandLineInterface:
Command Line Interface
======================
Lemur installs a command line script under the name ``lemur``. This will allow you to
perform most required operations that are unachievable within the web UI.
If you're using a non-standard configuration location, you'll need to prefix every command with
--config (excluding create_config, which is a special case). For example::
lemur --config=/etc/lemur.conf.py help
For a list of commands, you can also use ``lemur help``, or ``lemur [command] --help``
for help on a specific command.
.. note:: The script is powered by a library called `Flask-Script <https://github.com/smurfix/flask-script>`_
Builtin Commands
----------------
All commands default to `~/.lemur/lemur.conf.py` if a configuration is not specified.
.. data:: create_config
Creates a default configuration file for Lemur.
Path defaults to ``~/.lemur/lemur.config.py``
::
lemur create_config .
.. note::
This command is a special case and does not depend on the configuration file
being set.
.. data:: init
Initializes the configuration file for Lemur.
::
lemur -c /etc/lemur.conf.py init
.. data:: start
Starts a Lemur service. You can also pass any flag that Gunicorn uses to specify the webserver configuration.
::
lemur start -w 6 -b 127.0.0.1:8080
.. data:: db upgrade
Performs any needed database migrations.
::
lemur db upgrade
.. data:: check_revoked
Traverses every certificate that Lemur is aware of and attempts to understand its validity.
It utilizes both OCSP and CRL. If Lemur is unable to come to a conclusion about a certificates
validity its status is marked 'unknown'
.. data:: sync
Sync attempts to discover certificates in the environment that were not created by Lemur. If you wish to only sync
a few sources you can pass a comma delimited list of sources to sync
::
lemur sync source1,source2
Additionally you can also list the available sources that Lemur can sync
::
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 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.
Within Lemur there are three main permissions: AdminPermission, CreatorPermission, OwnerPermission. Sub-permissions such
as ViewPrivateKeyPermission are compositions of these three main Permissions.
Lets take a look at how these permissions are used:
Each `Authority` has a set of roles associated with it. If a user is also associated with the same roles
that the `Authority` is associated with, Lemur allows that user to user/view/update that `Authority`.
This RBAC is also used when determining which users can access which certificate private key. Lemur's current permission
structure is setup such that if the user is a `Creator` or `Owner` of a given certificate they are allow to view that
private key. Owners can also be a role name, such that any user with the same role as owner will be allowed to view the
private key information.
These permissions are applied to the user upon login and refreshed on every request.
.. seealso::
`Flask-Principal <https://pythonhosted.org/Flask-Principal>`_
Upgrading Lemur
===============
To upgrade Lemur to the newest release you will need to ensure you have the lastest code and have run any needed
database migrations.
To get the latest code from github run
::
cd <lemur-source-directory>
git pull -t <version>
python setup.py develop
.. note::
It's important to grab the latest release by specifying the release tag. This tags denote stable versions of Lemur.
If you want to try the bleeding edge version of Lemur you can by using the master branch.
After you have the latest version of the Lemur code base you must run any needed database migrations. To run migrations
::
cd <lemur-source-directory>/lemur
lemur db upgrade
This will ensure that any needed tables or columns are created or destroyed.

View File

@ -11,231 +11,240 @@
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
from unittest.mock import MagicMock
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath(".."))
# Mock packages that cannot be installed on rtd
on_rtd = os.environ.get("READTHEDOCS") == "True"
if on_rtd:
class Mock(MagicMock):
@classmethod
def __getattr__(cls, name):
return MagicMock()
MOCK_MODULES = ["ldap"]
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinxcontrib.autohttp.flask',
'sphinx.ext.todo',
]
extensions = ["sphinx.ext.autodoc", "sphinxcontrib.autohttp.flask", "sphinx.ext.todo"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'lemur'
copyright = u'2015, Netflix Inc.'
project = u"lemur"
copyright = u"2018, Netflix Inc."
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.1'
# The full version, including alpha/beta/rc tags.
release = '0.1.3'
base_dir = os.path.join(os.path.dirname(__file__), os.pardir)
about = {}
with open(os.path.join(base_dir, "lemur", "__about__.py")) as f:
exec(f.read(), about) # nosec
version = release = about["__version__"]
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
if not on_rtd: # only import and set the theme if we're building docs locally
import sphinx_rtd_theme
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# 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
# documentation.
#html_theme_options = {}
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'lemurdoc'
htmlhelp_basename = "lemurdoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'lemur.tex', u'Lemur Documentation',
u'Kevin Glisson', 'manual'),
("index", "lemur.tex", u"Lemur Documentation", u"Netflix Security", "manual")
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'Lemur', u'Lemur Documentation',
[u'Kevin Glisson'], 1)
]
man_pages = [("index", "Lemur", u"Lemur Documentation", [u"Netflix Security"], 1)]
# If true, show URL addresses after external links.
#man_show_urls = False
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
@ -244,19 +253,25 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Lemur', u'Lemur Documentation',
u'Kevin Glisson', 'Lemur', 'SSL Certificate Management',
'Miscellaneous'),
(
"index",
"Lemur",
u"Lemur Documentation",
u"Netflix Security",
"Lemur",
"SSL Certificate Management",
"Miscellaneous",
)
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# texinfo_no_detailmenu = False

View File

@ -22,12 +22,18 @@ Once you've got all that, the rest is simple:
# If you have a fork, you'll want to clone it instead
git clone git://github.com/netflix/lemur.git
# Create a python virtualenv
mkvirtualenv lemur
# Create and activate python virtualenv from within the lemur repo
python3 -m venv env
. env/bin/activate
# Install doc requirements
# Make the magic happen
make dev-docs
# Make the docs
cd docs
make html
Running ``make dev-docs`` will install the basic requirements to get Sphinx running.
@ -48,7 +54,7 @@ of Lemur. You'll want to make sure you have a few things on your local system fi
* pip
* virtualenv (ideally virtualenvwrapper)
* node.js (for npm and building css/javascript)
* (Optional) Potgresql
+* `PostgreSQL <https://lemur.readthedocs.io/en/latest/quickstart/index.html#setup-postgres>`_
Once you've got all that, the rest is simple:
@ -58,7 +64,7 @@ Once you've got all that, the rest is simple:
git clone git://github.com/lemur/lemur.git
# Create a python virtualenv
mkvirtualenv lemur
python3 -m venv env
# Make the magic happen
make
@ -77,6 +83,7 @@ Create a default Lemur configuration just as if this were a production instance:
::
lemur create_config
lemur init
You'll likely want to make some changes to the default configuration (we recommend developing against Postgres, for example). Once done, migrate your database using the following command:
@ -86,7 +93,13 @@ You'll likely want to make some changes to the default configuration (we recomme
lemur upgrade
.. note:: The ``upgrade`` shortcut is simply a shorcut to Alembic's upgrade command.
.. note:: The ``upgrade`` shortcut is simply a shortcut to Alembic's upgrade command.
Running tests with Docker and docker-compose
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Alternatively you can use Docker and docker-compose for running the tests with ``docker-compose run test``.
Coding Standards
@ -113,6 +126,12 @@ HTML:
2 Spaces
Git hooks
~~~~~~~~~
To help developers maintain the above standards, Lemur includes a configuration file for Yelp's `pre-commit <http://pre-commit.com/>`_. This is an optional dependency and is not required in order to contribute to Lemur.
Running the Test Suite
----------------------
@ -122,7 +141,7 @@ The test suite consists of multiple parts, testing both the Python and JavaScrip
make test
If you only need to run the Python tests, you can do so with ``make test-python``, as well as ``test-js`` for the JavaScript tests.
If you only need to run the Python tests, you can do so with ``make test-python``, as well as ``make test-js`` for the JavaScript tests.
You'll notice that the test suite is structured based on where the code lives, and strongly encourages using the mock library to drive more accurate individual tests.
@ -144,8 +163,19 @@ If you've made changes and need to compile them by hand for any reason, you can
The minified and processed files should be committed alongside the unprocessed changes.
It's also important to note that Lemur's frontend and API are not tied together. The API does not serve any of the static assets, we rely on nginx or some other file server to server all of the static assets.
During development that means we need an additional server to serve those static files for the GUI.
This is accomplished with a Gulp task:
::
./node_modules/.bin/gulp serve
The gulp task compiles all the JS/CSS/HTML files and opens the Lemur welcome page in your default browsers. Additionally any changes to made to the JS/CSS/HTML with be reloaded in your browsers.
Developing with Flask
----------------------
---------------------
Because Lemur is just Flask, you can use all of the standard Flask functionality. The only difference is you'll be accessing commands that would normally go through manage.py using the ``lemur`` CLI helper instead.
@ -164,7 +194,7 @@ Schema changes should always introduce the new schema in a commit, and then intr
Removing columns and tables requires a slightly more painful flow, and should resemble the follow multi-commit flow:
- Remove all references to the column or table (but dont remove the Model itself)
- Remove all references to the column or table (but don't remove the Model itself)
- Remove the model code
- Remove the table or column
@ -180,19 +210,116 @@ You can see a list of open pull requests (pending changes) by visiting https://g
Pull requests should be against **master** and pass all TravisCI checks
Plugins
=======
Writing a Plugin
================
.. toctree::
:maxdepth: 1
:maxdepth: 2
plugins/index
REST API
========
Lemur's front end is entirely API driven. Any action that you can accomplish via the UI can also be accomplished by the
API. The following is documents and provides examples on how to make requests to the Lemur API.
Authentication
--------------
.. automodule:: lemur.auth.views
:members:
:undoc-members:
:show-inheritance:
Destinations
------------
.. automodule:: lemur.destinations.views
:members:
:undoc-members:
:show-inheritance:
Notifications
-------------
.. automodule:: lemur.notifications.views
:members:
:undoc-members:
:show-inheritance:
Users
-----
.. automodule:: lemur.users.views
:members:
:undoc-members:
:show-inheritance:
Roles
-----
.. automodule:: lemur.roles.views
:members:
:undoc-members:
:show-inheritance:
Certificates
------------
.. automodule:: lemur.certificates.views
:members:
:undoc-members:
:show-inheritance:
Authorities
-----------
.. automodule:: lemur.authorities.views
:members:
:undoc-members:
:show-inheritance:
Domains
-------
.. automodule:: lemur.domains.views
:members:
:undoc-members:
:show-inheritance:
Endpoints
---------
.. automodule:: lemur.endpoints.views
:members:
:undoc-members:
:show-inheritance:
Logs
----
.. automodule:: lemur.logs.views
:members:
:undoc-members:
:show-inheritance:
Sources
-------
.. automodule:: lemur.sources.views
:members:
:undoc-members:
:show-inheritance:
Internals
=========
.. toctree::
:maxdepth: 1
:maxdepth: 2
internals/lemur

View File

@ -1,15 +1,6 @@
certificates Package
====================
:mod:`exceptions` Module
------------------------
.. automodule:: lemur.certificates.exceptions
:noindex:
:members:
:undoc-members:
:show-inheritance:
:mod:`models` Module
--------------------

View File

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

View File

@ -10,15 +10,6 @@ lemur_verisign Package
:undoc-members:
:show-inheritance:
:mod:`constants` Module
-----------------------
.. automodule:: lemur.plugins.lemur_verisign.constants
:noindex:
:members:
:undoc-members:
:show-inheritance:
:mod:`plugin` Module
--------------------

View File

@ -27,6 +27,6 @@ Subpackages
lemur.plugins.base
lemur.plugins.bases
lemur.plugins.lemur_aws
lemur.plugins.lemur_cloudca
lemur.plugins.lemur_cfssl
lemur.plugins.lemur_email
lemur.plugins.lemur_verisign

View File

@ -96,5 +96,19 @@ Subpackages
lemur.notifications
lemur.plugins
lemur.roles
lemur.status
lemur.users
lemur.sources
lemur.logs
lemur.reporting
lemur.tests
lemur.deployment
lemur.endpoints
lemur.defaults
lemur.plugins.lemur_acme
lemur.plugins.lemur_atlas
lemur.plugins.lemur_cryptography
lemur.plugins.lemur_digicert
lemur.plugins.lemur_java
lemur.plugins.lemur_kubernetes
lemur.plugins.lemur_openssl
lemur.plugins.lemur_slack

View File

@ -1,11 +0,0 @@
status Package
==============
:mod:`views` Module
-------------------
.. automodule:: lemur.status.views
:noindex:
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,6 +1,3 @@
Writing a Plugin
================
Several interfaces exist for extending Lemur:
* Issuer (lemur.plugins.base.issuer)
@ -28,7 +25,7 @@ if you want to pull the version using pkg_resources (which is what we recommend)
try:
VERSION = __import__('pkg_resources') \
.get_distribution(__name__).version
except Exception, e:
except Exception as e:
VERSION = 'unknown'
Inside of ``plugin.py``, you'll declare your Plugin class::
@ -73,10 +70,18 @@ at multiple plugins within your package::
},
)
Once your plugin files are in place and the ``/www/lemur/setup.py`` file has been modified, you can load your plugin into your instance by reinstalling lemur:
::
(lemur)$cd /www/lemur
(lemur)$pip install -e .
That's it! Users will be able to install your plugin via ``pip install <package name>``.
.. SeeAlso:: For more information about python packages see `Python Packaging <https://packaging.python.org/en/latest/distributing.html>`_
.. SeeAlso:: For an example of a plugin operation outside of Lemur's core, see `lemur-digicert <https://github.com/opendns/lemur-digicert>`_
.. _PluginInterfaces:
Plugin Interfaces
@ -95,10 +100,16 @@ If you have a third party or internal service that creates authorities (EJBCA, e
it can treat any issuer plugin as both a source of creating new certificates as well as new authorities.
The `IssuerPlugin` exposes two functions::
The `IssuerPlugin` exposes four functions functions::
def create_certificate(self, options):
def create_certificate(self, csr, issuer_options):
# requests.get('a third party')
def revoke_certificate(self, certificate, comments):
# requests.put('a third party')
def get_ordered_certificate(self, order_id):
# requests.get('already existing certificate')
def canceled_ordered_certificate(self, pending_cert, **kwargs):
# requests.put('cancel an order that has yet to be issued')
Lemur will pass a dictionary of all possible options for certificate creation. Including a valid CSR, and the raw options associated with the request.
@ -134,15 +145,34 @@ The `IssuerPlugin` doesn't have any options like Destination, Source, and Notifi
any fields you might need to submit a request to a third party. If there are additional options you need
in your plugin feel free to open an issue, or look into adding additional options to issuers yourself.
Asynchronous Certificates
^^^^^^^^^^^^^^^^^^^^^^^^^
An issuer may take some time to actually issue a certificate for an order. In this case, a `PendingCertificate` is returned, which holds information to recreate a `Certificate` object at a later time. Then, `get_ordered_certificate()` should be run periodically via `python manage.py pending_certs fetch -i all` to attempt to retrieve an ordered certificate::
def get_ordered_ceriticate(self, order_id):
# order_id is the external id of the order, not the external_id of the certificate
# retrieve an order, and check if there is an issued certificate attached to it
`cancel_ordered_certificate()` should be implemented to allow an ordered certificate to be canceled before it is issued::
def cancel_ordered_certificate(self, pending_cert, **kwargs):
# pending_cert should contain the necessary information to match an order
# kwargs can be given to provide information to the issuer for canceling
Destination
-----------
Destination plugins allow you to propagate certificates managed by Lemur to additional third parties. This provides flexibility when
different orchestration systems have their own way of manage certificates or there is an existing system you wish to integrate with Lemur.
By default destination plugins have a private key requirement. If your plugin does not require a certificates private key mark `requires_key = False`
in the plugins base class like so::
class MyDestinationPlugin(DestinationPlugin):
requires_key = False
The DestinationPlugin requires only one function to be implemented::
def upload(self, cert, private_key, cert_chain, options, **kwargs):
def upload(self, name, body, private_key, cert_chain, options, **kwargs):
# request.post('a third party')
Additionally the DestinationPlugin allows the plugin author to add additional options
@ -151,25 +181,25 @@ that can be used to help define sub-destinations.
For example, if we look at the aws-destination plugin we can see that it defines an `accountNumber` option::
options = [
{
'name': 'accountNumber',
'type': 'int',
'required': True,
'validation': '/^[0-9]{12,12}$/',
'helpMessage': 'Must be a valid AWS account number!',
}
{
'name': 'accountNumber',
'type': 'int',
'required': True,
'validation': '/^[0-9]{12,12}$/',
'helpMessage': 'Must be a valid AWS account number!',
}
]
By defining an `accountNumber` we can make this plugin handle many N number of AWS accounts instead of just one.
The schema for defining plugin options are pretty straightforward:
- **Name**: name of the variable you wish to present the user, snake case (snakeCase) is preferrred as Lemur
- **Name**: name of the variable you wish to present the user, snake case (snakeCase) is preferred as Lemur
will parse these and create pretty variable titles
- **Type** there are currently four supported variable types
- **Int** creates an html integer box for the user to enter integers into
- **Str** creates a html text input box
- **Boolean** creates a checkbox for the user to signify truithyness
- **Boolean** creates a checkbox for the user to signify truthiness
- **Select** creates a select box that gives the user a list of options
- When used a `available` key must be provided with a list of selectable options
- **Required** determines if this option is required, this **must be a boolean value**
@ -185,21 +215,24 @@ Notification
------------
Lemur includes the ability to create Email notifications by **default**. These notifications
currently come in the form of expiration noticies. Lemur periodically checks certifications expiration dates and
currently come in the form of expiration and rotation notices. Lemur periodically checks certificate expiration dates and
determines if a given certificate is eligible for notification. There are currently only two parameters used to
determine if a certificate is eligible; validity expiration (date the certificate is no longer valid) and the number
of days the current date (UTC) is from that expiration date.
There are currently two objects that available for notification plugins the first is `NotficationPlugin`. This is the base object for
any notification within Lemur. Currently the only support notification type is an certificate expiration notification. If you
Expiration notifications can also be configured for Slack or AWS SNS. Rotation notifications are not configurable.
Notifications sent to a certificate owner and security team (`LEMUR_SECURITY_TEAM_EMAIL`) can currently only be sent via email.
There are currently two objects that are available for notification plugins. The first is `NotificationPlugin`, which is the base object for
any notification within Lemur. Currently the only supported notification type is a certificate expiration notification. If you
are trying to create a new notification type (audit, failed logins, etc.) this would be the object to base your plugin on.
You would also then need to build additional code to trigger the new notification type.
The second is `ExpirationNotificationPlugin`, this object inherits from `NotificationPlugin` object.
You will most likely want to base your plugin on, if you want to add new channels for expiration notices (Slack, Hipcat, Jira, etc.). It adds default options that are required by
by all expiration notifications (interval, unit). This interface expects for the child to define the following function::
The second is `ExpirationNotificationPlugin`, which inherits from the `NotificationPlugin` object.
You will most likely want to base your plugin on this object if you want to add new channels for expiration notices (HipChat, Jira, etc.). It adds default options that are required by
all expiration notifications (interval, unit). This interface expects for the child to define the following function::
def send(self):
def send(self, notification_type, message, targets, options, **kwargs):
# request.post("some alerting infrastructure")
@ -207,27 +240,48 @@ Source
------
When building Lemur we realized that although it would be nice if every certificate went through Lemur to get issued, but this is not
always be the case. Often times there are third parties that will issue certificates on your behalf and these can get deployed
always be the case. Oftentimes there are third parties that will issue certificates on your behalf and these can get deployed
to infrastructure without any interaction with Lemur. In an attempt to combat this and try to track every certificate, Lemur has a notion of
certificate **Sources**. Lemur will contact the source at periodic intervals and attempt to **sync** against the source. This means downloading or discovering any
certificate Lemur does not know about and adding the certificate to it's inventory to be tracked and alerted on.
certificate Lemur does not know about and adding the certificate to its inventory to be tracked and alerted on.
The `SourcePlugin` object has one default option of `pollRate`. This controls the number of seconds which to get new certificates.
.. warning::
Lemur currently has a very basic polling system of running a cron job every 15min to see which source plugins need to be run. A lock file is generated to guarantee that
.. warning::
Lemur currently has a very basic polling system of running a cron job every 15min to see which source plugins need to be run. A lock file is generated to guarantee that
only one sync is running at a time. It also means that the minimum resolution of a source plugin poll rate is effectively 15min. You can always specify a faster cron
job if you need a higher resolution sync job.
The `SourcePlugin` object requires implementation of one function::
def get_certificates(self, **kwargs):
def get_certificates(self, options, **kwargs):
# request.get("some source of certificates")
.. Note::
Often times to facilitate code re-use it makes sense put source and destination plugins into one package.
.. note::
Oftentimes to facilitate code re-use it makes sense put source and destination plugins into one package.
Export
------
Formats, formats and more formats. That's the current PKI landscape. See the always relevant `xkcd <https://xkcd.com/927/>`_.
Thankfully Lemur supports the ability to output your certificates into whatever format you want. This integration comes by the way
of Export plugins. Support is still new and evolving, the goal of these plugins is to return raw data in a new format that
can then be used by any number of applications. Included in Lemur is the `JavaExportPlugin` which currently supports generating
a Java Key Store (JKS) file for use in Java based applications.
The `ExportPlugin` object requires the implementation of one function::
def export(self, body, chain, key, options, **kwargs):
# sys.call('openssl hokuspocus')
# return "extension", passphrase, raw
.. note::
Support of various formats sometimes relies on external tools system calls. Always be mindful of sanitizing any input to these calls.
Testing
@ -246,9 +300,9 @@ Augment your setup.py to ensure at least the following:
setup(
# ...
install_requires=[
install_requires=[
'lemur',
]
]
)
@ -259,11 +313,7 @@ The ``conftest.py`` file is our main entry-point for py.test. We need to configu
.. code-block:: python
from __future__ import absolute_import
pytest_plugins = [
'lemur.utils.pytest'
]
from lemur.tests.conftest import * # noqa
Test Cases
@ -273,14 +323,18 @@ You can now inherit from Lemur's core test classes. These are Django-based and e
.. code-block:: python
# test_myextension.py
from __future__ import absolute_import
import pytest
from lemur.tests.vectors import INTERNAL_CERTIFICATE_A_STR, INTERNAL_PRIVATE_KEY_A_STR
from lemur.testutils import TestCase
def test_export_keystore(app):
from lemur.plugins.base import plugins
p = plugins.get('java-keystore-jks')
options = [{'name': 'passphrase', 'value': 'test1234'}]
with pytest.raises(Exception):
p.export(INTERNAL_CERTIFICATE_A_STR, "", "", options)
class MyExtensionTest(TestCase):
def test_simple(self):
assert 1 != 2
raw = p.export(INTERNAL_CERTIFICATE_A_STR, "", INTERNAL_PRIVATE_KEY_A_STR, options)
assert raw != b""
Running Tests
@ -292,13 +346,14 @@ Running tests follows the py.test standard. As long as your test files and metho
$ py.test -v
============================== test session starts ==============================
platform darwin -- Python 2.7.9 -- py-1.4.26 -- pytest-2.6.4/python2.7
plugins: django
collected 1 items
platform darwin -- Python 2.7.10, pytest-2.8.5, py-1.4.30, pluggy-0.3.1
cachedir: .cache
plugins: flask-0.10.0
collected 346 items
tests/test_myextension.py::MyExtensionTest::test_simple PASSED
lemur/plugins/lemur_acme/tests/test_acme.py::test_get_certificates PASSED
=========================== 1 passed in 0.35 seconds ============================
.. SeeAlso:: Lemur bundles several plugins that use the same interfaces mentioned above. View the source: # TODO
.. SeeAlso:: Lemur bundles several plugins that use the same interfaces mentioned above.

View File

@ -1,66 +0,0 @@
Lemur's front end is entirely API driven. Any action that you can accomplish via the UI can also be accomplished by the
UI. The following is documents and provides examples on how to make requests to the Lemur API.
Authentication
--------------
.. automodule:: lemur.auth.views
:members:
:undoc-members:
:show-inheritance:
Destinations
------------
.. automodule:: lemur.destinations.views
:members:
:undoc-members:
:show-inheritance:
Notifications
-------------
.. automodule:: lemur.notifications.views
:members:
:undoc-members:
:show-inheritance:
Users
-----
.. automodule:: lemur.users.views
:members:
:undoc-members:
:show-inheritance:
Roles
-----
.. automodule:: lemur.roles.views
:members:
:undoc-members:
:show-inheritance:
Certificates
------------
.. automodule:: lemur.certificates.views
:members:
:undoc-members:
:show-inheritance:
Authorities
-----------
.. automodule:: lemur.authorities.views
:members:
:undoc-members:
:show-inheritance:
Domains
-------
.. automodule:: lemur.domains.views
:members:
:undoc-members:
:show-inheritance:

53
docs/doing-a-release.rst Normal file
View File

@ -0,0 +1,53 @@
Doing a release
===============
Doing a release of ``lemur`` requires a few steps.
Bumping the version number
--------------------------
The next step in doing a release is bumping the version number in the
software.
* Update the version number in ``lemur/__about__.py``.
* Set the release date in the :doc:`/changelog`.
* Do a commit indicating this.
* Send a pull request with this.
* Wait for it to be merged.
Performing the release
----------------------
The commit that merged the version number bump is now the official release
commit for this release. You will need to have ``gpg`` installed and a ``gpg``
key in order to do a release. Once this has happened:
* Run ``invoke release {version}``.
The release should now be available on PyPI and a tag should be available in
the repository.
Verifying the release
---------------------
You should verify that ``pip install lemur`` works correctly:
.. code-block:: pycon
>>> import lemur
>>> lemur.__version__
'...'
Verify that this is the version you just released.
Post-release tasks
------------------
* Update the version number to the next major (e.g. ``0.5.dev1``) in
``lemur/__about__.py`` and
* Add new :doc:`/changelog` entry with next version and note that it is under
active development
* Send a pull request with these items
* Check for any outstanding code undergoing a deprecation cycle by looking in
``lemur.utils`` for ``DeprecatedIn**`` definitions. If any exist open
a ticket to increment them for the next release.

View File

@ -6,7 +6,7 @@ Common Problems
In my startup logs I see *'Aborting... Lemur cannot locate db encryption key, is LEMUR_ENCRYPTION_KEYS set?'*
You likely have not correctly configured **LEMUR_ENCRYPTION_KEYS**. See
:doc:`administration/index` for more information.
:doc:`administration` for more information.
I am seeing Lemur's javascript load in my browser but not the CSS.
@ -14,6 +14,11 @@ I am seeing Lemur's javascript load in my browser but not the CSS.
:doc:`production/index` for example configurations.
After installing Lemur I am unable to login
Ensure that you are trying to login with the credentials you entered during `lemur init`. These are separate
from the postgres database credentials.
Running 'lemur db upgrade' seems stuck.
Most likely, the upgrade is stuck because an existing query on the database is holding onto a lock that the
migration needs.

View File

@ -18,7 +18,7 @@ that Lemur can then manage.
.. figure:: create_authority.png
Enter a authority name and short description about the authority. Enter an owner,
Enter an authority name and short description about the authority. Enter an owner,
and certificate common name. Depending on the authority and the authority/issuer plugin
these values may or may not be used.
@ -56,7 +56,7 @@ Import an Existing Certificate
.. figure:: upload_certificate.png
Enter a owner, short description and public certificate. If there are intermediates and private keys
Enter an owner, short description and public certificate. If there are intermediates and private keys
Lemur will track them just as it does if the certificate were created through Lemur. Lemur generates
a certificate name but you can override that by passing a value to the `Custom Name` field.

View File

@ -27,8 +27,7 @@ Administration
.. toctree::
:maxdepth: 2
administration/index
plugins/index
administration
Developers
----------
@ -38,17 +37,24 @@ Developers
developer/index
REST API
Security
--------
.. toctree::
:maxdepth: 2
developer/rest
security
Doing a Release
---------------
.. toctree::
:maxdepth: 1
doing-a-release
FAQ
----
---
.. toctree::
:maxdepth: 1

View File

@ -1,20 +0,0 @@
Plugins
=======
There are several interfaces currently available to extend Lemur. These are a work in
progress and the API is not frozen.
Bundled Plugins
---------------
Lemur includes several plugins by default. Including extensive support for AWS, VeriSign/Symantec and CloudCA services.
3rd Party Extensions
--------------------
The following extensions are available and maintained by members of the Lemur community:
Have an extension that should be listed here? Submit a `pull request <https://github.com/netflix/lemur>`_ and we'll
get it added.
Want to create your own extension? See :doc:`../developer/plugins/index` to get started.

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -37,20 +37,22 @@ Entropy
-------
Lemur generates private keys for the certificates it creates. This means that it is vitally important that Lemur has enough entropy to draw from. To generate private keys Lemur uses the python library `Cryptography <https://cryptography.io>`_. In turn Cryptography uses OpenSSL bindings to generate
keys just like you might from the OpenSSL command line. OpenSSL draws it's initial entropy from system during startup and uses PRNGs to generate a stream of random bytes (as output by /dev/urandom) whenever it needs to do a cryptographic operation.
keys just like you might from the OpenSSL command line. OpenSSL draws its initial entropy from system during startup and uses PRNGs to generate a stream of random bytes (as output by /dev/urandom) whenever it needs to do a cryptographic operation.
What does all this mean? Well in order for the keys
that Lemur generates to be strong, the system needs to interact with the outside world. This is typically accomplished through the systems hardware (thermal, sound, video user-input, etc.) since the physical world is much more "random" than the computer world.
If you are running Lemur on its own server with its own hardware "bare metal" then the entropy of the system is typically "good enough" for generating keys. If however you are using an VM on shared hardware there is a potential that your initial seed data (data that was initially
fed to the PRNG) is not very good. What's more VMs have been known to be unable to inject more entropy into the system once it has been started. This is because there is typically very little interaction with the server once it has been started.
If you are running Lemur on its own server with its own hardware "bare metal" then the entropy of the system is typically "good enough" for generating keys. If however you are using a VM on shared hardware there is a potential that your initial seed data (data that was initially
fed to the PRNG) is not very good. What's more, VMs have been known to be unable to inject more entropy into the system once it has been started. This is because there is typically very little interaction with the server once it has been started.
The amount of effort you wish to expend ensuring that Lemur has good entropy to draw from is up to your specific risk tolerance and how Lemur is configured.
If you wish to generate more entropy for your system we would suggest you take a look at the following resources:
- `WES-entropy-client <https://github.com/WhitewoodCrypto/WES-entropy-client>`_
- `haveaged <http://www.issihosts.com/haveged/>`_
- `WES-entropy-client <https://github.com/Virginian/WES-entropy-client>`_
- `haveged <http://www.issihosts.com/haveged/>`_
The original *WES-entropy-client* repository by WhitewoodCrypto was removed, the link now points to a fork of it.
For additional information about OpenSSL entropy issues:
@ -72,7 +74,7 @@ Nginx is a very popular choice to serve a Python project:
Nginx doesn't run any Python process, it only serves requests from outside to
the Python server.
Therefore there are two steps:
Therefore, there are two steps:
- Run the Python process.
- Run Nginx.
@ -110,7 +112,7 @@ You can make some adjustments to get a better user experience::
error_log /var/log/nginx/log/lemur.error.log;
location /api {
proxy_pass http://127.0.0.1:5000;
proxy_pass http://127.0.0.1:8000;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
proxy_buffering off;
@ -176,7 +178,7 @@ sensitive nature of Lemur and what it controls makes this essential. This is a s
resolver <IP DNS resolver>;
location /api {
proxy_pass http://127.0.0.1:5000;
proxy_pass http://127.0.0.1:8000;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
proxy_buffering off;
@ -217,13 +219,30 @@ An example apache config::
# HSTS (mod_headers is required) (15768000 seconds = 6 months)
Header always set Strict-Transport-Security "max-age=15768000"
...
# Set the lemur DocumentRoot to static/dist
DocumentRoot /www/lemur/lemur/static/dist
# Uncomment to force http 1.0 connections to proxy
# SetEnv force-proxy-request-1.0 1
#Don't keep proxy connections alive
SetEnv proxy-nokeepalive 1
# Only need to do reverse proxy
ProxyRequests Off
# Proxy requests to the api to the lemur service (and sanitize redirects from it)
ProxyPass "/api" "http://127.0.0.1:8000/api"
ProxyPassReverse "/api" "http://127.0.0.1:8000/api"
</VirtualHost>
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::
This is a rather incomplete apache config for running Lemur (needs mod_wsgi etc.,), if you have a working apache config please let us know!
This is a rather incomplete apache config for running Lemur (needs mod_wsgi etc.), if you have a working apache config please let us know!
.. seealso::
`Mozilla SSL Configuration Generator <https://mozilla.github.io/server-side-tls/ssl-config-generator/>`_
@ -240,10 +259,10 @@ most of the time), but here is a quick overview on how to use it.
Create a configuration file named supervisor.ini::
[unix_http_server]
file=/tmp/supervisor.sock;
file=/tmp/supervisor.sock
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock;
serverurl=unix:///tmp/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
@ -257,13 +276,12 @@ Create a configuration file named supervisor.ini::
nodaemon=false
minfds=1024
minprocs=200
user=lemur
[program:lemur]
command=python /path/to/lemur/manage.py manage.py start
directory=/path/to/lemur/
environment=PYTHONPATH='/path/to/lemur/'
environment=PYTHONPATH='/path/to/lemur/',LEMUR_CONF='/home/lemur/.lemur/lemur.conf.py'
user=lemur
autostart=true
autorestart=true
@ -296,3 +314,244 @@ Then you can manage the process by running::
It will start a shell from which you can start/stop/restart the service.
You can read all errors that might occur from /tmp/lemur.log.
.. _PeriodicTasks:
Periodic Tasks
==============
Lemur contains a few tasks that are run and scheduled basis, currently the recommend way to run these tasks is to create
celery tasks or cron jobs that run these commands.
There are currently three commands that could/should be run on a periodic basis:
- `notify`
- `check_revoked`
- `sync`
If you are using LetsEncrypt, you must also run the following:
- `fetch_all_pending_acme_certs`
- `remove_old_acme_certs`
How often you run these commands is largely up to the user. `notify` and `check_revoked` are typically run at least once a day.
`sync` is typically run every 15 minutes. `fetch_all_pending_acme_certs` should be ran frequently (Every minute is fine).
`remove_old_acme_certs` can be ran more rarely, such as once every week.
Example cron entries::
0 22 * * * lemuruser export LEMUR_CONF=/Users/me/.lemur/lemur.conf.py; /www/lemur/bin/lemur notify expirations
*/15 * * * * lemuruser export LEMUR_CONF=/Users/me/.lemur/lemur.conf.py; /www/lemur/bin/lemur source sync -s all
0 22 * * * lemuruser export LEMUR_CONF=/Users/me/.lemur/lemur.conf.py; /www/lemur/bin/lemur certificate check_revoked
Example Celery configuration (To be placed in your configuration file)::
CELERYBEAT_SCHEDULE = {
'fetch_all_pending_acme_certs': {
'task': 'lemur.common.celery.fetch_all_pending_acme_certs',
'options': {
'expires': 180
},
'schedule': crontab(minute="*"),
},
'remove_old_acme_certs': {
'task': 'lemur.common.celery.remove_old_acme_certs',
'options': {
'expires': 180
},
'schedule': crontab(hour=7, minute=30, day_of_week=1),
},
'clean_all_sources': {
'task': 'lemur.common.celery.clean_all_sources',
'options': {
'expires': 180
},
'schedule': crontab(hour=1, minute=0, day_of_week=1),
},
'sync_all_sources': {
'task': 'lemur.common.celery.sync_all_sources',
'options': {
'expires': 180
},
'schedule': crontab(hour="*/3", minute=5),
},
'sync_source_destination': {
'task': 'lemur.common.celery.sync_source_destination',
'options': {
'expires': 180
},
'schedule': crontab(hour="*"),
}
}
To enable celery support, you must also have configuration values that tell Celery which broker and backend to use.
Here are the Celery configuration variables that should be set::
CELERY_RESULT_BACKEND = 'redis://your_redis_url:6379'
CELERY_BROKER_URL = 'redis://your_redis_url:6379/0'
CELERY_IMPORTS = ('lemur.common.celery')
CELERY_TIMEZONE = 'UTC'
REDIS_HOST="your_redis_url"
REDIS_PORT=6379
REDIS_DB=0
Out of the box, every Redis instance supports 16 databases. The default database (`REDIS_DB`) is set to 0, however, you can use any of the databases from 0-15. Via `redis.conf` more databases can be supported.
In the `redis://` url, the database number can be added with a slash after the port. (defaults to 0, if omitted)
Do not forget to import crontab module in your configuration file::
from celery.task.schedules import crontab
You must start a single Celery scheduler instance and one or more worker instances in order to handle incoming tasks.
The scheduler can be started with::
LEMUR_CONF='/location/to/conf.py' /location/to/lemur/bin/celery -A lemur.common.celery beat
And the worker can be started with desired options such as the following::
LEMUR_CONF='/location/to/conf.py' /location/to/lemur/bin/celery -A lemur.common.celery worker --concurrency 10 -E -n lemurworker1@%%h
supervisor or systemd configurations should be created for these in production environments as appropriate.
Add support for LetsEncrypt
===========================
LetsEncrypt is a free, limited-feature certificate authority that offers publicly trusted certificates that are valid
for 90 days. LetsEncrypt does not use organizational validation (OV), and instead relies on domain validation (DV).
LetsEncrypt requires that we prove ownership of a domain before we're able to issue a certificate for that domain, each
time we want a certificate.
The most common methods to prove ownership are HTTP validation and DNS validation. Lemur supports DNS validation
through the creation of DNS TXT records.
In a nutshell, when we send a certificate request to LetsEncrypt, they generate a random token and ask us to put that
token in a DNS text record to prove ownership of a domain. If a certificate request has multiple domains, we must
prove ownership of all of these domains through this method. The token is typically written to a TXT record at
-acme_challenge.domain.com. Once we create the appropriate TXT record(s), Lemur will try to validate propagation
before requesting that LetsEncrypt finalize the certificate request and send us the certificate.
.. figure:: letsencrypt_flow.png
To start issuing certificates through LetsEncrypt, you must enable Celery support within Lemur first. After doing so,
you need to create a LetsEncrypt authority. To do this, visit
Authorities -> Create. Set the applicable attributes and click "More Options".
.. figure:: letsencrypt_authority_1.png
You will need to set "Certificate" to LetsEncrypt's active chain of trust for the authority you want to use. To find
the active chain of trust at the time of writing, please visit `LetsEncrypt
<https://letsencrypt.org/certificates/>`_.
Under Acme_url, enter in the appropriate endpoint URL. Lemur supports LetsEncrypt's V2 API, and we recommend you to use
this. At the time of writing, the staging and production URLs for LetsEncrypt V2 are
https://acme-staging-v02.api.letsencrypt.org/directory and https://acme-v02.api.letsencrypt.org/directory.
.. figure:: letsencrypt_authority_2.png
After creating the authorities, we will need to create a DNS provider. Visit `Admin` -> `DNS Providers` and click
`Create`. Lemur comes with a few provider plugins built in, with different options. Create a DNS provider with the
appropriate choices.
.. figure:: create_dns_provider.png
By default, users will need to select the DNS provider that is authoritative over their domain in order for the
LetsEncrypt flow to function. However, Lemur will attempt to automatically determine the appropriate provider if
possible. To enable this functionality, periodically (or through Cron/Celery) run `lemur dns_providers get_all_zones`.
This command will traverse all DNS providers, determine which zones they control, and upload this list of zones to
Lemur's database (in the dns_providers table). Alternatively, you can manually input this data.
LetsEncrypt: pinning to cross-signed ICA
----------------------------------------
Let's Encrypt has been using a `cross-signed <https://letsencrypt.org/certificates/>`_ intermediate CA by DST Root CA X3,
which is included in many older devices' TrustStore.
Let's Encrypt is `transitioning <https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html>`_ to use
the intermediate CA issued by their own root (ISRG X1) starting from September 29th 2020.
This is in preparation of concluding the initial bootstrapping of their CA, by having it cross-signed by an older CA.
Lemur can temporarily pin to the cross-signed intermediate CA (same public/private key pair as the ICA signed by ISRG X1).
This will prolong support for incompatible devices.
The following must be added to the config file to activate the pinning (the pinning will be removed by September 2021)::
# remove or update after Mar 17 16:40:46 2021 GMT
IDENTRUST_CROSS_SIGNED_LE_ICA_EXPIRATION_DATE = "17/03/21"
IDENTRUST_CROSS_SIGNED_LE_ICA = """
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----
"""
.. _AcmeAccountReuse:
LetsEncrypt: Using a pre-existing ACME account
-----------------------------------------------
Let's Encrypt allows reusing an existing ACME account, to create and especially revoke certificates. The current
implementation in the acme plugin, only allows for a single account for all ACME authorities, which might be an issue,
when you try to use Let's Encrypt together with another certificate authority that uses the ACME protocol.
To use an existing account, you need to configure the `ACME_PRIVATE_KEY` and `ACME_REGR` variables in the lemur
configuration.
`ACME_PRIVATE_KEY` needs to be in the JWK format::
{
"kty": "RSA",
"n": "yr1qBwHizA7ME_iV32bY10ILp.....",
"e": "AQAB",
"d": "llBlYhil3I.....",
"p": "-5LW2Lewogo.........",
"q": "zk6dHqHfHksd.........",
"dp": "qfe9fFIu3mu.......",
"dq": "cXFO-loeOyU.......",
"qi": "AfK1sh0_8sLTb..........."
}
Using `python-jwt` converting an existing private key in PEM format is quite easy::
import python_jwt as jwt, jwcrypto.jwk as jwk
priv_key = jwk.JWK.from_pem(b"""-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----""")
print(priv_key.export())
`ACME_REGR` needs to be a valid JSON with a `body` and a `uri` attribute, similar to this::
{"body": {}, "uri": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/<ACCOUNT_NUMBER>"}
The URI can be retrieved from the ACME create account endpoint when creating a new account, using the existing key.

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -1,178 +1,226 @@
Quickstart
**********
This guide will step you through setting up a Python-based virtualenv, installing the required packages, and configuring the basic web service.
This guide assumes a clean Ubuntu 14.04 instance, commands may differ based on the OS and configuration being used.
This guide will step you through setting up a Python-based virtualenv, installing the required packages, and configuring the basic web service. This guide assumes a clean Ubuntu 14.04 instance, commands may differ based on the OS and configuration being used.
Pressed for time? See the Lemur docker file on `Github <https://github.com/Netflix/lemur-docker>`_.
Dependencies
------------
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
* A UNIX-based operating system (we test on Ubuntu, develop on OS X)
* Python 3.7 or greater
* PostgreSQL 9.4 or greater
* Nginx
.. 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.
.. 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 as generic as possible and are not intended to document every step of launching Lemur into a given environment.
Installing Build Dependencies
-----------------------------
If installing Lemur on a bare Ubuntu OS you will need to grab the following packages so that Lemur can correctly build its dependencies:
.. code-block:: bash
sudo apt-get update
sudo apt-get install nodejs nodejs-legacy python-pip python-dev python3-dev libpq-dev build-essential libssl-dev libffi-dev libsasl2-dev libldap2-dev nginx git supervisor npm postgresql
.. note:: PostgreSQL is only required if your database is going to be on the same host as the webserver. npm is needed if you're installing Lemur from the source (e.g., from git).
.. note:: Installing node from a package manager may create the nodejs bin at /usr/bin/nodejs instead of /usr/bin/node If that is the case run the following
sudo ln -s /user/bin/nodejs /usr/bin/node
Now, install Python ``virtualenv`` package:
.. code-block:: bash
sudo pip install -U virtualenv
Setting up an Environment
-------------------------
The first thing you'll need is the Python ``virtualenv`` package. You probably already
have this, but if not, you can install it with::
In this guide, Lemur will be installed in ``/www``, so you need to create that structure first:
pip install -U virtualenv
.. code-block:: bash
Once that's done, choose a location for the environment, and create it with the ``virtualenv``
command. For our guide, we're going to choose ``/www/lemur/``::
sudo mkdir /www
cd /www
virtualenv /www/lemur/
Clone Lemur inside the just created directory and give yourself write permission (we assume ``lemur`` is the user):
Finally, activate your virtualenv::
.. code-block:: bash
source /www/lemur/bin/activate
sudo useradd lemur
sudo passwd lemur
sudo mkdir /home/lemur
sudo chown lemur:lemur /home/lemur
sudo git clone https://github.com/Netflix/lemur
sudo chown -R lemur lemur/
.. note:: Activating the environment adjusts your PATH, so that things like pip now
install into the virtualenv by default.
Create the virtual environment, activate it and enter the Lemur's directory:
.. code-block:: bash
Installing build dependencies
-----------------------------
su lemur
virtualenv -p python3 lemur
source /www/lemur/bin/activate
cd lemur
If installing Lemur on truely bare Ubuntu OS you will need to grab the following packages so that Lemur can correctly build it's
dependencies::
$ sudo apt-get update
$ sudo apt-get install nodejs-legacy python-pip libpq-dev python-dev build-essential libssl-dev libffi-dev nginx git supervisor
And optionally if your database is going to be on the same host as the webserver::
$ sudo apt-get install postgresql
.. note:: Activating the environment adjusts your PATH, so that things like pip now install into the virtualenv by default.
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, ensure that you are in the virtualenv:
.. code-block:: bash
$ which python
which python
And then run:
.. code-block:: bash
$ make develop
make release
.. Note:: This command will install npm dependencies as well as compile static assets.
.. note:: This command will install npm dependencies as well as compile static assets.
You may also run with the urlContextPath variable set. If this is set it will add the desired context path for subsequent calls back to lemur. This will only edit the front end code for calls back to the server, you will have to make sure the server knows about these routes.
::
Example:
urlContextPath=lemur
/api/1/auth/providers -> /lemur/api/1/auth/providers
.. code-block:: bash
make release urlContextPath={desired context path}
Creating a configuration
------------------------
Before we run Lemur we must create a valid configuration file for it.
The Lemur cli comes with a simple command to get you up and running quickly.
Before we run Lemur, we must create a valid configuration file for it. The Lemur command line interface comes with a simple command to get you up and running quickly.
Simply run:
.. code-block:: bash
$ lemur create_config
lemur create_config
.. Note:: This command will create a default configuration under `~/.lemur/lemur.conf.py` you
can specify this location by passing the `config_path` parameter to the `create_config` command.
.. note:: This command will create a default configuration under ``~/.lemur/lemur.conf.py`` you can specify this location by passing the ``config_path`` parameter to the ``create_config`` command.
You can specify ``-c`` or ``--config`` to any Lemur command to specify the current environment you are working in. Lemur will also look under the environmental variable ``LEMUR_CONF`` should that be easier to set up in your environment.
You can specify `-c` or `--config` to any Lemur command to specify the current environment
you are working in. Lemur will also look under the environmental variable `LEMUR_CONF` should
that be easier to setup in your environment.
Update your configuration
-------------------------
Once created you will need to update the configuration file with information about your environment,
such as which database to talk to, where keys are stored etc..
Once created, you will need to update the configuration file with information about your environment, such as which database to talk to, where keys are stored etc.
.. Note:: If you are unfamiliar with with the SQLALCHEMY_DATABASE_URI string it can be broken up like so:
postgresql://userame:password@databasefqdn:databaseport/databasename
.. code-block:: bash
Setup Postgres
vi ~/.lemur/lemur.conf.py
.. note:: If you are unfamiliar with the SQLALCHEMY_DATABASE_URI string it can be broken up like so:
``postgresql://userame:password@<database-fqdn>:<database-port>/<database-name>``
Before Lemur will run you need to fill in a few required variables in the configuration file:
.. code-block:: bash
LEMUR_SECURITY_TEAM_EMAIL
#/the e-mail address needs to be enclosed in quotes
LEMUR_DEFAULT_COUNTRY
LEMUR_DEFAULT_STATE
LEMUR_DEFAULT_LOCATION
LEMUR_DEFAULT_ORGANIZATION
LEMUR_DEFAULT_ORGANIZATIONAL_UNIT
Set Up Postgres
--------------
For production a dedicated database is recommended, for this guide we will assume postgres has been installed and is on
the same machine that Lemur is installed on.
For production, a dedicated database is recommended, for this guide we will assume postgres has been installed and is on the same machine that Lemur is installed on.
First, set a password for the postgres user. For this guide, we will use **lemur** as an example but you should use the database password generated for by Lemur::
First, set a password for the postgres user. For this guide, we will use ``lemur`` as an example but you should use the database password generated by Lemur:
$ sudo -u postgres psql postgres
# \password postgres
Enter new password: lemur
Enter it again: lemur
.. code-block:: bash
Type CTRL-D to exit psql once you have changed the password.
sudo -u postgres -i
psql
postgres=# CREATE USER lemur WITH PASSWORD 'lemur';
Next, we will create our new database::
Once successful, type CTRL-D to exit the Postgres shell.
$ sudo -u postgres createdb lemur
Next, we will create our new database:
.. code-block:: bash
sudo -u postgres createdb lemur
.. _InitializingLemur:
.. note::
For this guide we assume you will use the `postgres` user to connect to your database, when deploying to a VM or container this is often all you will need. If you have a shared database it is recommend you give Lemur its own user.
.. note::
Postgres 9.4 or greater is required as Lemur relies advanced data columns (e.g. JSON Column type)
Initializing Lemur
------------------
Lemur provides a helpful command that will initialize your database for you. It creates a default user (lemur) that is
used by Lemur to help associate certificates that do not currently have an owner. This is most commonly the case when
Lemur has discovered certificates from a third party source. This is also a default user that can be used to
administer Lemur.
Lemur provides a helpful command that will initialize your database for you. It creates a default user (``lemur``) that is used by Lemur to help associate certificates that do not currently have an owner. This is most commonly the case when Lemur has discovered certificates from a third party source. This is also a default user that can be used to administer Lemur.
In addition to creating a new user, Lemur also creates a few default email notifications. These notifications are based
on a few configuration options such as `LEMUR_SECURITY_TEAM_EMAIL`. They basically guarantee that every cerificate within
Lemur will send one expiration notification to the security team.
In addition to creating a new user, Lemur also creates a few default email notifications. These notifications are based on a few configuration options such as ``LEMUR_SECURITY_TEAM_EMAIL``. They basically guarantee that every certificate within Lemur will send one expiration notification to the security team.
Additional notifications can be created through the UI or API.
See :ref:`Creating Notifications <CreatingNotifications>` and :ref:`Command Line Interface <CommandLineInterface>` for details.
Your database installation requires the pg_trgm extension. If you do not have this installed already, you can allow the script to install this for you by adding the SUPERUSER permission to the lemur database user.
**Make note of the password used as this will be used during first login to the Lemur UI**
.. code-block:: bash
sudo -u postgres -i
psql
postgres=# ALTER USER lemur WITH SUPERUSER
Additional notifications can be created through the UI or API. See :ref:`Creating Notifications <CreatingNotifications>` and :ref:`Command Line Interface <CommandLineInterface>` for details.
**Make note of the password used as this will be used during first login to the Lemur UI.**
.. code-block:: bash
$ lemur db init
cd /www/lemur/lemur
lemur init
.. note:: If you added the SUPERUSER permission to the lemur database user above, it is recommended you revoke that permission now.
.. code-block:: bash
sudo -u postgres -i
psql
postgres=# ALTER USER lemur WITH NOSUPERUSER
$ lemur init
.. note:: It is recommended that once the 'lemur' user is created that you create individual users for every day access.
There is currently no way for a user to self enroll for Lemur access, they must have an administrator create an account
for them or be enrolled automatically through SSO. This can be done through the CLI or UI.
See :ref:`Creating Users <CreatingUsers>` and :ref:`Command Line Interface <CommandLineInterface>` for details
.. note:: It is recommended that once the ``lemur`` user is created that you create individual users for every day access. There is currently no way for a user to self enroll for Lemur access, they must have an administrator create an account for them or be enrolled automatically through SSO. This can be done through the CLI or UI. See :ref:`Creating Users <CreatingUsers>` and :ref:`Command Line Interface <CommandLineInterface>` for details.
Setup a Reverse Proxy
Set Up 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 need setup a
simple web proxy. There are many different web servers you can use for this, we like and recommend Nginx.
By default, Lemur runs on port 8000. 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 need to set up a simple web proxy. There are many different web servers you can use for this, we like and recommend Nginx.
Proxying with Nginx
~~~~~~~~~~~~~~~~~~~
You'll use the builtin HttpProxyModule within Nginx to handle proxying
You'll use the builtin ``HttpProxyModule`` within Nginx to handle proxying. Edit the ``/etc/nginx/sites-available/default`` file according to the lines below
::
location /api {
proxy_pass http://127.0.0.1:5000;
proxy_pass http://127.0.0.1:8000;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
proxy_buffering off;
@ -180,23 +228,29 @@ You'll use the builtin HttpProxyModule within Nginx to handle proxying
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
root /www/lemur/lemur/static/dist;
include mime.types;
index index.html;
}
See :doc:`../production/index` for more details on using Nginx.
.. note:: See :doc:`../production/index` for more details on using Nginx.
After making these changes, restart Nginx service to apply them:
.. code-block:: bash
sudo service nginx restart
Starting the Web Service
------------------------
Lemur provides a built-in webserver (powered by gunicorn and eventlet) to get you off the ground quickly.
Lemur provides a built-in web server (powered by gunicorn and eventlet) to get you off the ground quickly.
To start the webserver, you simply use ``lemur start``. If you opted to use an alternative configuration path
you can pass that via the --config option.
To start the web server, you simply use ``lemur start``. If you opted to use an alternative configuration path
you can pass that via the ``--config`` option.
.. note::
You can login with the default user created during :ref:`Initializing Lemur <InitializingLemur>` or any other
@ -204,23 +258,23 @@ you can pass that via the --config option.
::
# Lemur's server runs on port 5000 by default. Make sure your client reflects
# Lemur's server runs on port 8000 by default. Make sure your client reflects
# the correct host and port!
lemur --config=/etc/lemur.conf.py start -b 127.0.0.1:5000
lemur --config=/etc/lemur.conf.py start -b 127.0.0.1:8000
You should now be able to test the web service by visiting ``http://localhost:8000/``.
You should now be able to test the web service by visiting `http://localhost:5000/`.
Running Lemur as a Service
---------------------------
--------------------------
We recommend using whatever software you are most familiar with for managing Lemur processes. One option is `Supervisor <http://supervisord.org/>`_.
We recommend using whatever software you are most familiar with for managing Lemur processes. One option is
`Supervisor <http://supervisord.org/>`_.
Configure ``supervisord``
~~~~~~~~~~~~~~~~~~~~~~~~~
Configuring Supervisor couldn't be more simple. Just point it to the ``lemur`` executable in your virtualenv's bin/
folder and you're good to go.
Configuring Supervisor couldn't be more simple. Just point it to the ``lemur`` executable in your virtualenv's ``bin/`` folder and you're good to go.
::
@ -230,49 +284,48 @@ folder and you're good to go.
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile syslog
stderr_logfile syslog
stdout_logfile=syslog
stderr_logfile=syslog
See :ref:`Using Supervisor <UsingSupervisor>` for more details on using Supervisor.
Syncing
-------
Lemur uses periodic sync tasks to make sure it is up-to-date with its environment. As always things can change outside
of Lemur, but we do our best to reconcile those changes.
Lemur uses periodic sync tasks to make sure it is up-to-date with its environment. Things change outside of Lemur we do our best to reconcile those changes. The recommended method is to use CRON:
.. code-block:: bash
$ crontab -e
* 3 * * * lemur sync --all
* 3 * * * lemur check_revoked
crontab -e
*/15 * * * * lemur sync -s all
0 22 * * * lemur check_revoked
0 22 * * * lemur notify
Additional Utilities
--------------------
If you're familiar with Python you'll quickly find yourself at home, and even more so if you've used Flask. The
``lemur`` command is just a simple wrapper around Flask's ``manage.py``, which means you get all of the
power and flexibility that goes with it.
If you're familiar with Python you'll quickly find yourself at home, and even more so if you've used Flask. The ``lemur`` command is just a simple wrapper around Flask's ``manage.py``, which means you get all of the power and flexibility that goes with it.
Some of the features which you'll likely find useful are listed below.
Some of the features which you'll likely find useful are:
lock
~~~~
Encrypts sensitive key material - This is most useful for storing encrypted secrets in source code.
Encrypts sensitive key material - this is most useful for storing encrypted secrets in source code.
unlock
~~~~~~
Decrypts sensitive key material - Used to decrypt the secrets stored in source during deployment.
Decrypts sensitive key material - used to decrypt the secrets stored in source during deployment.
What's Next?
------------
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.
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.
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.
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.

View File

@ -1,29 +0,0 @@
Jinja2>=2.3
Pygments>=1.2
Sphinx>=1.3
docutils>=0.7
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

@ -44,7 +44,7 @@ containing:
new releases, and publicly disclose the issue.
Simultaneously, the reporter of the issue will receive notification of the date
on which we plan to take the issue public.
on which we plan to make the issue public.
On the day of disclosure, we will take the following steps:
@ -59,8 +59,8 @@ known exploit in the wild, for example the time between advance notification
and public disclosure may be shortened considerably.
The list of people and organizations who receives advanced notification of
security issues is not and will not be made public. This list generally
consists of high profile downstream distributors and is entirely at the
security issues is not, and will not, be made public. This list generally
consists of high-profile downstream distributors and is entirely at the
discretion of the ``lemur`` team.
.. _`master`: https://github.com/Netflix/lemur

View File

@ -1,13 +1,12 @@
'use strict';
var gulp = require('gulp'),
minifycss = require('gulp-minify-css'),
concat = require('gulp-concat'),
less = require('gulp-less'),
gulpif = require('gulp-if'),
order = require('gulp-order'),
gutil = require('gulp-util'),
rename = require('gulp-rename'),
foreach = require('gulp-foreach'),
debug = require('gulp-debug'),
path =require('path'),
merge = require('merge-stream'),
del = require('del'),
@ -27,7 +26,8 @@ var gulp = require('gulp'),
minifyHtml = require('gulp-minify-html'),
bowerFiles = require('main-bower-files'),
karma = require('karma'),
replace = require('gulp-replace');
replace = require('gulp-replace'),
argv = require('yargs').argv;
gulp.task('default', ['clean'], function () {
gulp.start('fonts', 'styles');
@ -79,8 +79,9 @@ gulp.task('dev:styles', function () {
'bower_components/angular-loading-bar/src/loading-bar.css',
'bower_components/angular-ui-switch/angular-ui-switch.css',
'bower_components/angular-wizard/dist/angular-wizard.css',
'bower_components/ng-table/ng-table.css',
'bower_components/ng-table/dist/ng-table.css',
'bower_components/angularjs-toaster/toaster.css',
'bower_components/angular-ui-select/dist/select.css',
'lemur/static/app/styles/lemur.css'
];
@ -88,9 +89,9 @@ gulp.task('dev:styles', function () {
.pipe(gulpif(isBootswatchFile, foreach(function (stream, file) {
var themeName = path.basename(path.dirname(file.path)),
content = replaceAll(baseContent, '$theme$', themeName),
file = string_src('bootstrap-' + themeName + '.less', content);
file2 = string_src('bootstrap-' + themeName + '.less', content);
return file;
return file2;
})))
.pipe(less())
.pipe(gulpif(isBootstrapFile, foreach(function (stream, file) {
@ -100,7 +101,7 @@ gulp.task('dev:styles', function () {
// http://stackoverflow.com/questions/21719833/gulp-how-to-add-src-files-in-the-middle-of-a-pipe
// https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md
return merge(stream, gulp.src(['.tmp/styles/font-awesome.css', '.tmp/styles/lemur.css']))
.pipe(concat('style-' + themeName + ".css"));
.pipe(concat('style-' + themeName + '.css'));
})))
.pipe(plumber())
.pipe(concat('styles.css'))
@ -112,7 +113,7 @@ gulp.task('dev:styles', function () {
// http://stackoverflow.com/questions/1144783/replacing-all-occurrences-of-a-string-in-javascript
function escapeRegExp(string) {
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
}
function replaceAll(string, find, replace) {
@ -122,7 +123,7 @@ function replaceAll(string, find, replace) {
function string_src(filename, string) {
var src = require('stream').Readable({ objectMode: true });
src._read = function () {
this.push(new gutil.File({ cwd: "", base: "", path: filename, contents: new Buffer(string) }));
this.push(new gutil.File({ cwd: '', base: '', path: filename, contents: new Buffer(string) }));
this.push(null);
};
return src;
@ -143,26 +144,18 @@ gulp.task('build:extras', function () {
function injectHtml(isDev) {
return gulp.src('lemur/static/app/index.html')
.pipe(
inject(gulp.src(bowerFiles({ base: 'app' }), {
read: false
}), {
inject(gulp.src(bowerFiles({ base: 'app' })), {
starttag: '<!-- inject:bower:{{ext}} -->',
addRootSlash: false,
ignorePath: isDev ? ['lemur/static/app/', '.tmp/'] : null
})
)
.pipe(inject(gulp.src(['lemur/static/app/angular/**/*.js'], {
read: false
}), {
read: false,
.pipe(inject(gulp.src(['lemur/static/app/angular/**/*.js']), {
starttag: '<!-- inject:{{ext}} -->',
addRootSlash: false,
ignorePath: isDev ? ['lemur/static/app/', '.tmp/'] : null
}))
.pipe(inject(gulp.src(['.tmp/styles/**/*.css'], {
read: false
}), {
read: false,
.pipe(inject(gulp.src(['.tmp/styles/**/*.css']), {
starttag: '<!-- inject:{{ext}} -->',
addRootSlash: false,
ignorePath: isDev ? ['lemur/static/app/', '.tmp/'] : null
@ -170,13 +163,11 @@ function injectHtml(isDev) {
.pipe(
gulpif(!isDev,
inject(gulp.src('lemur/static/dist/ngviews/ngviews.min.js'), {
read: false,
starttag: '<!-- inject:ngviews -->',
addRootSlash: false
})
)
)
.pipe(gulp.dest('.tmp/'));
).pipe(gulp.dest('.tmp/'));
}
gulp.task('dev:inject', ['dev:styles', 'dev:scripts'], function () {
@ -199,23 +190,17 @@ gulp.task('build:ngviews', function () {
});
gulp.task('build:html', ['dev:styles', 'dev:scripts', 'build:ngviews', 'build:inject'], function () {
var jsFilter = filter('**/*.js');
var cssFilter = filter('**/*.css');
var assets = useref.assets();
var jsFilter = filter(['**/*.js'], {'restore': true});
var cssFilter = filter(['**/*.css'], {'restore': true});
return gulp.src('.tmp/index.html')
.pipe(assets)
.pipe(rev())
.pipe(jsFilter)
.pipe(ngAnnotate())
.pipe(jsFilter.restore())
.pipe(jsFilter.restore)
.pipe(cssFilter)
.pipe(csso())
.pipe(cssFilter.restore())
.pipe(assets.restore())
.pipe(cssFilter.restore)
.pipe(useref())
.pipe(revReplace())
.pipe(gulp.dest('lemur/static/dist'))
.pipe(size());
});
@ -238,13 +223,42 @@ gulp.task('build:images', function () {
gulp.task('package:strip', function () {
return gulp.src(['lemur/static/dist/scripts/main*'])
.pipe(replace('http:\/\/localhost:5000', ''))
.pipe(replace('http:\/\/localhost:3000', ''))
.pipe(replace('http:\/\/localhost:8000', ''))
.pipe(useref())
.pipe(revReplace())
.pipe(gulp.dest('lemur/static/dist/scripts'))
.pipe(size());
});
gulp.task('addUrlContextPath',['addUrlContextPath:revreplace'], function(){
var urlContextPathExists = argv.urlContextPath ? true : false;
['lemur/static/dist/scripts/main*.js',
'lemur/static/dist/angular/**/*.html']
.forEach(function(file){
return gulp.src(file)
.pipe(gulpif(urlContextPathExists, replace('api/', argv.urlContextPath + '/api/')))
.pipe(gulpif(urlContextPathExists, replace('/angular/', '/' + argv.urlContextPath + '/angular/')))
.pipe(gulp.dest(function(file){
return file.base;
}))
})
});
gulp.task('addUrlContextPath:revision', function(){
return gulp.src(['lemur/static/dist/**/*.css','lemur/static/dist/**/*.js'])
.pipe(rev())
.pipe(gulp.dest('lemur/static/dist'))
.pipe(rev.manifest())
.pipe(gulp.dest('lemur/static/dist'))
})
gulp.task('addUrlContextPath:revreplace', ['addUrlContextPath:revision'], function(){
var manifest = gulp.src("lemur/static/dist/rev-manifest.json");
var urlContextPathExists = argv.urlContextPath ? true : false;
return gulp.src( "lemur/static/dist/index.html")
.pipe(gulp.dest('lemur/static/dist'));
})
gulp.task('build', ['build:ngviews', 'build:inject', 'build:images', 'build:fonts', 'build:html', 'build:extras']);
gulp.task('package', ['package:strip']);
gulp.task('package', ['addUrlContextPath', 'package:strip']);

View File

@ -6,31 +6,31 @@ var browserSync = require('browser-sync');
var httpProxy = require('http-proxy');
/* This configuration allow you to configure browser sync to proxy your backend */
/*
var proxyTarget = 'http://localhost/context/'; // The location of your backend
var proxyApiPrefix = 'api'; // The element in the URL which differentiate between API request and static file request
var proxyTarget = 'http://localhost:8000/'; // The location of your backend
var proxyApiPrefix = '/api/'; // The element in the URL which differentiate between API request and static file request
var proxy = httpProxy.createProxyServer({
target: proxyTarget
target: proxyTarget
});
function proxyMiddleware(req, res, next) {
if (req.url.indexOf(proxyApiPrefix) !== -1) {
proxy.web(req, res);
} else {
next();
if (req.url.indexOf(proxyApiPrefix) !== -1) {
proxy.web(req, res);
} else {
next();
}
}
}
*/
function browserSyncInit(baseDir, files, browser) {
browser = browser === undefined ? 'default' : browser;
browserSync.instance = browserSync.init(files, {
startPath: '/index.html',
server: {
baseDir: baseDir,
routes: {
'/bower_components': './bower_components'
}
server: {
middleware: [proxyMiddleware],
baseDir: baseDir,
routes: {
'/bower_components': './bower_components'
}
},
browser: browser,
ghostMode: false

View File

@ -1,46 +0,0 @@
#!/usr/bin/env python
import glob
import os
import sys
os.environ['PYFLAKES_NODOCTEST'] = '1'
# pep8.py uses sys.argv to find setup.cfg
sys.argv = [os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)]
# git usurbs your bin path for hooks and will always run system python
if 'VIRTUAL_ENV' in os.environ:
site_packages = glob.glob(
'%s/lib/*/site-packages' % os.environ['VIRTUAL_ENV'])[0]
sys.path.insert(0, site_packages)
def py_lint(files_modified):
from flake8.main import DEFAULT_CONFIG
from flake8.engine import get_style_guide
# remove non-py files and files which no longer exist
files_modified = filter(lambda x: x.endswith('.py'), files_modified)
flake8_style = get_style_guide(parse_argv=True, config_file=DEFAULT_CONFIG)
report = flake8_style.check_files(files_modified)
return report.total_errors != 0
def main():
from flake8.hooks import run
gitcmd = "git diff-index --cached --name-only HEAD"
_, files_modified, _ = run(gitcmd)
files_modified = filter(lambda x: os.path.exists(x), files_modified)
if py_lint(files_modified):
return 1
return 0
if __name__ == '__main__':
sys.exit(main())

24
lemur/__about__.py Normal file
View File

@ -0,0 +1,24 @@
from __future__ import absolute_import, division, print_function
__all__ = [
"__title__",
"__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
]
__title__ = "lemur"
__summary__ = "Certificate management and orchestration service"
__uri__ = "https://github.com/Netflix/lemur"
__version__ = "0.8.0"
__author__ = "The Lemur developers"
__email__ = "security@netflix.com"
__license__ = "Apache License, Version 2.0"
__copyright__ = "Copyright 2018 {0}".format(__author__)

View File

@ -1,14 +1,19 @@
"""
.. module: lemur
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
.. moduleauthor:: Curtis Castrapel <ccastrapel@netflix.com>
.. moduleauthor:: Hossein Shafagh <hshafagh@netflix.com>
"""
import time
from flask import g, request
from lemur import factory
from lemur.extensions import metrics
from lemur.users.views import mod as users_bp
from lemur.roles.views import mod as roles_bp
@ -21,7 +26,34 @@ from lemur.defaults.views import mod as defaults_bp
from lemur.plugins.views import mod as plugins_bp
from lemur.notifications.views import mod as notifications_bp
from lemur.sources.views import mod as sources_bp
from lemur.endpoints.views import mod as endpoints_bp
from lemur.logs.views import mod as logs_bp
from lemur.api_keys.views import mod as api_key_bp
from lemur.pending_certificates.views import mod as pending_certificates_bp
from lemur.dns_providers.views import mod as dns_providers_bp
from lemur.__about__ import (
__author__,
__copyright__,
__email__,
__license__,
__summary__,
__title__,
__uri__,
__version__,
)
__all__ = [
"__title__",
"__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
]
LEMUR_BLUEPRINTS = (
users_bp,
@ -34,12 +66,19 @@ LEMUR_BLUEPRINTS = (
defaults_bp,
plugins_bp,
notifications_bp,
sources_bp
sources_bp,
endpoints_bp,
logs_bp,
api_key_bp,
pending_certificates_bp,
dns_providers_bp,
)
def create_app(config=None):
app = factory.create_app(app_name=__name__, blueprints=LEMUR_BLUEPRINTS, config=config)
def create_app(config_path=None):
app = factory.create_app(
app_name=__name__, blueprints=LEMUR_BLUEPRINTS, config=config_path
)
configure_hook(app)
return app
@ -50,16 +89,40 @@ def configure_hook(app):
:param app:
:return:
"""
from flask.ext.principal import PermissionDenied
from lemur.decorators import crossdomain
if app.config.get('CORS'):
@app.after_request
@crossdomain(origin=u"http://localhost:3000", methods=['PUT', 'HEAD', 'GET', 'POST', 'OPTIONS', 'DELETE'])
def after(response):
from flask import jsonify
from werkzeug.exceptions import HTTPException
@app.errorhandler(Exception)
def handle_error(e):
code = 500
if isinstance(e, HTTPException):
code = e.code
app.logger.exception(e)
return jsonify(error=str(e)), code
@app.before_request
def before_request():
g.request_start_time = time.time()
@app.after_request
def after_request(response):
# Return early if we don't have the start time
if not hasattr(g, "request_start_time"):
return response
@app.errorhandler(PermissionDenied)
def handle_invalid_usage(error):
response = {'message': 'You are not allow to access this resource'}
response.status_code = 403
# Get elapsed time in milliseconds
elapsed = time.time() - g.request_start_time
elapsed = int(round(1000 * elapsed))
# Collect request/response tags
tags = {
"endpoint": request.endpoint,
"request_method": request.method.lower(),
"status_code": response.status_code,
}
# Record our response time metric
metrics.send("response_time", "TIMER", elapsed, metric_tags=tags)
metrics.send("status_code_{}".format(response.status_code), "counter", 1)
return response

191
lemur/acme_providers/cli.py Normal file
View File

@ -0,0 +1,191 @@
import time
import json
import arrow
from flask_script import Manager
from flask import current_app
from lemur.extensions import sentry
from lemur.constants import SUCCESS_METRIC_STATUS
from lemur.plugins import plugins
from lemur.plugins.lemur_acme.plugin import AcmeHandler
from lemur.plugins.lemur_aws import s3
manager = Manager(
usage="Handles all ACME related tasks"
)
@manager.option(
"-d",
"--domain",
dest="domain",
required=True,
help="Name of the Domain to store to (ex. \"_acme-chall.test.com\".",
)
@manager.option(
"-t",
"--token",
dest="token",
required=True,
help="Value of the Token to store in DNS as content.",
)
def dnstest(domain, token):
"""
Create, verify, and delete DNS TXT records using an autodetected provider.
"""
print("[+] Starting ACME Tests.")
change_id = (domain, token)
acme_handler = AcmeHandler()
acme_handler.autodetect_dns_providers(domain)
if not acme_handler.dns_providers_for_domain[domain]:
raise Exception(f"No DNS providers found for domain: {format(domain)}.")
# Create TXT Records
for dns_provider in acme_handler.dns_providers_for_domain[domain]:
dns_provider_plugin = acme_handler.get_dns_provider(dns_provider.provider_type)
dns_provider_options = json.loads(dns_provider.credentials)
account_number = dns_provider_options.get("account_id")
print(f"[+] Creating TXT Record in `{dns_provider.name}` provider")
change_id = dns_provider_plugin.create_txt_record(domain, token, account_number)
print("[+] Verifying TXT Record has propagated to DNS.")
print("[+] This step could take a while...")
time.sleep(10)
# Verify TXT Records
for dns_provider in acme_handler.dns_providers_for_domain[domain]:
dns_provider_plugin = acme_handler.get_dns_provider(dns_provider.provider_type)
dns_provider_options = json.loads(dns_provider.credentials)
account_number = dns_provider_options.get("account_id")
try:
dns_provider_plugin.wait_for_dns_change(change_id, account_number)
print(f"[+] Verified TXT Record in `{dns_provider.name}` provider")
except Exception:
sentry.captureException()
current_app.logger.debug(
f"Unable to resolve DNS challenge for change_id: {change_id}, account_id: "
f"{account_number}",
exc_info=True,
)
print(f"[+] Unable to Verify TXT Record in `{dns_provider.name}` provider")
time.sleep(10)
# Delete TXT Records
for dns_provider in acme_handler.dns_providers_for_domain[domain]:
dns_provider_plugin = acme_handler.get_dns_provider(dns_provider.provider_type)
dns_provider_options = json.loads(dns_provider.credentials)
account_number = dns_provider_options.get("account_id")
# TODO(csine@: Add Exception Handling
dns_provider_plugin.delete_txt_record(change_id, account_number, domain, token)
print(f"[+] Deleted TXT Record in `{dns_provider.name}` provider")
status = SUCCESS_METRIC_STATUS
print("[+] Done with ACME Tests.")
@manager.option(
"-t",
"--token",
dest="token",
default="date: " + arrow.utcnow().format("YYYY-MM-DDTHH-mm-ss"),
required=False,
help="Value of the Token",
)
@manager.option(
"-n",
"--token_name",
dest="token_name",
default="Token-" + arrow.utcnow().format("YYYY-MM-DDTHH-mm-ss"),
required=False,
help="path",
)
@manager.option(
"-p",
"--prefix",
dest="prefix",
default="test/",
required=False,
help="S3 bucket prefix",
)
@manager.option(
"-a",
"--account_number",
dest="account_number",
required=True,
help="AWS Account",
)
@manager.option(
"-b",
"--bucket_name",
dest="bucket_name",
required=True,
help="Bucket Name",
)
def upload_acme_token_s3(token, token_name, prefix, account_number, bucket_name):
"""
This method serves for testing the upload_acme_token to S3, fetching the token to verify it, and then deleting it.
It mainly serves for testing purposes.
:param token:
:param token_name:
:param prefix:
:param account_number:
:param bucket_name:
:return:
"""
additional_options = [
{
"name": "bucket",
"value": bucket_name,
"type": "str",
"required": True,
"validation": r"[0-9a-z.-]{3,63}",
"helpMessage": "Must be a valid S3 bucket name!",
},
{
"name": "accountNumber",
"type": "str",
"value": account_number,
"required": True,
"validation": r"[0-9]{12}",
"helpMessage": "A valid AWS account number with permission to access S3",
},
{
"name": "region",
"type": "str",
"default": "us-east-1",
"required": False,
"helpMessage": "Region bucket exists",
"available": ["us-east-1", "us-west-2", "eu-west-1"],
},
{
"name": "encrypt",
"type": "bool",
"value": False,
"required": False,
"helpMessage": "Enable server side encryption",
"default": True,
},
{
"name": "prefix",
"type": "str",
"value": prefix,
"required": False,
"helpMessage": "Must be a valid S3 object prefix!",
},
]
p = plugins.get("aws-s3")
p.upload_acme_token(token_name, token, additional_options)
if not prefix.endswith("/"):
prefix + "/"
token_res = s3.get(bucket_name, prefix + token_name, account_number=account_number)
assert(token_res == token)
s3.delete(bucket_name, prefix + token_name, account_number=account_number)

View File

@ -1,64 +0,0 @@
"""
.. module: lemur.analyze.service
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
# def analyze(endpoints, truststores):
# results = {"headings": ["Endpoint"],
# "results": [],
# "time": datetime.now().strftime("#Y%m%d %H:%M:%S")}
#
# for store in truststores:
# results['headings'].append(os.path.basename(store))
#
# for endpoint in endpoints:
# result_row = [endpoint]
# for store in truststores:
# result = {'details': []}
#
# tests = []
# for region, ip in REGIONS.items():
# try:
# domain = dns.name.from_text(endpoint)
# if not domain.is_absolute():
# domain = domain.concatenate(dns.name.root)
#
# my_resolver = dns.resolver.Resolver()
# my_resolver.nameservers = [ip]
# answer = my_resolver.query(domain)
#
# #force the testing of regional enpoints by changing the dns server
# response = requests.get('https://' + str(answer[0]), verify=store)
# tests.append('pass')
# result['details'].append("{}: SSL testing completed without errors".format(region))
#
# except SSLError as e:
# log.debug(e)
# if 'hostname' in str(e):
# tests.append('pass')
# result['details'].append(
# "{}: This test passed ssl negotiation but failed hostname verification because \
# the hostname is not included in the certificate".format(region))
# elif 'certificate verify failed' in str(e):
# tests.append('fail')
# result['details'].append("{}: This test failed to verify the SSL certificate".format(region))
# else:
# tests.append('fail')
# result['details'].append("{}: {}".format(region, str(e)))
#
# except Exception as e:
# log.debug(e)
# tests.append('fail')
# result['details'].append("{}: {}".format(region, str(e)))
#
# #any failing tests fails the whole endpoint
# if 'fail' in tests:
# result['test'] = 'fail'
# else:
# result['test'] = 'pass'
#
# result_row.append(result)
# results['results'].append(result_row)
# return results
#

View File

50
lemur/api_keys/cli.py Normal file
View File

@ -0,0 +1,50 @@
"""
.. module: lemur.api_keys.cli
:platform: Unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Eric Coan <kungfury@instructure.com>
"""
from flask_script import Manager
from lemur.api_keys import service as api_key_service
from lemur.auth.service import create_token
from datetime import datetime
manager = Manager(usage="Handles all api key related tasks.")
@manager.option(
"-u", "--user-id", dest="uid", help="The User ID this access key belongs too."
)
@manager.option("-n", "--name", dest="name", help="The name of this API Key.")
@manager.option(
"-t", "--ttl", dest="ttl", help="The TTL of this API Key. -1 for forever."
)
def create(uid, name, ttl):
"""
Create a new api key for a user.
:return:
"""
print("[+] Creating a new api key.")
key = api_key_service.create(
user_id=uid,
name=name,
ttl=ttl,
issued_at=int(datetime.utcnow().timestamp()),
revoked=False,
)
print("[+] Successfully created a new api key. Generating a JWT...")
jwt = create_token(uid, key.id, key.ttl)
print("[+] Your JWT is: {jwt}".format(jwt=jwt))
@manager.option("-a", "--api-key-id", dest="aid", help="The API Key ID to revoke.")
def revoke(aid):
"""
Revokes an api key for a user.
:return:
"""
print("[-] Revoking the API Key api key.")
api_key_service.revoke(aid=aid)
print("[+] Successfully revoked the api key")

30
lemur/api_keys/models.py Normal file
View File

@ -0,0 +1,30 @@
"""
.. module: lemur.api_keys.models
:platform: Unix
:synopsis: This module contains all of the models need to create an api key within Lemur.
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Eric Coan <kungfury@instructure.com>
"""
from sqlalchemy import BigInteger, Boolean, Column, ForeignKey, Integer, String
from lemur.database import db
class ApiKey(db.Model):
__tablename__ = "api_keys"
id = Column(Integer, primary_key=True)
name = Column(String)
user_id = Column(Integer, ForeignKey("users.id"))
ttl = Column(BigInteger)
issued_at = Column(BigInteger)
revoked = Column(Boolean)
def __repr__(self):
return "ApiKey(name={name}, user_id={user_id}, ttl={ttl}, issued_at={iat}, revoked={revoked})".format(
user_id=self.user_id,
name=self.name,
ttl=self.ttl,
iat=self.issued_at,
revoked=self.revoked,
)

63
lemur/api_keys/schemas.py Normal file
View File

@ -0,0 +1,63 @@
"""
.. module: lemur.api_keys.schemas
:platform: Unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Eric Coan <kungfury@instructure.com>
"""
from flask import g
from marshmallow import fields
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
from lemur.users.schemas import UserNestedOutputSchema, UserInputSchema
def current_user_id():
return {
"id": g.current_user.id,
"email": g.current_user.email,
"username": g.current_user.username,
}
class ApiKeyInputSchema(LemurInputSchema):
name = fields.String(required=False)
user = fields.Nested(
UserInputSchema, missing=current_user_id, default=current_user_id
)
ttl = fields.Integer()
class ApiKeyRevokeSchema(LemurInputSchema):
id = fields.Integer(required=True)
name = fields.String()
user = fields.Nested(UserInputSchema, required=True)
revoked = fields.Boolean()
ttl = fields.Integer()
issued_at = fields.Integer(required=False)
class UserApiKeyInputSchema(LemurInputSchema):
name = fields.String(required=False)
ttl = fields.Integer()
class ApiKeyOutputSchema(LemurOutputSchema):
jwt = fields.String()
class ApiKeyDescribedOutputSchema(LemurOutputSchema):
id = fields.Integer()
name = fields.String()
user = fields.Nested(UserNestedOutputSchema)
ttl = fields.Integer()
issued_at = fields.Integer()
revoked = fields.Boolean()
api_key_input_schema = ApiKeyInputSchema()
api_key_revoke_schema = ApiKeyRevokeSchema()
api_key_output_schema = ApiKeyOutputSchema()
api_keys_output_schema = ApiKeyDescribedOutputSchema(many=True)
api_key_described_output_schema = ApiKeyDescribedOutputSchema()
user_api_key_input_schema = UserApiKeyInputSchema()

97
lemur/api_keys/service.py Normal file
View File

@ -0,0 +1,97 @@
"""
.. module: lemur.api_keys.service
:platform: Unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Eric Coan <kungfury@instructure.com>
"""
from lemur import database
from lemur.api_keys.models import ApiKey
def get(aid):
"""
Retrieves an api key by its ID.
:param aid: The access key id to get.
:return:
"""
return database.get(ApiKey, aid)
def delete(access_key):
"""
Delete an access key. This is one way to remove a key, though you probably should just set revoked.
:param access_key:
:return:
"""
database.delete(access_key)
def revoke(aid):
"""
Revokes an api key.
:param aid:
:return:
"""
api_key = get(aid)
setattr(api_key, "revoked", False)
return database.update(api_key)
def get_all_api_keys():
"""
Retrieves all Api Keys.
:return:
"""
return ApiKey.query.all()
def create(**kwargs):
"""
Creates a new API Key.
:param kwargs:
:return:
"""
api_key = ApiKey(**kwargs)
database.create(api_key)
return api_key
def update(api_key, **kwargs):
"""
Updates an api key.
:param api_key:
:param kwargs:
:return:
"""
for key, value in kwargs.items():
setattr(api_key, key, value)
return database.update(api_key)
def render(args):
"""
Helper to parse REST Api requests
:param args:
:return:
"""
query = database.session_query(ApiKey)
user_id = args.pop("user_id", None)
aid = args.pop("id", None)
has_permission = args.pop("has_permission", False)
requesting_user_id = args.pop("requesting_user_id")
if user_id:
query = query.filter(ApiKey.user_id == user_id)
if aid:
query = query.filter(ApiKey.id == aid)
if not has_permission:
query = query.filter(ApiKey.user_id == requesting_user_id)
return database.sort_and_page(query, ApiKey, args)

621
lemur/api_keys/views.py Normal file
View File

@ -0,0 +1,621 @@
"""
.. module: lemur.api_keys.views
:platform: Unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Eric Coan <kungfury@instructure.com>
"""
from datetime import datetime
from flask import Blueprint, g
from flask_restful import reqparse, Api
from lemur.api_keys import service
from lemur.auth.service import AuthenticatedResource, create_token
from lemur.auth.permissions import ApiKeyCreatorPermission
from lemur.common.schema import validate_schema
from lemur.common.utils import paginated_parser
from lemur.api_keys.schemas import (
api_key_input_schema,
api_key_revoke_schema,
api_key_output_schema,
api_keys_output_schema,
api_key_described_output_schema,
user_api_key_input_schema,
)
mod = Blueprint("api_keys", __name__)
api = Api(mod)
class ApiKeyList(AuthenticatedResource):
""" Defines the 'api_keys' endpoint """
def __init__(self):
super(ApiKeyList, self).__init__()
@validate_schema(None, api_keys_output_schema)
def get(self):
"""
.. http:get:: /keys
The current list of api keys, that you can see.
**Example request**:
.. sourcecode:: http
GET /keys HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"items": [
{
"id": 1,
"name": "custom name",
"user_id": 1,
"ttl": -1,
"issued_at": 12,
"revoked": false
}
],
"total": 1
}
:query sortBy: field to sort on
:query sortDir: asc or desc
:query page: int default is 1
:query count: count number. default is 10
:query user_id: a user to filter by.
:query id: an access key to filter by.
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
parser = paginated_parser.copy()
args = parser.parse_args()
args["has_permission"] = ApiKeyCreatorPermission().can()
args["requesting_user_id"] = g.current_user.id
return service.render(args)
@validate_schema(api_key_input_schema, api_key_output_schema)
def post(self, data=None):
"""
.. http:post:: /keys
Creates an API Key.
**Example request**:
.. sourcecode:: http
POST /keys HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
{
"name": "my custom name",
"user_id": 1,
"ttl": -1
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"jwt": ""
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
if not ApiKeyCreatorPermission().can():
if data["user"]["id"] != g.current_user.id:
return (
dict(
message="You are not authorized to create tokens for: {0}".format(
data["user"]["username"]
)
),
403,
)
access_token = service.create(
name=data["name"],
user_id=data["user"]["id"],
ttl=data["ttl"],
revoked=False,
issued_at=int(datetime.utcnow().timestamp()),
)
return dict(
jwt=create_token(access_token.user_id, access_token.id, access_token.ttl)
)
class ApiKeyUserList(AuthenticatedResource):
""" Defines the 'keys' endpoint on the 'users' endpoint. """
def __init__(self):
super(ApiKeyUserList, self).__init__()
@validate_schema(None, api_keys_output_schema)
def get(self, user_id):
"""
.. http:get:: /users/:user_id/keys
The current list of api keys for a user, that you can see.
**Example request**:
.. sourcecode:: http
GET /users/1/keys HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"items": [
{
"id": 1,
"name": "custom name",
"user_id": 1,
"ttl": -1,
"issued_at": 12,
"revoked": false
}
],
"total": 1
}
:query sortBy: field to sort on
:query sortDir: asc or desc
:query page: int default is 1
:query count: count number. default is 10
:query id: an access key to filter by.
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
parser = paginated_parser.copy()
args = parser.parse_args()
args["has_permission"] = ApiKeyCreatorPermission().can()
args["requesting_user_id"] = g.current_user.id
args["user_id"] = user_id
return service.render(args)
@validate_schema(user_api_key_input_schema, api_key_output_schema)
def post(self, user_id, data=None):
"""
.. http:post:: /users/:user_id/keys
Creates an API Key for a user.
**Example request**:
.. sourcecode:: http
POST /users/1/keys HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
{
"name": "my custom name"
"ttl": -1
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"jwt": ""
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
if not ApiKeyCreatorPermission().can():
if user_id != g.current_user.id:
return (
dict(
message="You are not authorized to create tokens for: {0}".format(
user_id
)
),
403,
)
access_token = service.create(
name=data["name"],
user_id=user_id,
ttl=data["ttl"],
revoked=False,
issued_at=int(datetime.utcnow().timestamp()),
)
return dict(
jwt=create_token(access_token.user_id, access_token.id, access_token.ttl)
)
class ApiKeys(AuthenticatedResource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(ApiKeys, self).__init__()
@validate_schema(None, api_key_output_schema)
def get(self, aid):
"""
.. http:get:: /keys/1
Fetch one api key
**Example request**:
.. sourcecode:: http
GET /keys/1 HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"jwt": ""
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
access_key = service.get(aid)
if access_key is None:
return dict(message="This token does not exist!"), 404
if access_key.user_id != g.current_user.id:
if not ApiKeyCreatorPermission().can():
return dict(message="You are not authorized to view this token!"), 403
return dict(jwt=create_token(access_key.user_id, access_key.id, access_key.ttl))
@validate_schema(api_key_revoke_schema, api_key_output_schema)
def put(self, aid, data=None):
"""
.. http:put:: /keys/1
update one api key
**Example request**:
.. sourcecode:: http
PUT /keys/1 HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
{
"name": "new_name",
"revoked": false,
"ttl": -1
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"jwt": ""
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
access_key = service.get(aid)
if access_key is None:
return dict(message="This token does not exist!"), 404
if access_key.user_id != g.current_user.id:
if not ApiKeyCreatorPermission().can():
return dict(message="You are not authorized to update this token!"), 403
service.update(
access_key, name=data["name"], revoked=data["revoked"], ttl=data["ttl"]
)
return dict(jwt=create_token(access_key.user_id, access_key.id, access_key.ttl))
def delete(self, aid):
"""
.. http:delete:: /keys/1
deletes one api key
**Example request**:
.. sourcecode:: http
DELETE /keys/1 HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"result": true
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
access_key = service.get(aid)
if access_key is None:
return dict(message="This token does not exist!"), 404
if access_key.user_id != g.current_user.id:
if not ApiKeyCreatorPermission().can():
return dict(message="You are not authorized to delete this token!"), 403
service.delete(access_key)
return {"result": True}
class UserApiKeys(AuthenticatedResource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(UserApiKeys, self).__init__()
@validate_schema(None, api_key_output_schema)
def get(self, uid, aid):
"""
.. http:get:: /users/1/keys/1
Fetch one api key
**Example request**:
.. sourcecode:: http
GET /users/1/api_keys/1 HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"jwt": ""
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
if uid != g.current_user.id:
if not ApiKeyCreatorPermission().can():
return dict(message="You are not authorized to view this token!"), 403
access_key = service.get(aid)
if access_key is None:
return dict(message="This token does not exist!"), 404
if access_key.user_id != uid:
return dict(message="You are not authorized to view this token!"), 403
return dict(jwt=create_token(access_key.user_id, access_key.id, access_key.ttl))
@validate_schema(api_key_revoke_schema, api_key_output_schema)
def put(self, uid, aid, data=None):
"""
.. http:put:: /users/1/keys/1
update one api key
**Example request**:
.. sourcecode:: http
PUT /users/1/keys/1 HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
{
"name": "new_name",
"revoked": false,
"ttl": -1
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"jwt": ""
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
if uid != g.current_user.id:
if not ApiKeyCreatorPermission().can():
return dict(message="You are not authorized to view this token!"), 403
access_key = service.get(aid)
if access_key is None:
return dict(message="This token does not exist!"), 404
if access_key.user_id != uid:
return dict(message="You are not authorized to update this token!"), 403
service.update(
access_key, name=data["name"], revoked=data["revoked"], ttl=data["ttl"]
)
return dict(jwt=create_token(access_key.user_id, access_key.id, access_key.ttl))
def delete(self, uid, aid):
"""
.. http:delete:: /users/1/keys/1
deletes one api key
**Example request**:
.. sourcecode:: http
DELETE /users/1/keys/1 HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"result": true
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
if uid != g.current_user.id:
if not ApiKeyCreatorPermission().can():
return dict(message="You are not authorized to view this token!"), 403
access_key = service.get(aid)
if access_key is None:
return dict(message="This token does not exist!"), 404
if access_key.user_id != uid:
return dict(message="You are not authorized to delete this token!"), 403
service.delete(access_key)
return {"result": True}
class ApiKeysDescribed(AuthenticatedResource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(ApiKeysDescribed, self).__init__()
@validate_schema(None, api_key_described_output_schema)
def get(self, aid):
"""
.. http:get:: /keys/1/described
Fetch one api key
**Example request**:
.. sourcecode:: http
GET /keys/1 HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 2,
"name": "hoi",
"user_id": 2,
"ttl": -1,
"issued_at": 1222222,
"revoked": false
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
access_key = service.get(aid)
if access_key is None:
return dict(message="This token does not exist!"), 404
if access_key.user_id != g.current_user.id:
if not ApiKeyCreatorPermission().can():
return dict(message="You are not authorized to view this token!"), 403
return access_key
api.add_resource(ApiKeyList, "/keys", endpoint="api_keys")
api.add_resource(ApiKeys, "/keys/<int:aid>", endpoint="api_key")
api.add_resource(
ApiKeysDescribed, "/keys/<int:aid>/described", endpoint="api_key_described"
)
api.add_resource(ApiKeyUserList, "/users/<int:user_id>/keys", endpoint="user_api_keys")
api.add_resource(
UserApiKeys, "/users/<int:uid>/keys/<int:aid>", endpoint="user_api_key"
)

230
lemur/auth/ldap.py Normal file
View File

@ -0,0 +1,230 @@
"""
.. module: lemur.auth.ldap
:platform: Unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Ian Stahnke <ian.stahnke@myob.com>
"""
import ldap
from flask import current_app
from lemur.users import service as user_service
from lemur.roles import service as role_service
from lemur.common.utils import validate_conf, get_psuedo_random_string
class LdapPrincipal:
"""
Provides methods for authenticating against an LDAP server.
"""
def __init__(self, args):
self._ldap_validate_conf()
# setup ldap config
if not args["username"]:
raise Exception("missing ldap username")
if not args["password"]:
self.error_message = "missing ldap password"
raise Exception("missing ldap password")
self.ldap_principal = args["username"]
self.ldap_email_domain = current_app.config.get("LDAP_EMAIL_DOMAIN", None)
if "@" not in self.ldap_principal:
self.ldap_principal = "%s@%s" % (
self.ldap_principal,
self.ldap_email_domain,
)
self.ldap_username = args["username"]
if "@" in self.ldap_username:
self.ldap_username = args["username"].split("@")[0]
self.ldap_password = args["password"]
self.ldap_server = current_app.config.get("LDAP_BIND_URI", None)
self.ldap_base_dn = current_app.config.get("LDAP_BASE_DN", None)
self.ldap_use_tls = current_app.config.get("LDAP_USE_TLS", False)
self.ldap_cacert_file = current_app.config.get("LDAP_CACERT_FILE", None)
self.ldap_default_role = current_app.config.get("LEMUR_DEFAULT_ROLE", None)
self.ldap_required_group = current_app.config.get("LDAP_REQUIRED_GROUP", None)
self.ldap_groups_to_roles = current_app.config.get("LDAP_GROUPS_TO_ROLES", None)
self.ldap_is_active_directory = current_app.config.get(
"LDAP_IS_ACTIVE_DIRECTORY", False
)
self.ldap_attrs = ["memberOf"]
self.ldap_client = None
self.ldap_groups = None
def _update_user(self, roles):
"""
create or update a local user instance.
"""
# try to get user from local database
user = user_service.get_by_email(self.ldap_principal)
# create them a local account
if not user:
user = user_service.create(
self.ldap_username,
get_psuedo_random_string(),
self.ldap_principal,
True,
"", # thumbnailPhotoUrl
list(roles),
)
else:
# we add 'lemur' specific roles, so they do not get marked as removed
for ur in user.roles:
if not ur.third_party:
roles.add(ur)
# update any changes to the user
user_service.update(
user.id,
self.ldap_username,
self.ldap_principal,
user.active,
user.profile_picture,
list(roles),
)
return user
def _authorize(self):
"""
check groups and roles to confirm access.
return a list of roles if ok.
raise an exception on error.
"""
if not self.ldap_principal:
return None
if self.ldap_required_group:
# ensure the user has the required group in their group list
if self.ldap_required_group not in self.ldap_groups:
return None
roles = set()
if self.ldap_default_role:
role = role_service.get_by_name(self.ldap_default_role)
if role:
if not role.third_party:
role = role_service.set_third_party(role.id, third_party_status=True)
roles.add(role)
# update their 'roles'
role = role_service.get_by_name(self.ldap_principal)
if not role:
description = "auto generated role based on owner: {0}".format(
self.ldap_principal
)
role = role_service.create(
self.ldap_principal, description=description, third_party=True
)
if not role.third_party:
role = role_service.set_third_party(role.id, third_party_status=True)
roles.add(role)
if not self.ldap_groups_to_roles:
return roles
for ldap_group_name, role_name in self.ldap_groups_to_roles.items():
role = role_service.get_by_name(role_name)
if role:
if ldap_group_name in self.ldap_groups:
current_app.logger.debug(
"assigning role {0} to ldap user {1}".format(
self.ldap_principal, role
)
)
if not role.third_party:
role = role_service.set_third_party(
role.id, third_party_status=True
)
roles.add(role)
return roles
def authenticate(self):
"""
orchestrate the ldap login.
raise an exception on error.
"""
self._bind()
roles = self._authorize()
if not roles:
raise Exception("ldap authorization failed")
return self._update_user(roles)
def _bind(self):
"""
authenticate an ldap user.
list groups for a user.
raise an exception on error.
"""
if "@" not in self.ldap_principal:
self.ldap_principal = "%s@%s" % (
self.ldap_principal,
self.ldap_email_domain,
)
ldap_filter = "userPrincipalName=%s" % self.ldap_principal
# query ldap for auth
try:
# build a client
if not self.ldap_client:
self.ldap_client = ldap.initialize(self.ldap_server)
# perform a synchronous bind
self.ldap_client.set_option(ldap.OPT_REFERRALS, 0)
if self.ldap_use_tls:
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
self.ldap_client.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
self.ldap_client.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND)
self.ldap_client.set_option(ldap.OPT_X_TLS_DEMAND, True)
self.ldap_client.set_option(ldap.OPT_DEBUG_LEVEL, 255)
if self.ldap_cacert_file:
self.ldap_client.set_option(
ldap.OPT_X_TLS_CACERTFILE, self.ldap_cacert_file
)
self.ldap_client.simple_bind_s(self.ldap_principal, self.ldap_password)
except ldap.INVALID_CREDENTIALS:
self.ldap_client.unbind()
raise Exception("The supplied ldap credentials are invalid")
except ldap.SERVER_DOWN:
raise Exception("ldap server unavailable")
except ldap.LDAPError as e:
raise Exception("ldap error: {0}".format(e))
if self.ldap_is_active_directory:
# Lookup user DN, needed to search for group membership
userdn = self.ldap_client.search_s(
self.ldap_base_dn,
ldap.SCOPE_SUBTREE,
ldap_filter,
["distinguishedName"],
)[0][1]["distinguishedName"][0]
userdn = userdn.decode("utf-8")
# Search all groups that have the userDN as a member
groupfilter = "(&(objectclass=group)(member:1.2.840.113556.1.4.1941:={0}))".format(
userdn
)
lgroups = self.ldap_client.search_s(
self.ldap_base_dn, ldap.SCOPE_SUBTREE, groupfilter, ["cn"]
)
# Create a list of group CN's from the result
self.ldap_groups = []
for group in lgroups:
(dn, values) = group
if type(values) == dict:
self.ldap_groups.append(values["cn"][0].decode("ascii"))
else:
lgroups = self.ldap_client.search_s(
self.ldap_base_dn, ldap.SCOPE_SUBTREE, ldap_filter, self.ldap_attrs
)[0][1]["memberOf"]
# lgroups is a list of utf-8 encoded strings
# convert to a single string of groups to allow matching
self.ldap_groups = b"".join(lgroups).decode("ascii")
self.ldap_client.unbind()
def _ldap_validate_conf(self):
"""
Confirms required ldap config settings exist.
"""
required_vars = ["LDAP_BIND_URI", "LDAP_BASE_DN", "LDAP_EMAIL_DOMAIN"]
validate_conf(current_app, required_vars)

View File

@ -2,55 +2,73 @@
.. module: lemur.auth.permissions
:platform: Unix
:synopsis: This module defines all the permission used within Lemur
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from functools import partial
from collections import namedtuple
from flask.ext.principal import Permission, RoleNeed
from flask import current_app
from flask_principal import Permission, RoleNeed
# Permissions
operator_permission = Permission(RoleNeed('operator'))
admin_permission = Permission(RoleNeed('admin'))
operator_permission = Permission(RoleNeed("operator"))
admin_permission = Permission(RoleNeed("admin"))
CertificateCreator = namedtuple('certificate', ['method', 'value'])
CertificateCreatorNeed = partial(CertificateCreator, 'key')
CertificateOwner = namedtuple("certificate", ["method", "value"])
CertificateOwnerNeed = partial(CertificateOwner, "role")
class ViewKeyPermission(Permission):
def __init__(self, certificate_id, owner):
c_need = CertificateCreatorNeed(certificate_id)
super(ViewKeyPermission, self).__init__(c_need, RoleNeed(owner), RoleNeed('admin'))
class SensitiveDomainPermission(Permission):
def __init__(self):
needs = [RoleNeed("admin")]
sensitive_domain_roles = current_app.config.get("SENSITIVE_DOMAIN_ROLES", [])
if sensitive_domain_roles:
for role in sensitive_domain_roles:
needs.append(RoleNeed(role))
super(SensitiveDomainPermission, self).__init__(*needs)
class UpdateCertificatePermission(Permission):
def __init__(self, certificate_id, owner):
c_need = CertificateCreatorNeed(certificate_id)
super(UpdateCertificatePermission, self).__init__(c_need, RoleNeed(owner), RoleNeed('admin'))
class CertificatePermission(Permission):
def __init__(self, owner, roles):
needs = [RoleNeed("admin"), RoleNeed(owner), RoleNeed("creator")]
for r in roles:
needs.append(CertificateOwnerNeed(str(r)))
# Backwards compatibility with mixed-case role names
if str(r) != str(r).lower():
needs.append(CertificateOwnerNeed(str(r).lower()))
super(CertificatePermission, self).__init__(*needs)
RoleUser = namedtuple('role', ['method', 'value'])
ViewRoleCredentialsNeed = partial(RoleUser, 'roleView')
class ApiKeyCreatorPermission(Permission):
def __init__(self):
super(ApiKeyCreatorPermission, self).__init__(RoleNeed("admin"))
class ViewRoleCredentialsPermission(Permission):
RoleMember = namedtuple("role", ["method", "value"])
RoleMemberNeed = partial(RoleMember, "member")
class RoleMemberPermission(Permission):
def __init__(self, role_id):
need = ViewRoleCredentialsNeed(role_id)
super(ViewRoleCredentialsPermission, self).__init__(need, RoleNeed('admin'))
needs = [RoleNeed("admin"), RoleMemberNeed(role_id)]
super(RoleMemberPermission, self).__init__(*needs)
AuthorityCreator = namedtuple('authority', ['method', 'value'])
AuthorityCreatorNeed = partial(AuthorityCreator, 'authorityUse')
AuthorityCreator = namedtuple("authority", ["method", "value"])
AuthorityCreatorNeed = partial(AuthorityCreator, "authorityUse")
AuthorityOwner = namedtuple('authority', ['method', 'value'])
AuthorityOwnerNeed = partial(AuthorityOwner, 'role')
AuthorityOwner = namedtuple("authority", ["method", "value"])
AuthorityOwnerNeed = partial(AuthorityOwner, "role")
class AuthorityPermission(Permission):
def __init__(self, authority_id, roles):
needs = [RoleNeed('admin'), AuthorityCreatorNeed(str(authority_id))]
needs = [RoleNeed("admin"), AuthorityCreatorNeed(str(authority_id))]
for r in roles:
needs.append(AuthorityOwnerNeed(str(r)))

View File

@ -3,16 +3,13 @@
:platform: Unix
:synopsis: This module contains all of the authentication duties for
lemur
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from __future__ import unicode_literals
from builtins import bytes
import jwt
import json
import base64
import binascii
from functools import wraps
@ -20,31 +17,18 @@ from datetime import datetime, timedelta
from flask import g, current_app, jsonify, request
from flask.ext.restful import Resource
from flask.ext.principal import identity_loaded, RoleNeed, UserNeed
from flask_restful import Resource
from flask_principal import identity_loaded, RoleNeed, UserNeed
from flask.ext.principal import Identity, identity_changed
from flask_principal import Identity, identity_changed
from cryptography.hazmat.backends import default_backend
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 CertificateCreatorNeed, \
AuthorityCreatorNeed, ViewRoleCredentialsNeed
def base64url_decode(data):
rem = len(data) % 4
if rem > 0:
data += '=' * (4 - rem)
return base64.urlsafe_b64decode(bytes(data.encode('latin-1')))
def base64url_encode(data):
return base64.urlsafe_b64encode(data).replace('=', '')
from lemur.api_keys import service as api_key_service
from lemur.auth.permissions import AuthorityCreatorNeed, RoleMemberNeed
def get_rsa_public_key(n, e):
@ -55,68 +39,103 @@ def get_rsa_public_key(n, e):
:param e:
:return: a RSA Public Key in PEM format
"""
n = int(binascii.hexlify(base64url_decode(n)), 16)
e = int(binascii.hexlify(base64url_decode(e)), 16)
n = int(binascii.hexlify(jwt.utils.base64url_decode(bytes(n, "utf-8"))), 16)
e = int(binascii.hexlify(jwt.utils.base64url_decode(bytes(e, "utf-8"))), 16)
pub = RSAPublicNumbers(e, n).public_key(default_backend())
return pub.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
def create_token(user):
def create_token(user, aid=None, ttl=None):
"""
Create a valid JWT for a given user, this token is then used to authenticate
Create a valid JWT for a given user/api key, this token is then used to authenticate
sessions until the token expires.
:param user:
:return:
"""
expiration_delta = timedelta(days=int(current_app.config.get('LEMUR_TOKEN_EXPIRATION', 1)))
payload = {
'sub': user.id,
'iat': datetime.now(),
'exp': datetime.now() + expiration_delta
}
token = jwt.encode(payload, current_app.config['LEMUR_TOKEN_SECRET'])
return token.decode('unicode_escape')
expiration_delta = timedelta(
days=int(current_app.config.get("LEMUR_TOKEN_EXPIRATION", 1))
)
payload = {"iat": datetime.utcnow(), "exp": datetime.utcnow() + expiration_delta}
# Handle Just a User ID & User Object.
if isinstance(user, int):
payload["sub"] = user
else:
payload["sub"] = user.id
if aid is not None:
payload["aid"] = aid
# Custom TTLs are only supported on Access Keys.
if ttl is not None and aid is not None:
# Tokens that are forever until revoked.
if ttl == -1:
del payload["exp"]
else:
payload["exp"] = ttl
token = jwt.encode(payload, current_app.config["LEMUR_TOKEN_SECRET"])
return token.decode("unicode_escape")
def login_required(f):
"""
Validates the JWT and ensures that is has not expired.
Validates the JWT and ensures that is has not expired and the user is still active.
:param f:
:return:
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if not request.headers.get('Authorization'):
response = jsonify(message='Missing authorization header')
if not request.headers.get("Authorization"):
response = jsonify(message="Missing authorization header")
response.status_code = 401
return response
try:
token = request.headers.get('Authorization').split()[1]
token = request.headers.get("Authorization").split()[1]
except Exception as e:
return dict(message='Token is invalid'), 403
return dict(message="Token is invalid"), 403
try:
payload = jwt.decode(token, current_app.config['LEMUR_TOKEN_SECRET'])
header_data = fetch_token_header(token)
payload = jwt.decode(token, current_app.config["LEMUR_TOKEN_SECRET"], algorithms=[header_data["alg"]])
except jwt.DecodeError:
return dict(message='Token is invalid'), 403
return dict(message="Token is invalid"), 403
except jwt.ExpiredSignatureError:
return dict(message='Token has expired'), 403
return dict(message="Token has expired"), 403
except jwt.InvalidTokenError:
return dict(message='Token is invalid'), 403
return dict(message="Token is invalid"), 403
g.current_user = user_service.get(payload['sub'])
if "aid" in payload:
access_key = api_key_service.get(payload["aid"])
if access_key.revoked:
return dict(message="Token has been revoked"), 403
if access_key.ttl != -1:
current_time = datetime.utcnow()
expired_time = datetime.fromtimestamp(
access_key.issued_at + access_key.ttl
)
if current_time >= expired_time:
return dict(message="Token has expired"), 403
user = user_service.get(payload["sub"])
if not user.active:
return dict(message="User is not currently active"), 403
g.current_user = user
if not g.current_user:
return dict(message='You are not logged in'), 403
return dict(message="You are not logged in"), 403
# Tell Flask-Principal the identity changed
identity_changed.send(current_app._get_current_object(), identity=Identity(g.current_user.id))
identity_changed.send(
current_app._get_current_object(), identity=Identity(g.current_user.id)
)
return f(*args, **kwargs)
@ -130,21 +149,18 @@ def fetch_token_header(token):
:param token:
:return: :raise jwt.DecodeError:
"""
token = token.encode('utf-8')
token = token.encode("utf-8")
try:
signing_input, crypto_segment = token.rsplit(b'.', 1)
header_segment, payload_segment = signing_input.split(b'.', 1)
signing_input, crypto_segment = token.rsplit(b".", 1)
header_segment, payload_segment = signing_input.split(b".", 1)
except ValueError:
raise jwt.DecodeError('Not enough segments')
raise jwt.DecodeError("Not enough segments")
try:
return json.loads(base64url_decode(header_segment))
return json.loads(jwt.utils.base64url_decode(header_segment).decode("utf-8"))
except TypeError as e:
current_app.logger.exception(e)
raise jwt.DecodeError('Invalid header padding')
except binascii.Error as e:
current_app.logger.exception(e)
raise jwt.DecodeError('Invalid header padding')
raise jwt.DecodeError("Invalid header padding")
@identity_loaded.connect
@ -163,21 +179,16 @@ def on_identity_loaded(sender, identity):
identity.provides.add(UserNeed(identity.id))
# identity with the roles that the user provides
if hasattr(user, 'roles'):
if hasattr(user, "roles"):
for role in user.roles:
identity.provides.add(ViewRoleCredentialsNeed(role.id))
identity.provides.add(RoleNeed(role.name))
identity.provides.add(RoleMemberNeed(role.id))
# apply ownership for authorities
if hasattr(user, 'authorities'):
if hasattr(user, "authorities"):
for authority in user.authorities:
identity.provides.add(AuthorityCreatorNeed(authority.id))
# apply ownership of certificates
if hasattr(user, 'certificates'):
for certificate in user.certificates:
identity.provides.add(CertificateCreatorNeed(certificate.id))
g.user = user
@ -185,6 +196,7 @@ class AuthenticatedResource(Resource):
"""
Inherited by all resources that need to be protected by authentication.
"""
method_decorators = [login_required]
def __init__(self):

View File

@ -1,7 +1,7 @@
"""
.. module: lemur.auth.views
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
@ -9,22 +9,224 @@ import jwt
import base64
import requests
from flask import g, Blueprint, current_app
from flask import Blueprint, current_app
from flask.ext.restful import reqparse, Resource, Api
from flask.ext.principal import Identity, identity_changed
from flask_restful import reqparse, Resource, Api
from flask_principal import Identity, identity_changed
from lemur.constants import SUCCESS_METRIC_STATUS, FAILURE_METRIC_STATUS
from lemur.extensions import metrics
from lemur.common.utils import get_psuedo_random_string
from lemur.users import service as user_service
from lemur.roles import service as role_service
from lemur.auth.service import create_token, fetch_token_header, get_rsa_public_key
from lemur.auth import ldap
mod = Blueprint('auth', __name__)
mod = Blueprint("auth", __name__)
api = Api(mod)
def exchange_for_access_token(
code, redirect_uri, client_id, secret, access_token_url=None, verify_cert=True
):
"""
Exchanges authorization code for access token.
:param code:
:param redirect_uri:
:param client_id:
:param secret:
:param access_token_url:
:param verify_cert:
:return:
:return:
"""
# take the information we have received from the provider to create a new request
params = {
"grant_type": "authorization_code",
"scope": "openid email profile address",
"code": code,
"redirect_uri": redirect_uri,
"client_id": client_id,
}
# the secret and cliendId will be given to you when you signup for the provider
token = "{0}:{1}".format(client_id, secret)
basic = base64.b64encode(bytes(token, "utf-8"))
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"authorization": "basic {0}".format(basic.decode("utf-8")),
}
# exchange authorization code for access token.
r = requests.post(
access_token_url, headers=headers, params=params, verify=verify_cert
)
if r.status_code == 400:
r = requests.post(
access_token_url, headers=headers, data=params, verify=verify_cert
)
id_token = r.json()["id_token"]
access_token = r.json()["access_token"]
return id_token, access_token
def validate_id_token(id_token, client_id, jwks_url):
"""
Ensures that the token we receive is valid.
:param id_token:
:param client_id:
:param jwks_url:
:return:
"""
# fetch token public key
header_data = fetch_token_header(id_token)
# retrieve the key material as specified by the token header
r = requests.get(jwks_url)
for key in r.json()["keys"]:
if key["kid"] == header_data["kid"]:
secret = get_rsa_public_key(key["n"], key["e"])
algo = header_data["alg"]
break
else:
return dict(message="Key not found"), 401
# validate your token based on the key it was signed with
try:
jwt.decode(
id_token, secret.decode("utf-8"), algorithms=[algo], audience=client_id
)
except jwt.DecodeError:
return dict(message="Token is invalid"), 401
except jwt.ExpiredSignatureError:
return dict(message="Token has expired"), 401
except jwt.InvalidTokenError:
return dict(message="Token is invalid"), 401
def retrieve_user(user_api_url, access_token):
"""
Fetch user information from provided user api_url.
:param user_api_url:
:param access_token:
:return:
"""
user_params = dict(access_token=access_token, schema="profile")
headers = {}
if current_app.config.get("PING_INCLUDE_BEARER_TOKEN"):
headers = {"Authorization": f"Bearer {access_token}"}
# retrieve information about the current user.
r = requests.get(user_api_url, params=user_params, headers=headers)
# Some IDPs, like "Keycloak", require a POST instead of a GET
if r.status_code == 400:
r = requests.post(user_api_url, data=user_params, headers=headers)
profile = r.json()
user = user_service.get_by_email(profile["email"])
return user, profile
def create_user_roles(profile):
"""Creates new roles based on profile information.
:param profile:
:return:
"""
roles = []
# update their google 'roles'
if "googleGroups" in profile:
for group in profile["googleGroups"]:
role = role_service.get_by_name(group)
if not role:
role = role_service.create(
group,
description="This is a google group based role created by Lemur",
third_party=True,
)
if not role.third_party:
role = role_service.set_third_party(role.id, third_party_status=True)
roles.append(role)
else:
current_app.logger.warning(
"'googleGroups' not sent by identity provider, no specific roles will assigned to the user."
)
role = role_service.get_by_name(profile["email"])
if not role:
role = role_service.create(
profile["email"],
description="This is a user specific role",
third_party=True,
)
if not role.third_party:
role = role_service.set_third_party(role.id, third_party_status=True)
roles.append(role)
# every user is an operator (tied to a default role)
if current_app.config.get("LEMUR_DEFAULT_ROLE"):
default = role_service.get_by_name(current_app.config["LEMUR_DEFAULT_ROLE"])
if not default:
default = role_service.create(
current_app.config["LEMUR_DEFAULT_ROLE"],
description="This is the default Lemur role.",
)
if not default.third_party:
role_service.set_third_party(default.id, third_party_status=True)
roles.append(default)
return roles
def update_user(user, profile, roles):
"""Updates user with current profile information and associated roles.
:param user:
:param profile:
:param roles:
"""
# if we get an sso user create them an account
if not user:
user = user_service.create(
profile["email"],
get_psuedo_random_string(),
profile["email"],
True,
profile.get("thumbnailPhotoUrl"),
roles,
)
else:
# we add 'lemur' specific roles, so they do not get marked as removed
for ur in user.roles:
if not ur.third_party:
roles.append(ur)
# update any changes to the user
user_service.update(
user.id,
profile["email"],
profile["email"],
True,
profile.get("thumbnailPhotoUrl"), # profile isn't google+ enabled
roles,
)
class Login(Resource):
"""
Provides an endpoint for Lemur's basic authentication. It takes a username and password
@ -42,6 +244,7 @@ class Login(Resource):
on your uses cases but. It is important to not that there is currently no build in method to revoke a users token \
and force re-authentication.
"""
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(Login, self).__init__()
@ -82,153 +285,299 @@ class Login(Resource):
:statuscode 401: invalid credentials
:statuscode 200: no error
"""
self.reqparse.add_argument('username', type=str, required=True, location='json')
self.reqparse.add_argument('password', type=str, required=True, location='json')
self.reqparse.add_argument("username", type=str, required=True, location="json")
self.reqparse.add_argument("password", type=str, required=True, location="json")
args = self.reqparse.parse_args()
if '@' in args['username']:
user = user_service.get_by_email(args['username'])
if "@" in args["username"]:
user = user_service.get_by_email(args["username"])
else:
user = user_service.get_by_username(args['username'])
user = user_service.get_by_username(args["username"])
if user and user.check_password(args['password']):
# default to local authentication
if user and user.check_password(args["password"]) and user.active:
# Tell Flask-Principal the identity changed
identity_changed.send(current_app._get_current_object(),
identity=Identity(user.id))
identity_changed.send(
current_app._get_current_object(), identity=Identity(user.id)
)
metrics.send(
"login", "counter", 1, metric_tags={"status": SUCCESS_METRIC_STATUS}
)
return dict(token=create_token(user))
return dict(message='The supplied credentials are invalid'), 401
# try ldap login
if current_app.config.get("LDAP_AUTH"):
try:
ldap_principal = ldap.LdapPrincipal(args)
user = ldap_principal.authenticate()
if user and user.active:
# Tell Flask-Principal the identity changed
identity_changed.send(
current_app._get_current_object(), identity=Identity(user.id)
)
metrics.send(
"login",
"counter",
1,
metric_tags={"status": SUCCESS_METRIC_STATUS},
)
return dict(token=create_token(user))
except Exception as e:
current_app.logger.error("ldap error: {0}".format(e))
ldap_message = "ldap error: %s" % e
metrics.send(
"login", "counter", 1, metric_tags={"status": FAILURE_METRIC_STATUS}
)
return dict(message=ldap_message), 403
def get(self):
return {'username': g.current_user.username, 'roles': [r.name for r in g.current_user.roles]}
# if not valid user - no certificates for you
metrics.send(
"login", "counter", 1, metric_tags={"status": FAILURE_METRIC_STATUS}
)
return dict(message="The supplied credentials are invalid"), 403
class Ping(Resource):
"""
This class serves as an example of how one might implement an SSO provider for use with Lemur. In
this example we use a OpenIDConnect authentication flow, that is essentially OAuth2 underneath. If you have an
this example we use an OpenIDConnect authentication flow, that is essentially OAuth2 underneath. If you have an
OAuth2 provider you want to use Lemur there would be two steps:
1. Define your own class that inherits from :class:`flask.ext.restful.Resource` and create the HTTP methods the \
provider uses for it's callbacks.
1. Define your own class that inherits from :class:`flask_restful.Resource` and create the HTTP methods the \
provider uses for its callbacks.
2. Add or change the Lemur AngularJS Configuration to point to your new provider
"""
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(Ping, self).__init__()
def get(self):
return "Redirecting..."
def post(self):
self.reqparse.add_argument('clientId', type=str, required=True, location='json')
self.reqparse.add_argument('redirectUri', type=str, required=True, location='json')
self.reqparse.add_argument('code', type=str, required=True, location='json')
self.reqparse.add_argument("clientId", type=str, required=True, location="json")
self.reqparse.add_argument(
"redirectUri", type=str, required=True, location="json"
)
self.reqparse.add_argument("code", type=str, required=True, location="json")
args = self.reqparse.parse_args()
# take the information we have received from the provider to create a new request
params = {
'client_id': args['clientId'],
'grant_type': 'authorization_code',
'scope': 'openid email profile address',
'redirect_uri': args['redirectUri'],
'code': args['code']
}
# you can either discover these dynamically or simply configure them
access_token_url = current_app.config.get('PING_ACCESS_TOKEN_URL')
user_api_url = current_app.config.get('PING_USER_API_URL')
access_token_url = current_app.config.get("PING_ACCESS_TOKEN_URL")
user_api_url = current_app.config.get("PING_USER_API_URL")
# the secret and cliendId will be given to you when you signup for the provider
basic = base64.b64encode('{0}:{1}'.format(args['clientId'], current_app.config.get("PING_SECRET")))
headers = {'Authorization': 'Basic {0}'.format(basic)}
secret = current_app.config.get("PING_SECRET")
# exchange authorization code for access token.
id_token, access_token = exchange_for_access_token(
args["code"],
args["redirectUri"],
args["clientId"],
secret,
access_token_url=access_token_url,
)
r = requests.post(access_token_url, headers=headers, params=params)
id_token = r.json()['id_token']
access_token = r.json()['access_token']
jwks_url = current_app.config.get("PING_JWKS_URL")
error_code = validate_id_token(id_token, args["clientId"], jwks_url)
if error_code:
return error_code
user, profile = retrieve_user(user_api_url, access_token)
roles = create_user_roles(profile)
update_user(user, profile, roles)
# fetch token public key
header_data = fetch_token_header(id_token)
jwks_url = current_app.config.get('PING_JWKS_URL')
# retrieve the key material as specified by the token header
r = requests.get(jwks_url)
for key in r.json()['keys']:
if key['kid'] == header_data['kid']:
secret = get_rsa_public_key(key['n'], key['e'])
algo = header_data['alg']
break
else:
return dict(message='Key not found'), 403
# validate your token based on the key it was signed with
try:
jwt.decode(id_token, secret, algorithms=[algo], audience=args['clientId'])
except jwt.DecodeError:
return dict(message='Token is invalid'), 403
except jwt.ExpiredSignatureError:
return dict(message='Token has expired'), 403
except jwt.InvalidTokenError:
return dict(message='Token is invalid'), 403
user_params = dict(access_token=access_token, schema='profile')
# retrieve information about the current user.
r = requests.get(user_api_url, params=user_params)
profile = r.json()
user = user_service.get_by_email(profile['email'])
# update their google 'roles'
roles = []
for group in profile['googleGroups']:
role = role_service.get_by_name(group)
if not role:
role = role_service.create(group, description='This is a google group based role created by Lemur')
roles.append(role)
# if we get an sso user create them an account
# we still pick a random password in case sso is down
if not user:
# every user is an operator (tied to a default role)
if current_app.config.get('LEMUR_DEFAULT_ROLE'):
v = role_service.get_by_name(current_app.config.get('LEMUR_DEFAULT_ROLE'))
if v:
roles.append(v)
user = user_service.create(
profile['email'],
get_psuedo_random_string(),
profile['email'],
True,
profile.get('thumbnailPhotoUrl'),
roles
)
else:
# we add 'lemur' specific roles, so they do not get marked as removed
for ur in user.roles:
if ur.authority_id:
roles.append(ur)
# update any changes to the user
user_service.update(
user.id,
profile['email'],
profile['email'],
True,
profile.get('thumbnailPhotoUrl'), # incase profile isn't google+ enabled
roles
if not user or not user.active:
metrics.send(
"login", "counter", 1, metric_tags={"status": FAILURE_METRIC_STATUS}
)
return dict(message="The supplied credentials are invalid"), 403
# Tell Flask-Principal the identity changed
identity_changed.send(current_app._get_current_object(), identity=Identity(user.id))
identity_changed.send(
current_app._get_current_object(), identity=Identity(user.id)
)
metrics.send(
"login", "counter", 1, metric_tags={"status": SUCCESS_METRIC_STATUS}
)
return dict(token=create_token(user))
class OAuth2(Resource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(OAuth2, self).__init__()
def get(self):
return "Redirecting..."
def post(self):
self.reqparse.add_argument("clientId", type=str, required=True, location="json")
self.reqparse.add_argument(
"redirectUri", type=str, required=True, location="json"
)
self.reqparse.add_argument("code", type=str, required=True, location="json")
args = self.reqparse.parse_args()
# you can either discover these dynamically or simply configure them
access_token_url = current_app.config.get("OAUTH2_ACCESS_TOKEN_URL")
user_api_url = current_app.config.get("OAUTH2_USER_API_URL")
verify_cert = current_app.config.get("OAUTH2_VERIFY_CERT")
secret = current_app.config.get("OAUTH2_SECRET")
id_token, access_token = exchange_for_access_token(
args["code"],
args["redirectUri"],
args["clientId"],
secret,
access_token_url=access_token_url,
verify_cert=verify_cert,
)
jwks_url = current_app.config.get("OAUTH2_JWKS_URL")
error_code = validate_id_token(id_token, args["clientId"], jwks_url)
if error_code:
return error_code
user, profile = retrieve_user(user_api_url, access_token)
roles = create_user_roles(profile)
update_user(user, profile, roles)
if not user.active:
metrics.send(
"login", "counter", 1, metric_tags={"status": FAILURE_METRIC_STATUS}
)
return dict(message="The supplied credentials are invalid"), 403
# Tell Flask-Principal the identity changed
identity_changed.send(
current_app._get_current_object(), identity=Identity(user.id)
)
metrics.send(
"login", "counter", 1, metric_tags={"status": SUCCESS_METRIC_STATUS}
)
return dict(token=create_token(user))
api.add_resource(Login, '/auth/login', endpoint='login')
api.add_resource(Ping, '/auth/ping', endpoint='ping')
class Google(Resource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(Google, self).__init__()
def post(self):
access_token_url = "https://accounts.google.com/o/oauth2/token"
people_api_url = "https://www.googleapis.com/plus/v1/people/me/openIdConnect"
self.reqparse.add_argument("clientId", type=str, required=True, location="json")
self.reqparse.add_argument(
"redirectUri", type=str, required=True, location="json"
)
self.reqparse.add_argument("code", type=str, required=True, location="json")
args = self.reqparse.parse_args()
# Step 1. Exchange authorization code for access token
payload = {
"client_id": args["clientId"],
"grant_type": "authorization_code",
"redirect_uri": args["redirectUri"],
"code": args["code"],
"client_secret": current_app.config.get("GOOGLE_SECRET"),
}
r = requests.post(access_token_url, data=payload)
token = r.json()
# Step 2. Retrieve information about the current user
headers = {"Authorization": "Bearer {0}".format(token["access_token"])}
r = requests.get(people_api_url, headers=headers)
profile = r.json()
user = user_service.get_by_email(profile["email"])
if not (user and user.active):
metrics.send(
"login", "counter", 1, metric_tags={"status": FAILURE_METRIC_STATUS}
)
return dict(message="The supplied credentials are invalid."), 403
if user:
metrics.send(
"login", "counter", 1, metric_tags={"status": SUCCESS_METRIC_STATUS}
)
return dict(token=create_token(user))
metrics.send(
"login", "counter", 1, metric_tags={"status": FAILURE_METRIC_STATUS}
)
class Providers(Resource):
def get(self):
active_providers = []
for provider in current_app.config.get("ACTIVE_PROVIDERS", []):
provider = provider.lower()
if provider == "google":
active_providers.append(
{
"name": "google",
"clientId": current_app.config.get("GOOGLE_CLIENT_ID"),
"url": api.url_for(Google),
}
)
elif provider == "ping":
active_providers.append(
{
"name": current_app.config.get("PING_NAME"),
"url": current_app.config.get("PING_REDIRECT_URI"),
"redirectUri": current_app.config.get("PING_REDIRECT_URI"),
"clientId": current_app.config.get("PING_CLIENT_ID"),
"responseType": "code",
"scope": ["openid", "email", "profile", "address"],
"scopeDelimiter": " ",
"authorizationEndpoint": current_app.config.get(
"PING_AUTH_ENDPOINT"
),
"requiredUrlParams": ["scope"],
"type": "2.0",
}
)
elif provider == "oauth2":
active_providers.append(
{
"name": current_app.config.get("OAUTH2_NAME"),
"url": current_app.config.get("OAUTH2_REDIRECT_URI"),
"redirectUri": current_app.config.get("OAUTH2_REDIRECT_URI"),
"clientId": current_app.config.get("OAUTH2_CLIENT_ID"),
"responseType": "code",
"scope": ["openid", "email", "profile", "groups"],
"scopeDelimiter": " ",
"authorizationEndpoint": current_app.config.get(
"OAUTH2_AUTH_ENDPOINT"
),
"requiredUrlParams": ["scope", "state", "nonce"],
"state": "STATE",
"nonce": get_psuedo_random_string(),
"type": "2.0",
}
)
return active_providers
api.add_resource(Login, "/auth/login", endpoint="login")
api.add_resource(Ping, "/auth/ping", endpoint="ping")
api.add_resource(Google, "/auth/google", endpoint="google")
api.add_resource(OAuth2, "/auth/oauth2", endpoint="oauth2")
api.add_resource(Providers, "/auth/providers", endpoint="providers")

View File

@ -1,58 +1,117 @@
"""
.. module: lemur.authorities.models
:platform: unix
:synopsis: This module contains all of the models need to create a authority within Lemur.
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:synopsis: This module contains all of the models need to create an authority within Lemur.
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from cryptography import x509
from cryptography.hazmat.backends import default_backend
import json
from flask import current_app
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Text, func, ForeignKey, DateTime, PassiveDefault, Boolean
from sqlalchemy import (
Column,
Integer,
String,
Text,
func,
ForeignKey,
DateTime,
DefaultClause,
Boolean,
)
from sqlalchemy.dialects.postgresql import JSON
from lemur.database import db
from lemur.certificates.models import get_cn, get_not_after, get_not_before
from lemur.plugins.base import plugins
from lemur.models import roles_authorities
class Authority(db.Model):
__tablename__ = 'authorities'
__tablename__ = "authorities"
id = Column(Integer, primary_key=True)
owner = Column(String(128))
owner = Column(String(128), nullable=False)
name = Column(String(128), unique=True)
body = Column(Text())
chain = Column(Text())
bits = Column(Integer())
cn = Column(String(128))
not_before = Column(DateTime)
not_after = Column(DateTime)
active = Column(Boolean, default=True)
date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
plugin_name = Column(String(64))
description = Column(Text)
options = Column(JSON)
roles = relationship('Role', backref=db.backref('authority'), lazy='dynamic')
user_id = Column(Integer, ForeignKey('users.id'))
certificates = relationship("Certificate", backref='authority')
date_created = Column(DateTime, DefaultClause(func.now()), nullable=False)
roles = relationship(
"Role",
secondary=roles_authorities,
passive_deletes=True,
backref=db.backref("authority"),
lazy="dynamic",
)
user_id = Column(Integer, ForeignKey("users.id"))
authority_certificate = relationship(
"Certificate",
backref="root_authority",
uselist=False,
foreign_keys="Certificate.root_authority_id",
)
certificates = relationship(
"Certificate", backref="authority", foreign_keys="Certificate.authority_id"
)
def __init__(self, name, owner, plugin_name, body, roles=None, chain=None, description=None):
self.name = name
self.body = body
self.chain = chain
self.owner = owner
self.plugin_name = plugin_name
cert = x509.load_pem_x509_certificate(str(body), default_backend())
self.cn = get_cn(cert)
self.not_before = get_not_before(cert)
self.not_after = get_not_after(cert)
self.roles = roles
self.description = description
authority_pending_certificate = relationship(
"PendingCertificate",
backref="root_authority",
uselist=False,
foreign_keys="PendingCertificate.root_authority_id",
)
pending_certificates = relationship(
"PendingCertificate",
backref="authority",
foreign_keys="PendingCertificate.authority_id",
)
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
def __init__(self, **kwargs):
self.owner = kwargs["owner"]
self.roles = kwargs.get("roles", [])
self.name = kwargs.get("name")
self.description = kwargs.get("description")
self.authority_certificate = kwargs["authority_certificate"]
self.plugin_name = kwargs["plugin"]["slug"]
self.options = kwargs.get("options")
def serialize(self):
blob = self.as_dict()
return blob
@property
def plugin(self):
return plugins.get(self.plugin_name)
@property
def is_cab_compliant(self):
"""
Parse the options to find whether authority is CAB Forum Compliant,
i.e., adhering to the CA/Browser Forum Baseline Requirements.
Returns None if option is not available
"""
if not self.options:
return None
options_array = json.loads(self.options)
if isinstance(options_array, list):
for option in options_array:
if "name" in option and option["name"] == 'cab_compliant':
return option["value"]
return None
@property
def max_issuance_days(self):
if self.is_cab_compliant:
return current_app.config.get("PUBLIC_CA_MAX_VALIDITY_DAYS", 397)
@property
def default_validity_days(self):
if self.is_cab_compliant:
return current_app.config.get("PUBLIC_CA_MAX_VALIDITY_DAYS", 397)
return current_app.config.get("DEFAULT_VALIDITY_DAYS", 365) # 1 year default
def __repr__(self):
return "Authority(name={name})".format(name=self.name)

View File

@ -0,0 +1,150 @@
"""
.. module: lemur.authorities.schemas
:platform: unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import current_app
from marshmallow import fields, validates_schema, pre_load
from marshmallow import validate
from marshmallow.exceptions import ValidationError
from lemur.schemas import (
PluginInputSchema,
PluginOutputSchema,
ExtensionSchema,
AssociatedAuthoritySchema,
AssociatedRoleSchema,
)
from lemur.users.schemas import UserNestedOutputSchema
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
from lemur.common import validators, missing
from lemur.common.fields import ArrowDateTime
from lemur.constants import CERTIFICATE_KEY_TYPES
class AuthorityInputSchema(LemurInputSchema):
name = fields.String(required=True)
owner = fields.Email(required=True)
description = fields.String()
common_name = fields.String(required=True, validate=validators.common_name)
validity_start = ArrowDateTime()
validity_end = ArrowDateTime()
validity_years = fields.Integer()
# certificate body fields
organizational_unit = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_ORGANIZATIONAL_UNIT")
)
organization = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_ORGANIZATION")
)
location = fields.String()
country = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_COUNTRY")
)
state = fields.String(missing=lambda: current_app.config.get("LEMUR_DEFAULT_STATE"))
# Creating a String field instead of Email to allow empty value
email = fields.String()
plugin = fields.Nested(PluginInputSchema)
# signing related options
type = fields.String(validate=validate.OneOf(["root", "subca"]), missing="root")
parent = fields.Nested(AssociatedAuthoritySchema)
signing_algorithm = fields.String(
validate=validate.OneOf(["sha256WithRSA", "sha1WithRSA",
"sha256WithECDSA", "SHA384withECDSA", "SHA512withECDSA"]),
missing="sha256WithRSA",
)
key_type = fields.String(
validate=validate.OneOf(CERTIFICATE_KEY_TYPES), missing="RSA2048"
)
key_name = fields.String()
sensitivity = fields.String(
validate=validate.OneOf(["medium", "high"]), missing="medium"
)
serial_number = fields.Integer()
first_serial = fields.Integer(missing=1)
extensions = fields.Nested(ExtensionSchema)
roles = fields.Nested(AssociatedRoleSchema(many=True))
@validates_schema
def validate_dates(self, data):
validators.dates(data)
@validates_schema
def validate_subca(self, data):
if data["type"] == "subca":
if not data.get("parent"):
raise ValidationError(
"If generating a subca, parent 'authority' must be specified."
)
@pre_load
def ensure_dates(self, data):
return missing.convert_validity_years(data)
class AuthorityUpdateSchema(LemurInputSchema):
owner = fields.Email(required=True)
description = fields.String()
active = fields.Boolean(missing=True)
roles = fields.Nested(AssociatedRoleSchema(many=True))
class RootAuthorityCertificateOutputSchema(LemurOutputSchema):
__envelope__ = False
id = fields.Integer()
active = fields.Boolean()
bits = fields.Integer()
body = fields.String()
chain = fields.String()
description = fields.String()
name = fields.String()
cn = fields.String()
not_after = fields.DateTime()
not_before = fields.DateTime()
owner = fields.Email()
status = fields.Boolean()
user = fields.Nested(UserNestedOutputSchema)
class AuthorityOutputSchema(LemurOutputSchema):
id = fields.Integer()
description = fields.String()
name = fields.String()
owner = fields.Email()
plugin = fields.Nested(PluginOutputSchema)
active = fields.Boolean()
options = fields.Dict()
roles = fields.List(fields.Nested(AssociatedRoleSchema))
max_issuance_days = fields.Integer()
default_validity_days = fields.Integer()
authority_certificate = fields.Nested(RootAuthorityCertificateOutputSchema)
class AuthorityNestedOutputSchema(LemurOutputSchema):
__envelope__ = False
id = fields.Integer()
description = fields.String()
name = fields.String()
owner = fields.Email()
plugin = fields.Nested(PluginOutputSchema)
active = fields.Boolean()
authority_certificate = fields.Nested(RootAuthorityCertificateOutputSchema, only=["not_after", "not_before"])
is_cab_compliant = fields.Boolean()
max_issuance_days = fields.Integer()
default_validity_days = fields.Integer()
authority_update_schema = AuthorityUpdateSchema()
authority_input_schema = AuthorityInputSchema()
authority_output_schema = AuthorityOutputSchema()
authorities_output_schema = AuthorityOutputSchema(many=True)

View File

@ -3,100 +3,145 @@
:platform: Unix
:synopsis: This module contains all of the services level functions used to
administer authorities in Lemur
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import g
from flask import current_app
import json
from lemur import database
from lemur.common.utils import truthiness
from lemur.extensions import metrics
from lemur.authorities.models import Authority
from lemur.roles import service as role_service
from lemur.notifications import service as notification_service
from lemur.roles.models import Role
from lemur.certificates.models import Certificate
from lemur.roles import service as role_service
from lemur.plugins.base import plugins
from lemur.certificates.service import upload
def update(authority_id, description=None, owner=None, active=None, roles=None):
def update(authority_id, description, owner, active, roles):
"""
Update a an authority with new values.
Update an authority with new values.
:param authority_id:
:param roles: roles that are allowed to use this authority
:rtype : Authority
:return:
"""
authority = get(authority_id)
if roles:
authority = database.update_list(authority, 'roles', Role, roles)
if active:
authority.active = active
authority.roles = roles
authority.active = active
authority.description = description
authority.owner = owner
return database.update(authority)
def create(kwargs):
def update_options(authority_id, options):
"""
Create a new authority.
Update an authority with new options.
:rtype : Authority
:param authority_id:
:param options: the new options to be saved into the authority
:return:
"""
issuer = plugins.get(kwargs.get('pluginName'))
authority = get(authority_id)
kwargs['creator'] = g.current_user.email
cert_body, intermediate, issuer_roles = issuer.create_authority(kwargs)
authority.options = options
cert = Certificate(cert_body, chain=intermediate)
cert.owner = kwargs['ownerEmail']
cert.description = "This is the ROOT certificate for the {0} certificate authority".format(kwargs.get('caName'))
cert.user = g.current_user
return database.update(authority)
cert.notifications = notification_service.create_default_expiration_notifications(
'DEFAULT_SECURITY',
current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL')
def mint(**kwargs):
"""
Creates the authority based on the plugin provided.
"""
issuer = kwargs["plugin"]["plugin_object"]
values = issuer.create_authority(kwargs)
# support older plugins
if len(values) == 3:
body, chain, roles = values
private_key = None
elif len(values) == 4:
body, private_key, chain, roles = values
roles = create_authority_roles(
roles,
kwargs["owner"],
kwargs["plugin"]["plugin_object"].title,
kwargs["creator"],
)
return body, private_key, chain, roles
# we create and attach any roles that the issuer gives us
def create_authority_roles(roles, owner, plugin_title, creator):
"""
Creates all of the necessary authority roles.
:param creator:
:param roles:
:return:
"""
role_objs = []
for r in issuer_roles:
role = role_service.create(
r['name'],
password=r['password'],
description="{0} auto generated role".format(kwargs.get('pluginName')),
username=r['username'])
for r in roles:
role = role_service.get_by_name(r["name"])
if not role:
role = role_service.create(
r["name"],
password=r["password"],
description="Auto generated role for {0}".format(plugin_title),
username=r["username"],
)
# the user creating the authority should be able to administer it
if role.username == 'admin':
g.current_user.roles.append(role)
if role.username == "admin":
creator.roles.append(role)
role_objs.append(role)
authority = Authority(
kwargs.get('caName'),
kwargs['ownerEmail'],
kwargs['pluginName'],
cert_body,
description=kwargs['caDescription'],
chain=intermediate,
roles=role_objs
)
# create an role for the owner and assign it
owner_role = role_service.get_by_name(owner)
if not owner_role:
owner_role = role_service.create(
owner, description="Auto generated role based on owner: {0}".format(owner)
)
database.update(cert)
role_objs.append(owner_role)
return role_objs
def create(**kwargs):
"""
Creates a new authority.
"""
body, private_key, chain, roles = mint(**kwargs)
kwargs["creator"].roles = list(set(list(kwargs["creator"].roles) + roles))
kwargs["body"] = body
kwargs["private_key"] = private_key
kwargs["chain"] = chain
if kwargs.get("roles"):
kwargs["roles"] += roles
else:
kwargs["roles"] = roles
cert = upload(**kwargs)
kwargs["authority_certificate"] = cert
if kwargs.get("plugin", {}).get("plugin_options", []):
kwargs["options"] = json.dumps(kwargs["plugin"]["plugin_options"])
authority = Authority(**kwargs)
authority = database.create(authority)
kwargs["creator"].authorities.append(authority)
g.current_user.authorities.append(authority)
metrics.send(
"authority_created", "counter", 1, metric_tags=dict(owner=authority.owner)
)
return authority
@ -115,7 +160,6 @@ def get(authority_id):
"""
Retrieves an authority given it's ID
:rtype : Authority
:param authority_id:
:return:
"""
@ -127,28 +171,22 @@ def get_by_name(authority_name):
Retrieves an authority given it's name.
:param authority_name:
:rtype : Authority
:return:
"""
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=None):
"""
Attempts to get the authority role for a given ca uses current_user
as a basis for accomplishing that.
:param ca_name:
"""
if g.current_user.is_admin:
authority = get_by_name(ca_name)
# TODO we should pick admin ca roles for admin
return authority.roles[0]
else:
for role in g.current_user.roles:
if role.authority:
if role.authority.name == ca_name:
return role
if creator:
if creator.is_admin:
return role_service.get_by_name("{0}_admin".format(ca_name))
return role_service.get_by_name("{0}_operator".format(ca_name))
def render(args):
@ -158,30 +196,33 @@ def render(args):
:return:
"""
query = database.session_query(Authority)
sort_by = args.pop('sort_by')
sort_dir = args.pop('sort_dir')
page = args.pop('page')
count = args.pop('count')
filt = args.pop('filter')
filt = args.pop("filter")
if filt:
terms = filt.split(';')
if 'active' in filt: # this is really weird but strcmp seems to not work here??
query = query.filter(Authority.active == terms[1])
terms = filt.split(";")
if "active" in filt:
query = query.filter(Authority.active == truthiness(terms[1]))
elif "cn" in filt:
term = "%{0}%".format(terms[1])
sub_query = (
database.session_query(Certificate.root_authority_id)
.filter(Certificate.cn.ilike(term))
.subquery()
)
query = query.filter(Authority.id.in_(sub_query))
else:
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
if not g.current_user.is_admin:
# we make sure that a user can only use an authority they either own are a member of - admins can see all
if not args["user"].is_admin:
authority_ids = []
for role in g.current_user.roles:
if role.authority:
authority_ids.append(role.authority.id)
for authority in args["user"].authorities:
authority_ids.append(authority.id)
for role in args["user"].roles:
for authority in role.authorities:
authority_ids.append(authority.id)
query = query.filter(Authority.id.in_(authority_ids))
query = database.find_all(query, Authority, args)
if sort_by and sort_dir:
query = database.sort(query, Authority, sort_by, sort_dir)
return database.paginate(query, page, count)
return database.sort_and_page(query, Authority, args)

View File

@ -1,48 +1,41 @@
"""
.. module: lemur.authorities.views
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import Blueprint, g
from flask.ext.restful import reqparse, fields, Api
from flask_restful import reqparse, Api
from lemur.authorities import service
from lemur.roles import service as role_service
from lemur.certificates import service as certificate_service
from lemur.common.utils import paginated_parser
from lemur.common.schema import validate_schema
from lemur.auth.service import AuthenticatedResource
from lemur.auth.permissions import AuthorityPermission
from lemur.common.utils import paginated_parser, marshal_items
from lemur.certificates import service as certificate_service
from lemur.authorities import service
from lemur.authorities.schemas import (
authority_input_schema,
authority_output_schema,
authorities_output_schema,
authority_update_schema,
)
FIELDS = {
'name': fields.String,
'owner': fields.String,
'description': fields.String,
'options': fields.Raw,
'pluginName': fields.String,
'body': fields.String,
'chain': fields.String,
'active': fields.Boolean,
'notBefore': fields.DateTime(dt_format='iso8601', attribute='not_before'),
'notAfter': fields.DateTime(dt_format='iso8601', attribute='not_after'),
'id': fields.Integer,
}
mod = Blueprint('authorities', __name__)
mod = Blueprint("authorities", __name__)
api = Api(mod)
class AuthoritiesList(AuthenticatedResource):
""" Defines the 'authorities' endpoint """
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(AuthoritiesList, self).__init__()
@marshal_items(FIELDS)
@validate_schema(None, authorities_output_schema)
def get(self):
"""
.. http:get:: /authorities
@ -66,28 +59,52 @@ class AuthoritiesList(AuthenticatedResource):
Content-Type: text/javascript
{
"items": [
{
"id": 1,
"name": "authority1",
"description": "this is authority1",
"pluginName": null,
"chain": "-----Begin ...",
"body": "-----Begin ...",
"active": true,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39"
"options": null
}
]
"items": [{
"name": "TestAuthority",
"roles": [{
"id": 123,
"name": "secure@example.com"
}, {
"id": 564,
"name": "TestAuthority_admin"
}, {
"id": 565,
"name": "TestAuthority_operator"
}],
"options": null,
"active": true,
"authorityCertificate": {
"body": "-----BEGIN CERTIFICATE-----IyMzU5MTVaMHk...",
"status": true,
"cn": "AcommonName",
"description": "This is the ROOT certificate for the TestAuthority certificate authority.",
"chain": "",
"notBefore": "2016-06-02T00:00:15+00:00",
"notAfter": "2023-06-02T23:59:15+00:00",
"owner": "secure@example.com",
"user": {
"username": "joe@example.com",
"active": true,
"email": "joe@example.com",
"id": 3
},
"active": true,
"bits": 2048,
"id": 2235,
"name": "TestAuthority"
},
"owner": "secure@example.com",
"id": 43,
"description": "This is the ROOT certificate for the TestAuthority certificate authority."
}],
"total": 1
}
:query sortBy: field to sort on
:query sortDir: acs or desc
:query page: int. default is 1
:query filter: key value pair. format is k=v;
:query limit: limit number. default is 10
:query sortDir: asc or desc
:query page: int default is 1
:query filter: key value pair. format is k;v
:query count: count number default is 10
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
@ -96,10 +113,11 @@ class AuthoritiesList(AuthenticatedResource):
"""
parser = paginated_parser.copy()
args = parser.parse_args()
args["user"] = g.current_user
return service.render(args)
@marshal_items(FIELDS)
def post(self):
@validate_schema(authority_input_schema, authority_output_schema)
def post(self, data=None):
"""
.. http:post:: /authorities
@ -113,31 +131,31 @@ class AuthoritiesList(AuthenticatedResource):
Host: example.com
Accept: application/json, text/javascript
{
"caDN": {
"country": "US",
"state": "CA",
"location": "A Location",
"organization": "ExampleInc",
"organizationalUnit": "Operations",
"commonName": "a common name"
},
"caType": "root",
"caSigningAlgo": "sha256WithRSA",
"caSensitivity": "medium",
{
"country": "US",
"state": "California",
"location": "Los Gatos",
"organization": "Netflix",
"organizationalUnit": "Operations",
"type": "root",
"signingAlgorithm": "sha256WithRSA",
"sensitivity": "medium",
"keyType": "RSA2048",
"pluginName": "cloudca",
"validityStart": "2015-06-11T07:00:00.000Z",
"validityEnd": "2015-06-13T07:00:00.000Z",
"caName": "DoctestCA",
"ownerEmail": "jimbob@example.com",
"caDescription": "Example CA",
"extensions": {
"subAltNames": {
"names": []
}
"plugin": {
"slug": "cloudca-issuer"
},
}
"name": "TimeTestAuthority5",
"owner": "secure@example.com",
"description": "test",
"commonName": "AcommonName",
"validityYears": "20",
"extensions": {
"subAltNames": {
"names": []
},
"custom": []
}
}
**Example response**:
@ -148,57 +166,68 @@ class AuthoritiesList(AuthenticatedResource):
Content-Type: text/javascript
{
"id": 1,
"name": "authority1",
"description": "this is authority1",
"pluginName": null,
"chain": "-----Begin ...",
"body": "-----Begin ...",
"name": "TestAuthority",
"roles": [{
"id": 123,
"name": "secure@example.com"
}, {
"id": 564,
"name": "TestAuthority_admin"
}, {
"id": 565,
"name": "TestAuthority_operator"
}],
"options": null,
"active": true,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39"
"options": null
"authorityCertificate": {
"body": "-----BEGIN CERTIFICATE-----IyMzU5MTVaMHk...",
"status": true,
"cn": "AcommonName",
"description": "This is the ROOT certificate for the TestAuthority certificate authority.",
"chain": "",
"notBefore": "2016-06-02T00:00:15+00:00",
"notAfter": "2023-06-02T23:59:15+00:00",
"owner": "secure@example.com",
"user": {
"username": "joe@example.com",
"active": true,
"email": "joe@example.com",
"id": 3
},
"active": true,
"bits": 2048,
"id": 2235,
"name": "TestAuthority"
},
"owner": "secure@example.com",
"id": 43,
"description": "This is the ROOT certificate for the TestAuthority certificate authority."
}
:arg caName: authority's name
:arg caDescription: a sensible description about what the CA with be used for
:arg ownerEmail: the team or person who 'owns' this authority
:arg name: authority's name
:arg description: a sensible description about what the CA with be used for
:arg owner: the team or person who 'owns' this authority
:arg validityStart: when this authority should start issuing certificates
:arg validityEnd: when this authority should stop issuing certificates
:arg validityYears: starting from `now` how many years into the future the authority should be valid
:arg extensions: certificate extensions
:arg pluginName: name of the plugin to create the authority
:arg caType: the type of authority (root/subca)
:arg caParent: the parent authority if this is to be a subca
:arg caSigningAlgo: algorithm used to sign the authority
:arg plugin: name of the plugin to create the authority
:arg type: the type of authority (root/subca)
:arg parent: the parent authority if this is to be a subca
:arg signingAlgorithm: algorithm used to sign the authority
:arg keyType: key type
:arg caSensitivity: the sensitivity of the root key, for CloudCA this determines if the root keys are stored
:arg sensitivity: the sensitivity of the root key, for CloudCA this determines if the root keys are stored
in an HSM
:arg caKeyName: name of the key to store in the HSM (CloudCA)
:arg caSerialNumber: serial number of the authority
:arg caFirstSerial: specifies the starting serial number for certificates issued off of this authority
:arg keyName: name of the key to store in the HSM (CloudCA)
:arg serialNumber: serial number of the authority
:arg firstSerial: specifies the starting serial number for certificates issued off of this authority
:reqheader Authorization: OAuth token to authenticate
:statuscode 403: unauthenticated
:statuscode 200: no error
"""
self.reqparse.add_argument('caName', type=str, location='json', required=True)
self.reqparse.add_argument('caDescription', type=str, location='json', required=False)
self.reqparse.add_argument('ownerEmail', type=str, location='json', required=True)
self.reqparse.add_argument('caDN', type=dict, location='json', required=False)
self.reqparse.add_argument('validityStart', type=str, location='json', required=False) # TODO validate
self.reqparse.add_argument('validityEnd', type=str, location='json', required=False) # TODO validate
self.reqparse.add_argument('extensions', type=dict, location='json', required=False)
self.reqparse.add_argument('pluginName', type=str, location='json', required=True)
self.reqparse.add_argument('caType', type=str, location='json', required=False)
self.reqparse.add_argument('caParent', type=str, location='json', required=False)
self.reqparse.add_argument('caSigningAlgo', type=str, location='json', required=False)
self.reqparse.add_argument('keyType', type=str, location='json', required=False)
self.reqparse.add_argument('caSensitivity', type=str, location='json', required=False)
self.reqparse.add_argument('caKeyName', type=str, location='json', required=False)
self.reqparse.add_argument('caSerialNumber', type=int, location='json', required=False)
self.reqparse.add_argument('caFirstSerial', type=int, location='json', required=False)
args = self.reqparse.parse_args()
return service.create(args)
data["creator"] = g.current_user
return service.create(**data)
class Authorities(AuthenticatedResource):
@ -206,7 +235,7 @@ class Authorities(AuthenticatedResource):
self.reqparse = reqparse.RequestParser()
super(Authorities, self).__init__()
@marshal_items(FIELDS)
@validate_schema(None, authority_output_schema)
def get(self, authority_id):
"""
.. http:get:: /authorities/1
@ -230,30 +259,40 @@ class Authorities(AuthenticatedResource):
Content-Type: text/javascript
{
"id": 1,
"name": "authority1",
"description": "this is authority1",
"pluginName": null,
"chain": "-----Begin ...",
"body": "-----Begin ...",
"roles": [{
"id": 123,
"name": "secure@example.com"
}, {
"id": 564,
"name": "TestAuthority_admin"
}, {
"id": 565,
"name": "TestAuthority_operator"
}],
"active": true,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39"
"options": null
"owner": "secure@example.com",
"id": 43,
"description": "This is the ROOT certificate for the TestAuthority certificate authority."
}
:arg description: a sensible description about what the CA with be used for
:arg owner: the team or person who 'owns' this authority
:arg active: set whether this authoritity is currently in use
:reqheader Authorization: OAuth token to authenticate
:statuscode 403: unauthenticated
:statuscode 200: no error
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
return service.get(authority_id)
@marshal_items(FIELDS)
def put(self, authority_id):
@validate_schema(authority_update_schema, authority_output_schema)
def put(self, authority_id, data=None):
"""
.. http:put:: /authorities/1
Update a authority
Update an authority
**Example request**:
@ -264,11 +303,42 @@ class Authorities(AuthenticatedResource):
Accept: application/json, text/javascript
{
"roles": [],
"active": false,
"owner": "bob@example.com",
"description": "this is authority1"
}
"name": "TestAuthority5",
"roles": [{
"id": 566,
"name": "TestAuthority5_admin"
}, {
"id": 567,
"name": "TestAuthority5_operator"
}, {
"id": 123,
"name": "secure@example.com"
}],
"active": true,
"authorityCertificate": {
"body": "-----BEGIN CERTIFICATE-----",
"status": null,
"cn": "AcommonName",
"description": "This is the ROOT certificate for the TestAuthority5 certificate authority.",
"chain": "",
"notBefore": "2016-06-03T00:00:51+00:00",
"notAfter": "2036-06-03T23:59:51+00:00",
"owner": "secure@example.com",
"user": {
"username": "joe@example.com",
"active": true,
"email": "joe@example.com",
"id": 3
},
"active": true,
"bits": 2048,
"id": 2280,
"name": "TestAuthority5"
},
"owner": "secure@example.com",
"id": 44,
"description": "This is the ROOT certificate for the TestAuthority5 certificate authority."
}
**Example response**:
@ -279,64 +349,74 @@ class Authorities(AuthenticatedResource):
Content-Type: text/javascript
{
"id": 1,
"name": "authority1",
"description": "this is authority1",
"pluginName": null,
"chain": "-----begin ...",
"body": "-----begin ...",
"active": false,
"notBefore": "2015-06-05t17:09:39",
"notAfter": "2015-06-10t17:09:39"
"options": null
"name": "TestAuthority",
"roles": [{
"id": 123,
"name": "secure@example.com"
}, {
"id": 564,
"name": "TestAuthority_admin"
}, {
"id": 565,
"name": "TestAuthority_operator"
}],
"options": null,
"active": true,
"authorityCertificate": {
"body": "-----BEGIN CERTIFICATE-----IyMzU5MTVaMHk...",
"status": true,
"cn": "AcommonName",
"description": "This is the ROOT certificate for the TestAuthority certificate authority.",
"chain": "",
"notBefore": "2016-06-02T00:00:15+00:00",
"notAfter": "2023-06-02T23:59:15+00:00",
"owner": "secure@example.com",
"user": {
"username": "joe@example.com",
"active": true,
"email": "joe@example.com",
"id": 3
},
"active": true,
"bits": 2048,
"id": 2235,
"name": "TestAuthority"
},
"owner": "secure@example.com",
"id": 43,
"description": "This is the ROOT certificate for the TestAuthority certificate authority."
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
self.reqparse.add_argument('roles', type=list, default=[], location='json')
self.reqparse.add_argument('active', type=str, location='json', required=True)
self.reqparse.add_argument('owner', type=str, location='json', required=True)
self.reqparse.add_argument('description', type=str, location='json', required=True)
args = self.reqparse.parse_args()
authority = service.get(authority_id)
role = role_service.get_by_name(authority.owner)
if not authority:
return dict(message="Not Found"), 404
# all the authority role members should be allowed
roles = [x.name for x in authority.roles]
# allow "owner" roles by team DL
roles.append(role)
permission = AuthorityPermission(authority_id, roles)
# we want to make sure that we cannot add roles that we are not members of
if not g.current_user.is_admin:
role_ids = set([r['id'] for r in args['roles']])
user_role_ids = set([r.id for r in g.current_user.roles])
if not role_ids.issubset(user_role_ids):
return dict(message="You are not allowed to associate a role which you are not a member of"), 400
if permission.can():
return service.update(
authority_id,
owner=args['owner'],
description=args['description'],
active=args['active'],
roles=args['roles']
owner=data["owner"],
description=data["description"],
active=data["active"],
roles=data["roles"],
)
return dict(message="You are not authorized to update this authority"), 403
return dict(message="You are not authorized to update this authority."), 403
class CertificateAuthority(AuthenticatedResource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificateAuthority, self).__init__()
@marshal_items(FIELDS)
@validate_schema(None, authority_output_schema)
def get(self, certificate_id):
"""
.. http:get:: /certificates/1/authority
@ -360,16 +440,42 @@ class CertificateAuthority(AuthenticatedResource):
Content-Type: text/javascript
{
"id": 1,
"name": "authority1",
"description": "this is authority1",
"pluginName": null,
"chain": "-----Begin ...",
"body": "-----Begin ...",
"name": "TestAuthority",
"roles": [{
"id": 123,
"name": "secure@example.com"
}, {
"id": 564,
"name": "TestAuthority_admin"
}, {
"id": 565,
"name": "TestAuthority_operator"
}],
"options": null,
"active": true,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39"
"options": null
"authorityCertificate": {
"body": "-----BEGIN CERTIFICATE-----IyMzU5MTVaMHk...",
"status": true,
"cn": "AcommonName",
"description": "This is the ROOT certificate for the TestAuthority certificate authority.",
"chain": "",
"notBefore": "2016-06-02T00:00:15+00:00",
"notAfter": "2023-06-02T23:59:15+00:00",
"owner": "secure@example.com",
"user": {
"username": "joe@example.com",
"active": true,
"email": "joe@example.com",
"id": 3
},
"active": true,
"bits": 2048,
"id": 2235,
"name": "TestAuthority"
},
"owner": "secure@example.com",
"id": 43,
"description": "This is the ROOT certificate for the TestAuthority certificate authority."
}
:reqheader Authorization: OAuth token to authenticate
@ -378,10 +484,48 @@ class CertificateAuthority(AuthenticatedResource):
"""
cert = certificate_service.get(certificate_id)
if not cert:
return dict(message="Certificate not found"), 404
return dict(message="Certificate not found."), 404
return cert.authority
api.add_resource(AuthoritiesList, '/authorities', endpoint='authorities')
api.add_resource(Authorities, '/authorities/<int:authority_id>', endpoint='authority')
api.add_resource(CertificateAuthority, '/certificates/<int:certificate_id>/authority', endpoint='certificateAuthority')
class AuthorityVisualizations(AuthenticatedResource):
def get(self, authority_id):
"""
{"name": "flare",
"children": [
{
"name": "analytics",
"children": [
{
"name": "cluster",
"children": [
{"name": "AgglomerativeCluster", "size": 3938},
{"name": "CommunityStructure", "size": 3812},
{"name": "HierarchicalCluster", "size": 6714},
{"name": "MergeEdge", "size": 743}
]
}
]
}
]}
"""
authority = service.get(authority_id)
return dict(
name=authority.name,
children=[{"name": c.name} for c in authority.certificates],
)
api.add_resource(AuthoritiesList, "/authorities", endpoint="authorities")
api.add_resource(Authorities, "/authorities/<int:authority_id>", endpoint="authority")
api.add_resource(
AuthorityVisualizations,
"/authorities/<int:authority_id>/visualize",
endpoint="authority_visualizations",
)
api.add_resource(
CertificateAuthority,
"/certificates/<int:certificate_id>/authority",
endpoint="certificateAuthority",
)

View File

View File

@ -0,0 +1,34 @@
"""
.. module: lemur.authorizations.models
:platform: unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Netflix Secops <secops@netflix.com>
"""
from sqlalchemy import Column, Integer, String
from sqlalchemy_utils import JSONType
from lemur.database import db
from lemur.plugins.base import plugins
class Authorization(db.Model):
__tablename__ = "pending_dns_authorizations"
id = Column(Integer, primary_key=True, autoincrement=True)
account_number = Column(String(128))
domains = Column(JSONType)
dns_provider_type = Column(String(128))
options = Column(JSONType)
@property
def plugin(self):
return plugins.get(self.plugin_name)
def __repr__(self):
return "Authorization(id={id})".format(id=self.id)
def __init__(self, account_number, domains, dns_provider_type, options=None):
self.account_number = account_number
self.domains = domains
self.dns_provider_type = dns_provider_type
self.options = options

View File

@ -0,0 +1,24 @@
"""
.. module: lemur.pending_certificates.service
Copyright (c) 2018 and onwards Netflix, Inc. All rights reserved.
.. moduleauthor:: Secops <secops@netflix.com>
"""
from lemur import database
from lemur.authorizations.models import Authorization
def get(authorization_id):
"""
Retrieve dns authorization by ID
"""
return database.get(Authorization, authorization_id)
def create(account_number, domains, dns_provider_type, options=None):
"""
Creates a new dns authorization.
"""
authorization = Authorization(account_number, domains, dns_provider_type, options)
return database.create(authorization)

779
lemur/certificates/cli.py Normal file
View File

@ -0,0 +1,779 @@
"""
.. module: lemur.certificate.cli
:platform: Unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import multiprocessing
import sys
from flask import current_app
from flask_principal import Identity, identity_changed
from flask_script import Manager
from sqlalchemy import or_
from tabulate import tabulate
from lemur import database
from lemur.authorities.models import Authority
from lemur.authorities.service import get as authorities_get_by_id
from lemur.certificates.models import Certificate
from lemur.certificates.schemas import CertificateOutputSchema
from lemur.certificates.service import (
reissue_certificate,
get_certificate_primitives,
get_all_pending_reissue,
get_by_name,
get_all_valid_certs,
get,
get_all_certs_attached_to_endpoint_without_autorotate,
)
from lemur.certificates.verify import verify_string
from lemur.constants import SUCCESS_METRIC_STATUS, FAILURE_METRIC_STATUS
from lemur.deployment import service as deployment_service
from lemur.domains.models import Domain
from lemur.endpoints import service as endpoint_service
from lemur.extensions import sentry, metrics
from lemur.notifications.messaging import send_rotation_notification
from lemur.plugins.base import plugins
manager = Manager(usage="Handles all certificate related tasks.")
def print_certificate_details(details):
"""
Print the certificate details with formatting.
:param details:
:return:
"""
details, errors = CertificateOutputSchema().dump(details)
print("[+] Re-issuing certificate with the following details: ")
print(
"\t[+] Common Name: {common_name}\n"
"\t[+] Subject Alternate Names: {sans}\n"
"\t[+] Authority: {authority_name}\n"
"\t[+] Validity Start: {validity_start}\n"
"\t[+] Validity End: {validity_end}\n".format(
common_name=details["commonName"],
sans=",".join(
x["value"] for x in details["extensions"]["subAltNames"]["names"]
)
or None,
authority_name=details["authority"]["name"],
validity_start=details["validityStart"],
validity_end=details["validityEnd"],
)
)
def validate_certificate(certificate_name):
"""
Ensuring that the specified certificate exists.
:param certificate_name:
:return:
"""
if certificate_name:
cert = get_by_name(certificate_name)
if not cert:
print("[-] No certificate found with name: {0}".format(certificate_name))
sys.exit(1)
return cert
def validate_endpoint(endpoint_name):
"""
Ensuring that the specified endpoint exists.
:param endpoint_name:
:return:
"""
if endpoint_name:
endpoint = endpoint_service.get_by_name(endpoint_name)
if not endpoint:
print("[-] No endpoint found with name: {0}".format(endpoint_name))
sys.exit(1)
return endpoint
def request_rotation(endpoint, certificate, message, commit):
"""
Rotates a certificate and handles any exceptions during
execution.
:param endpoint:
:param certificate:
:param message:
:param commit:
:return:
"""
status = FAILURE_METRIC_STATUS
if commit:
try:
deployment_service.rotate_certificate(endpoint, certificate)
if message:
send_rotation_notification(certificate)
status = SUCCESS_METRIC_STATUS
except Exception as e:
print(
"[!] Failed to rotate endpoint {0} to certificate {1} reason: {2}".format(
endpoint.name, certificate.name, e
)
)
metrics.send("endpoint_rotation", "counter", 1, metric_tags={"status": status})
def request_reissue(certificate, commit):
"""
Reissuing certificate and handles any exceptions.
:param certificate:
:param commit:
:return:
"""
status = FAILURE_METRIC_STATUS
try:
print("[+] {0} is eligible for re-issuance".format(certificate.name))
# set the lemur identity for all cli commands
identity_changed.send(current_app._get_current_object(), identity=Identity(1))
details = get_certificate_primitives(certificate)
print_certificate_details(details)
if commit:
new_cert = reissue_certificate(certificate, replace=True)
print("[+] New certificate named: {0}".format(new_cert.name))
status = SUCCESS_METRIC_STATUS
except Exception as e:
sentry.captureException(extra={"certificate_name": str(certificate.name)})
current_app.logger.exception(
f"Error reissuing certificate: {certificate.name}", exc_info=True
)
print(f"[!] Failed to reissue certificate: {certificate.name}. Reason: {e}")
metrics.send(
"certificate_reissue",
"counter",
1,
metric_tags={"status": status, "certificate": certificate.name},
)
@manager.option(
"-e",
"--endpoint",
dest="endpoint_name",
help="Name of the endpoint you wish to rotate.",
)
@manager.option(
"-n",
"--new-certificate",
dest="new_certificate_name",
help="Name of the certificate you wish to rotate to.",
)
@manager.option(
"-o",
"--old-certificate",
dest="old_certificate_name",
help="Name of the certificate you wish to rotate.",
)
@manager.option(
"-a",
"--notify",
dest="message",
action="store_true",
help="Send a rotation notification to the certificates owner.",
)
@manager.option(
"-c",
"--commit",
dest="commit",
action="store_true",
default=False,
help="Persist changes.",
)
def rotate(endpoint_name, new_certificate_name, old_certificate_name, message, commit):
"""
Rotates an endpoint and reissues it if it has not already been replaced. If it has
been replaced, will use the replacement certificate for the rotation.
"""
if commit:
print("[!] Running in COMMIT mode.")
print("[+] Starting endpoint rotation.")
status = FAILURE_METRIC_STATUS
log_data = {
"function": f"{__name__}.{sys._getframe().f_code.co_name}",
}
try:
old_cert = validate_certificate(old_certificate_name)
new_cert = validate_certificate(new_certificate_name)
endpoint = validate_endpoint(endpoint_name)
if endpoint and new_cert:
print(
f"[+] Rotating endpoint: {endpoint.name} to certificate {new_cert.name}"
)
log_data["message"] = "Rotating endpoint"
log_data["endpoint"] = endpoint.dnsname
log_data["certificate"] = new_cert.name
request_rotation(endpoint, new_cert, message, commit)
current_app.logger.info(log_data)
elif old_cert and new_cert:
print(f"[+] Rotating all endpoints from {old_cert.name} to {new_cert.name}")
log_data["message"] = "Rotating all endpoints"
log_data["certificate"] = new_cert.name
log_data["certificate_old"] = old_cert.name
log_data["message"] = "Rotating endpoint from old to new cert"
for endpoint in old_cert.endpoints:
print(f"[+] Rotating {endpoint.name}")
log_data["endpoint"] = endpoint.dnsname
request_rotation(endpoint, new_cert, message, commit)
current_app.logger.info(log_data)
else:
print("[+] Rotating all endpoints that have new certificates available")
log_data["message"] = "Rotating all endpoints that have new certificates available"
for endpoint in endpoint_service.get_all_pending_rotation():
log_data["endpoint"] = endpoint.dnsname
if len(endpoint.certificate.replaced) == 1:
print(
f"[+] Rotating {endpoint.name} to {endpoint.certificate.replaced[0].name}"
)
log_data["certificate"] = endpoint.certificate.replaced[0].name
request_rotation(
endpoint, endpoint.certificate.replaced[0], message, commit
)
current_app.logger.info(log_data)
else:
log_data["message"] = "Failed to rotate endpoint due to Multiple replacement certificates found"
print(log_data)
metrics.send(
"endpoint_rotation",
"counter",
1,
metric_tags={
"status": FAILURE_METRIC_STATUS,
"old_certificate_name": str(old_cert),
"new_certificate_name": str(
endpoint.certificate.replaced[0].name
),
"endpoint_name": str(endpoint.name),
"message": str(message),
},
)
print(
f"[!] Failed to rotate endpoint {endpoint.name} reason: "
"Multiple replacement certificates found."
)
status = SUCCESS_METRIC_STATUS
print("[+] Done!")
except Exception as e:
sentry.captureException(
extra={
"old_certificate_name": str(old_certificate_name),
"new_certificate_name": str(new_certificate_name),
"endpoint_name": str(endpoint_name),
"message": str(message),
}
)
metrics.send(
"endpoint_rotation_job",
"counter",
1,
metric_tags={
"status": status,
"old_certificate_name": str(old_certificate_name),
"new_certificate_name": str(new_certificate_name),
"endpoint_name": str(endpoint_name),
"message": str(message),
"endpoint": str(globals().get("endpoint")),
},
)
def request_rotation_region(endpoint, new_cert, message, commit, log_data, region):
if region in endpoint.dnsname:
log_data["message"] = "Rotating endpoint in region"
request_rotation(endpoint, new_cert, message, commit)
else:
log_data["message"] = "Skipping rotation, region mismatch"
print(log_data)
current_app.logger.info(log_data)
@manager.option(
"-e",
"--endpoint",
dest="endpoint_name",
help="Name of the endpoint you wish to rotate.",
)
@manager.option(
"-n",
"--new-certificate",
dest="new_certificate_name",
help="Name of the certificate you wish to rotate to.",
)
@manager.option(
"-o",
"--old-certificate",
dest="old_certificate_name",
help="Name of the certificate you wish to rotate.",
)
@manager.option(
"-a",
"--notify",
dest="message",
action="store_true",
help="Send a rotation notification to the certificates owner.",
)
@manager.option(
"-c",
"--commit",
dest="commit",
action="store_true",
default=False,
help="Persist changes.",
)
@manager.option(
"-r",
"--region",
dest="region",
required=True,
help="Region in which to rotate the endpoint.",
)
def rotate_region(endpoint_name, new_certificate_name, old_certificate_name, message, commit, region):
"""
Rotates an endpoint in a defined region it if it has not already been replaced. If it has
been replaced, will use the replacement certificate for the rotation.
:param old_certificate_name: Name of the certificate you wish to rotate.
:param new_certificate_name: Name of the certificate you wish to rotate to.
:param endpoint_name: Name of the endpoint you wish to rotate.
:param message: Send a rotation notification to the certificates owner.
:param commit: Persist changes.
:param region: Region in which to rotate the endpoint.
"""
if commit:
print("[!] Running in COMMIT mode.")
print("[+] Starting endpoint rotation.")
status = FAILURE_METRIC_STATUS
log_data = {
"function": f"{__name__}.{sys._getframe().f_code.co_name}",
"region": region,
}
try:
old_cert = validate_certificate(old_certificate_name)
new_cert = validate_certificate(new_certificate_name)
endpoint = validate_endpoint(endpoint_name)
if endpoint and new_cert:
log_data["endpoint"] = endpoint.dnsname
log_data["certificate"] = new_cert.name
request_rotation_region(endpoint, new_cert, message, commit, log_data, region)
elif old_cert and new_cert:
log_data["certificate"] = new_cert.name
log_data["certificate_old"] = old_cert.name
log_data["message"] = "Rotating endpoint from old to new cert"
print(log_data)
current_app.logger.info(log_data)
for endpoint in old_cert.endpoints:
log_data["endpoint"] = endpoint.dnsname
request_rotation_region(endpoint, new_cert, message, commit, log_data, region)
else:
log_data["message"] = "Rotating all endpoints that have new certificates available"
print(log_data)
current_app.logger.info(log_data)
all_pending_rotation_endpoints = endpoint_service.get_all_pending_rotation()
for endpoint in all_pending_rotation_endpoints:
log_data["endpoint"] = endpoint.dnsname
if region not in endpoint.dnsname:
log_data["message"] = "Skipping rotation, region mismatch"
print(log_data)
current_app.logger.info(log_data)
metrics.send(
"endpoint_rotation_region_skipped",
"counter",
1,
metric_tags={
"region": region,
"old_certificate_name": str(old_cert),
"new_certificate_name": str(endpoint.certificate.replaced[0].name),
"endpoint_name": str(endpoint.dnsname),
},
)
if len(endpoint.certificate.replaced) == 1:
log_data["certificate"] = endpoint.certificate.replaced[0].name
log_data["message"] = "Rotating all endpoints in region"
print(log_data)
current_app.logger.info(log_data)
request_rotation(endpoint, endpoint.certificate.replaced[0], message, commit)
status = SUCCESS_METRIC_STATUS
else:
status = FAILURE_METRIC_STATUS
log_data["message"] = "Failed to rotate endpoint due to Multiple replacement certificates found"
print(log_data)
current_app.logger.info(log_data)
metrics.send(
"endpoint_rotation_region",
"counter",
1,
metric_tags={
"status": FAILURE_METRIC_STATUS,
"old_certificate_name": str(old_cert),
"new_certificate_name": str(endpoint.certificate.replaced[0].name),
"endpoint_name": str(endpoint.dnsname),
"message": str(message),
"region": str(region),
},
)
status = SUCCESS_METRIC_STATUS
print("[+] Done!")
except Exception as e:
sentry.captureException(
extra={
"old_certificate_name": str(old_certificate_name),
"new_certificate_name": str(new_certificate_name),
"endpoint": str(endpoint_name),
"message": str(message),
"region": str(region),
}
)
metrics.send(
"endpoint_rotation_region_job",
"counter",
1,
metric_tags={
"status": status,
"old_certificate_name": str(old_certificate_name),
"new_certificate_name": str(new_certificate_name),
"endpoint_name": str(endpoint_name),
"message": str(message),
"endpoint": str(globals().get("endpoint")),
"region": str(region),
},
)
@manager.option(
"-o",
"--old-certificate",
dest="old_certificate_name",
help="Name of the certificate you wish to reissue.",
)
@manager.option(
"-c",
"--commit",
dest="commit",
action="store_true",
default=False,
help="Persist changes.",
)
def reissue(old_certificate_name, commit):
"""
Reissues certificate with the same parameters as it was originally issued with.
If not time period is provided, reissues certificate as valid from today to
today + length of original.
"""
if commit:
print("[!] Running in COMMIT mode.")
print("[+] Starting certificate re-issuance.")
status = FAILURE_METRIC_STATUS
try:
old_cert = validate_certificate(old_certificate_name)
if not old_cert:
for certificate in get_all_pending_reissue():
request_reissue(certificate, commit)
else:
request_reissue(old_cert, commit)
status = SUCCESS_METRIC_STATUS
print("[+] Done!")
except Exception as e:
sentry.captureException()
current_app.logger.exception("Error reissuing certificate.", exc_info=True)
print("[!] Failed to reissue certificates. Reason: {}".format(e))
metrics.send(
"certificate_reissue_job", "counter", 1, metric_tags={"status": status}
)
@manager.option(
"-f",
"--fqdns",
dest="fqdns",
help="FQDNs to query. Multiple fqdns specified via comma.",
)
@manager.option("-i", "--issuer", dest="issuer", help="Issuer to query for.")
@manager.option("-o", "--owner", dest="owner", help="Owner to query for.")
@manager.option(
"-e",
"--expired",
dest="expired",
type=bool,
default=False,
help="Include expired certificates.",
)
def query(fqdns, issuer, owner, expired):
"""Prints certificates that match the query params."""
table = []
q = database.session_query(Certificate)
if issuer:
sub_query = (
database.session_query(Authority.id)
.filter(Authority.name.ilike("%{0}%".format(issuer)))
.subquery()
)
q = q.filter(
or_(
Certificate.issuer.ilike("%{0}%".format(issuer)),
Certificate.authority_id.in_(sub_query),
)
)
if owner:
q = q.filter(Certificate.owner.ilike("%{0}%".format(owner)))
if not expired:
q = q.filter(Certificate.expired == False) # noqa
if fqdns:
for f in fqdns.split(","):
q = q.filter(
or_(
Certificate.cn.ilike("%{0}%".format(f)),
Certificate.domains.any(Domain.name.ilike("%{0}%".format(f))),
)
)
for c in q.all():
table.append([c.id, c.name, c.owner, c.issuer])
print(tabulate(table, headers=["Id", "Name", "Owner", "Issuer"], tablefmt="csv"))
def worker(data, commit, reason):
parts = [x for x in data.split(" ") if x]
try:
cert = get(int(parts[0].strip()))
plugin = plugins.get(cert.authority.plugin_name)
print("[+] Revoking certificate. Id: {0} Name: {1}".format(cert.id, cert.name))
if commit:
plugin.revoke_certificate(cert, reason)
metrics.send(
"certificate_revoke",
"counter",
1,
metric_tags={"status": SUCCESS_METRIC_STATUS},
)
except Exception as e:
sentry.captureException()
metrics.send(
"certificate_revoke",
"counter",
1,
metric_tags={"status": FAILURE_METRIC_STATUS},
)
print("[!] Failed to revoke certificates. Reason: {}".format(e))
@manager.command
def clear_pending():
"""
Function clears all pending certificates.
:return:
"""
v = plugins.get("verisign-issuer")
v.clear_pending_certificates()
@manager.option(
"-p", "--path", dest="path", help="Absolute file path to a Lemur query csv."
)
@manager.option("-r", "--reason", dest="reason", help="Reason to revoke certificate.")
@manager.option(
"-c",
"--commit",
dest="commit",
action="store_true",
default=False,
help="Persist changes.",
)
def revoke(path, reason, commit):
"""
Revokes given certificate.
"""
if commit:
print("[!] Running in COMMIT mode.")
print("[+] Starting certificate revocation.")
with open(path, "r") as f:
args = [[x, commit, reason] for x in f.readlines()[2:]]
with multiprocessing.Pool(processes=3) as pool:
pool.starmap(worker, args)
@manager.command
def check_revoked():
"""
Function attempts to update Lemur's internal cache with revoked
certificates. This is called periodically by Lemur. It checks both
CRLs and OCSP to see if a certificate is revoked. If Lemur is unable
encounters an issue with verification it marks the certificate status
as `unknown`.
"""
log_data = {
"function": f"{__name__}.{sys._getframe().f_code.co_name}",
"message": "Checking for revoked Certificates"
}
certs = get_all_valid_certs(current_app.config.get("SUPPORTED_REVOCATION_AUTHORITY_PLUGINS", []))
for cert in certs:
try:
if cert.chain:
status = verify_string(cert.body, cert.chain)
else:
status = verify_string(cert.body, "")
cert.status = "valid" if status else "revoked"
if cert.status == "revoked":
log_data["valid"] = cert.status
log_data["certificate_name"] = cert.name
log_data["certificate_id"] = cert.id
metrics.send(
"certificate_revoked",
"counter",
1,
metric_tags={"status": log_data["valid"],
"certificate_name": log_data["certificate_name"],
"certificate_id": log_data["certificate_id"]},
)
current_app.logger.info(log_data)
except Exception as e:
sentry.captureException()
current_app.logger.exception(e)
cert.status = "unknown"
database.update(cert)
@manager.command
def automatically_enable_autorotate():
"""
This function automatically enables auto-rotation for unexpired certificates that are
attached to an endpoint but do not have autorotate enabled.
WARNING: This will overwrite the Auto-rotate toggle!
"""
log_data = {
"function": f"{__name__}.{sys._getframe().f_code.co_name}",
"message": "Enabling auto-rotate for certificate"
}
permitted_authorities = current_app.config.get("ENABLE_AUTO_ROTATE_AUTHORITY", [])
eligible_certs = get_all_certs_attached_to_endpoint_without_autorotate()
for cert in eligible_certs:
if cert.authority_id not in permitted_authorities:
continue
log_data["certificate"] = cert.name
log_data["certificate_id"] = cert.id
log_data["authority_id"] = cert.authority_id
log_data["authority_name"] = authorities_get_by_id(cert.authority_id).name
if cert.destinations:
log_data["destination_names"] = ', '.join([d.label for d in cert.destinations])
else:
log_data["destination_names"] = "NONE"
current_app.logger.info(log_data)
metrics.send("automatically_enable_autorotate",
"counter", 1,
metric_tags={"certificate": log_data["certificate"],
"certificate_id": log_data["certificate_id"],
"authority_id": log_data["authority_id"],
"authority_name": log_data["authority_name"],
"destination_names": log_data["destination_names"]
})
cert.rotation = True
database.update(cert)
@manager.command
def deactivate_entrust_certificates():
"""
Attempt to deactivate test certificates issued by Entrust
"""
log_data = {
"function": f"{__name__}.{sys._getframe().f_code.co_name}",
"message": "Deactivating Entrust certificates"
}
certificates = get_all_valid_certs(['entrust-issuer'])
entrust_plugin = plugins.get('entrust-issuer')
for cert in certificates:
try:
response = entrust_plugin.deactivate_certificate(cert)
if response == 200:
cert.status = "revoked"
else:
cert.status = "unknown"
log_data["valid"] = cert.status
log_data["certificate_name"] = cert.name
log_data["certificate_id"] = cert.id
metrics.send(
"certificate_deactivate",
"counter",
1,
metric_tags={"status": log_data["valid"],
"certificate_name": log_data["certificate_name"],
"certificate_id": log_data["certificate_id"]},
)
current_app.logger.info(log_data)
database.update(cert)
except Exception as e:
current_app.logger.info(log_data)
sentry.captureException()
current_app.logger.exception(e)

View File

@ -1,88 +0,0 @@
"""
.. module: lemur.certificates.exceptions
:synopsis: Defines all monterey specific exceptions
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import current_app
from lemur.exceptions import LemurException
class UnknownAuthority(LemurException):
def __init__(self, authority):
self.code = 404
self.authority = authority
self.data = {"message": "The authority specified '{}' is not a valid authority".format(self.authority)}
current_app.logger.warning(self)
def __str__(self):
return repr(self.data['message'])
class InsufficientDomains(LemurException):
def __init__(self):
self.code = 400
self.data = {"message": "Need at least one domain specified in order create a certificate"}
current_app.logger.warning(self)
def __str__(self):
return repr(self.data['message'])
class InvalidCertificate(LemurException):
def __init__(self):
self.code = 400
self.data = {"message": "Need at least one domain specified in order create a certificate"}
current_app.logger.warning(self)
def __str__(self):
return repr(self.data['message'])
class UnableToCreateCSR(LemurException):
def __init__(self):
self.code = 500
self.data = {"message": "Unable to generate CSR"}
current_app.logger.error(self)
def __str__(self):
return repr(self.data['message'])
class UnableToCreatePrivateKey(LemurException):
def __init__(self):
self.code = 500
self.data = {"message": "Unable to generate Private Key"}
current_app.logger.error(self)
def __str__(self):
return repr(self.data['message'])
class MissingFiles(LemurException):
def __init__(self, path):
self.code = 500
self.path = path
self.data = {"path": self.path, "message": "Expecting missing files"}
current_app.logger.error(self)
def __str__(self):
return repr(self.data['message'])
class NoPersistanceFound(LemurException):
def __init__(self):
self.code = 500
self.data = {"code": 500, "message": "No peristence method found, Lemur cannot persist sensitive information"}
current_app.logger.error(self)
def __str__(self):
return repr(self.data['message'])

View File

@ -0,0 +1,47 @@
"""
Debugging hooks for dumping imported or generated CSR and certificate details to stdout via OpenSSL.
.. module: lemur.certificates.hooks
:platform: Unix
:copyright: (c) 2018 by Marti Raudsepp, see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Marti Raudsepp <marti@juffo.org>
"""
import subprocess
from flask import current_app
from lemur.certificates.service import (
csr_created,
csr_imported,
certificate_issued,
certificate_imported,
)
def csr_dump_handler(sender, csr, **kwargs):
try:
subprocess.run(
["openssl", "req", "-text", "-noout", "-reqopt", "no_sigdump,no_pubkey"],
input=csr.encode("utf8"),
)
except Exception as err:
current_app.logger.warning("Error inspecting CSR: %s", err)
def cert_dump_handler(sender, certificate, **kwargs):
try:
subprocess.run(
["openssl", "x509", "-text", "-noout", "-certopt", "no_sigdump,no_pubkey"],
input=certificate.body.encode("utf8"),
)
except Exception as err:
current_app.logger.warning("Error inspecting certificate: %s", err)
def activate_debug_dump():
csr_created.connect(csr_dump_handler)
csr_imported.connect(csr_dump_handler)
certificate_issued.connect(cert_dump_handler)
certificate_imported.connect(cert_dump_handler)

View File

@ -1,290 +1,493 @@
"""
.. module: lemur.certificates.models
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import datetime
from flask import current_app
from datetime import timedelta
import arrow
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from flask import current_app
from idna.core import InvalidCodepoint
from sqlalchemy import (
event,
Integer,
ForeignKey,
String,
DefaultClause,
func,
Column,
Text,
Boolean,
Index,
)
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from sqlalchemy import event, Integer, ForeignKey, String, DateTime, PassiveDefault, func, Column, Text, Boolean
from sqlalchemy.sql.expression import case, extract
from sqlalchemy_utils.types.arrow import ArrowType
from werkzeug.utils import cached_property
from lemur.utils import Vault
from lemur.common import defaults, utils, validators
from lemur.constants import SUCCESS_METRIC_STATUS, FAILURE_METRIC_STATUS
from lemur.database import db
from lemur.plugins.base import plugins
from lemur.domains.models import Domain
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE
from lemur.models import certificate_associations, certificate_source_associations, \
certificate_destination_associations, certificate_notification_associations
from lemur.extensions import metrics
from lemur.extensions import sentry
from lemur.models import (
certificate_associations,
certificate_source_associations,
certificate_destination_associations,
certificate_notification_associations,
certificate_replacement_associations,
roles_certificates,
pending_cert_replacement_associations,
)
from lemur.plugins.base import plugins
from lemur.policies.models import RotationPolicy
from lemur.utils import Vault
def create_name(issuer, not_before, not_after, subject, san):
"""
Create a name for our certificate. A naming standard
is based on a series of templates. The name includes
useful information such as Common Name, Validation dates,
and Issuer.
def get_sequence(name):
if "-" not in name:
return name, None
:rtype : str
:return:
"""
if san:
t = SAN_NAMING_TEMPLATE
else:
t = DEFAULT_NAMING_TEMPLATE
parts = name.split("-")
temp = t.format(
subject=subject,
issuer=issuer,
not_before=not_before.strftime('%Y%m%d'),
not_after=not_after.strftime('%Y%m%d')
)
# NOTE we may want to give more control over naming
# aws doesn't allow special chars except '-'
disallowed_chars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())
disallowed_chars = disallowed_chars.replace("-", "")
disallowed_chars = disallowed_chars.replace(".", "")
temp = temp.replace('*', "WILDCARD")
for c in disallowed_chars:
temp = temp.replace(c, "")
# white space is silly too
return temp.replace(" ", "-")
def get_signing_algorithm(cert):
return cert.signature_hash_algorithm.name
def get_cn(cert):
"""
Attempts to get a sane common name from a given certificate.
:param cert:
:return: Common name or None
"""
return cert.subject.get_attributes_for_oid(
x509.OID_COMMON_NAME
)[0].value.strip()
def get_domains(cert):
"""
Attempts to get an domains listed in a certificate.
If 'subjectAltName' extension is not available we simply
return the common name.
:param cert:
:return: List of domains
"""
domains = []
# see if we have an int at the end of our name
try:
ext = cert.extensions.get_extension_for_oid(x509.OID_SUBJECT_ALTERNATIVE_NAME)
entries = ext.value.get_values_for_type(x509.DNSName)
for entry in entries:
domains.append(entry)
except Exception as e:
current_app.logger.warning("Failed to get SubjectAltName: {0}".format(e))
seq = int(parts[-1])
except ValueError:
return name, None
return domains
# we might have a date at the end of our name
if len(parts[-1]) == 8:
return name, None
root = "-".join(parts[:-1])
return root, seq
def get_serial(cert):
"""
Fetch the serial number from the certificate.
def get_or_increase_name(name, serial):
certificates = Certificate.query.filter(Certificate.name == name).all()
:param cert:
:return: serial number
"""
return cert.serial
if not certificates:
return name
serial_name = "{0}-{1}".format(name, hex(int(serial))[2:].upper())
certificates = Certificate.query.filter(Certificate.name == serial_name).all()
def is_san(cert):
"""
Determines if a given certificate is a SAN certificate.
SAN certificates are simply certificates that cover multiple domains.
if not certificates:
return serial_name
:param cert:
:return: Bool
"""
if len(get_domains(cert)) > 1:
return True
certificates = Certificate.query.filter(
Certificate.name.ilike("{0}%".format(serial_name))
).all()
ends = [0]
root, end = get_sequence(serial_name)
for cert in certificates:
root, end = get_sequence(cert.name)
if end:
ends.append(end)
def is_wildcard(cert):
"""
Determines if certificate is a wildcard certificate.
:param cert:
:return: Bool
"""
domains = get_domains(cert)
if len(domains) == 1 and domains[0][0:1] == "*":
return True
if cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value[0:1] == "*":
return True
def get_bitstrength(cert):
"""
Calculates a certificates public key bit length.
:param cert:
:return: Integer
"""
return cert.public_key().key_size
def get_issuer(cert):
"""
Gets a sane issuer from a given certificate.
:param cert:
:return: Issuer
"""
delchars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())
try:
issuer = str(cert.issuer.get_attributes_for_oid(x509.OID_ORGANIZATION_NAME)[0].value)
for c in delchars:
issuer = issuer.replace(c, "")
return issuer
except Exception as e:
current_app.logger.error("Unable to get issuer! {0}".format(e))
def get_not_before(cert):
"""
Gets the naive datetime of the certificates 'not_before' field.
This field denotes the first date in time which the given certificate
is valid.
:param cert:
:return: Datetime
"""
return cert.not_valid_before
def get_not_after(cert):
"""
Gets the naive datetime of the certificates 'not_after' field.
This field denotes the last date in time which the given certificate
is valid.
:param cert:
:return: Datetime
"""
return cert.not_valid_after
def get_name_from_arn(arn):
"""
Extract the certificate name from an arn.
:param arn: IAM SSL arn
:return: name of the certificate as uploaded to AWS
"""
return arn.split("/", 1)[1]
def get_account_number(arn):
"""
Extract the account number from an arn.
:param arn: IAM SSL arn
:return: account number associated with ARN
"""
return arn.split(":")[4]
return "{0}-{1}".format(root, max(ends) + 1)
class Certificate(db.Model):
__tablename__ = 'certificates'
__tablename__ = "certificates"
__table_args__ = (
Index(
"ix_certificates_cn",
"cn",
postgresql_ops={"cn": "gin_trgm_ops"},
postgresql_using="gin",
),
Index(
"ix_certificates_name",
"name",
postgresql_ops={"name": "gin_trgm_ops"},
postgresql_using="gin",
),
)
id = Column(Integer, primary_key=True)
owner = Column(String(128))
body = Column(Text())
private_key = Column(Vault)
status = Column(String(128))
deleted = Column(Boolean, index=True)
name = Column(String(128))
ix = Index(
"ix_certificates_id_desc", id.desc(), postgresql_using="btree", unique=True
)
external_id = Column(String(128))
owner = Column(String(128), nullable=False)
name = Column(String(256), unique=True)
description = Column(String(1024))
notify = Column(Boolean, default=True)
body = Column(Text(), nullable=False)
chain = Column(Text())
bits = Column(Integer())
csr = Column(Text())
private_key = Column(Vault)
issuer = Column(String(128))
serial = Column(String(128))
cn = Column(String(128))
description = Column(String(1024))
active = Column(Boolean, default=True)
san = Column(String(1024))
not_before = Column(DateTime)
not_after = Column(DateTime)
date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
deleted = Column(Boolean, index=True, default=False)
dns_provider_id = Column(
Integer(), ForeignKey("dns_providers.id", ondelete="CASCADE"), nullable=True
)
not_before = Column(ArrowType)
not_after = Column(ArrowType)
not_after_ix = Index("ix_certificates_not_after", not_after.desc())
date_created = Column(ArrowType, DefaultClause(func.now()), nullable=False)
signing_algorithm = Column(String(128))
user_id = Column(Integer, ForeignKey('users.id'))
authority_id = Column(Integer, ForeignKey('authorities.id'))
notifications = relationship("Notification", secondary=certificate_notification_associations, backref='certificate')
destinations = relationship("Destination", secondary=certificate_destination_associations, backref='certificate')
sources = relationship("Source", secondary=certificate_source_associations, backref='certificate')
domains = relationship("Domain", secondary=certificate_associations, backref="certificate")
status = Column(String(128))
bits = Column(Integer())
san = Column(String(1024)) # TODO this should be migrated to boolean
def __init__(self, body, private_key=None, chain=None):
self.body = body
# We encrypt the private_key on creation
self.private_key = private_key
self.chain = chain
cert = x509.load_pem_x509_certificate(str(self.body), default_backend())
self.signing_algorithm = get_signing_algorithm(cert)
self.bits = get_bitstrength(cert)
self.issuer = get_issuer(cert)
self.serial = get_serial(cert)
self.cn = get_cn(cert)
self.san = is_san(cert)
self.not_before = get_not_before(cert)
self.not_after = get_not_after(cert)
self.name = create_name(self.issuer, self.not_before, self.not_after, self.cn, self.san)
rotation = Column(Boolean, default=False)
user_id = Column(Integer, ForeignKey("users.id"))
authority_id = Column(Integer, ForeignKey("authorities.id", ondelete="CASCADE"))
root_authority_id = Column(
Integer, ForeignKey("authorities.id", ondelete="CASCADE")
)
rotation_policy_id = Column(Integer, ForeignKey("rotation_policies.id"))
key_type = Column(String(128))
for domain in get_domains(cert):
notifications = relationship(
"Notification",
secondary=certificate_notification_associations,
backref="certificate",
)
destinations = relationship(
"Destination",
secondary=certificate_destination_associations,
backref="certificate",
)
sources = relationship(
"Source", secondary=certificate_source_associations, backref="certificate"
)
domains = relationship(
"Domain", secondary=certificate_associations, backref="certificate"
)
roles = relationship("Role", secondary=roles_certificates, backref="certificate")
replaces = relationship(
"Certificate",
secondary=certificate_replacement_associations,
primaryjoin=id == certificate_replacement_associations.c.certificate_id, # noqa
secondaryjoin=id
== certificate_replacement_associations.c.replaced_certificate_id, # noqa
backref="replaced",
)
replaced_by_pending = relationship(
"PendingCertificate",
secondary=pending_cert_replacement_associations,
backref="pending_replace",
)
logs = relationship("Log", backref="certificate")
endpoints = relationship("Endpoint", backref="certificate")
rotation_policy = relationship("RotationPolicy")
sensitive_fields = ("private_key",)
def __init__(self, **kwargs):
self.body = kwargs["body"].strip()
cert = self.parsed_cert
self.issuer = defaults.issuer(cert)
self.cn = defaults.common_name(cert)
self.san = defaults.san(cert)
self.not_before = defaults.not_before(cert)
self.not_after = defaults.not_after(cert)
self.serial = defaults.serial(cert)
# when destinations are appended they require a valid name.
if kwargs.get("name"):
self.name = get_or_increase_name(
defaults.text_to_slug(kwargs["name"]), self.serial
)
else:
self.name = get_or_increase_name(
defaults.certificate_name(
self.cn, self.issuer, self.not_before, self.not_after, self.san
),
self.serial,
)
self.owner = kwargs["owner"]
if kwargs.get("private_key"):
self.private_key = kwargs["private_key"].strip()
if kwargs.get("chain"):
self.chain = kwargs["chain"].strip()
if kwargs.get("csr"):
self.csr = kwargs["csr"].strip()
self.notify = kwargs.get("notify", True)
self.destinations = kwargs.get("destinations", [])
self.notifications = kwargs.get("notifications", [])
self.description = kwargs.get("description")
self.roles = list(set(kwargs.get("roles", [])))
self.replaces = kwargs.get("replaces", [])
self.rotation = kwargs.get("rotation")
self.rotation_policy = kwargs.get("rotation_policy")
self.key_type = kwargs.get("key_type")
self.signing_algorithm = defaults.signing_algorithm(cert)
self.bits = defaults.bitstrength(cert)
self.external_id = kwargs.get("external_id")
self.authority_id = kwargs.get("authority_id")
self.dns_provider_id = kwargs.get("dns_provider_id")
for domain in defaults.domains(cert):
self.domains.append(Domain(name=domain))
@property
def is_expired(self):
if self.not_after < datetime.datetime.now():
return True
# Check integrity before saving anything into the database.
# For user-facing API calls, validation should also be done in schema validators.
self.check_integrity()
@property
def is_unused(self):
if self.elb_listeners.count() == 0:
return True
@property
def is_revoked(self):
# we might not yet know the condition of the cert
if self.status:
if 'revoked' in self.status:
return True
def get_arn(self, account_number):
def check_integrity(self):
"""
Generate a valid AWS IAM arn
Integrity checks: Does the cert have a valid chain and matching private key?
"""
if self.private_key:
validators.verify_private_key_match(
utils.parse_private_key(self.private_key),
self.parsed_cert,
error_class=AssertionError,
)
:rtype : str
:param account_number:
if self.chain:
chain = [self.parsed_cert] + utils.parse_cert_chain(self.chain)
validators.verify_cert_chain(chain, error_class=AssertionError)
@cached_property
def parsed_cert(self):
assert self.body, "Certificate body not set"
return utils.parse_certificate(self.body)
@property
def active(self):
return self.notify
@property
def organization(self):
return defaults.organization(self.parsed_cert)
@property
def organizational_unit(self):
return defaults.organizational_unit(self.parsed_cert)
@property
def country(self):
return defaults.country(self.parsed_cert)
@property
def state(self):
return defaults.state(self.parsed_cert)
@property
def location(self):
return defaults.location(self.parsed_cert)
@property
def distinguished_name(self):
return self.parsed_cert.subject.rfc4514_string()
"""
# Commenting this property as key_type is now added as a column. This code can be removed in future.
@property
def key_type(self):
if isinstance(self.parsed_cert.public_key(), rsa.RSAPublicKey):
return "RSA{key_size}".format(
key_size=self.parsed_cert.public_key().key_size
)
elif isinstance(self.parsed_cert.public_key(), ec.EllipticCurvePublicKey):
return get_key_type_from_ec_curve(self.parsed_cert.public_key().curve.name)
"""
@property
def validity_remaining(self):
return abs(self.not_after - arrow.utcnow())
@property
def validity_range(self):
return self.not_after - self.not_before
@property
def subject(self):
return self.parsed_cert.subject
@property
def public_key(self):
return self.parsed_cert.public_key()
@hybrid_property
def expired(self):
# can't compare offset-naive and offset-aware datetimes
if arrow.Arrow.fromdatetime(self.not_after) <= arrow.utcnow():
return True
@expired.expression
def expired(cls):
return case([(cls.not_after <= arrow.utcnow(), True)], else_=False)
@hybrid_property
def revoked(self):
if "revoked" == self.status:
return True
@revoked.expression
def revoked(cls):
return case([(cls.status == "revoked", True)], else_=False)
@hybrid_property
def has_private_key(self):
return self.private_key is not None
@has_private_key.expression
def has_private_key(cls):
return case([(cls.private_key.is_(None), True)], else_=False)
@hybrid_property
def in_rotation_window(self):
"""
Determines if a certificate is available for rotation based
on the rotation policy associated.
:return:
"""
return "arn:aws:iam::{}:server-certificate/{}".format(account_number, self.name)
now = arrow.utcnow()
end = now + timedelta(days=self.rotation_policy.days)
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
if self.not_after <= end:
return True
@in_rotation_window.expression
def in_rotation_window(cls):
"""
Determines if a certificate is available for rotation based
on the rotation policy associated.
:return:
"""
return case(
[(extract("day", cls.not_after - func.now()) <= RotationPolicy.days, True)],
else_=False,
)
@property
def extensions(self):
# setup default values
return_extensions = {"sub_alt_names": {"names": []}}
try:
for extension in self.parsed_cert.extensions:
value = extension.value
if isinstance(value, x509.BasicConstraints):
return_extensions["basic_constraints"] = value
elif isinstance(value, x509.SubjectAlternativeName):
return_extensions["sub_alt_names"]["names"] = value
elif isinstance(value, x509.ExtendedKeyUsage):
return_extensions["extended_key_usage"] = value
elif isinstance(value, x509.KeyUsage):
return_extensions["key_usage"] = value
elif isinstance(value, x509.SubjectKeyIdentifier):
return_extensions["subject_key_identifier"] = {"include_ski": True}
elif isinstance(value, x509.AuthorityInformationAccess):
return_extensions["certificate_info_access"] = {"include_aia": True}
elif isinstance(value, x509.AuthorityKeyIdentifier):
aki = {"use_key_identifier": False, "use_authority_cert": False}
if value.key_identifier:
aki["use_key_identifier"] = True
if value.authority_cert_issuer:
aki["use_authority_cert"] = True
return_extensions["authority_key_identifier"] = aki
elif isinstance(value, x509.CRLDistributionPoints):
return_extensions["crl_distribution_points"] = {
"include_crl_dp": value
}
# TODO: Not supporting custom OIDs yet. https://github.com/Netflix/lemur/issues/665
else:
current_app.logger.warning(
"Custom OIDs not yet supported for clone operation."
)
except InvalidCodepoint as e:
sentry.captureException()
current_app.logger.warning(
"Unable to parse extensions due to underscore in dns name"
)
except ValueError as e:
sentry.captureException()
current_app.logger.warning("Unable to parse")
current_app.logger.exception(e)
return return_extensions
def __repr__(self):
return "Certificate(name={name})".format(name=self.name)
@event.listens_for(Certificate.destinations, 'append')
@event.listens_for(Certificate.destinations, "append")
def update_destinations(target, value, initiator):
"""
Attempt to upload certificate to the new destination
:param target:
:param value:
:param initiator:
:return:
"""
destination_plugin = plugins.get(value.plugin_name)
destination_plugin.upload(target.name, target.body, target.private_key, target.chain, value.options)
status = FAILURE_METRIC_STATUS
if target.expired:
return
try:
if target.private_key or not destination_plugin.requires_key:
destination_plugin.upload(
target.name,
target.body,
target.private_key,
target.chain,
value.options,
)
status = SUCCESS_METRIC_STATUS
except Exception as e:
sentry.captureException()
raise
metrics.send(
"destination_upload",
"counter",
1,
metric_tags={
"status": status,
"certificate": target.name,
"destination": value.label,
},
)
@event.listens_for(Certificate.replaces, "append")
def update_replacement(target, value, initiator):
"""
When a certificate is marked as 'replaced' we should not notify.
:param target:
:param value:
:param initiator:
:return:
"""
value.notify = False

View File

@ -0,0 +1,480 @@
"""
.. module: lemur.certificates.schemas
:platform: unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import current_app
from flask_restful import inputs
from flask_restful.reqparse import RequestParser
from marshmallow import fields, validate, validates_schema, post_load, pre_load, post_dump
from marshmallow.exceptions import ValidationError
from lemur.authorities.schemas import AuthorityNestedOutputSchema
from lemur.certificates import utils as cert_utils
from lemur.common import missing, utils, validators
from lemur.common.fields import ArrowDateTime, Hex
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
from lemur.constants import CERTIFICATE_KEY_TYPES
from lemur.destinations.schemas import DestinationNestedOutputSchema
from lemur.dns_providers.schemas import DnsProvidersNestedOutputSchema
from lemur.domains.schemas import DomainNestedOutputSchema
from lemur.notifications import service as notification_service
from lemur.notifications.schemas import NotificationNestedOutputSchema
from lemur.policies.schemas import RotationPolicyNestedOutputSchema
from lemur.roles import service as roles_service
from lemur.roles.schemas import RoleNestedOutputSchema
from lemur.schemas import (
AssociatedAuthoritySchema,
AssociatedDestinationSchema,
AssociatedCertificateSchema,
AssociatedNotificationSchema,
AssociatedDnsProviderSchema,
PluginInputSchema,
ExtensionSchema,
AssociatedRoleSchema,
EndpointNestedOutputSchema,
AssociatedRotationPolicySchema,
)
from lemur.users.schemas import UserNestedOutputSchema
class CertificateSchema(LemurInputSchema):
owner = fields.Email(required=True)
description = fields.String(missing="", allow_none=True)
class CertificateCreationSchema(CertificateSchema):
@post_load
def default_notification(self, data):
if not data["notifications"]:
data[
"notifications"
] += notification_service.create_default_expiration_notifications(
"DEFAULT_{0}".format(data["owner"].split("@")[0].upper()),
[data["owner"]],
)
data[
"notifications"
] += notification_service.create_default_expiration_notifications(
"DEFAULT_SECURITY",
current_app.config.get("LEMUR_SECURITY_TEAM_EMAIL"),
current_app.config.get("LEMUR_SECURITY_TEAM_EMAIL_INTERVALS", None),
)
return data
class CertificateInputSchema(CertificateCreationSchema):
name = fields.String()
common_name = fields.String(required=True, validate=validators.common_name)
authority = fields.Nested(AssociatedAuthoritySchema, required=True)
validity_start = ArrowDateTime(allow_none=True)
validity_end = ArrowDateTime(allow_none=True)
validity_years = fields.Integer(allow_none=True)
destinations = fields.Nested(AssociatedDestinationSchema, missing=[], many=True)
notifications = fields.Nested(AssociatedNotificationSchema, missing=[], many=True)
replaces = fields.Nested(AssociatedCertificateSchema, missing=[], many=True)
replacements = fields.Nested(
AssociatedCertificateSchema, missing=[], many=True
) # deprecated
roles = fields.Nested(AssociatedRoleSchema, missing=[], many=True)
dns_provider = fields.Nested(
AssociatedDnsProviderSchema, missing=None, allow_none=True, required=False
)
csr = fields.String(allow_none=True, validate=validators.csr)
key_type = fields.String(
validate=validate.OneOf(CERTIFICATE_KEY_TYPES), missing="RSA2048"
)
notify = fields.Boolean(default=True)
rotation = fields.Boolean()
rotation_policy = fields.Nested(
AssociatedRotationPolicySchema,
missing={"name": "default"},
allow_none=True,
default={"name": "default"},
)
# certificate body fields
organizational_unit = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_ORGANIZATIONAL_UNIT")
)
organization = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_ORGANIZATION")
)
location = fields.String()
country = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_COUNTRY")
)
state = fields.String(missing=lambda: current_app.config.get("LEMUR_DEFAULT_STATE"))
extensions = fields.Nested(ExtensionSchema)
@validates_schema
def validate_authority(self, data):
if 'authority' not in data:
raise ValidationError("Missing Authority.")
if isinstance(data["authority"], str):
raise ValidationError("Authority not found.")
if not data["authority"].active:
raise ValidationError("The authority is inactive.", ["authority"])
@validates_schema
def validate_dates(self, data):
validators.dates(data)
@pre_load
def load_data(self, data):
if data.get("replacements"):
data["replaces"] = data[
"replacements"
] # TODO remove when field is deprecated
if data.get("csr"):
csr_sans = cert_utils.get_sans_from_csr(data["csr"])
if not data.get("extensions"):
data["extensions"] = {"subAltNames": {"names": []}}
elif not data["extensions"].get("subAltNames"):
data["extensions"]["subAltNames"] = {"names": []}
elif not data["extensions"]["subAltNames"].get("names"):
data["extensions"]["subAltNames"]["names"] = []
data["extensions"]["subAltNames"]["names"] = csr_sans
common_name = cert_utils.get_cn_from_csr(data["csr"])
if common_name:
data["common_name"] = common_name
key_type = cert_utils.get_key_type_from_csr(data["csr"])
if key_type:
data["key_type"] = key_type
# This code will be exercised for certificate import (without CSR)
if data.get("key_type") is None:
if data.get("body"):
data["key_type"] = utils.get_key_type_from_certificate(data["body"])
else:
data["key_type"] = "RSA2048" # default value
return missing.convert_validity_years(data)
class CertificateEditInputSchema(CertificateSchema):
owner = fields.String()
notify = fields.Boolean()
rotation = fields.Boolean()
destinations = fields.Nested(AssociatedDestinationSchema, missing=[], many=True)
notifications = fields.Nested(AssociatedNotificationSchema, missing=[], many=True)
replaces = fields.Nested(AssociatedCertificateSchema, missing=[], many=True)
replacements = fields.Nested(
AssociatedCertificateSchema, missing=[], many=True
) # deprecated
roles = fields.Nested(AssociatedRoleSchema, missing=[], many=True)
@pre_load
def load_data(self, data):
if data.get("replacements"):
data["replaces"] = data[
"replacements"
] # TODO remove when field is deprecated
if data.get("owner"):
# Check if role already exists. This avoids adding duplicate role.
if data.get("roles") and any(r.get("name") == data["owner"] for r in data["roles"]):
return data
# Add required role
owner_role = roles_service.get_or_create(
data["owner"],
description=f"Auto generated role based on owner: {data['owner']}"
)
# Put role info in correct format using RoleNestedOutputSchema
owner_role_dict = RoleNestedOutputSchema().dump(owner_role).data
if data.get("roles"):
data["roles"].append(owner_role_dict)
else:
data["roles"] = [owner_role_dict]
return data
@post_load
def enforce_notifications(self, data):
"""
Add default notification for current owner if none exist.
This ensures that the default notifications are added in the event of owner change.
Old owner notifications are retained unless explicitly removed later in the code path.
:param data:
:return:
"""
if data.get("owner"):
notification_name = "DEFAULT_{0}".format(
data["owner"].split("@")[0].upper()
)
# Even if one default role exists, return
# This allows a User to remove unwanted default notification for current owner
if any(n.label.startswith(notification_name) for n in data["notifications"]):
return data
data[
"notifications"
] += notification_service.create_default_expiration_notifications(
notification_name, [data["owner"]]
)
return data
class CertificateNestedOutputSchema(LemurOutputSchema):
__envelope__ = False
id = fields.Integer()
name = fields.String()
owner = fields.Email()
creator = fields.Nested(UserNestedOutputSchema)
description = fields.String()
status = fields.String()
bits = fields.Integer()
body = fields.String()
chain = fields.String()
csr = fields.String()
active = fields.Boolean()
rotation = fields.Boolean()
notify = fields.Boolean()
rotation_policy = fields.Nested(RotationPolicyNestedOutputSchema)
# Note aliasing is the first step in deprecating these fields.
cn = fields.String() # deprecated
common_name = fields.String(attribute="cn")
not_after = fields.DateTime() # deprecated
validity_end = ArrowDateTime(attribute="not_after")
not_before = fields.DateTime() # deprecated
validity_start = ArrowDateTime(attribute="not_before")
issuer = fields.Nested(AuthorityNestedOutputSchema)
class CertificateCloneSchema(LemurOutputSchema):
__envelope__ = False
description = fields.String()
common_name = fields.String()
class CertificateOutputSchema(LemurOutputSchema):
id = fields.Integer()
external_id = fields.String()
bits = fields.Integer()
body = fields.String()
chain = fields.String()
csr = fields.String()
deleted = fields.Boolean(default=False)
description = fields.String()
issuer = fields.String()
name = fields.String()
dns_provider_id = fields.Integer(required=False, allow_none=True)
date_created = ArrowDateTime()
resolved = fields.Boolean(required=False, allow_none=True)
resolved_cert_id = fields.Integer(required=False, allow_none=True)
rotation = fields.Boolean()
# Note aliasing is the first step in deprecating these fields.
notify = fields.Boolean()
active = fields.Boolean(attribute="notify")
has_private_key = fields.Boolean()
cn = fields.String()
common_name = fields.String(attribute="cn")
distinguished_name = fields.String()
not_after = fields.DateTime()
validity_end = ArrowDateTime(attribute="not_after")
not_before = fields.DateTime()
validity_start = ArrowDateTime(attribute="not_before")
owner = fields.Email()
san = fields.Boolean()
serial = fields.String()
serial_hex = Hex(attribute="serial")
signing_algorithm = fields.String()
key_type = fields.String(allow_none=True)
status = fields.String()
user = fields.Nested(UserNestedOutputSchema)
extensions = fields.Nested(ExtensionSchema)
# associated objects
domains = fields.Nested(DomainNestedOutputSchema, many=True)
destinations = fields.Nested(DestinationNestedOutputSchema, many=True)
notifications = fields.Nested(NotificationNestedOutputSchema, many=True)
replaces = fields.Nested(CertificateNestedOutputSchema, many=True)
authority = fields.Nested(AuthorityNestedOutputSchema)
dns_provider = fields.Nested(DnsProvidersNestedOutputSchema)
roles = fields.Nested(RoleNestedOutputSchema, many=True)
endpoints = fields.Nested(EndpointNestedOutputSchema, many=True, missing=[])
replaced_by = fields.Nested(
CertificateNestedOutputSchema, many=True, attribute="replaced"
)
rotation_policy = fields.Nested(RotationPolicyNestedOutputSchema)
country = fields.String()
location = fields.String()
state = fields.String()
organization = fields.String()
organizational_unit = fields.String()
@post_dump
def handle_subject_details(self, data):
subject_details = ["country", "state", "location", "organization", "organizational_unit"]
# Remove subject details if authority is CA/Browser Forum compliant. The code will use default set of values in that case.
# If CA/Browser Forum compliance of an authority is unknown (None), it is safe to fallback to default values. Thus below
# condition checks for 'not False' ==> 'True or None'
if data.get("authority"):
is_cab_compliant = data.get("authority").get("isCabCompliant")
if is_cab_compliant is not False:
for field in subject_details:
data.pop(field, None)
# Removing subject fields if None, else it complains in de-serialization
for field in subject_details:
if field in data and data[field] is None:
data.pop(field)
class CertificateShortOutputSchema(LemurOutputSchema):
id = fields.Integer()
name = fields.String()
owner = fields.Email()
notify = fields.Boolean()
authority = fields.Nested(AuthorityNestedOutputSchema)
issuer = fields.String()
cn = fields.String()
class CertificateUploadInputSchema(CertificateCreationSchema):
name = fields.String()
authority = fields.Nested(AssociatedAuthoritySchema, required=False)
notify = fields.Boolean(missing=True)
external_id = fields.String(missing=None, allow_none=True)
private_key = fields.String()
body = fields.String(required=True)
chain = fields.String(missing=None, allow_none=True)
csr = fields.String(required=False, allow_none=True, validate=validators.csr)
key_type = fields.String()
destinations = fields.Nested(AssociatedDestinationSchema, missing=[], many=True)
notifications = fields.Nested(AssociatedNotificationSchema, missing=[], many=True)
replaces = fields.Nested(AssociatedCertificateSchema, missing=[], many=True)
roles = fields.Nested(AssociatedRoleSchema, missing=[], many=True)
@validates_schema
def keys(self, data):
if data.get("destinations"):
if not data.get("private_key"):
raise ValidationError("Destinations require private key.")
@validates_schema
def validate_cert_private_key_chain(self, data):
cert = None
key = None
if data.get("body"):
try:
cert = utils.parse_certificate(data["body"])
except ValueError:
raise ValidationError(
"Public certificate presented is not valid.", field_names=["body"]
)
if data.get("private_key"):
try:
key = utils.parse_private_key(data["private_key"])
except ValueError:
raise ValidationError(
"Private key presented is not valid.", field_names=["private_key"]
)
if cert and key:
# Throws ValidationError
validators.verify_private_key_match(key, cert)
if data.get("chain"):
try:
chain = utils.parse_cert_chain(data["chain"])
except ValueError:
raise ValidationError(
"Invalid certificate in certificate chain.", field_names=["chain"]
)
# Throws ValidationError
validators.verify_cert_chain([cert] + chain)
@pre_load
def load_data(self, data):
if data.get("body"):
try:
data["key_type"] = utils.get_key_type_from_certificate(data["body"])
except ValueError:
raise ValidationError(
"Public certificate presented is not valid.", field_names=["body"]
)
class CertificateExportInputSchema(LemurInputSchema):
plugin = fields.Nested(PluginInputSchema)
class CertificateNotificationOutputSchema(LemurOutputSchema):
description = fields.String()
issuer = fields.String()
name = fields.String()
owner = fields.Email()
user = fields.Nested(UserNestedOutputSchema)
validity_end = ArrowDateTime(attribute="not_after")
replaced_by = fields.Nested(
CertificateNestedOutputSchema, many=True, attribute="replaced"
)
endpoints = fields.Nested(EndpointNestedOutputSchema, many=True, missing=[])
class CertificateRevokeSchema(LemurInputSchema):
comments = fields.String()
certificates_list_request_parser = RequestParser()
certificates_list_request_parser.add_argument("short", type=inputs.boolean, default=False, location="args")
def certificates_list_output_schema_factory():
args = certificates_list_request_parser.parse_args()
if args["short"]:
return certificates_short_output_schema
else:
return certificates_output_schema
certificate_input_schema = CertificateInputSchema()
certificate_output_schema = CertificateOutputSchema()
certificates_output_schema = CertificateOutputSchema(many=True)
certificates_short_output_schema = CertificateShortOutputSchema(many=True)
certificate_upload_input_schema = CertificateUploadInputSchema()
certificate_export_input_schema = CertificateExportInputSchema()
certificate_edit_input_schema = CertificateEditInputSchema()
certificate_notification_output_schema = CertificateNotificationOutputSchema()
certificate_revoke_schema = CertificateRevokeSchema()

View File

@ -1,34 +1,47 @@
"""
.. module: service
.. module: lemur.certificate.service
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import arrow
from sqlalchemy import func, or_
from flask import g, current_app
from lemur import database
from lemur.plugins.base import plugins
from lemur.certificates.models import Certificate
from lemur.destinations.models import Destination
from lemur.notifications.models import Notification
from lemur.authorities.models import Authority
from lemur.roles.models import Role
import re
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from flask import current_app
from sqlalchemy import func, or_, not_, cast, Integer
from sqlalchemy.sql.expression import false, true
from lemur import database
from lemur.authorities.models import Authority
from lemur.certificates.models import Certificate
from lemur.certificates.schemas import CertificateOutputSchema, CertificateInputSchema
from lemur.common.utils import generate_private_key, truthiness
from lemur.destinations.models import Destination
from lemur.domains.models import Domain
from lemur.extensions import metrics, sentry, signals
from lemur.models import certificate_associations
from lemur.notifications.models import Notification
from lemur.pending_certificates.models import PendingCertificate
from lemur.plugins.base import plugins
from lemur.roles import service as role_service
from lemur.roles.models import Role
csr_created = signals.signal("csr_created", "CSR generated")
csr_imported = signals.signal("csr_imported", "CSR imported from external source")
certificate_issued = signals.signal(
"certificate_issued", "Authority issued a certificate"
)
certificate_imported = signals.signal(
"certificate_imported", "Certificate imported from external source"
)
def get(cert_id):
"""
Retrieves certificate by it's ID.
Retrieves certificate by its ID.
:param cert_id:
:return:
@ -38,12 +51,40 @@ def get(cert_id):
def get_by_name(name):
"""
Retrieves certificate by it's Name.
Retrieves certificate by its Name.
:param name:
:return:
"""
return database.get(Certificate, name, field='name')
return database.get(Certificate, name, field="name")
def get_by_serial(serial):
"""
Retrieves certificate(s) by serial number.
:param serial:
:return:
"""
if isinstance(serial, int):
# although serial is a number, the DB column is String(128)
serial = str(serial)
return Certificate.query.filter(Certificate.serial == serial).all()
def get_by_attributes(conditions):
"""
Retrieves certificate(s) by conditions given in a hash of given key=>value pairs.
:param serial:
:return:
"""
# Ensure that each of the given conditions corresponds to actual columns
# if not, silently remove it
for attr in conditions.keys():
if attr not in Certificate.__table__.columns:
conditions.pop(attr)
query = database.session_query(Certificate)
return database.find_all(query, Certificate, conditions).all()
def delete(cert_id):
@ -64,74 +105,213 @@ def get_all_certs():
return Certificate.query.all()
def find_duplicates(cert_body):
def get_all_valid_certs(authority_plugin_name):
"""
Retrieves all valid (not expired & not revoked) certificates within Lemur, for the given authority plugin names
ignored if no authority_plugin_name provided.
Note that depending on the DB size retrieving all certificates might an expensive operation
:return:
"""
if authority_plugin_name:
return (
Certificate.query.outerjoin(Authority, Authority.id == Certificate.authority_id).filter(
Certificate.not_after > arrow.now().format("YYYY-MM-DD")).filter(
Authority.plugin_name.in_(authority_plugin_name)).filter(Certificate.revoked.is_(False)).all()
)
else:
return (
Certificate.query.filter(Certificate.not_after > arrow.now().format("YYYY-MM-DD")).filter(
Certificate.revoked.is_(False)).all()
)
def get_all_pending_cleaning_expired(source):
"""
Retrieves all certificates that are available for cleaning. These are certificates which are expired and are not
attached to any endpoints.
:param source: the source to search for certificates
:return: list of pending certificates
"""
return (
Certificate.query.filter(Certificate.sources.any(id=source.id))
.filter(not_(Certificate.endpoints.any()))
.filter(Certificate.expired)
.all()
)
def get_all_certs_attached_to_endpoint_without_autorotate():
"""
Retrieves all certificates that are attached to an endpoint, but that do not have autorotate enabled.
:return: list of certificates attached to an endpoint without autorotate
"""
return (
Certificate.query.filter(Certificate.endpoints.any())
.filter(Certificate.rotation == false())
.filter(Certificate.not_after >= arrow.now())
.filter(not_(Certificate.replaced.any()))
.all() # noqa
)
def get_all_pending_cleaning_expiring_in_days(source, days_to_expire):
"""
Retrieves all certificates that are available for cleaning, not attached to endpoint,
and within X days from expiration.
:param days_to_expire: defines how many days till the certificate is expired
:param source: the source to search for certificates
:return: list of pending certificates
"""
expiration_window = arrow.now().shift(days=+days_to_expire).format("YYYY-MM-DD")
return (
Certificate.query.filter(Certificate.sources.any(id=source.id))
.filter(not_(Certificate.endpoints.any()))
.filter(Certificate.not_after < expiration_window)
.all()
)
def get_all_pending_cleaning_issued_since_days(source, days_since_issuance):
"""
Retrieves all certificates that are available for cleaning: not attached to endpoint, and X days since issuance.
:param days_since_issuance: defines how many days since the certificate is issued
:param source: the source to search for certificates
:return: list of pending certificates
"""
not_in_use_window = (
arrow.now().shift(days=-days_since_issuance).format("YYYY-MM-DD")
)
return (
Certificate.query.filter(Certificate.sources.any(id=source.id))
.filter(not_(Certificate.endpoints.any()))
.filter(Certificate.date_created > not_in_use_window)
.all()
)
def get_all_pending_reissue():
"""
Retrieves all certificates that need to be rotated.
Must be X days from expiration, uses the certificates rotation
policy to determine how many days from expiration the certificate must be
for rotation to be pending.
:return:
"""
return (
Certificate.query.filter(Certificate.rotation == true())
.filter(not_(Certificate.replaced.any()))
.filter(Certificate.in_rotation_window == true())
.all()
) # noqa
def find_duplicates(cert):
"""
Finds certificates that already exist within Lemur. We do this by looking for
certificate bodies that are the same. This is the most reliable way to determine
if a certificate is already being tracked by Lemur.
:param cert_body:
:param cert:
:return:
"""
return Certificate.query.filter_by(body=cert_body).all()
if cert["chain"]:
return Certificate.query.filter_by(
body=cert["body"].strip(), chain=cert["chain"].strip()
).all()
else:
return Certificate.query.filter_by(body=cert["body"].strip(), chain=None).all()
def update(cert_id, owner, description, active, destinations, notifications):
def export(cert, export_plugin):
"""
Updates a certificate.
Exports a certificate to the requested format. This format
may be a binary format.
:param export_plugin:
:param cert:
:return:
"""
plugin = plugins.get(export_plugin["slug"])
return plugin.export(
cert.body, cert.chain, cert.private_key, export_plugin["pluginOptions"]
)
def update(cert_id, **kwargs):
"""
Updates a certificate
:param cert_id:
:param owner:
:param active:
:return:
"""
from lemur.notifications import service as notification_service
cert = get(cert_id)
cert.active = active
cert.description = description
# we might have to create new notifications if the owner changes
new_notifications = []
# get existing names to remove
notification_name = "DEFAULT_{0}".format(cert.owner.split('@')[0].upper())
for n in notifications:
if notification_name not in n.label:
new_notifications.append(n)
notification_name = "DEFAULT_{0}".format(owner.split('@')[0].upper())
new_notifications += notification_service.create_default_expiration_notifications(notification_name, owner)
cert.notifications = new_notifications
database.update_list(cert, 'destinations', Destination, destinations)
cert.owner = owner
for key, value in kwargs.items():
setattr(cert, key, value)
return database.update(cert)
def mint(issuer_options):
def cleanup_owner_roles_notification(owner_name, kwargs):
kwargs["roles"] = [r for r in kwargs["roles"] if r.name != owner_name]
notification_prefix = f"DEFAULT_{owner_name.split('@')[0].upper()}"
kwargs["notifications"] = [n for n in kwargs["notifications"] if not n.label.startswith(notification_prefix)]
def update_notify(cert, notify_flag):
"""
Toggle notification value which is a boolean
:param notify_flag: new notify value
:param cert: Certificate object to be updated
:return:
"""
cert.notify = notify_flag
return database.update(cert)
def create_certificate_roles(**kwargs):
# create a role for the owner and assign it
owner_role = role_service.get_or_create(
kwargs["owner"],
description=f"Auto generated role based on owner: {kwargs['owner']}"
)
# ensure that the authority's owner is also associated with the certificate
if kwargs.get("authority"):
authority_owner_role = role_service.get_by_name(kwargs["authority"].owner)
return [owner_role, authority_owner_role]
return [owner_role]
def mint(**kwargs):
"""
Minting is slightly different for each authority.
Support for multiple authorities is handled by individual plugins.
:param issuer_options:
"""
authority = issuer_options['authority']
authority = kwargs["authority"]
issuer = plugins.get(authority.plugin_name)
csr, private_key = create_csr(issuer_options)
# allow the CSR to be specified by the user
if not kwargs.get("csr"):
csr, private_key = create_csr(**kwargs)
csr_created.send(authority=authority, csr=csr)
else:
csr = str(kwargs.get("csr"))
private_key = None
csr_imported.send(authority=authority, csr=csr)
issuer_options['creator'] = g.user.email
cert_body, cert_chain = issuer.create_certificate(csr, issuer_options)
cert = Certificate(cert_body, private_key, cert_chain)
cert.user = g.user
cert.authority = authority
database.update(cert)
return cert, private_key, cert_chain,
cert_body, cert_chain, external_id = issuer.create_certificate(csr, kwargs)
return cert_body, private_key, cert_chain, external_id, csr
def import_certificate(**kwargs):
@ -147,67 +327,31 @@ def import_certificate(**kwargs):
:param kwargs:
"""
from lemur.users import service as user_service
from lemur.notifications import service as notification_service
cert = Certificate(kwargs['public_certificate'], chain=kwargs['intermediate_certificate'])
if not kwargs.get("owner"):
kwargs["owner"] = current_app.config.get("LEMUR_SECURITY_TEAM_EMAIL")[0]
# TODO future source plugins might have a better understanding of who the 'owner' is we should support this
cert.owner = kwargs.get('owner', current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL')[0])
cert.creator = kwargs.get('creator', user_service.get_by_email('lemur@nobody'))
# NOTE existing certs may not follow our naming standard we will
# overwrite the generated name with the actual cert name
if kwargs.get('name'):
cert.name = kwargs.get('name')
if kwargs.get('user'):
cert.user = kwargs.get('user')
notification_name = 'DEFAULT_SECURITY'
notifications = notification_service.create_default_expiration_notifications(notification_name, current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL'))
cert.notifications = notifications
cert = database.create(cert)
return cert
return upload(**kwargs)
def upload(**kwargs):
"""
Allows for pre-made certificates to be imported into Lemur.
"""
from lemur.notifications import service as notification_service
cert = Certificate(
kwargs.get('public_cert'),
kwargs.get('private_key'),
kwargs.get('intermediate_cert'),
)
roles = create_certificate_roles(**kwargs)
# we override the generated name if one is provided
if kwargs.get('name'):
cert.name = kwargs['name']
if kwargs.get("roles"):
kwargs["roles"] += roles
else:
kwargs["roles"] = roles
cert.description = kwargs.get('description')
cert.owner = kwargs['owner']
cert = Certificate(**kwargs)
cert.authority = kwargs.get("authority")
cert = database.create(cert)
g.user.certificates.append(cert)
kwargs["creator"].certificates.append(cert)
database.update_list(cert, 'destinations', Destination, kwargs.get('destinations'))
database.update_list(cert, 'notifications', Notification, kwargs.get('notifications'))
# create default notifications for this certificate if none are provided
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])
notification_name = 'DEFAULT_SECURITY'
notifications += notification_service.create_default_expiration_notifications(notification_name, current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL'))
cert.notifications = notifications
database.update(cert)
cert = database.update(cert)
certificate_imported.send(certificate=cert, authority=cert.authority)
return cert
@ -215,34 +359,59 @@ def create(**kwargs):
"""
Creates a new certificate.
"""
from lemur.notifications import service as notification_service
cert, private_key, cert_chain = mint(kwargs)
try:
cert_body, private_key, cert_chain, external_id, csr = mint(**kwargs)
except Exception:
log_data = {
"message": "Exception minting certificate",
"issuer": kwargs["authority"].name,
"cn": kwargs["common_name"],
}
current_app.logger.error(log_data, exc_info=True)
sentry.captureException()
raise
kwargs["body"] = cert_body
kwargs["private_key"] = private_key
kwargs["chain"] = cert_chain
kwargs["external_id"] = external_id
kwargs["csr"] = csr
cert.owner = kwargs['owner']
roles = create_certificate_roles(**kwargs)
database.create(cert)
cert.description = kwargs['description']
g.user.certificates.append(cert)
database.update(g.user)
if kwargs.get("roles"):
kwargs["roles"] += roles
else:
kwargs["roles"] = roles
# do this after the certificate has already been created because if it fails to upload to the third party
# we do not want to lose the certificate information.
database.update_list(cert, 'destinations', Destination, kwargs.get('destinations'))
if cert_body:
cert = Certificate(**kwargs)
kwargs["creator"].certificates.append(cert)
else:
cert = PendingCertificate(**kwargs)
kwargs["creator"].pending_certificates.append(cert)
database.update_list(cert, 'notifications', Notification, kwargs.get('notifications'))
cert.authority = kwargs["authority"]
# create default notifications for this certificate if none are provided
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])
database.commit()
notification_name = 'DEFAULT_SECURITY'
notifications += notification_service.create_default_expiration_notifications(notification_name,
current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL'))
cert.notifications = notifications
if isinstance(cert, Certificate):
certificate_issued.send(certificate=cert, authority=cert.authority)
metrics.send(
"certificate_issued",
"counter",
1,
metric_tags=dict(owner=cert.owner, issuer=cert.issuer),
)
if isinstance(cert, PendingCertificate):
# We need to refresh the pending certificate to avoid "Instance is not bound to a Session; "
# "attribute refresh operation cannot proceed"
pending_cert = database.session_query(PendingCertificate).get(cert.id)
from lemur.common.celery import fetch_acme_cert
if not current_app.config.get("ACME_DISABLE_AUTORESOLVE", False):
fetch_acme_cert.apply_async((pending_cert.id,), countdown=5)
database.update(cert)
return cert
@ -255,161 +424,241 @@ def render(args):
"""
query = database.session_query(Certificate)
time_range = args.pop('time_range')
destination_id = args.pop('destination_id')
notification_id = args.pop('notification_id', None)
show = args.pop('show')
show_expired = args.pop("showExpired")
if show_expired != 1:
one_month_old = (
arrow.now()
.shift(months=current_app.config.get("HIDE_EXPIRED_CERTS_AFTER_MONTHS", -1))
.format("YYYY-MM-DD")
)
query = query.filter(Certificate.not_after > one_month_old)
time_range = args.pop("time_range")
destination_id = args.pop("destination_id")
notification_id = args.pop("notification_id", None)
show = args.pop("show")
# owner = args.pop('owner')
# creator = args.pop('creator') # TODO we should enabling filtering by owner
filt = args.pop('filter')
filt = args.pop("filter")
if filt:
terms = filt.split(';')
if 'issuer' in terms:
terms = filt.split(";")
term = "%{0}%".format(terms[1])
# Exact matches for quotes. Only applies to name, issuer, and cn
if terms[1].startswith('"') and terms[1].endswith('"'):
term = terms[1][1:-1]
if "issuer" in terms:
# we can't rely on issuer being correct in the cert directly so we combine queries
sub_query = database.session_query(Authority.id)\
.filter(Authority.name.ilike('%{0}%'.format(terms[1])))\
sub_query = (
database.session_query(Authority.id)
.filter(Authority.name.ilike(term))
.subquery()
)
query = query.filter(
or_(
Certificate.issuer.ilike('%{0}%'.format(terms[1])),
Certificate.authority_id.in_(sub_query)
Certificate.issuer.ilike(term),
Certificate.authority_id.in_(sub_query),
)
)
return database.sort_and_page(query, Certificate, args)
if 'destination' in terms:
query = query.filter(Certificate.destinations.any(Destination.id == terms[1]))
elif 'active' in filt: # this is really weird but strcmp seems to not work here??
query = query.filter(Certificate.active == terms[1])
elif "destination" in terms:
query = query.filter(
Certificate.destinations.any(Destination.id == terms[1])
)
elif "notify" in filt:
query = query.filter(Certificate.notify == truthiness(terms[1]))
elif "active" in filt:
query = query.filter(Certificate.active == truthiness(terms[1]))
elif "cn" in terms:
query = query.filter(
or_(
func.lower(Certificate.cn).like(term.lower()),
Certificate.id.in_(like_domain_query(term)),
)
)
elif "id" in terms:
query = query.filter(Certificate.id == cast(terms[1], Integer))
elif "name" in terms:
query = query.filter(
or_(
func.lower(Certificate.name).like(term.lower()),
Certificate.id.in_(like_domain_query(term)),
func.lower(Certificate.cn).like(term.lower()),
)
)
elif "fixedName" in terms:
# only what matches the fixed name directly if a fixedname is provided
query = query.filter(Certificate.name == terms[1])
else:
query = database.filter(query, Certificate, terms)
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(
or_(
Certificate.user_id == g.user.id,
Certificate.owner.in_(sub_query)
Certificate.user_id == args["user"].id, Certificate.owner.in_(sub_query)
)
)
if destination_id:
query = query.filter(Certificate.destinations.any(Destination.id == destination_id))
query = query.filter(
Certificate.destinations.any(Destination.id == destination_id)
)
if notification_id:
query = query.filter(Certificate.notifications.any(Notification.id == notification_id))
query = query.filter(
Certificate.notifications.any(Notification.id == notification_id)
)
if time_range:
to = arrow.now().replace(weeks=+time_range).format('YYYY-MM-DD')
now = arrow.now().format('YYYY-MM-DD')
query = query.filter(Certificate.not_after <= to).filter(Certificate.not_after >= now)
to = arrow.now().shift(weeks=+time_range).format("YYYY-MM-DD")
now = arrow.now().format("YYYY-MM-DD")
query = query.filter(Certificate.not_after <= to).filter(
Certificate.not_after >= now
)
return database.sort_and_page(query, Certificate, args)
if current_app.config.get("ALLOW_CERT_DELETION", False):
query = query.filter(Certificate.deleted == false())
result = database.sort_and_page(query, Certificate, args)
return result
def create_csr(csr_config):
def like_domain_query(term):
domain_query = database.session_query(Domain.id)
domain_query = domain_query.filter(func.lower(Domain.name).like(term.lower()))
assoc_query = database.session_query(certificate_associations.c.certificate_id)
assoc_query = assoc_query.filter(certificate_associations.c.domain_id.in_(domain_query))
return assoc_query
def query_name(certificate_name, args):
"""
Helper function that queries for a certificate by name
:param args:
:return:
"""
query = database.session_query(Certificate)
query = query.filter(Certificate.name == certificate_name)
result = database.sort_and_page(query, Certificate, args)
return result
def query_common_name(common_name, args):
"""
Helper function that queries for not expired certificates by common name (and owner)
:param common_name:
:param args:
:return:
"""
owner = args.pop("owner")
# only not expired certificates
current_time = arrow.utcnow()
query = Certificate.query.filter(Certificate.not_after >= current_time.format("YYYY-MM-DD"))\
.filter(not_(Certificate.revoked))\
.filter(not_(Certificate.replaced.any())) # ignore rotated certificates to avoid duplicates
if owner:
query = query.filter(Certificate.owner.ilike(owner))
if common_name != "%":
# if common_name is a wildcard ('%'), no need to include it in the query
query = query.filter(Certificate.cn.ilike(common_name))
return query.all()
def create_csr(**csr_config):
"""
Given a list of domains create the appropriate csr
for those domains
:param csr_config:
"""
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
private_key = generate_private_key(csr_config.get("key_type"))
# TODO When we figure out a better way to validate these options they should be parsed as str
builder = x509.CertificateSigningRequestBuilder()
builder = builder.subject_name(x509.Name([
x509.NameAttribute(x509.OID_COMMON_NAME, csr_config['commonName']),
x509.NameAttribute(x509.OID_ORGANIZATION_NAME, csr_config['organization']),
x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, csr_config['organizationalUnit']),
x509.NameAttribute(x509.OID_COUNTRY_NAME, csr_config['country']),
x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, csr_config['state']),
x509.NameAttribute(x509.OID_LOCALITY_NAME, csr_config['location']),
]))
name_list = [x509.NameAttribute(x509.OID_COMMON_NAME, csr_config["common_name"])]
if current_app.config.get("LEMUR_OWNER_EMAIL_IN_SUBJECT", True):
name_list.append(
x509.NameAttribute(x509.OID_EMAIL_ADDRESS, csr_config["owner"])
)
if "organization" in csr_config and csr_config["organization"].strip():
name_list.append(
x509.NameAttribute(x509.OID_ORGANIZATION_NAME, csr_config["organization"])
)
if (
"organizational_unit" in csr_config
and csr_config["organizational_unit"].strip()
):
name_list.append(
x509.NameAttribute(
x509.OID_ORGANIZATIONAL_UNIT_NAME, csr_config["organizational_unit"]
)
)
if "country" in csr_config and csr_config["country"].strip():
name_list.append(
x509.NameAttribute(x509.OID_COUNTRY_NAME, csr_config["country"])
)
if "state" in csr_config and csr_config["state"].strip():
name_list.append(
x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, csr_config["state"])
)
if "location" in csr_config and csr_config["location"].strip():
name_list.append(
x509.NameAttribute(x509.OID_LOCALITY_NAME, csr_config["location"])
)
builder = builder.subject_name(x509.Name(name_list))
builder = builder.add_extension(
x509.BasicConstraints(ca=False, path_length=None), critical=True,
)
if csr_config.get('extensions'):
for k, v in csr_config.get('extensions', {}).items():
if k == 'subAltNames':
# map types to their x509 objects
general_names = []
for name in v['names']:
if name['nameType'] == 'DNSName':
general_names.append(x509.DNSName(name['value']))
builder = builder.add_extension(
x509.SubjectAlternativeName(general_names), critical=True
extensions = csr_config.get("extensions", {})
critical_extensions = ["basic_constraints", "sub_alt_names", "key_usage"]
noncritical_extensions = ["extended_key_usage"]
for k, v in extensions.items():
if v:
if k in critical_extensions:
current_app.logger.debug(
"Adding Critical Extension: {0} {1}".format(k, v)
)
if k == "sub_alt_names":
if v["names"]:
builder = builder.add_extension(v["names"], critical=True)
else:
builder = builder.add_extension(v, critical=True)
# TODO support more CSR options, none of the authority plugins currently support these options
# builder.add_extension(
# x509.KeyUsage(
# digital_signature=digital_signature,
# content_commitment=content_commitment,
# key_encipherment=key_enipherment,
# data_encipherment=data_encipherment,
# key_agreement=key_agreement,
# key_cert_sign=key_cert_sign,
# crl_sign=crl_sign,
# encipher_only=enchipher_only,
# decipher_only=decipher_only
# ), critical=True
# )
#
# # we must maintain our own list of OIDs here
# builder.add_extension(
# x509.ExtendedKeyUsage(
# server_authentication=server_authentication,
# email=
# )
# )
#
# builder.add_extension(
# x509.AuthorityInformationAccess()
# )
#
# builder.add_extension(
# x509.AuthorityKeyIdentifier()
# )
#
# builder.add_extension(
# x509.SubjectKeyIdentifier()
# )
#
# builder.add_extension(
# x509.CRLDistributionPoints()
# )
#
# builder.add_extension(
# x509.ObjectIdentifier(oid)
# )
if k in noncritical_extensions:
current_app.logger.debug("Adding Extension: {0} {1}".format(k, v))
builder = builder.add_extension(v, critical=False)
request = builder.sign(
private_key, hashes.SHA256(), default_backend()
)
ski = extensions.get("subject_key_identifier", {})
if ski.get("include_ski", False):
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
critical=False,
)
request = builder.sign(private_key, hashes.SHA256(), default_backend())
# serialize our private key and CSR
pem = private_key.private_bytes(
private_key = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL, # would like to use PKCS8 but AWS ELBs don't like it
encryption_algorithm=serialization.NoEncryption()
)
encryption_algorithm=serialization.NoEncryption(),
).decode("utf-8")
csr = request.public_bytes(
encoding=serialization.Encoding.PEM
)
csr = request.public_bytes(encoding=serialization.Encoding.PEM).decode("utf-8")
return csr, pem
return csr, private_key
def stats(**kwargs):
@ -419,16 +668,19 @@ def stats(**kwargs):
:param kwargs:
:return:
"""
if kwargs.get('metric') == 'not_after':
if kwargs.get("metric") == "not_after":
start = arrow.utcnow()
end = start.replace(weeks=+32)
items = database.db.session.query(Certificate.issuer, func.count(Certificate.id))\
.group_by(Certificate.issuer)\
.filter(Certificate.not_after <= end.format('YYYY-MM-DD')) \
.filter(Certificate.not_after >= start.format('YYYY-MM-DD')).all()
end = start.shift(weeks=+32)
items = (
database.db.session.query(Certificate.issuer, func.count(Certificate.id))
.group_by(Certificate.issuer)
.filter(Certificate.not_after <= end.format("YYYY-MM-DD"))
.filter(Certificate.not_after >= start.format("YYYY-MM-DD"))
.all()
)
else:
attr = getattr(Certificate, kwargs.get('metric'))
attr = getattr(Certificate, kwargs.get("metric"))
query = database.db.session.query(attr, func.count(attr))
items = query.group_by(attr).all()
@ -439,4 +691,109 @@ def stats(**kwargs):
keys.append(key)
values.append(count)
return {'labels': keys, 'values': values}
return {"labels": keys, "values": values}
def get_account_number(arn):
"""
Extract the account number from an arn.
:param arn: IAM SSL arn
:return: account number associated with ARN
"""
return arn.split(":")[4]
def get_name_from_arn(arn):
"""
Extract the certificate name from an arn.
:param arn: IAM SSL arn
:return: name of the certificate as uploaded to AWS
"""
return arn.split("/", 1)[1]
def calculate_reissue_range(start, end):
"""
Determine what the new validity_start and validity_end dates should be.
:param start:
:param end:
:return:
"""
span = end - start
new_start = arrow.utcnow()
new_end = new_start + span
return new_start, arrow.get(new_end)
def get_certificate_primitives(certificate):
"""
Retrieve key primitive from a certificate such that the certificate
could be recreated with new expiration or be used to build upon.
:param certificate:
:return: dict of certificate primitives, should be enough to effectively re-issue
certificate via `create`.
"""
start, end = calculate_reissue_range(certificate.not_before, certificate.not_after)
ser = CertificateInputSchema().load(
CertificateOutputSchema().dump(certificate).data
)
assert not ser.errors, "Error re-serializing certificate: %s" % ser.errors
data = ser.data
# we can't quite tell if we are using a custom name, as this is an automated process (typically)
# we will rely on the Lemur generated name
data.pop("name", None)
# TODO this can be removed once we migrate away from cn
data["cn"] = data["common_name"]
# needed until we move off not_*
data["not_before"] = start
data["not_after"] = end
data["validity_start"] = start
data["validity_end"] = end
return data
def reissue_certificate(certificate, replace=None, user=None):
"""
Reissue certificate with the same properties of the given certificate.
:param certificate:
:param replace:
:param user:
:return:
"""
primitives = get_certificate_primitives(certificate)
if primitives.get("csr"):
# We do not want to re-use the CSR when creating a certificate because this defeats the purpose of rotation.
del primitives["csr"]
if not user:
primitives["creator"] = certificate.user
else:
primitives["creator"] = user
if replace:
primitives["replaces"] = [certificate]
# Modify description to include the certificate ID being reissued and mention that this is created by Lemur
# as part of reissue
reissue_message_prefix = "Reissued by Lemur for cert ID "
reissue_message = re.compile(f"{reissue_message_prefix}([0-9]+)")
if primitives["description"]:
match = reissue_message.search(primitives["description"])
if match:
primitives["description"] = primitives["description"].replace(match.group(1), str(certificate.id))
else:
primitives["description"] = f"{reissue_message_prefix}{certificate.id}, {primitives['description']}"
else:
primitives["description"] = f"{reissue_message_prefix}{certificate.id}"
new_cert = create(**primitives)
return new_cert

View File

@ -0,0 +1,85 @@
"""
Utils to parse certificate data.
.. module: lemur.certificates.hooks
:platform: Unix
:copyright: (c) 2019 by Javier Ramos, see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Javier Ramos <javier.ramos@booking.com>
"""
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from marshmallow.exceptions import ValidationError
from cryptography.hazmat.primitives.asymmetric import rsa, ec
from lemur.common.utils import get_key_type_from_ec_curve
def get_sans_from_csr(data):
"""
Fetches SubjectAlternativeNames from CSR.
Works with any kind of SubjectAlternativeName
:param data: PEM-encoded string with CSR
:return: List of LemurAPI-compatible subAltNames
"""
sub_alt_names = []
try:
request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend())
except Exception:
raise ValidationError("CSR presented is not valid.")
try:
alt_names = request.extensions.get_extension_for_class(
x509.SubjectAlternativeName
)
for alt_name in alt_names.value:
sub_alt_names.append(
{"nameType": type(alt_name).__name__, "value": alt_name.value}
)
except x509.ExtensionNotFound:
pass
return sub_alt_names
def get_cn_from_csr(data):
"""
Fetches common name (CN) from CSR.
Works with any kind of SubjectAlternativeName
:param data: PEM-encoded string with CSR
:return: the common name
"""
try:
request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend())
except Exception:
raise ValidationError("CSR presented is not valid.")
common_name = request.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
return common_name[0].value
def get_key_type_from_csr(data):
"""
Fetches key_type from CSR.
Works with any kind of SubjectAlternativeName
:param data: PEM-encoded string with CSR
:return: key_type
"""
try:
request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend())
except Exception:
raise ValidationError("CSR presented is not valid.")
try:
if isinstance(request.public_key(), rsa.RSAPublicKey):
return "RSA{key_size}".format(
key_size=request.public_key().key_size
)
elif isinstance(request.public_key(), ec.EllipticCurvePublicKey):
return get_key_type_from_ec_curve(request.public_key().curve.name)
else:
raise Exception("Unsupported key type")
except NotImplemented:
raise NotImplementedError

View File

@ -1,84 +1,138 @@
"""
.. module: lemur.certificates.verify
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import os
import requests
import subprocess
from OpenSSL import crypto
from flask import current_app
from lemur.extensions import sentry
from requests.exceptions import ConnectionError, InvalidSchema
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from flask import current_app
from lemur.utils import mktempfile
from lemur.common.utils import parse_certificate
from contextlib import contextmanager
from tempfile import NamedTemporaryFile
crl_cache = {}
@contextmanager
def mktempfile():
with NamedTemporaryFile(delete=False) as f:
name = f.name
try:
yield name
finally:
os.unlink(name)
def ocsp_verify(cert_path, issuer_chain_path):
def ocsp_verify(cert, cert_path, issuer_chain_path):
"""
Attempts to verify a certificate via OCSP. OCSP is a more modern version
of CRL in that it will query the OCSP URI in order to determine if the
certificate as been revoked
certificate has been revoked
:param cert:
:param cert_path:
:param issuer_chain_path:
:return bool: True if certificate is valid, False otherwise
"""
command = ['openssl', 'x509', '-noout', '-ocsp_uri', '-in', cert_path]
command = ["openssl", "x509", "-noout", "-ocsp_uri", "-in", cert_path]
p1 = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
url, err = p1.communicate()
p2 = subprocess.Popen(['openssl', 'ocsp', '-issuer', issuer_chain_path,
'-cert', cert_path, "-url", url.strip()], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if not url:
current_app.logger.debug(
"No OCSP URL in certificate {}".format(cert.serial_number)
)
return None
p2 = subprocess.Popen(
[
"openssl",
"ocsp",
"-issuer",
issuer_chain_path,
"-cert",
cert_path,
"-url",
url.strip(),
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
message, err = p2.communicate()
if 'error' in message or 'Error' in message:
p_message = message.decode("utf-8")
if "error" in p_message or "Error" in p_message:
raise Exception("Got error when parsing OCSP url")
elif 'revoked' in message:
return
elif "revoked" in p_message:
current_app.logger.debug(
"OCSP reports certificate revoked: {}".format(cert.serial_number)
)
return False
elif 'good' not in message:
elif "good" not in p_message:
raise Exception("Did not receive a valid response")
return True
def crl_verify(cert_path):
def crl_verify(cert, cert_path):
"""
Attempts to verify a certificate using CRL.
:param cert:
:param cert_path:
:return: True if certificate is valid, False otherwise
:raise Exception: If certificate does not have CRL
"""
with open(cert_path, 'rt') as c:
cert = x509.load_pem_x509_certificate(c.read(), default_backend())
try:
distribution_points = cert.extensions.get_extension_for_oid(
x509.OID_CRL_DISTRIBUTION_POINTS
).value
except x509.ExtensionNotFound:
current_app.logger.debug(
"No CRLDP extension in certificate {}".format(cert.serial_number)
)
return None
distribution_points = cert.extensions.get_extension_for_oid(x509.OID_CRL_DISTRIBUTION_POINTS).value
for p in distribution_points:
point = p.full_name[0].value
response = requests.get(point)
crl = crypto.load_crl(crypto.FILETYPE_ASN1, response.content) # TODO this should be switched to cryptography when support exists
revoked = crl.get_revoked()
for r in revoked:
if cert.serial == r.get_serial():
return
if point not in crl_cache:
current_app.logger.debug("Retrieving CRL: {}".format(point))
try:
response = requests.get(point)
if response.status_code != 200:
raise Exception("Unable to retrieve CRL: {0}".format(point))
except InvalidSchema:
# Unhandled URI scheme (like ldap://); skip this distribution point.
continue
except ConnectionError:
raise Exception("Unable to retrieve CRL: {0}".format(point))
crl_cache[point] = x509.load_der_x509_crl(
response.content, backend=default_backend()
)
else:
current_app.logger.debug("CRL point is cached {}".format(point))
for r in crl_cache[point]:
if cert.serial_number == r.serial_number:
try:
reason = r.extensions.get_extension_for_class(x509.CRLReason).value
# Handle "removeFromCRL" revoke reason as unrevoked;
# continue with the next distribution point.
# Per RFC 5280 section 6.3.3 (k):
# https://tools.ietf.org/html/rfc5280#section-6.3.3
if reason == x509.ReasonFlags.remove_from_crl:
break
except x509.ExtensionNotFound:
pass
current_app.logger.debug(
"CRL reports certificate " "revoked: {}".format(cert.serial_number)
)
return False
return True
@ -90,18 +144,33 @@ def verify(cert_path, issuer_chain_path):
:param issuer_chain_path:
:return: True if valid, False otherwise
"""
with open(cert_path, "rt") as c:
try:
cert = parse_certificate(c.read())
except ValueError as e:
current_app.logger.error(e)
return None
# OCSP is our main source of truth, in a lot of cases CRLs
# have been deprecated and are no longer updated
verify_result = None
try:
return ocsp_verify(cert_path, issuer_chain_path)
verify_result = ocsp_verify(cert, cert_path, issuer_chain_path)
except Exception as e:
current_app.logger.debug("Could not use OCSP: {0}".format(e))
sentry.captureException()
current_app.logger.exception(e)
if verify_result is None:
try:
return crl_verify(cert_path)
verify_result = crl_verify(cert, cert_path)
except Exception as e:
current_app.logger.debug("Could not use CRL: {0}".format(e))
raise Exception("Failed to verify")
raise Exception("Failed to verify")
sentry.captureException()
current_app.logger.exception(e)
if verify_result is None:
current_app.logger.debug("Failed to verify {}".format(cert.serial_number))
return verify_result
def verify_string(cert_string, issuer_string):
@ -113,10 +182,10 @@ def verify_string(cert_string, issuer_string):
:return: True if valid, False otherwise
"""
with mktempfile() as cert_tmp:
with open(cert_tmp, 'w') as f:
with open(cert_tmp, "w") as f:
f.write(cert_string)
with mktempfile() as issuer_tmp:
with open(issuer_tmp, 'w') as f:
with open(issuer_tmp, "w") as f:
f.write(issuer_string)
status = verify(cert_tmp, issuer_tmp)
return status

View File

@ -1,113 +1,257 @@
"""
.. module: lemur.certificates.views
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import base64
from builtins import str
from flask import Blueprint, make_response, jsonify
from flask.ext.restful import reqparse, Api, fields
from flask import Blueprint, make_response, jsonify, g, current_app
from flask_restful import reqparse, Api, inputs
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from lemur.certificates import service
from lemur.authorities.models import Authority
from lemur.common.schema import validate_schema
from lemur.common.utils import paginated_parser
from lemur.auth.service import AuthenticatedResource
from lemur.auth.permissions import ViewKeyPermission, AuthorityPermission, UpdateCertificatePermission
from lemur.auth.permissions import AuthorityPermission, CertificatePermission
from lemur.certificates import service
from lemur.certificates.models import Certificate
from lemur.plugins.base import plugins
from lemur.certificates.schemas import (
certificate_input_schema,
certificate_output_schema,
certificate_upload_input_schema,
certificates_output_schema,
certificate_export_input_schema,
certificate_edit_input_schema,
certificates_list_output_schema_factory,
)
from lemur.roles import service as role_service
from lemur.common.utils import marshal_items, paginated_parser
from lemur.notifications.views import notification_list
from lemur.logs import service as log_service
mod = Blueprint('certificates', __name__)
mod = Blueprint("certificates", __name__)
api = Api(mod)
FIELDS = {
'name': fields.String,
'id': fields.Integer,
'bits': fields.Integer,
'deleted': fields.String,
'issuer': fields.String,
'serial': fields.String,
'owner': fields.String,
'chain': fields.String,
'san': fields.String,
'active': fields.Boolean,
'description': fields.String,
'notBefore': fields.DateTime(dt_format='iso8601', attribute='not_before'),
'notAfter': fields.DateTime(dt_format='iso8601', attribute='not_after'),
'cn': fields.String,
'signingAlgorithm': fields.String(attribute='signing_algorithm'),
'status': fields.String,
'body': fields.String
}
class CertificatesListValid(AuthenticatedResource):
""" Defines the 'certificates/valid' endpoint """
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificatesListValid, self).__init__()
@validate_schema(None, certificates_output_schema)
def get(self):
"""
.. http:get:: /certificates/valid/<query>
The current list of not-expired certificates for a given common name, and owner
**Example request**:
.. sourcecode:: http
GET /certificates/valid?filter=cn;*.test.example.net&owner=joe@example.com
HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"items": [{
"status": null,
"cn": "*.test.example.net",
"chain": "",
"csr": "-----BEGIN CERTIFICATE REQUEST-----"
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}],
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"replaced": [],
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}],
"total": 1
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
parser = paginated_parser.copy()
args = parser.parse_args()
args["user"] = g.user
common_name = args["filter"].split(";")[1]
return service.query_common_name(common_name, args)
def valid_authority(authority_options):
"""
Defends against invalid authorities
class CertificatesNameQuery(AuthenticatedResource):
""" Defines the 'certificates/name' endpoint """
:param authority_options:
:return: :raise ValueError:
"""
name = authority_options['name']
authority = Authority.query.filter(Authority.name == name).one()
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificatesNameQuery, self).__init__()
if not authority:
raise ValueError("Unable to find authority specified")
@validate_schema(None, certificates_output_schema)
def get(self, certificate_name):
"""
.. http:get:: /certificates/name/<query>
if not authority.active:
raise ValueError("Selected authority [{0}] is not currently active".format(name))
The current list of certificates
return authority
**Example request**:
.. sourcecode:: http
def pem_str(value, name):
"""
Used to validate that the given string is a PEM formatted string
GET /certificates/name/WILDCARD.test.example.net-SymantecCorporation-20160603-20180112 HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
:param value:
:param name:
:return: :raise ValueError:
"""
try:
x509.load_pem_x509_certificate(bytes(value), default_backend())
except Exception:
raise ValueError("The parameter '{0}' needs to be a valid PEM string".format(name))
return value
**Example response**:
.. sourcecode:: http
def private_key_str(value, name):
"""
User to validate that a given string is a RSA private key
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
:param value:
:param name:
:return: :raise ValueError:
"""
try:
serialization.load_pem_private_key(bytes(value), None, backend=default_backend())
except Exception:
raise ValueError("The parameter '{0}' needs to be a valid RSA private key".format(name))
return value
{
"items": [{
"status": null,
"cn": "*.test.example.net",
"chain": "",
"csr": "-----BEGIN CERTIFICATE REQUEST-----"
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}],
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"replaced": [],
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}],
"total": 1
}
:query sortBy: field to sort on
:query sortDir: asc or desc
:query page: int. default is 1
:query filter: key value pair format is k;v
:query count: count number. default is 10
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
parser = paginated_parser.copy()
parser.add_argument("timeRange", type=int, dest="time_range", location="args")
parser.add_argument("owner", type=inputs.boolean, location="args")
parser.add_argument("id", type=str, location="args")
parser.add_argument("active", type=inputs.boolean, location="args")
parser.add_argument(
"destinationId", type=int, dest="destination_id", location="args"
)
parser.add_argument("creator", type=str, location="args")
parser.add_argument("show", type=str, location="args")
args = parser.parse_args()
args["user"] = g.user
return service.query_name(certificate_name, args)
class CertificatesList(AuthenticatedResource):
""" Defines the 'certificates' endpoint """
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificatesList, self).__init__()
@marshal_items(FIELDS)
@validate_schema(None, certificates_list_output_schema_factory)
def get(self):
"""
.. http:get:: /certificates
@ -131,52 +275,87 @@ class CertificatesList(AuthenticatedResource):
Content-Type: text/javascript
{
"items": [
{
"id": 1,
"name": "cert1",
"description": "this is cert1",
"bits": 2048,
"deleted": false,
"issuer": "ExampeInc.",
"serial": "123450",
"chain": "-----Begin ...",
"body": "-----Begin ...",
"san": true,
"owner": 'bob@example.com",
"active": true,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39",
"cn": "example.com",
"status": "unknown"
}
]
"items": [{
"status": null,
"cn": "*.test.example.net",
"chain": "",
"csr": "-----BEGIN CERTIFICATE REQUEST-----"
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}],
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"replaced": [],
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}],
"total": 1
}
:query sortBy: field to sort on
:query sortDir: acs or desc
:query sortDir: asc or desc
:query page: int. default is 1
:query filter: key value pair. format is k=v;
:query limit: limit number. default is 10
:query filter: key value pair format is k;v
:query count: count number. default is 10
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
parser = paginated_parser.copy()
parser.add_argument('timeRange', type=int, dest='time_range', location='args')
parser.add_argument('owner', type=bool, location='args')
parser.add_argument('id', type=str, location='args')
parser.add_argument('active', type=bool, location='args')
parser.add_argument('destinationId', type=int, dest="destination_id", location='args')
parser.add_argument('creator', type=str, location='args')
parser.add_argument('show', type=str, location='args')
parser.add_argument("timeRange", type=int, dest="time_range", location="args")
parser.add_argument("owner", type=inputs.boolean, location="args")
parser.add_argument("id", type=str, location="args")
parser.add_argument("active", type=inputs.boolean, location="args")
parser.add_argument(
"destinationId", type=int, dest="destination_id", location="args"
)
parser.add_argument("creator", type=str, location="args")
parser.add_argument("show", type=str, location="args")
parser.add_argument("showExpired", type=int, location="args")
args = parser.parse_args()
args["user"] = g.user
return service.render(args)
@marshal_items(FIELDS)
def post(self):
@validate_schema(certificate_input_schema, certificate_output_schema)
def post(self, data=None):
"""
.. http:post:: /certificates
@ -191,86 +370,38 @@ class CertificatesList(AuthenticatedResource):
Accept: application/json, text/javascript
{
"country": "US",
"state": "CA",
"location": "A Place",
"organization": "ExampleInc.",
"organizationalUnit": "Operations",
"owner": "bob@example.com",
"description": "test",
"selectedAuthority": "timetest2",
"authority": {
"body": "-----BEGIN...",
"name": "timetest2",
"chain": "",
"notBefore": "2015-06-05T15:20:59",
"active": true,
"id": 50,
"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": {
"isCritical": true,
"useKeyEncipherment": true,
"useDigitalSignature": true
},
"extendedKeyUsage": {
"isCritical": true,
"useServerAuthentication": true
},
"subjectKeyIdentifier": {
"includeSKI": true
},
"owner": "secure@example.net",
"commonName": "test.example.net",
"country": "US",
"extensions": {
"subAltNames": {
"names": []
"names": [
{
"nameType": "DNSName",
"value": "*.test.example.net"
},
{
"nameType": "DNSName",
"value": "www.test.example.net"
}
]
}
},
"commonName": "test",
"validityStart": "2015-06-05T07:00:00.000Z",
"validityEnd": "2015-06-16T07:00:00.000Z"
}
},
"replacements": [{
"id": 1
}],
"notify": true,
"validityEnd": "2026-01-01T08:00:00.000Z",
"authority": {
"name": "verisign"
},
"organization": "Netflix, Inc.",
"location": "Los Gatos",
"state": "California",
"validityStart": "2016-11-11T04:19:48.000Z",
"organizationalUnit": "Operations"
}
**Example response**:
@ -281,80 +412,99 @@ class CertificatesList(AuthenticatedResource):
Content-Type: text/javascript
{
"id": 1,
"name": "cert1",
"description": "this is cert1",
"status": null,
"cn": "*.test.example.net",
"chain": "",
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"deleted": false,
"issuer": "ExampeInc.",
"serial": "123450",
"chain": "-----Begin ...",
"body": "-----Begin ...",
"san": true,
"owner": "jimbob@example.com",
"active": false,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39",
"cn": "example.com",
"status": "unknown"
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}],
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [{
"id": 1
}],
"rotation": true,
"rotationPolicy": {"name": "default"},
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}
:arg extensions: extensions to be used in the certificate
:arg description: description for new certificate
:arg owner: owner email
:arg validityStart: when the certificate should start being valid
:arg validityEnd: when the certificate should expire
:arg authority: authority that should issue the certificate
:arg country: country for the CSR
:arg state: state for the CSR
:arg location: location for the CSR
:arg organization: organization for CSR
:arg commonName: certiifcate common name
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
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('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', required=True)
self.reqparse.add_argument('description', type=str, location='json')
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()
authority = args['authority']
role = role_service.get_by_name(authority.owner)
role = role_service.get_by_name(data["authority"].owner)
# all the authority role members should be allowed
roles = [x.name for x in authority.roles]
roles = [x.name for x in data["authority"].roles]
# allow "owner" roles by team DL
roles.append(role)
permission = AuthorityPermission(authority.id, roles)
authority_permission = AuthorityPermission(data["authority"].id, roles)
if permission.can():
return service.create(**args)
if authority_permission.can():
data["creator"] = g.user
cert = service.create(**data)
if isinstance(cert, Certificate):
# only log if created, not pending
log_service.create(g.user, "create_cert", certificate=cert)
return cert
return dict(message="You are not authorized to use {0}".format(args['authority'].name)), 403
return (
dict(
message="You are not authorized to use the authority: {0}".format(
data["authority"].name
)
),
403,
)
class CertificatesUpload(AuthenticatedResource):
""" Defines the 'certificates' upload endpoint """
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificatesUpload, self).__init__()
@marshal_items(FIELDS)
def post(self):
@validate_schema(certificate_upload_input_schema, certificate_output_schema)
def post(self, data=None):
"""
.. http:post:: /certificates/upload
@ -369,12 +519,16 @@ class CertificatesUpload(AuthenticatedResource):
Accept: application/json, text/javascript
{
"owner": "joe@exmaple.com",
"publicCert": "---Begin Public...",
"intermediateCert": "---Begin Public...",
"privateKey": "---Begin Private..."
"owner": "joe@example.com",
"body": "-----BEGIN CERTIFICATE-----...",
"chain": "-----BEGIN CERTIFICATE-----...",
"privateKey": "-----BEGIN RSA PRIVATE KEY-----..."
"csr": "-----BEGIN CERTIFICATE REQUEST-----..."
"destinations": [],
"notifications": [],
"replacements": [],
"roles": [],
"notify": true,
"name": "cert1"
}
@ -387,63 +541,86 @@ class CertificatesUpload(AuthenticatedResource):
Content-Type: text/javascript
{
"id": 1,
"name": "cert1",
"description": "this is cert1",
"bits": 2048,
"deleted": false,
"issuer": "ExampeInc.",
"serial": "123450",
"chain": "-----Begin ...",
"body": "-----Begin ...",
"san": true,
"owner": "joe@example.com",
"active": true,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39",
"signingAlgorithm": "sha2"
"cn": "example.com",
"status": "unknown"
"status": null,
"cn": "*.test.example.net",
"chain": "",
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}],
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"rotation": true,
"rotationPolicy": {"name": "default"},
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}
:arg owner: owner email for certificate
:arg publicCert: valid PEM public key for certificate
:arg intermediateCert valid PEM intermediate key for certificate
:arg privateKey: valid PEM private key for certificate
:arg destinations: list of aws destinations to upload the certificate to
:reqheader Authorization: OAuth token to authenticate
:statuscode 403: unauthenticated
:statuscode 200: no error
"""
self.reqparse.add_argument('description', type=str, location='json')
self.reqparse.add_argument('owner', type=str, required=True, location='json')
self.reqparse.add_argument('name', type=str, location='json')
self.reqparse.add_argument('publicCert', type=pem_str, required=True, dest='public_cert', location='json')
self.reqparse.add_argument('destinations', type=list, default=[], dest='destinations', location='json')
self.reqparse.add_argument('notifications', type=list, default=[], dest='notifications', location='json')
self.reqparse.add_argument('intermediateCert', type=pem_str, dest='intermediate_cert', location='json')
self.reqparse.add_argument('privateKey', type=private_key_str, dest='private_key', location='json')
args = self.reqparse.parse_args()
if args.get('destinations'):
if args.get('private_key'):
return service.upload(**args)
"""
data["creator"] = g.user
if data.get("destinations"):
if data.get("private_key"):
return service.upload(**data)
else:
raise Exception("Private key must be provided in order to upload certificate to AWS")
return service.upload(**args)
raise Exception(
"Private key must be provided in order to upload certificate to AWS"
)
return service.upload(**data)
class CertificatesStats(AuthenticatedResource):
""" Defines the 'certificates' stats endpoint """
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificatesStats, self).__init__()
def get(self):
self.reqparse.add_argument('metric', type=str, location='args')
self.reqparse.add_argument('range', default=32, type=int, location='args')
self.reqparse.add_argument('destinationId', dest='destination_id', location='args')
self.reqparse.add_argument('active', type=str, default='true', location='args')
self.reqparse.add_argument("metric", type=str, location="args")
self.reqparse.add_argument("range", default=32, type=int, location="args")
self.reqparse.add_argument(
"destinationId", dest="destination_id", location="args"
)
self.reqparse.add_argument("active", type=str, default="true", location="args")
args = self.reqparse.parse_args()
@ -478,7 +655,7 @@ class CertificatePrivateKey(AuthenticatedResource):
Content-Type: text/javascript
{
"key": "----Begin ...",
"key": "-----BEGIN ..."
}
:reqheader Authorization: OAuth token to authenticate
@ -489,17 +666,19 @@ class CertificatePrivateKey(AuthenticatedResource):
if not cert:
return dict(message="Cannot find specified certificate"), 404
role = role_service.get_by_name(cert.owner)
# allow creators
if g.current_user != cert.user:
owner_role = role_service.get_by_name(cert.owner)
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
permission = ViewKeyPermission(certificate_id, getattr(role, 'name', None))
if not permission.can():
return dict(message="You are not authorized to view this key"), 403
if permission.can():
response = make_response(jsonify(key=cert.private_key), 200)
response.headers['cache-control'] = 'private, max-age=0, no-cache, no-store'
response.headers['pragma'] = 'no-cache'
return response
return dict(message='You are not authorized to view this key'), 403
log_service.create(g.current_user, "key_view", certificate=cert)
response = make_response(jsonify(key=cert.private_key), 200)
response.headers["cache-control"] = "private, max-age=0, no-cache, no-store"
response.headers["pragma"] = "no-cache"
return response
class Certificates(AuthenticatedResource):
@ -507,7 +686,7 @@ class Certificates(AuthenticatedResource):
self.reqparse = reqparse.RequestParser()
super(Certificates, self).__init__()
@marshal_items(FIELDS)
@validate_schema(None, certificate_output_schema)
def get(self, certificate_id):
"""
.. http:get:: /certificates/1
@ -531,33 +710,67 @@ class Certificates(AuthenticatedResource):
Content-Type: text/javascript
{
"id": 1,
"name": "cert1",
"description": "this is cert1",
"status": null,
"cn": "*.test.example.net",
"chain": "",
"csr": "-----BEGIN CERTIFICATE REQUEST-----"
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"deleted": false,
"issuer": "ExampeInc.",
"serial": "123450",
"chain": "-----Begin ...",
"body": "-----Begin ...",
"san": true,
"owner": "bob@example.com",
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}],
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39",
"signingAlgorithm": "sha2",
"cn": "example.com",
"status": "unknown"
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"rotation": true,
"rotationPolicy": {"name": "default"},
"replaces": [],
"replaced": [],
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
return service.get(certificate_id)
@marshal_items(FIELDS)
def put(self, certificate_id):
@validate_schema(certificate_edit_input_schema, certificate_output_schema)
def put(self, certificate_id, data=None):
"""
.. http:put:: /certificates/1
@ -575,7 +788,8 @@ class Certificates(AuthenticatedResource):
"owner": "jimbob@example.com",
"active": false
"notifications": [],
"destinations": []
"destinations": [],
"replacements": []
}
**Example response**:
@ -587,60 +801,262 @@ class Certificates(AuthenticatedResource):
Content-Type: text/javascript
{
"id": 1,
"name": "cert1",
"description": "this is cert1",
"status": null,
"cn": "*.test.example.net",
"chain": "",
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"deleted": false,
"issuer": "ExampeInc.",
"serial": "123450",
"chain": "-----Begin ...",
"body": "-----Begin ...",
"san": true,
"owner": "jimbob@example.com",
"active": false,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39",
"cn": "example.com",
"status": "unknown",
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}]
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"rotation": true,
"rotationPolicy": {"name": "default"},
"san": null
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
self.reqparse.add_argument('active', type=bool, location='json')
self.reqparse.add_argument('owner', type=str, location='json')
self.reqparse.add_argument('description', type=str, location='json')
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
self.reqparse.add_argument('notifications', type=notification_list, default=[], location='json')
args = self.reqparse.parse_args()
cert = service.get(certificate_id)
if not cert:
return dict(message="Cannot find specified certificate"), 404
# allow creators
if g.current_user != cert.user:
owner_role = role_service.get_by_name(cert.owner)
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
if not permission.can():
return (
dict(message="You are not authorized to update this certificate"),
403,
)
for destination in data["destinations"]:
if destination.plugin.requires_key:
if not cert.private_key:
return (
dict(
message="Unable to add destination: {0}. Certificate does not have required private key.".format(
destination.label
)
),
400,
)
# if owner is changed, remove all notifications and roles associated with old owner
if cert.owner != data["owner"]:
service.cleanup_owner_roles_notification(cert.owner, data)
cert = service.update(certificate_id, **data)
log_service.create(g.current_user, "update_cert", certificate=cert)
return cert
@validate_schema(certificate_edit_input_schema, certificate_output_schema)
def post(self, certificate_id, data=None):
"""
.. http:post:: /certificates/1/update/notify
Update certificate notification
**Example request**:
.. sourcecode:: http
POST /certificates/1/update/notify HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
{
"notify": false
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"status": null,
"cn": "*.test.example.net",
"chain": "",
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notify": false,
"notifications": [{
"id": 1
}]
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"rotation": true,
"rotationPolicy": {"name": "default"},
"san": null
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
cert = service.get(certificate_id)
if not cert:
return dict(message="Cannot find specified certificate"), 404
# allow creators
if g.current_user != cert.user:
owner_role = role_service.get_by_name(cert.owner)
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
if not permission.can():
return (
dict(message="You are not authorized to update this certificate"),
403,
)
cert = service.update_notify(cert, data.get("notify"))
log_service.create(g.current_user, "update_cert", certificate=cert)
return cert
def delete(self, certificate_id, data=None):
"""
.. http:delete:: /certificates/1
Delete a certificate
**Example request**:
.. sourcecode:: http
DELETE /certificates/1 HTTP/1.1
Host: example.com
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 OK
:reqheader Authorization: OAuth token to authenticate
:statuscode 204: no error
:statuscode 403: unauthenticated
:statuscode 404: certificate not found
:statuscode 405: certificate deletion is disabled
"""
if not current_app.config.get("ALLOW_CERT_DELETION", False):
return dict(message="Certificate deletion is disabled"), 405
cert = service.get(certificate_id)
role = role_service.get_by_name(cert.owner)
permission = UpdateCertificatePermission(certificate_id, getattr(role, 'name', None))
if not cert:
return dict(message="Cannot find specified certificate"), 404
if permission.can():
return service.update(
certificate_id,
args['owner'],
args['description'],
args['active'],
args['destinations'],
args['notifications']
)
if cert.deleted:
return dict(message="Certificate is already deleted"), 412
return dict(message='You are not authorized to update this certificate'), 403
# allow creators
if g.current_user != cert.user:
owner_role = role_service.get_by_name(cert.owner)
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
if not permission.can():
return (
dict(message="You are not authorized to delete this certificate"),
403,
)
service.update(certificate_id, deleted=True)
log_service.create(g.current_user, "delete_cert", certificate=cert)
return "Certificate deleted", 204
class NotificationCertificatesList(AuthenticatedResource):
""" Defines the 'certificates' endpoint """
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(NotificationCertificatesList, self).__init__()
@marshal_items(FIELDS)
@validate_schema(None, certificates_output_schema)
def get(self, notification_id):
"""
.. http:get:: /notifications/1/certificates
@ -664,56 +1080,413 @@ class NotificationCertificatesList(AuthenticatedResource):
Content-Type: text/javascript
{
"items": [
{
"id": 1,
"name": "cert1",
"description": "this is cert1",
"bits": 2048,
"deleted": false,
"issuer": "ExampeInc.",
"serial": "123450",
"chain": "-----Begin ...",
"body": "-----Begin ...",
"san": true,
"owner": 'bob@example.com",
"active": true,
"notBefore": "2015-06-05T17:09:39",
"notAfter": "2015-06-10T17:09:39",
"signingAlgorithm": "sha2",
"cn": "example.com",
"status": "unknown"
}
]
"items": [{
"status": null,
"cn": "*.test.example.net",
"chain": "",
"csr": "-----BEGIN CERTIFICATE REQUEST-----"
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}],
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"replaced": [],
"rotation": true,
"rotationPolicy": {"name": "default"},
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}],
"total": 1
}
:query sortBy: field to sort on
:query sortDir: acs or desc
:query page: int. default is 1
:query filter: key value pair. format is k=v;
:query limit: limit number. default is 10
:query sortDir: asc or desc
:query page: int default is 1
:query filter: key value pair format is k;v
:query count: count number default is 10
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
parser = paginated_parser.copy()
parser.add_argument('timeRange', type=int, dest='time_range', location='args')
parser.add_argument('owner', type=bool, location='args')
parser.add_argument('id', type=str, location='args')
parser.add_argument('active', type=bool, location='args')
parser.add_argument('destinationId', type=int, dest="destination_id", location='args')
parser.add_argument('creator', type=str, location='args')
parser.add_argument('show', type=str, location='args')
parser.add_argument("timeRange", type=int, dest="time_range", location="args")
parser.add_argument("owner", type=inputs.boolean, location="args")
parser.add_argument("id", type=str, location="args")
parser.add_argument("active", type=inputs.boolean, location="args")
parser.add_argument(
"destinationId", type=int, dest="destination_id", location="args"
)
parser.add_argument("creator", type=str, location="args")
parser.add_argument("show", type=str, location="args")
parser.add_argument("showExpired", type=int, location="args")
args = parser.parse_args()
args['notification_id'] = notification_id
args["notification_id"] = notification_id
args["user"] = g.current_user
return service.render(args)
api.add_resource(CertificatesList, '/certificates', endpoint='certificates')
api.add_resource(Certificates, '/certificates/<int:certificate_id>', endpoint='certificate')
api.add_resource(CertificatesStats, '/certificates/stats', endpoint='certificateStats')
api.add_resource(CertificatesUpload, '/certificates/upload', endpoint='certificateUpload')
api.add_resource(CertificatePrivateKey, '/certificates/<int:certificate_id>/key', endpoint='privateKeyCertificates')
api.add_resource(NotificationCertificatesList, '/notifications/<int:notification_id>/certificates', endpoint='notificationCertificates')
class CertificatesReplacementsList(AuthenticatedResource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificatesReplacementsList, self).__init__()
@validate_schema(None, certificates_output_schema)
def get(self, certificate_id):
"""
.. http:get:: /certificates/1/replacements
One certificate
**Example request**:
.. sourcecode:: http
GET /certificates/1/replacements HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"items": [{
"status": null,
"cn": "*.test.example.net",
"chain": "",
"csr": "-----BEGIN CERTIFICATE REQUEST-----",
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}]
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"replaced": [],
"rotation": true,
"rotationPolicy": {"name": "default"},
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}],
"total": 1
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
return service.get(certificate_id).replaces
class CertificateExport(AuthenticatedResource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificateExport, self).__init__()
@validate_schema(certificate_export_input_schema, None)
def post(self, certificate_id, data=None):
"""
.. http:post:: /certificates/1/export
Export a certificate
**Example request**:
.. sourcecode:: http
PUT /certificates/1/export HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
{
"export": {
"plugin": {
"pluginOptions": [{
"available": ["Java Key Store (JKS)"],
"required": true,
"type": "select",
"name": "type",
"helpMessage": "Choose the format you wish to export",
"value": "Java Key Store (JKS)"
}, {
"required": false,
"type": "str",
"name": "passphrase",
"validation": "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[$@$!%*#?&])[A-Za-z\\d$@$!%*#?&]{8,}$",
"helpMessage": "If no passphrase is given one will be generated for you, we highly recommend this. Minimum length is 8."
}, {
"required": false,
"type": "str",
"name": "alias",
"helpMessage": "Enter the alias you wish to use for the keystore."
}],
"version": "unknown",
"description": "Attempts to generate a JKS keystore or truststore",
"title": "Java",
"author": "Kevin Glisson",
"type": "export",
"slug": "java-export"
}
}
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"data": "base64encodedstring",
"passphrase": "UAWOHW#&@_%!tnwmxh832025",
"extension": "jks"
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
cert = service.get(certificate_id)
if not cert:
return dict(message="Cannot find specified certificate"), 404
plugin = data["plugin"]["plugin_object"]
if plugin.requires_key:
if not cert.private_key:
return (
dict(
message="Unable to export certificate, plugin: {0} requires a private key but no key was found.".format(
plugin.slug
)
),
400,
)
else:
# allow creators
if g.current_user != cert.user:
owner_role = role_service.get_by_name(cert.owner)
permission = CertificatePermission(
owner_role, [x.name for x in cert.roles]
)
if not permission.can():
return (
dict(
message="You are not authorized to export this certificate."
),
403,
)
options = data["plugin"]["plugin_options"]
log_service.create(g.current_user, "key_view", certificate=cert)
extension, passphrase, data = plugin.export(
cert.body, cert.chain, cert.private_key, options
)
# we take a hit in message size when b64 encoding
return dict(
extension=extension,
passphrase=passphrase,
data=base64.b64encode(data).decode("utf-8"),
)
class CertificateRevoke(AuthenticatedResource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificateRevoke, self).__init__()
@validate_schema(None, None)
def put(self, certificate_id, data=None):
"""
.. http:put:: /certificates/1/revoke
Revoke a certificate
**Example request**:
.. sourcecode:: http
POST /certificates/1/revoke HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
'id': 1
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
cert = service.get(certificate_id)
if not cert:
return dict(message="Cannot find specified certificate"), 404
# allow creators
if g.current_user != cert.user:
owner_role = role_service.get_by_name(cert.owner)
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
if not permission.can():
return (
dict(message="You are not authorized to revoke this certificate."),
403,
)
if not cert.external_id:
return dict(message="Cannot revoke certificate. No external id found."), 400
if cert.endpoints:
return (
dict(
message="Cannot revoke certificate. Endpoints are deployed with the given certificate."
),
403,
)
plugin = plugins.get(cert.authority.plugin_name)
plugin.revoke_certificate(cert, data)
log_service.create(g.current_user, "revoke_cert", certificate=cert)
return dict(id=cert.id)
api.add_resource(
CertificateRevoke,
"/certificates/<int:certificate_id>/revoke",
endpoint="revokeCertificate",
)
api.add_resource(
CertificatesNameQuery,
"/certificates/name/<string:certificate_name>",
endpoint="certificatesNameQuery",
)
api.add_resource(CertificatesList, "/certificates", endpoint="certificates")
api.add_resource(
CertificatesListValid, "/certificates/valid", endpoint="certificatesListValid"
)
api.add_resource(
Certificates, "/certificates/<int:certificate_id>", endpoint="certificate"
)
api.add_resource(
Certificates, "/certificates/<int:certificate_id>/update/notify", endpoint="certificateUpdateNotify"
)
api.add_resource(CertificatesStats, "/certificates/stats", endpoint="certificateStats")
api.add_resource(
CertificatesUpload, "/certificates/upload", endpoint="certificateUpload"
)
api.add_resource(
CertificatePrivateKey,
"/certificates/<int:certificate_id>/key",
endpoint="privateKeyCertificates",
)
api.add_resource(
CertificateExport,
"/certificates/<int:certificate_id>/export",
endpoint="exportCertificate",
)
api.add_resource(
NotificationCertificatesList,
"/notifications/<int:notification_id>/certificates",
endpoint="notificationCertificates",
)
api.add_resource(
CertificatesReplacementsList,
"/certificates/<int:certificate_id>/replacements",
endpoint="replacements",
)

880
lemur/common/celery.py Normal file
View File

@ -0,0 +1,880 @@
"""
This module controls defines celery tasks and their applicable schedules. The celery beat server and workers will start
when invoked.
When ran in development mode (LEMUR_CONFIG=<location of development configuration file. To run both the celery
beat scheduler and a worker simultaneously, and to have jobs kick off starting at the next minute, run the following
command: celery -A lemur.common.celery worker --loglevel=info -l DEBUG -B
"""
import copy
import sys
import time
from celery import Celery
from celery.app.task import Context
from celery.exceptions import SoftTimeLimitExceeded
from celery.signals import task_failure, task_received, task_revoked, task_success
from datetime import datetime, timezone, timedelta
from flask import current_app
from lemur.authorities.service import get as get_authority
from lemur.certificates import cli as cli_certificate
from lemur.common.redis import RedisHandler
from lemur.destinations import service as destinations_service
from lemur.dns_providers import cli as cli_dns_providers
from lemur.endpoints import cli as cli_endpoints
from lemur.extensions import metrics, sentry
from lemur.factory import create_app
from lemur.notifications import cli as cli_notification
from lemur.notifications.messaging import send_pending_failure_notification
from lemur.pending_certificates import service as pending_certificate_service
from lemur.plugins.base import plugins
from lemur.sources.cli import clean, sync, validate_sources
from lemur.sources.service import add_aws_destination_to_sources
if current_app:
flask_app = current_app
else:
flask_app = create_app()
red = RedisHandler().redis()
def make_celery(app):
celery = Celery(
app.import_name,
backend=app.config.get("CELERY_RESULT_BACKEND"),
broker=app.config.get("CELERY_BROKER_URL"),
)
celery.conf.update(app.config)
TaskBase = celery.Task
class ContextTask(TaskBase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return TaskBase.__call__(self, *args, **kwargs)
celery.Task = ContextTask
return celery
celery = make_celery(flask_app)
def is_task_active(fun, task_id, args):
from celery.task.control import inspect
if not args:
args = "()" # empty args
i = inspect()
active_tasks = i.active()
for _, tasks in active_tasks.items():
for task in tasks:
if task.get("id") == task_id:
continue
if task.get("name") == fun and task.get("args") == str(args):
return True
return False
def get_celery_request_tags(**kwargs):
request = kwargs.get("request")
sender_hostname = "unknown"
sender = kwargs.get("sender")
if sender:
try:
sender_hostname = sender.hostname
except AttributeError:
sender_hostname = vars(sender.request).get("origin", "unknown")
if request and not isinstance(
request, Context
): # unlike others, task_revoked sends a Context for `request`
task_name = request.name
task_id = request.id
receiver_hostname = request.hostname
else:
task_name = sender.name
task_id = sender.request.id
receiver_hostname = sender.request.hostname
tags = {
"task_name": task_name,
"task_id": task_id,
"sender_hostname": sender_hostname,
"receiver_hostname": receiver_hostname,
}
if kwargs.get("exception"):
tags["error"] = repr(kwargs["exception"])
return tags
@celery.task()
def report_celery_last_success_metrics():
"""
For each celery task, this will determine the number of seconds since it has last been successful.
Celery tasks should be emitting redis stats with a deterministic key (In our case, `f"{task}.last_success"`.
report_celery_last_success_metrics should be ran periodically to emit metrics on when a task was last successful.
Admins can then alert when tasks are not ran when intended. Admins should also alert when no metrics are emitted
from this function.
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "recurrent task",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_time = int(time.time())
schedule = current_app.config.get("CELERYBEAT_SCHEDULE")
for _, t in schedule.items():
task = t.get("task")
last_success = int(red.get(f"{task}.last_success") or 0)
metrics.send(
f"{task}.time_since_last_success", "gauge", current_time - last_success
)
red.set(
f"{function}.last_success", int(time.time())
) # Alert if this metric is not seen
metrics.send(f"{function}.success", "counter", 1)
@task_received.connect
def report_number_pending_tasks(**kwargs):
"""
Report the number of pending tasks to our metrics broker every time a task is published. This metric can be used
for autoscaling workers.
https://docs.celeryproject.org/en/latest/userguide/signals.html#task-received
"""
with flask_app.app_context():
metrics.send(
"celery.new_pending_task",
"TIMER",
1,
metric_tags=get_celery_request_tags(**kwargs),
)
@task_success.connect
def report_successful_task(**kwargs):
"""
Report a generic success metric as tasks to our metrics broker every time a task finished correctly.
This metric can be used for autoscaling workers.
https://docs.celeryproject.org/en/latest/userguide/signals.html#task-success
"""
with flask_app.app_context():
tags = get_celery_request_tags(**kwargs)
red.set(f"{tags['task_name']}.last_success", int(time.time()))
metrics.send("celery.successful_task", "TIMER", 1, metric_tags=tags)
@task_failure.connect
def report_failed_task(**kwargs):
"""
Report a generic failure metric as tasks to our metrics broker every time a task fails.
This metric can be used for alerting.
https://docs.celeryproject.org/en/latest/userguide/signals.html#task-failure
"""
with flask_app.app_context():
log_data = {
"function": f"{__name__}.{sys._getframe().f_code.co_name}",
"Message": "Celery Task Failure",
}
# Add traceback if exception info is in the kwargs
einfo = kwargs.get("einfo")
if einfo:
log_data["traceback"] = einfo.traceback
error_tags = get_celery_request_tags(**kwargs)
log_data.update(error_tags)
current_app.logger.error(log_data)
metrics.send("celery.failed_task", "TIMER", 1, metric_tags=error_tags)
@task_revoked.connect
def report_revoked_task(**kwargs):
"""
Report a generic failure metric as tasks to our metrics broker every time a task is revoked.
This metric can be used for alerting.
https://docs.celeryproject.org/en/latest/userguide/signals.html#task-revoked
"""
with flask_app.app_context():
log_data = {
"function": f"{__name__}.{sys._getframe().f_code.co_name}",
"Message": "Celery Task Revoked",
}
error_tags = get_celery_request_tags(**kwargs)
log_data.update(error_tags)
current_app.logger.error(log_data)
metrics.send("celery.revoked_task", "TIMER", 1, metric_tags=error_tags)
@celery.task(soft_time_limit=600)
def fetch_acme_cert(id):
"""
Attempt to get the full certificate for the pending certificate listed.
Args:
id: an id of a PendingCertificate
"""
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
function = f"{__name__}.{sys._getframe().f_code.co_name}"
log_data = {
"function": function,
"message": "Resolving pending certificate {}".format(id),
"task_id": task_id,
"id": id,
}
current_app.logger.debug(log_data)
if task_id and is_task_active(log_data["function"], task_id, (id,)):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
pending_certs = pending_certificate_service.get_pending_certs([id])
new = 0
failed = 0
wrong_issuer = 0
acme_certs = []
# We only care about certs using the acme-issuer plugin
for cert in pending_certs:
cert_authority = get_authority(cert.authority_id)
if cert_authority.plugin_name == "acme-issuer":
acme_certs.append(cert)
else:
wrong_issuer += 1
authority = plugins.get("acme-issuer")
resolved_certs = authority.get_ordered_certificates(acme_certs)
for cert in resolved_certs:
real_cert = cert.get("cert")
# It's necessary to reload the pending cert due to detached instance: http://sqlalche.me/e/bhk3
pending_cert = pending_certificate_service.get(cert.get("pending_cert").id)
if not pending_cert:
log_data[
"message"
] = "Pending certificate doesn't exist anymore. Was it resolved by another process?"
current_app.logger.error(log_data)
continue
if real_cert:
# If a real certificate was returned from issuer, then create it in Lemur and mark
# the pending certificate as resolved
final_cert = pending_certificate_service.create_certificate(
pending_cert, real_cert, pending_cert.user
)
pending_certificate_service.update(
cert.get("pending_cert").id, resolved_cert_id=final_cert.id
)
pending_certificate_service.update(
cert.get("pending_cert").id, resolved=True
)
# add metrics to metrics extension
new += 1
else:
failed += 1
error_log = copy.deepcopy(log_data)
error_log["message"] = "Pending certificate creation failure"
error_log["pending_cert_id"] = pending_cert.id
error_log["last_error"] = cert.get("last_error")
error_log["cn"] = pending_cert.cn
if pending_cert.number_attempts > 4:
error_log["message"] = "Deleting pending certificate"
send_pending_failure_notification(
pending_cert, notify_owner=pending_cert.notify
)
# Mark the pending cert as resolved
pending_certificate_service.update(
cert.get("pending_cert").id, resolved=True
)
else:
pending_certificate_service.increment_attempt(pending_cert)
pending_certificate_service.update(
cert.get("pending_cert").id, status=str(cert.get("last_error"))
)
# Add failed pending cert task back to queue
fetch_acme_cert.delay(id)
current_app.logger.error(error_log)
log_data["message"] = "Complete"
log_data["new"] = new
log_data["failed"] = failed
log_data["wrong_issuer"] = wrong_issuer
current_app.logger.debug(log_data)
metrics.send(f"{function}.resolved", "gauge", new)
metrics.send(f"{function}.failed", "gauge", failed)
metrics.send(f"{function}.wrong_issuer", "gauge", wrong_issuer)
print(
"[+] Certificates: New: {new} Failed: {failed} Not using ACME: {wrong_issuer}".format(
new=new, failed=failed, wrong_issuer=wrong_issuer
)
)
return log_data
@celery.task()
def fetch_all_pending_acme_certs():
"""Instantiate celery workers to resolve all pending Acme certificates"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "Starting job.",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
pending_certs = pending_certificate_service.get_unresolved_pending_certs()
# We only care about certs using the acme-issuer plugin
for cert in pending_certs:
cert_authority = get_authority(cert.authority_id)
if cert_authority.plugin_name == "acme-issuer":
if datetime.now(timezone.utc) - cert.last_updated > timedelta(minutes=5):
log_data["message"] = "Triggering job for cert {}".format(cert.name)
log_data["cert_name"] = cert.name
log_data["cert_id"] = cert.id
current_app.logger.debug(log_data)
fetch_acme_cert.delay(cert.id)
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task()
def remove_old_acme_certs():
"""Prune old pending acme certificates from the database"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "Starting job.",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
pending_certs = pending_certificate_service.get_pending_certs("all")
# Delete pending certs more than a week old
for cert in pending_certs:
if datetime.now(timezone.utc) - cert.last_updated > timedelta(days=7):
log_data["pending_cert_id"] = cert.id
log_data["pending_cert_name"] = cert.name
log_data["message"] = "Deleting pending certificate"
current_app.logger.debug(log_data)
pending_certificate_service.delete(cert)
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task()
def clean_all_sources():
"""
This function will clean unused certificates from sources. This is a destructive operation and should only
be ran periodically. This function triggers one celery task per source.
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "Creating celery task to clean source",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
sources = validate_sources("all")
for source in sources:
log_data["source"] = source.label
current_app.logger.debug(log_data)
clean_source.delay(source.label)
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=3600)
def clean_source(source):
"""
This celery task will clean the specified source. This is a destructive operation that will delete unused
certificates from each source.
:param source:
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "Cleaning source",
"source": source,
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, (source,)):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
clean([source], True)
except SoftTimeLimitExceeded:
log_data["message"] = "Clean source: Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return log_data
@celery.task()
def sync_all_sources():
"""
This function will sync certificates from all sources. This function triggers one celery task per source.
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "creating celery task to sync source",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
sources = validate_sources("all")
for source in sources:
log_data["source"] = source.label
current_app.logger.debug(log_data)
sync_source.delay(source.label)
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=7200)
def sync_source(source):
"""
This celery task will sync the specified source.
:param source:
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "Syncing source",
"source": source,
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, (source,)):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
sync([source])
metrics.send(
f"{function}.success", "counter", 1, metric_tags={"source": source}
)
except SoftTimeLimitExceeded:
log_data["message"] = "Error syncing source: Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send(
"sync_source_timeout", "counter", 1, metric_tags={"source": source}
)
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return
log_data["message"] = "Done syncing source"
current_app.logger.debug(log_data)
metrics.send(f"{function}.success", "counter", 1, metric_tags={"source": source})
return log_data
@celery.task()
def sync_source_destination():
"""
This celery task will sync destination and source, to make sure all new destinations are also present as source.
Some destinations do not qualify as sources, and hence should be excluded from being added as sources
We identify qualified destinations based on the sync_as_source attributed of the plugin.
The destination sync_as_source_name reveals the name of the suitable source-plugin.
We rely on account numbers to avoid duplicates.
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "syncing AWS destinations and sources",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
for dst in destinations_service.get_all():
if add_aws_destination_to_sources(dst):
log_data["message"] = "new source added"
log_data["source"] = dst.label
current_app.logger.debug(log_data)
log_data["message"] = "completed Syncing AWS destinations and sources"
current_app.logger.debug(log_data)
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=3600)
def certificate_reissue():
"""
This celery task reissues certificates which are pending reissue
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "reissuing certificates",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
cli_certificate.reissue(None, True)
except SoftTimeLimitExceeded:
log_data["message"] = "Certificate reissue: Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return
log_data["message"] = "reissuance completed"
current_app.logger.debug(log_data)
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=3600)
def certificate_rotate(**kwargs):
"""
This celery task rotates certificates which are reissued but having endpoints attached to the replaced cert
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
region = kwargs.get("region")
log_data = {
"function": function,
"message": "rotating certificates",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
if region:
log_data["region"] = region
cli_certificate.rotate_region(None, None, None, None, True, region)
else:
cli_certificate.rotate(None, None, None, None, True)
except SoftTimeLimitExceeded:
log_data["message"] = "Certificate rotate: Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return
log_data["message"] = "rotation completed"
current_app.logger.debug(log_data)
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=3600)
def endpoints_expire():
"""
This celery task removes all endpoints that have not been recently updated
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "endpoints expire",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
cli_endpoints.expire(2) # Time in hours
except SoftTimeLimitExceeded:
log_data["message"] = "endpoint expire: Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=600)
def get_all_zones():
"""
This celery syncs all zones from the available dns providers
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "refresh all zones from available DNS providers",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
cli_dns_providers.get_all_zones()
except SoftTimeLimitExceeded:
log_data["message"] = "get all zones: Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=3600)
def check_revoked():
"""
This celery task attempts to check if any certs are expired
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "check if any valid certificate is revoked",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
cli_certificate.check_revoked()
except SoftTimeLimitExceeded:
log_data["message"] = "Checking revoked: Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=3600)
def notify_expirations():
"""
This celery task notifies about expiring certs
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "notify for cert expiration",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
cli_notification.expirations(
current_app.config.get("EXCLUDE_CN_FROM_NOTIFICATION", [])
)
except SoftTimeLimitExceeded:
log_data["message"] = "Notify expiring Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=3600)
def enable_autorotate_for_certs_attached_to_endpoint():
"""
This celery task automatically enables autorotation for unexpired certificates that are
attached to an endpoint but do not have autorotate enabled.
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"task_id": task_id,
"message": "Enabling autorotate to eligible certificates",
}
current_app.logger.debug(log_data)
cli_certificate.automatically_enable_autorotate()
metrics.send(f"{function}.success", "counter", 1)
return log_data
@celery.task(soft_time_limit=3600)
def deactivate_entrust_test_certificates():
"""
This celery task attempts to deactivate all not yet deactivated Entrust certificates, and should only run in TEST
:return:
"""
function = f"{__name__}.{sys._getframe().f_code.co_name}"
task_id = None
if celery.current_task:
task_id = celery.current_task.request.id
log_data = {
"function": function,
"message": "deactivate entrust certificates",
"task_id": task_id,
}
if task_id and is_task_active(function, task_id, None):
log_data["message"] = "Skipping task: Task is already active"
current_app.logger.debug(log_data)
return
current_app.logger.debug(log_data)
try:
cli_certificate.deactivate_entrust_certificates()
except SoftTimeLimitExceeded:
log_data["message"] = "Time limit exceeded."
current_app.logger.error(log_data)
sentry.captureException()
metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function})
return
metrics.send(f"{function}.success", "counter", 1)
return log_data

298
lemur/common/defaults.py Normal file
View File

@ -0,0 +1,298 @@
import re
import unicodedata
from cryptography import x509
from cryptography.hazmat.primitives.serialization import Encoding
from flask import current_app
from lemur.common.utils import is_selfsigned
from lemur.extensions import sentry
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE
def text_to_slug(value, joiner="-"):
"""
Normalize a string to a "slug" value, stripping character accents and removing non-alphanum characters.
A series of non-alphanumeric characters is replaced with the joiner character.
"""
# Strip all character accents: decompose Unicode characters and then drop combining chars.
value = "".join(
c for c in unicodedata.normalize("NFKD", value) if not unicodedata.combining(c)
)
# Replace all remaining non-alphanumeric characters with joiner string. Multiple characters get collapsed into a
# single joiner. Except, keep 'xn--' used in IDNA domain names as is.
value = re.sub(r"[^A-Za-z0-9.]+(?<!xn--)", joiner, value)
# '-' in the beginning or end of string looks ugly.
return value.strip(joiner)
def certificate_name(common_name, issuer, not_before, not_after, san):
"""
Create a name for our certificate. A naming standard
is based on a series of templates. The name includes
useful information such as Common Name, Validation dates,
and Issuer.
:param san:
:param common_name:
:param not_after:
:param issuer:
:param not_before:
:rtype: str
:return:
"""
if san:
t = SAN_NAMING_TEMPLATE
else:
t = DEFAULT_NAMING_TEMPLATE
temp = t.format(
subject=common_name,
issuer=issuer.replace(" ", ""),
not_before=not_before.strftime("%Y%m%d"),
not_after=not_after.strftime("%Y%m%d"),
)
temp = temp.replace("*", "WILDCARD")
return text_to_slug(temp)
def signing_algorithm(cert):
return cert.signature_hash_algorithm.name
def common_name(cert):
"""
Attempts to get a sane common name from a given certificate.
:param cert:
:return: Common name or None
"""
try:
subject_oid = cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)
if len(subject_oid) > 0:
return subject_oid[0].value.strip()
return None
except Exception as e:
sentry.captureException()
current_app.logger.error(
{
"message": "Unable to get common name",
"error": e,
"public_key": cert.public_bytes(Encoding.PEM).decode("utf-8")
},
exc_info=True
)
def organization(cert):
"""
Attempt to get the organization name from a given certificate.
:param cert:
:return:
"""
try:
o = cert.subject.get_attributes_for_oid(x509.OID_ORGANIZATION_NAME)
if not o:
return None
return o[0].value.strip()
except Exception as e:
sentry.captureException()
current_app.logger.error("Unable to get organization! {0}".format(e))
def organizational_unit(cert):
"""
Attempt to get the organization unit from a given certificate.
:param cert:
:return:
"""
try:
ou = cert.subject.get_attributes_for_oid(x509.OID_ORGANIZATIONAL_UNIT_NAME)
if not ou:
return None
return ou[0].value.strip()
except Exception as e:
sentry.captureException()
current_app.logger.error("Unable to get organizational unit! {0}".format(e))
def country(cert):
"""
Attempt to get the country from a given certificate.
:param cert:
:return:
"""
try:
c = cert.subject.get_attributes_for_oid(x509.OID_COUNTRY_NAME)
if not c:
return None
return c[0].value.strip()
except Exception as e:
sentry.captureException()
current_app.logger.error("Unable to get country! {0}".format(e))
def state(cert):
"""
Attempt to get the from a given certificate.
:param cert:
:return:
"""
try:
s = cert.subject.get_attributes_for_oid(x509.OID_STATE_OR_PROVINCE_NAME)
if not s:
return None
return s[0].value.strip()
except Exception as e:
sentry.captureException()
current_app.logger.error("Unable to get state! {0}".format(e))
def location(cert):
"""
Attempt to get the location name from a given certificate.
:param cert:
:return:
"""
try:
loc = cert.subject.get_attributes_for_oid(x509.OID_LOCALITY_NAME)
if not loc:
return None
return loc[0].value.strip()
except Exception as e:
sentry.captureException()
current_app.logger.error("Unable to get location! {0}".format(e))
def domains(cert):
"""
Attempts to get an domains listed in a certificate.
If 'subjectAltName' extension is not available we simply
return the common name.
:param cert:
:return: List of domains
"""
domains = []
try:
ext = cert.extensions.get_extension_for_oid(x509.OID_SUBJECT_ALTERNATIVE_NAME)
entries = ext.value.get_values_for_type(x509.DNSName)
for entry in entries:
domains.append(entry)
except x509.ExtensionNotFound:
if current_app.config.get("LOG_SSL_SUBJ_ALT_NAME_ERRORS", True):
sentry.captureException()
except Exception as e:
sentry.captureException()
return domains
def serial(cert):
"""
Fetch the serial number from the certificate.
:param cert:
:return: serial number
"""
return cert.serial_number
def san(cert):
"""
Determines if a given certificate is a SAN certificate.
SAN certificates are simply certificates that cover multiple domains.
:param cert:
:return: Bool
"""
if len(domains(cert)) > 1:
return True
def is_wildcard(cert):
"""
Determines if certificate is a wildcard certificate.
:param cert:
:return: Bool
"""
d = domains(cert)
if len(d) == 1 and d[0][0:1] == "*":
return True
if cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value[0:1] == "*":
return True
def bitstrength(cert):
"""
Calculates a certificates public key bit length.
:param cert:
:return: Integer
"""
try:
return cert.public_key().key_size
except AttributeError:
sentry.captureException()
current_app.logger.debug("Unable to get bitstrength.")
def issuer(cert):
"""
Gets a sane issuer slug from a given certificate, stripping non-alphanumeric characters.
For self-signed certificates, the special value '<selfsigned>' is returned.
If issuer cannot be determined, '<unknown>' is returned.
:param cert: Parsed certificate object
:return: Issuer slug
"""
# If certificate is self-signed, we return a special value -- there really is no distinct "issuer" for it
if is_selfsigned(cert):
return "<selfsigned>"
# Try Common Name or fall back to Organization name
attrs = cert.issuer.get_attributes_for_oid(
x509.OID_COMMON_NAME
) or cert.issuer.get_attributes_for_oid(x509.OID_ORGANIZATION_NAME)
if not attrs:
current_app.logger.error(
"Unable to get issuer! Cert serial {:x}".format(cert.serial_number)
)
return "<unknown>"
return text_to_slug(attrs[0].value, "")
def not_before(cert):
"""
Gets the naive datetime of the certificates 'not_before' field.
This field denotes the first date in time which the given certificate
is valid.
:param cert:
:return: Datetime
"""
return cert.not_valid_before
def not_after(cert):
"""
Gets the naive datetime of the certificates 'not_after' field.
This field denotes the last date in time which the given certificate
is valid.
:return: Datetime
"""
return cert.not_valid_after

443
lemur/common/fields.py Normal file
View File

@ -0,0 +1,443 @@
"""
.. module: lemur.common.fields
:platform: Unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import arrow
import warnings
import ipaddress
from flask import current_app
from datetime import datetime as dt
from cryptography import x509
from marshmallow import utils
from marshmallow.fields import Field
from marshmallow.exceptions import ValidationError
from lemur.common import validators
class Hex(Field):
"""
A hex formatted string.
"""
def _serialize(self, value, attr, obj):
if value:
value = hex(int(value))[2:].upper()
return value
class ArrowDateTime(Field):
"""A formatted datetime string in UTC.
Example: ``'2014-12-22T03:12:58.019077+00:00'``
Timezone-naive `datetime` objects are converted to
UTC (+00:00) by :meth:`Schema.dump <marshmallow.Schema.dump>`.
:meth:`Schema.load <marshmallow.Schema.load>` returns `datetime`
objects that are timezone-aware.
:param str format: Either ``"rfc"`` (for RFC822), ``"iso"`` (for ISO8601),
or a date format string. If `None`, defaults to "iso".
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
DATEFORMAT_SERIALIZATION_FUNCS = {
"iso": utils.isoformat,
"iso8601": utils.isoformat,
"rfc": utils.rfcformat,
"rfc822": utils.rfcformat,
}
DATEFORMAT_DESERIALIZATION_FUNCS = {
"iso": utils.from_iso,
"iso8601": utils.from_iso,
"rfc": utils.from_rfc,
"rfc822": utils.from_rfc,
}
DEFAULT_FORMAT = "iso"
localtime = False
default_error_messages = {
"invalid": "Not a valid datetime.",
"format": '"{input}" cannot be formatted as a datetime.',
}
def __init__(self, format=None, **kwargs):
super(ArrowDateTime, self).__init__(**kwargs)
# Allow this to be None. It may be set later in the ``_serialize``
# or ``_desrialize`` methods This allows a Schema to dynamically set the
# dateformat, e.g. from a Meta option
self.dateformat = format
def _add_to_schema(self, field_name, schema):
super(ArrowDateTime, self)._add_to_schema(field_name, schema)
self.dateformat = self.dateformat or schema.opts.dateformat
def _serialize(self, value, attr, obj):
if value is None:
return None
self.dateformat = self.dateformat or self.DEFAULT_FORMAT
format_func = self.DATEFORMAT_SERIALIZATION_FUNCS.get(self.dateformat, None)
if format_func:
try:
return format_func(value, localtime=self.localtime)
except (AttributeError, ValueError) as err:
self.fail("format", input=value)
else:
return value.strftime(self.dateformat)
def _deserialize(self, value, attr, data):
if not value: # Falsy values, e.g. '', None, [] are not valid
raise self.fail("invalid")
self.dateformat = self.dateformat or self.DEFAULT_FORMAT
func = self.DATEFORMAT_DESERIALIZATION_FUNCS.get(self.dateformat)
if func:
try:
return arrow.get(func(value))
except (TypeError, AttributeError, ValueError):
raise self.fail("invalid")
elif self.dateformat:
try:
return dt.datetime.strptime(value, self.dateformat)
except (TypeError, AttributeError, ValueError):
raise self.fail("invalid")
elif utils.dateutil_available:
try:
return arrow.get(utils.from_datestring(value))
except TypeError:
raise self.fail("invalid")
else:
warnings.warn(
"It is recommended that you install python-dateutil "
"for improved datetime deserialization."
)
raise self.fail("invalid")
class KeyUsageExtension(Field):
"""An x509.KeyUsage ExtensionType object
Dict of KeyUsage names/values are deserialized into an x509.KeyUsage object
and back.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
def _serialize(self, value, attr, obj):
return {
"useDigitalSignature": value.digital_signature,
"useNonRepudiation": value.content_commitment,
"useKeyEncipherment": value.key_encipherment,
"useDataEncipherment": value.data_encipherment,
"useKeyAgreement": value.key_agreement,
"useKeyCertSign": value.key_cert_sign,
"useCRLSign": value.crl_sign,
"useEncipherOnly": value._encipher_only,
"useDecipherOnly": value._decipher_only,
}
def _deserialize(self, value, attr, data):
keyusages = {
"digital_signature": False,
"content_commitment": False,
"key_encipherment": False,
"data_encipherment": False,
"key_agreement": False,
"key_cert_sign": False,
"crl_sign": False,
"encipher_only": False,
"decipher_only": False,
}
for k, v in value.items():
if k == "useDigitalSignature":
keyusages["digital_signature"] = v
elif k == "useNonRepudiation":
keyusages["content_commitment"] = v
elif k == "useKeyEncipherment":
keyusages["key_encipherment"] = v
elif k == "useDataEncipherment":
keyusages["data_encipherment"] = v
elif k == "useKeyCertSign":
keyusages["key_cert_sign"] = v
elif k == "useCRLSign":
keyusages["crl_sign"] = v
elif k == "useKeyAgreement":
keyusages["key_agreement"] = v
elif k == "useEncipherOnly" and v:
keyusages["encipher_only"] = True
keyusages["key_agreement"] = True
elif k == "useDecipherOnly" and v:
keyusages["decipher_only"] = True
keyusages["key_agreement"] = True
if keyusages["encipher_only"] and keyusages["decipher_only"]:
raise ValidationError(
"A certificate cannot have both Encipher Only and Decipher Only Extended Key Usages."
)
return x509.KeyUsage(
digital_signature=keyusages["digital_signature"],
content_commitment=keyusages["content_commitment"],
key_encipherment=keyusages["key_encipherment"],
data_encipherment=keyusages["data_encipherment"],
key_agreement=keyusages["key_agreement"],
key_cert_sign=keyusages["key_cert_sign"],
crl_sign=keyusages["crl_sign"],
encipher_only=keyusages["encipher_only"],
decipher_only=keyusages["decipher_only"],
)
class ExtendedKeyUsageExtension(Field):
"""An x509.ExtendedKeyUsage ExtensionType object
Dict of ExtendedKeyUsage names/values are deserialized into an x509.ExtendedKeyUsage object
and back.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
def _serialize(self, value, attr, obj):
usages = value._usages
usage_list = {}
for usage in usages:
if usage == x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH:
usage_list["useClientAuthentication"] = True
elif usage == x509.oid.ExtendedKeyUsageOID.SERVER_AUTH:
usage_list["useServerAuthentication"] = True
elif usage == x509.oid.ExtendedKeyUsageOID.CODE_SIGNING:
usage_list["useCodeSigning"] = True
elif usage == x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION:
usage_list["useEmailProtection"] = True
elif usage == x509.oid.ExtendedKeyUsageOID.TIME_STAMPING:
usage_list["useTimestamping"] = True
elif usage == x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING:
usage_list["useOCSPSigning"] = True
elif usage.dotted_string == "1.3.6.1.5.5.7.3.14":
usage_list["useEapOverLAN"] = True
elif usage.dotted_string == "1.3.6.1.5.5.7.3.13":
usage_list["useEapOverPPP"] = True
elif usage.dotted_string == "1.3.6.1.4.1.311.20.2.2":
usage_list["useSmartCardLogon"] = True
else:
current_app.logger.warning(
"Unable to serialize ExtendedKeyUsage with OID: {usage}".format(
usage=usage.dotted_string
)
)
return usage_list
def _deserialize(self, value, attr, data):
usage_oids = []
for k, v in value.items():
if k == "useClientAuthentication" and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH)
elif k == "useServerAuthentication" and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.SERVER_AUTH)
elif k == "useCodeSigning" and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.CODE_SIGNING)
elif k == "useEmailProtection" and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION)
elif k == "useTimestamping" and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.TIME_STAMPING)
elif k == "useOCSPSigning" and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING)
elif k == "useEapOverLAN" and v:
usage_oids.append(x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.3.14"))
elif k == "useEapOverPPP" and v:
usage_oids.append(x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.3.13"))
elif k == "useSmartCardLogon" and v:
usage_oids.append(x509.oid.ObjectIdentifier("1.3.6.1.4.1.311.20.2.2"))
else:
current_app.logger.warning(
"Unable to deserialize ExtendedKeyUsage with name: {key}".format(
key=k
)
)
return x509.ExtendedKeyUsage(usage_oids)
class BasicConstraintsExtension(Field):
"""An x509.BasicConstraints ExtensionType object
Dict of CA boolean and a path_length integer names/values are deserialized into an x509.BasicConstraints object
and back.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
def _serialize(self, value, attr, obj):
return {"ca": value.ca, "path_length": value.path_length}
def _deserialize(self, value, attr, data):
ca = value.get("ca", False)
path_length = value.get("path_length", None)
if ca:
if not isinstance(path_length, (type(None), int)):
raise ValidationError(
"A CA certificate path_length (for BasicConstraints) must be None or an integer."
)
return x509.BasicConstraints(ca=True, path_length=path_length)
else:
return x509.BasicConstraints(ca=False, path_length=None)
class SubjectAlternativeNameExtension(Field):
"""An x509.SubjectAlternativeName ExtensionType object
Dict of CA boolean and a path_length integer names/values are deserialized into an x509.BasicConstraints object
and back.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
def _serialize(self, value, attr, obj):
general_names = []
name_type = None
if value:
for name in value._general_names:
value = name.value
if isinstance(name, x509.DNSName):
name_type = "DNSName"
elif isinstance(name, x509.IPAddress):
if isinstance(value, ipaddress.IPv4Network):
name_type = "IPNetwork"
else:
name_type = "IPAddress"
value = str(value)
elif isinstance(name, x509.UniformResourceIdentifier):
name_type = "uniformResourceIdentifier"
elif isinstance(name, x509.DirectoryName):
name_type = "directoryName"
elif isinstance(name, x509.RFC822Name):
name_type = "rfc822Name"
elif isinstance(name, x509.RegisteredID):
name_type = "registeredID"
value = value.dotted_string
else:
current_app.logger.warning(
"Unknown SubAltName type: {name}".format(name=name)
)
continue
general_names.append({"nameType": name_type, "value": value})
return general_names
def _deserialize(self, value, attr, data):
general_names = []
for name in value:
if name["nameType"] == "DNSName":
validators.sensitive_domain(name["value"])
general_names.append(x509.DNSName(name["value"]))
elif name["nameType"] == "IPAddress":
general_names.append(
x509.IPAddress(ipaddress.ip_address(name["value"]))
)
elif name["nameType"] == "IPNetwork":
general_names.append(
x509.IPAddress(ipaddress.ip_network(name["value"]))
)
elif name["nameType"] == "uniformResourceIdentifier":
general_names.append(x509.UniformResourceIdentifier(name["value"]))
elif name["nameType"] == "directoryName":
# TODO: Need to parse a string in name['value'] like:
# 'CN=Common Name, O=Org Name, OU=OrgUnit Name, C=US, ST=ST, L=City/emailAddress=person@example.com'
# or
# 'CN=Common Name/O=Org Name/OU=OrgUnit Name/C=US/ST=NH/L=City/emailAddress=person@example.com'
# and turn it into something like:
# x509.Name([
# x509.NameAttribute(x509.OID_COMMON_NAME, "Common Name"),
# x509.NameAttribute(x509.OID_ORGANIZATION_NAME, "Org Name"),
# x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, "OrgUnit Name"),
# x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
# x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, "NH"),
# x509.NameAttribute(x509.OID_LOCALITY_NAME, "City"),
# x509.NameAttribute(x509.OID_EMAIL_ADDRESS, "person@example.com")
# ]
# general_names.append(x509.DirectoryName(x509.Name(BLAH))))
pass
elif name["nameType"] == "rfc822Name":
general_names.append(x509.RFC822Name(name["value"]))
elif name["nameType"] == "registeredID":
general_names.append(
x509.RegisteredID(x509.ObjectIdentifier(name["value"]))
)
elif name["nameType"] == "otherName":
# This has two inputs (type and value), so it doesn't fit the mold of the rest of these GeneralName entities.
# general_names.append(x509.OtherName(name['type'], bytes(name['value']), 'utf-8'))
pass
elif name["nameType"] == "x400Address":
# The Python Cryptography library doesn't support x400Address types (yet?)
pass
elif name["nameType"] == "EDIPartyName":
# The Python Cryptography library doesn't support EDIPartyName types (yet?)
pass
else:
current_app.logger.warning(
"Unable to deserialize SubAltName with type: {name_type}".format(
name_type=name["nameType"]
)
)
return x509.SubjectAlternativeName(general_names)

View File

@ -1,16 +1,29 @@
"""
.. module: lemur.common.health
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import Blueprint
from lemur.database import db
from lemur.extensions import sentry
mod = Blueprint('healthCheck', __name__)
mod = Blueprint("healthCheck", __name__)
@mod.route('/healthcheck')
@mod.route("/healthcheck")
def health():
return 'ok'
try:
if healthcheck(db):
return "ok"
except Exception:
sentry.captureException()
return "db check failed"
def healthcheck(db):
with db.engine.connect() as connection:
connection.execute("SELECT 1;")
return True

View File

@ -1,13 +1,15 @@
"""
.. module: lemur.common.managers
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import current_app
from lemur.exceptions import InvalidConfiguration
# inspired by https://github.com/getsentry/sentry
class InstanceManager(object):
@ -50,7 +52,7 @@ class InstanceManager(object):
results = []
for cls_path in class_list:
module_name, class_name = cls_path.rsplit('.', 1)
module_name, class_name = cls_path.rsplit(".", 1)
try:
module = __import__(module_name, {}, {}, class_name)
cls = getattr(module, class_name)
@ -58,9 +60,18 @@ class InstanceManager(object):
results.append(cls())
else:
results.append(cls)
except Exception:
current_app.logger.exception('Unable to import %s', cls_path)
except InvalidConfiguration as e:
current_app.logger.warning(
"Plugin '{0}' may not work correctly. {1}".format(class_name, e)
)
except Exception as e:
current_app.logger.exception(
"Unable to import {0}. Reason: {1}".format(cls_path, e)
)
continue
self.cache = results
return results

25
lemur/common/missing.py Normal file
View File

@ -0,0 +1,25 @@
import arrow
from flask import current_app
from lemur.common.utils import is_weekend
def convert_validity_years(data):
"""
Convert validity years to validity_start and validity_end
:param data:
:return:
"""
if data.get("validity_years"):
now = arrow.utcnow()
data["validity_start"] = now.isoformat()
end = now.shift(years=+int(data["validity_years"]))
if not current_app.config.get("LEMUR_ALLOW_WEEKEND_EXPIRATION", True):
if is_weekend(end):
end = end.shift(days=-2)
data["validity_end"] = end.isoformat()
return data

52
lemur/common/redis.py Normal file
View File

@ -0,0 +1,52 @@
"""
Helper Class for Redis
"""
import redis
import sys
from flask import current_app
from lemur.extensions import sentry
from lemur.factory import create_app
if current_app:
flask_app = current_app
else:
flask_app = create_app()
class RedisHandler:
def __init__(self, host=flask_app.config.get('REDIS_HOST', 'localhost'),
port=flask_app.config.get('REDIS_PORT', 6379),
db=flask_app.config.get('REDIS_DB', 0)):
self.host = host
self.port = port
self.db = db
def redis(self, db=0):
# The decode_responses flag here directs the client to convert the responses from Redis into Python strings
# using the default encoding utf-8. This is client specific.
function = f"{__name__}.{sys._getframe().f_code.co_name}"
try:
red = redis.StrictRedis(host=self.host, port=self.port, db=self.db, encoding="utf-8", decode_responses=True)
red.set("test", 0)
except redis.ConnectionError:
log_data = {
"function": function,
"message": "Redis Connection error",
"host": self.host,
"port": self.port
}
current_app.logger.error(log_data)
sentry.captureException()
return red
def redis_get(key, default=None):
red = RedisHandler().redis()
try:
v = red.get(key)
except redis.exceptions.ConnectionError:
v = None
if not v:
return default
return v

181
lemur/common/schema.py Normal file
View File

@ -0,0 +1,181 @@
"""
.. module: lemur.common.schema
:platform: unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from functools import wraps
from flask import request, current_app
from sqlalchemy.orm.collections import InstrumentedList
from inflection import camelize, underscore
from marshmallow import Schema, post_dump, pre_load
from lemur.extensions import sentry
class LemurSchema(Schema):
"""
Base schema from which all grouper schema's inherit
"""
__envelope__ = True
def under(self, data, many=None):
items = []
if many:
for i in data:
items.append({underscore(key): value for key, value in i.items()})
return items
return {underscore(key): value for key, value in data.items()}
def camel(self, data, many=None):
items = []
if many:
for i in data:
items.append(
{
camelize(key, uppercase_first_letter=False): value
for key, value in i.items()
}
)
return items
return {
camelize(key, uppercase_first_letter=False): value
for key, value in data.items()
}
def wrap_with_envelope(self, data, many):
if many:
if "total" in self.context.keys():
return dict(total=self.context["total"], items=data)
return data
class LemurInputSchema(LemurSchema):
@pre_load(pass_many=True)
def preprocess(self, data, many):
if isinstance(data, dict) and data.get("owner"):
data["owner"] = data["owner"].lower()
return self.under(data, many=many)
class LemurOutputSchema(LemurSchema):
@pre_load(pass_many=True)
def preprocess(self, data, many):
if many:
data = self.unwrap_envelope(data, many)
return self.under(data, many=many)
def unwrap_envelope(self, data, many):
if many:
if data["items"]:
if isinstance(data, InstrumentedList) or isinstance(data, list):
self.context["total"] = len(data)
return data
else:
self.context["total"] = data["total"]
else:
self.context["total"] = 0
data = {"items": []}
return data["items"]
return data
@post_dump(pass_many=True)
def post_process(self, data, many):
if data:
data = self.camel(data, many=many)
if self.__envelope__:
return self.wrap_with_envelope(data, many=many)
else:
return data
def format_errors(messages):
errors = {}
for k, v in messages.items():
key = camelize(k, uppercase_first_letter=False)
if isinstance(v, dict):
errors[key] = format_errors(v)
elif isinstance(v, list):
errors[key] = v[0]
return errors
def wrap_errors(messages):
errors = dict(message="Validation Error.")
if messages.get("_schema"):
errors["reasons"] = {"Schema": {"rule": messages["_schema"]}}
else:
errors["reasons"] = format_errors(messages)
return errors
def unwrap_pagination(data, output_schema):
if not output_schema:
return data
if isinstance(data, dict):
if "total" in data.keys():
if data.get("total") == 0:
return data
marshaled_data = {"total": data["total"]}
marshaled_data["items"] = output_schema.dump(data["items"], many=True).data
return marshaled_data
return output_schema.dump(data).data
elif isinstance(data, list):
marshaled_data = {"total": len(data)}
marshaled_data["items"] = output_schema.dump(data, many=True).data
return marshaled_data
return output_schema.dump(data).data
def validate_schema(input_schema, output_schema):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if input_schema:
if request.get_json():
request_data = request.get_json()
else:
request_data = request.args
data, errors = input_schema.load(request_data)
if errors:
return wrap_errors(errors), 400
kwargs["data"] = data
try:
resp = f(*args, **kwargs)
except Exception as e:
sentry.captureException()
current_app.logger.exception(e)
return dict(message=str(e)), 500
if isinstance(resp, tuple):
return resp[0], resp[1]
if not resp:
return dict(message="No data found"), 404
if callable(output_schema):
output_schema_to_use = output_schema()
else:
output_schema_to_use = output_schema
return unwrap_pagination(resp, output_schema_to_use), 200
return decorated_function
return decorator

View File

@ -1,78 +1,376 @@
"""
.. module: lemur.common.utils
:platform: Unix
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import string
import random
from functools import wraps
import re
import string
import pem
from flask import current_app
import sqlalchemy
from cryptography import x509
from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, ec, padding
from cryptography.hazmat.primitives.serialization import load_pem_private_key, Encoding, pkcs7
from flask_restful.reqparse import RequestParser
from sqlalchemy import and_, func
from flask.ext.restful import marshal
from flask.ext.restful.reqparse import RequestParser
from flask.ext.sqlalchemy import Pagination
from lemur.constants import CERTIFICATE_KEY_TYPES
from lemur.exceptions import InvalidConfiguration
paginated_parser = RequestParser()
paginated_parser.add_argument("count", type=int, default=10, location="args")
paginated_parser.add_argument("page", type=int, default=1, location="args")
paginated_parser.add_argument("sortDir", type=str, dest="sort_dir", location="args")
paginated_parser.add_argument("sortBy", type=str, dest="sort_by", location="args")
paginated_parser.add_argument("filter", type=str, location="args")
paginated_parser.add_argument("owner", type=str, location="args")
def get_psuedo_random_string():
"""
Create a random and strongish challenge.
"""
challenge = ''.join(random.choice(string.ascii_uppercase) for x in range(6)) # noqa
challenge += ''.join(random.choice("~!@#$%^&*()_+") for x in range(6)) # noqa
challenge += ''.join(random.choice(string.ascii_lowercase) for x in range(6))
challenge += ''.join(random.choice(string.digits) for x in range(6)) # noqa
challenge = "".join(random.choice(string.ascii_uppercase) for x in range(6)) # noqa
challenge += "".join(random.choice("~!@#$%^&*()_+") for x in range(6)) # noqa
challenge += "".join(random.choice(string.ascii_lowercase) for x in range(6))
challenge += "".join(random.choice(string.digits) for x in range(6)) # noqa
return challenge
class marshal_items(object):
def __init__(self, fields, envelope=None):
self.fields = fields
self.envelop = envelope
def parse_certificate(body):
"""
Helper function that parses a PEM certificate.
def __call__(self, f):
def _filter_items(items):
filtered_items = []
for item in items:
filtered_items.append(marshal(item, self.fields))
return filtered_items
:param body:
:return:
"""
assert isinstance(body, str)
@wraps(f)
def wrapper(*args, **kwargs):
try:
resp = f(*args, **kwargs)
# this is a bit weird way to handle non standard error codes returned from the marshaled function
if isinstance(resp, tuple):
return resp[0], resp[1]
if isinstance(resp, Pagination):
return {'items': _filter_items(resp.items), 'total': resp.total}
if isinstance(resp, list):
return {'items': _filter_items(resp), 'total': len(resp)}
return marshal(resp, self.fields)
except Exception as e:
current_app.logger.exception(e)
# this is a little weird hack to respect flask restful parsing errors on marshaled functions
if hasattr(e, 'code'):
if hasattr(e, 'data'):
return {'message': e.data['message']}, 400
else:
return {'message': 'unknown'}, 400
else:
return {'message': str(e)}, 400
return wrapper
return x509.load_pem_x509_certificate(body.encode("utf-8"), default_backend())
paginated_parser = RequestParser()
def parse_private_key(private_key):
"""
Parses a PEM-format private key (RSA, DSA, ECDSA or any other supported algorithm).
paginated_parser.add_argument('count', type=int, default=10, location='args')
paginated_parser.add_argument('page', type=int, default=1, location='args')
paginated_parser.add_argument('sortDir', type=str, dest='sort_dir', location='args')
paginated_parser.add_argument('sortBy', type=str, dest='sort_by', location='args')
paginated_parser.add_argument('filter', type=str, location='args')
Raises ValueError for an invalid string. Raises AssertionError when passed value is not str-type.
:param private_key: String containing PEM private key
"""
assert isinstance(private_key, str)
return load_pem_private_key(
private_key.encode("utf8"), password=None, backend=default_backend()
)
def get_key_type_from_certificate(body):
"""
Helper function to determine key type by pasrding given PEM certificate
:param body: PEM string
:return: Key type string
"""
parsed_cert = parse_certificate(body)
if isinstance(parsed_cert.public_key(), rsa.RSAPublicKey):
return "RSA{key_size}".format(
key_size=parsed_cert.public_key().key_size
)
elif isinstance(parsed_cert.public_key(), ec.EllipticCurvePublicKey):
return get_key_type_from_ec_curve(parsed_cert.public_key().curve.name)
def split_pem(data):
"""
Split a string of several PEM payloads to a list of strings.
:param data: String
:return: List of strings
"""
return re.split("\n(?=-----BEGIN )", data)
def parse_cert_chain(pem_chain):
"""
Helper function to split and parse a series of PEM certificates.
:param pem_chain: string
:return: List of parsed certificates
"""
if pem_chain is None:
return []
return [parse_certificate(cert) for cert in split_pem(pem_chain) if cert]
def parse_csr(csr):
"""
Helper function that parses a CSR.
:param csr:
:return:
"""
assert isinstance(csr, str)
return x509.load_pem_x509_csr(csr.encode("utf-8"), default_backend())
def get_authority_key(body):
"""Returns the authority key for a given certificate in hex format"""
parsed_cert = parse_certificate(body)
authority_key = parsed_cert.extensions.get_extension_for_class(
x509.AuthorityKeyIdentifier
).value.key_identifier
return authority_key.hex()
def get_key_type_from_ec_curve(curve_name):
"""
Give an EC curve name, return the matching key_type.
:param: curve_name
:return: key_type
"""
_CURVE_TYPES = {
ec.SECP192R1().name: "ECCPRIME192V1",
ec.SECP256R1().name: "ECCPRIME256V1",
ec.SECP224R1().name: "ECCSECP224R1",
ec.SECP384R1().name: "ECCSECP384R1",
ec.SECP521R1().name: "ECCSECP521R1",
ec.SECP256K1().name: "ECCSECP256K1",
ec.SECT163K1().name: "ECCSECT163K1",
ec.SECT233K1().name: "ECCSECT233K1",
ec.SECT283K1().name: "ECCSECT283K1",
ec.SECT409K1().name: "ECCSECT409K1",
ec.SECT571K1().name: "ECCSECT571K1",
ec.SECT163R2().name: "ECCSECT163R2",
ec.SECT233R1().name: "ECCSECT233R1",
ec.SECT283R1().name: "ECCSECT283R1",
ec.SECT409R1().name: "ECCSECT409R1",
ec.SECT571R1().name: "ECCSECT571R2",
}
if curve_name in _CURVE_TYPES.keys():
return _CURVE_TYPES[curve_name]
else:
return None
def generate_private_key(key_type):
"""
Generates a new private key based on key_type.
Valid key types: RSA2048, RSA4096', 'ECCPRIME192V1', 'ECCPRIME256V1', 'ECCSECP192R1',
'ECCSECP224R1', 'ECCSECP256R1', 'ECCSECP384R1', 'ECCSECP521R1', 'ECCSECP256K1',
'ECCSECT163K1', 'ECCSECT233K1', 'ECCSECT283K1', 'ECCSECT409K1', 'ECCSECT571K1',
'ECCSECT163R2', 'ECCSECT233R1', 'ECCSECT283R1', 'ECCSECT409R1', 'ECCSECT571R2'
:param key_type:
:return:
"""
_CURVE_TYPES = {
"ECCPRIME192V1": ec.SECP192R1(), # duplicate
"ECCPRIME256V1": ec.SECP256R1(), # duplicate
"ECCSECP192R1": ec.SECP192R1(), # duplicate
"ECCSECP224R1": ec.SECP224R1(),
"ECCSECP256R1": ec.SECP256R1(), # duplicate
"ECCSECP384R1": ec.SECP384R1(),
"ECCSECP521R1": ec.SECP521R1(),
"ECCSECP256K1": ec.SECP256K1(),
"ECCSECT163K1": ec.SECT163K1(),
"ECCSECT233K1": ec.SECT233K1(),
"ECCSECT283K1": ec.SECT283K1(),
"ECCSECT409K1": ec.SECT409K1(),
"ECCSECT571K1": ec.SECT571K1(),
"ECCSECT163R2": ec.SECT163R2(),
"ECCSECT233R1": ec.SECT233R1(),
"ECCSECT283R1": ec.SECT283R1(),
"ECCSECT409R1": ec.SECT409R1(),
"ECCSECT571R2": ec.SECT571R1(),
}
if key_type not in CERTIFICATE_KEY_TYPES:
raise Exception(
"Invalid key type: {key_type}. Supported key types: {choices}".format(
key_type=key_type, choices=",".join(CERTIFICATE_KEY_TYPES)
)
)
if "RSA" in key_type:
key_size = int(key_type[3:])
return rsa.generate_private_key(
public_exponent=65537, key_size=key_size, backend=default_backend()
)
elif "ECC" in key_type:
return ec.generate_private_key(
curve=_CURVE_TYPES[key_type], backend=default_backend()
)
def check_cert_signature(cert, issuer_public_key):
"""
Check a certificate's signature against an issuer public key.
Before EC validation, make sure we support the algorithm, otherwise raise UnsupportedAlgorithm
On success, returns None; on failure, raises UnsupportedAlgorithm or InvalidSignature.
"""
if isinstance(issuer_public_key, rsa.RSAPublicKey):
# RSA requires padding, just to make life difficult for us poor developers :(
if cert.signature_algorithm_oid == x509.SignatureAlgorithmOID.RSASSA_PSS:
# In 2005, IETF devised a more secure padding scheme to replace PKCS #1 v1.5. To make sure that
# nobody can easily support or use it, they mandated lots of complicated parameters, unlike any
# other X.509 signature scheme.
# https://tools.ietf.org/html/rfc4056
raise UnsupportedAlgorithm("RSASSA-PSS not supported")
else:
padder = padding.PKCS1v15()
issuer_public_key.verify(
cert.signature,
cert.tbs_certificate_bytes,
padder,
cert.signature_hash_algorithm,
)
elif isinstance(issuer_public_key, ec.EllipticCurvePublicKey) and isinstance(
ec.ECDSA(cert.signature_hash_algorithm), ec.ECDSA
):
issuer_public_key.verify(
cert.signature,
cert.tbs_certificate_bytes,
ec.ECDSA(cert.signature_hash_algorithm),
)
else:
raise UnsupportedAlgorithm(
"Unsupported Algorithm '{var}'.".format(
var=cert.signature_algorithm_oid._name
)
)
def is_selfsigned(cert):
"""
Returns True if the certificate is self-signed.
Returns False for failed verification or unsupported signing algorithm.
"""
try:
check_cert_signature(cert, cert.public_key())
# If verification was successful, it's self-signed.
return True
except InvalidSignature:
return False
def is_weekend(date):
"""
Determines if a given date is on a weekend.
:param date:
:return:
"""
if date.weekday() > 5:
return True
def validate_conf(app, required_vars):
"""
Ensures that the given fields are set in the applications conf.
:param app:
:param required_vars: list
"""
for var in required_vars:
if var not in app.config:
raise InvalidConfiguration(
"Required variable '{var}' is not set in Lemur's conf.".format(var=var)
)
# https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/WindowedRangeQuery
def column_windows(session, column, windowsize):
"""Return a series of WHERE clauses against
a given column that break it into windows.
Result is an iterable of tuples, consisting of
((start, end), whereclause), where (start, end) are the ids.
Requires a database that supports window functions,
i.e. Postgresql, SQL Server, Oracle.
Enhance this yourself ! Add a "where" argument
so that windows of just a subset of rows can
be computed.
"""
def int_for_range(start_id, end_id):
if end_id:
return and_(column >= start_id, column < end_id)
else:
return column >= start_id
q = session.query(
column, func.row_number().over(order_by=column).label("rownum")
).from_self(column)
if windowsize > 1:
q = q.filter(sqlalchemy.text("rownum %% %d=1" % windowsize))
intervals = [id for id, in q]
while intervals:
start = intervals.pop(0)
if intervals:
end = intervals[0]
else:
end = None
yield int_for_range(start, end)
def windowed_query(q, column, windowsize):
""""Break a Query into windows on a given column."""
for whereclause in column_windows(q.session, column, windowsize):
for row in q.filter(whereclause).order_by(column):
yield row
def truthiness(s):
"""If input string resembles something truthy then return True, else False."""
return s.lower() in ("true", "yes", "on", "t", "1")
def find_matching_certificates_by_hash(cert, matching_certs):
"""Given a Cryptography-formatted certificate cert, and Lemur-formatted certificates (matching_certs),
determine if any of the certificate hashes match and return the matches."""
matching = []
for c in matching_certs:
if parse_certificate(c.body).fingerprint(hashes.SHA256()) == cert.fingerprint(
hashes.SHA256()
):
matching.append(c)
return matching
def convert_pkcs7_bytes_to_pem(certs_pkcs7):
"""
Given a list of certificates in pkcs7 encoding (bytes), covert them into a list of PEM encoded files
:raises ValueError or ValidationError
:param certs_pkcs7:
:return: list of certs in PEM format
"""
certificates = pkcs7.load_pem_pkcs7_certificates(certs_pkcs7)
certificates_pem = []
for cert in certificates:
certificates_pem.append(pem.parse(cert.public_bytes(encoding=Encoding.PEM))[0])
return certificates_pem

204
lemur/common/validators.py Normal file
View File

@ -0,0 +1,204 @@
import re
from cryptography import x509
from cryptography.exceptions import UnsupportedAlgorithm, InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.x509 import NameOID
from flask import current_app
from marshmallow.exceptions import ValidationError
from lemur.auth.permissions import SensitiveDomainPermission
from lemur.common.utils import check_cert_signature, is_weekend
def common_name(value):
"""If the common name could be a domain name, apply domain validation rules."""
# Common name could be a domain name, or a human-readable name of the subject (often used in CA names or client
# certificates). As a simple heuristic, we assume that human-readable names always include a space.
# However, to avoid confusion for humans, we also don't count spaces at the beginning or end of the string.
if " " not in value.strip():
return sensitive_domain(value)
def sensitive_domain(domain):
"""
Checks if user has the admin role, the domain does not match sensitive domains and allowed domain patterns.
:param domain: domain name (str)
:return:
"""
if SensitiveDomainPermission().can():
# User has permission, no need to check anything
return
allowlist = current_app.config.get("LEMUR_ALLOWED_DOMAINS", [])
if allowlist and not any(re.match(pattern, domain) for pattern in allowlist):
raise ValidationError(
"Domain {0} does not match allowed domain patterns. "
"Contact an administrator to issue the certificate.".format(domain)
)
# Avoid circular import.
from lemur.domains import service as domain_service
if domain_service.is_domain_sensitive(domain):
raise ValidationError(
"Domain {0} has been marked as sensitive. "
"Contact an administrator to issue the certificate.".format(domain)
)
def encoding(oid_encoding):
"""
Determines if the specified oid type is valid.
:param oid_encoding:
:return:
"""
valid_types = ["b64asn1", "string", "ia5string"]
if oid_encoding.lower() not in [o_type.lower() for o_type in valid_types]:
raise ValidationError(
"Invalid Oid Encoding: {0} choose from {1}".format(
oid_encoding, ",".join(valid_types)
)
)
def sub_alt_type(alt_type):
"""
Determines if the specified subject alternate type is valid.
:param alt_type:
:return:
"""
valid_types = [
"DNSName",
"IPAddress",
"uniFormResourceIdentifier",
"directoryName",
"rfc822Name",
"registrationID",
"otherName",
"x400Address",
"EDIPartyName",
]
if alt_type.lower() not in [a_type.lower() for a_type in valid_types]:
raise ValidationError(
"Invalid SubAltName Type: {0} choose from {1}".format(
type, ",".join(valid_types)
)
)
def csr(data):
"""
Determines if the CSR is valid and allowed.
:param data:
:return:
"""
try:
request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend())
except Exception:
raise ValidationError("CSR presented is not valid.")
# Validate common name and SubjectAltNames
try:
for name in request.subject.get_attributes_for_oid(NameOID.COMMON_NAME):
common_name(name.value)
except ValueError as err:
current_app.logger.info("Error parsing Subject from CSR: %s", err)
raise ValidationError("Invalid Subject value in supplied CSR")
try:
alt_names = request.extensions.get_extension_for_class(
x509.SubjectAlternativeName
)
for name in alt_names.value.get_values_for_type(x509.DNSName):
sensitive_domain(name)
except x509.ExtensionNotFound:
pass
def dates(data):
if not data.get("validity_start") and data.get("validity_end"):
raise ValidationError("If validity start is specified so must validity end.")
if not data.get("validity_end") and data.get("validity_start"):
raise ValidationError("If validity end is specified so must validity start.")
if data.get("validity_start") and data.get("validity_end"):
if not current_app.config.get("LEMUR_ALLOW_WEEKEND_EXPIRATION", True):
if is_weekend(data.get("validity_end")):
raise ValidationError("Validity end must not land on a weekend.")
if not data["validity_start"] < data["validity_end"]:
raise ValidationError("Validity start must be before validity end.")
if data.get("authority"):
if (
data.get("validity_start").date()
< data["authority"].authority_certificate.not_before.date()
):
raise ValidationError(
"Validity start must not be before {0}".format(
data["authority"].authority_certificate.not_before
)
)
if (
data.get("validity_end").date()
> data["authority"].authority_certificate.not_after.date()
):
raise ValidationError(
"Validity end must not be after {0}".format(
data["authority"].authority_certificate.not_after
)
)
return data
def verify_private_key_match(key, cert, error_class=ValidationError):
"""
Checks that the supplied private key matches the certificate.
:param cert: Parsed certificate
:param key: Parsed private key
:param error_class: Exception class to raise on error
"""
if key.public_key().public_numbers() != cert.public_key().public_numbers():
raise error_class("Private key does not match certificate.")
def verify_cert_chain(certs, error_class=ValidationError):
"""
Verifies that the certificates in the chain are correct.
We don't bother with full cert validation but just check that certs in the chain are signed by the next, to avoid
basic human errors -- such as pasting the wrong certificate.
:param certs: List of parsed certificates, use parse_cert_chain()
:param error_class: Exception class to raise on error
"""
cert = certs[0]
for issuer in certs[1:]:
# Use the current cert's public key to verify the previous signature.
# "certificate validation is a complex problem that involves much more than just signature checks"
try:
check_cert_signature(cert, issuer.public_key())
except InvalidSignature:
# Avoid circular import.
from lemur.common import defaults
raise error_class(
"Incorrect chain certificate(s) provided: '%s' is not signed by '%s'"
% (
defaults.common_name(cert) or "Unknown",
defaults.common_name(issuer),
)
)
except UnsupportedAlgorithm as err:
current_app.logger.warning("Skipping chain validation: %s", err)
# Next loop will validate that *this issuer* cert is signed by the next chain cert.
cert = issuer

View File

@ -1,8 +1,34 @@
"""
.. module: lemur.constants
:copyright: (c) 2015 by Netflix Inc.
:copyright: (c) 2018 by Netflix Inc.
:license: Apache, see LICENSE for more details.
"""
SAN_NAMING_TEMPLATE = "SAN-{subject}-{issuer}-{not_before}-{not_after}"
DEFAULT_NAMING_TEMPLATE = "{subject}-{issuer}-{not_before}-{not_after}"
NONSTANDARD_NAMING_TEMPLATE = "{issuer}-{not_before}-{not_after}"
SUCCESS_METRIC_STATUS = "success"
FAILURE_METRIC_STATUS = "failure"
CERTIFICATE_KEY_TYPES = [
"RSA2048",
"RSA4096",
"ECCPRIME192V1",
"ECCPRIME256V1",
"ECCSECP192R1",
"ECCSECP224R1",
"ECCSECP256R1",
"ECCSECP384R1",
"ECCSECP521R1",
"ECCSECP256K1",
"ECCSECT163K1",
"ECCSECT233K1",
"ECCSECT283K1",
"ECCSECT409K1",
"ECCSECT571K1",
"ECCSECT163R2",
"ECCSECT233R1",
"ECCSECT283R1",
"ECCSECT409R1",
"ECCSECT571R2",
]

View File

@ -4,17 +4,18 @@
:synopsis: This module contains all of the database related methods
needed for lemur to interact with a datastore
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from sqlalchemy import exc
from inflection import underscore
from sqlalchemy import exc, func, distinct
from sqlalchemy.orm import make_transient, lazyload
from sqlalchemy.sql import and_, or_
from sqlalchemy.orm.exc import NoResultFound
from lemur.extensions import db
from lemur.exceptions import AttrNotFound, DuplicateError
from lemur.extensions import db
def filter_none(kwargs):
@ -42,7 +43,7 @@ def session_query(model):
:param model: sqlalchemy model
:return: query object for model
"""
return model.query if hasattr(model, 'query') else db.session.query(model)
return model.query if hasattr(model, "query") else db.session.query(model)
def create_query(model, kwargs):
@ -75,6 +76,16 @@ def add(model):
db.session.add(model)
def get_model_column(model, field):
if field in getattr(model, "sensitive_fields", ()):
raise AttrNotFound(field)
column = model.__table__.columns._data.get(field, None)
if column is None:
raise AttrNotFound(field)
return column
def find_all(query, model, kwargs):
"""
Returns a query object that ensures that all kwargs
@ -89,9 +100,9 @@ def find_all(query, model, kwargs):
kwargs = filter_none(kwargs)
for attr, value in kwargs.items():
if not isinstance(value, list):
value = value.split(',')
value = value.split(",")
conditions.append(getattr(model, attr).in_(value))
conditions.append(get_model_column(model, attr).in_(value))
return query.filter(and_(*conditions))
@ -108,7 +119,7 @@ def find_any(query, model, kwargs):
"""
or_args = []
for attr, value in kwargs.items():
or_args.append(or_(getattr(model, attr) == value))
or_args.append(or_(get_model_column(model, attr) == value))
exprs = or_(*or_args)
return query.filter(exprs)
@ -123,10 +134,7 @@ def get(model, value, field="id"):
:return:
"""
query = session_query(model)
try:
return query.filter(getattr(model, field) == value).one()
except NoResultFound as e:
return
return query.filter(get_model_column(model, field) == value).scalar()
def get_all(model, value, field="id"):
@ -139,7 +147,7 @@ def get_all(model, value, field="id"):
:return:
"""
query = session_query(model)
return query.filter(getattr(model, field) == value)
return query.filter(get_model_column(model, field) == value)
def create(model):
@ -191,7 +199,8 @@ def filter(query, model, terms):
:param terms:
:return:
"""
return query.filter(getattr(model, terms[0]).ilike('%{}%'.format(terms[1])))
column = get_model_column(model, underscore(terms[0]))
return query.filter(column.ilike("%{}%".format(terms[1])))
def sort(query, model, field, direction):
@ -204,13 +213,8 @@ def sort(query, model, field, direction):
:param field:
:param direction:
"""
try:
field = getattr(model, field)
direction = getattr(field, direction)
query = query.order_by(direction())
return query
except AttributeError:
raise AttrNotFound(field)
column = get_model_column(model, underscore(field))
return query.order_by(column.desc() if direction == "desc" else column.asc())
def paginate(query, page, count):
@ -237,23 +241,71 @@ def update_list(model, model_attr, item_model, items):
"""
ids = []
for i in items:
ids.append(i['id'])
for i in getattr(model, model_attr):
if i.id not in ids:
getattr(model, model_attr).remove(i)
for i in items:
for item in getattr(model, model_attr):
if item.id == i['id']:
if item.id == i["id"]:
break
else:
getattr(model, model_attr).append(get(item_model, i['id']))
getattr(model, model_attr).append(get(item_model, i["id"]))
return model
def clone(model):
"""
Clones the given model and removes it's primary key
:param model:
:return:
"""
db.session.expunge(model)
make_transient(model)
model.id = None
return model
def get_count(q):
"""
Count the number of rows in a table. More efficient than count(*)
:param q:
:return:
"""
disable_group_by = False
if len(q._entities) > 1:
# currently support only one entity
raise Exception("only one entity is supported for get_count, got: %s" % q)
entity = q._entities[0]
if hasattr(entity, "column"):
# _ColumnEntity has column attr - on case: query(Model.column)...
col = entity.column
if q._group_by and q._distinct:
# which query can have both?
raise NotImplementedError
if q._group_by or q._distinct:
col = distinct(col)
if q._group_by:
# need to disable group_by and enable distinct - we can do this because we have only 1 entity
disable_group_by = True
count_func = func.count(col)
else:
# _MapperEntity doesn't have column attr - on case: query(Model)...
count_func = func.count()
if q._group_by and not disable_group_by:
count_func = count_func.over(None)
count_q = (
q.options(lazyload("*"))
.statement.with_only_columns([count_func])
.order_by(None)
)
if disable_group_by:
count_q = count_q.group_by(None)
count = q.session.execute(count_q).scalar()
return count
def sort_and_page(query, model, args):
"""
Helper that allows us to combine sorting and paging
@ -263,14 +315,22 @@ def sort_and_page(query, model, args):
:param args:
:return:
"""
sort_by = args.pop('sort_by')
sort_dir = args.pop('sort_dir')
page = args.pop('page')
count = args.pop('count')
sort_by = args.pop("sort_by")
sort_dir = args.pop("sort_dir")
page = args.pop("page")
count = args.pop("count")
if args.get("user"):
user = args.pop("user")
query = find_all(query, model, args)
if sort_by and sort_dir:
query = sort(query, model, sort_by, sort_dir)
return paginate(query, page, count)
total = get_count(query)
# offset calculated at zero
page -= 1
items = query.offset(count * page).limit(count).all()
return dict(items=items, total=total)

View File

@ -1,56 +0,0 @@
"""
.. module: lemur.decorators
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
"""
from builtins import str
from datetime import timedelta
from flask import make_response, request, current_app
from functools import update_wrapper
# this is only used for dev
def crossdomain(origin=None, methods=None, headers=None,
max_age=21600, attach_to_all=True,
automatic_options=True): # pragma: no cover
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, str):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, str):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
h['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, Authorization "
h['Access-Control-Allow-Credentials'] = 'true'
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator

View File

@ -1,9 +1,8 @@
# This is just Python which means you can inherit and tweak settings
import os
_basedir = os.path.abspath(os.path.dirname(__file__))
ADMINS = frozenset([''])
_basedir = os.path.abspath(os.path.dirname(__file__))
THREADS_PER_PAGE = 8
@ -11,7 +10,7 @@ THREADS_PER_PAGE = 8
# These will need to be set to `True` if you are developing locally
CORS = False
debug = False
DEBUG = False
# Logging

23
lemur/defaults/schemas.py Normal file
View File

@ -0,0 +1,23 @@
"""
.. module: lemur.defaults.schemas
:platform: unix
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from marshmallow import fields
from lemur.common.schema import LemurOutputSchema
from lemur.authorities.schemas import AuthorityNestedOutputSchema
class DefaultOutputSchema(LemurOutputSchema):
authority = fields.Nested(AuthorityNestedOutputSchema)
country = fields.String()
state = fields.String()
location = fields.String()
organization = fields.String()
organizational_unit = fields.String()
issuer_plugin = fields.String()
default_output_schema = DefaultOutputSchema()

Some files were not shown because too many files have changed in this diff Show More