Merge pull request #3081 from charhate/pub_issuer

Cert validity should not exceed 397 days for publicly trusted issuers
This commit is contained in:
charhate 2020-08-17 17:41:25 -07:00 committed by GitHub
commit 14b73b73cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 30 deletions

View File

@ -155,6 +155,22 @@ Specifying the `SQLALCHEMY_MAX_OVERFLOW` to 0 will enforce limit to not create c
LEMUR_ENCRYPTION_KEYS = ['1YeftooSbxCiX2zo8m1lXtpvQjy27smZcUUaGmffhMY=', 'LAfQt6yrkLqOK5lwpvQcT4jf2zdeTQJV1uYeh9coT5s=']
.. data:: PUBLIC_CA_AUTHORITY_NAMES
:noindex:
A list of public issuers which would be checked against to determine whether limit of max validity of 397 days
should be applied to the certificate. Configure public CA authority names in this list to enforce validity check.
This is an optional setting. Using this will allow the sanity check as mentioned. The name check is a case-insensitive
string comparision.
.. data:: PUBLIC_CA_MAX_VALIDITY_DAYS
:noindex:
Use this config to override the limit of 397 days of validity for certificates issued by public issuers configured
using PUBLIC_CA_AUTHORITY_NAMES. Below example overrides the default validity of 397 days and sets it to 365 days.
::
PUBLIC_CA_MAX_VALIDITY_DAYS = 365
.. data:: DEBUG_DUMP
:noindex:
@ -729,16 +745,16 @@ The following configuration properties are required to use the Digicert issuer p
This is the root to be used for your CA chain
.. data:: DIGICERT_DEFAULT_VALIDITY
.. data:: DIGICERT_DEFAULT_VALIDITY_DAYS
:noindex:
This is the default validity (in years), if no end date is specified. (Default: 1)
This is the default validity (in days), if no end date is specified. (Default: 397)
.. data:: DIGICERT_MAX_VALIDITY
.. data:: DIGICERT_MAX_VALIDITY_DAYS
:noindex:
This is the maximum validity (in years). (Default: value of DIGICERT_DEFAULT_VALIDITY)
This is the maximum validity (in days). (Default: value of DIGICERT_DEFAULT_VALIDITY_DAYS)
.. data:: DIGICERT_PRIVATE

View File

@ -152,6 +152,18 @@ def dates(data):
data["authority"].authority_certificate.not_after
)
)
# Allow no more than PUBLIC_CA_MAX_VALIDITY_DAYS (Default: 397) days of validity
# for certs issued by public CA
# The list of public issuers can be managed through a config named PUBLIC_CA
public_CA = current_app.config.get("PUBLIC_CA_AUTHORITY_NAMES", [])
if data["authority"].name.lower() in [ca.lower() for ca in public_CA]:
max_validity_days = current_app.config.get("PUBLIC_CA_MAX_VALIDITY_DAYS", 397)
if (
(data.get("validity_end").date() - data.get("validity_start").date()).days
> max_validity_days
):
raise ValidationError("Certificate cannot be valid for more than " +
str(max_validity_days) + " days")
return data

View File

@ -61,18 +61,16 @@ def signature_hash(signing_algorithm):
def determine_validity_years(years):
"""Given an end date determine how many years into the future that date is.
:param years:
:return: validity in years
"""
default_years = current_app.config.get("DIGICERT_DEFAULT_VALIDITY", 1)
max_years = current_app.config.get("DIGICERT_MAX_VALIDITY", default_years)
Considering maximum allowed certificate validity period of 397 days, this method should not return
more than 1 year of validity. Thus changing it to always return 1.
Lemur will change this method in future to handle validity in months (determine_validity_months)
instead of years. This will allow flexibility to handle short-lived certificates.
if years > max_years:
return max_years
if years not in [1, 2, 3]:
return default_years
return years
:param years:
:return: 1
"""
return 1
def determine_end_date(end_date):
@ -82,11 +80,11 @@ def determine_end_date(end_date):
:param end_date:
:return: validity_end
"""
default_years = current_app.config.get("DIGICERT_DEFAULT_VALIDITY", 1)
max_validity_end = arrow.utcnow().shift(years=current_app.config.get("DIGICERT_MAX_VALIDITY", default_years))
default_days = current_app.config.get("DIGICERT_DEFAULT_VALIDITY_DAYS", 397)
max_validity_end = arrow.utcnow().shift(days=current_app.config.get("DIGICERT_MAX_VALIDITY_DAYS", default_days))
if not end_date:
end_date = arrow.utcnow().shift(years=default_years)
end_date = arrow.utcnow().shift(days=default_days)
if end_date > max_validity_end:
end_date = max_validity_end

View File

@ -14,8 +14,6 @@ def config_mock(*args):
"DIGICERT_ORG_ID": 111111,
"DIGICERT_PRIVATE": False,
"DIGICERT_DEFAULT_SIGNING_ALGORITHM": "sha256",
"DIGICERT_DEFAULT_VALIDITY": 1,
"DIGICERT_MAX_VALIDITY": 2,
"DIGICERT_CIS_PROFILE_NAMES": {"digicert": 'digicert'},
"DIGICERT_CIS_SIGNING_ALGORITHMS": {"digicert": 'digicert'},
}
@ -24,19 +22,18 @@ def config_mock(*args):
@patch("lemur.plugins.lemur_digicert.plugin.current_app")
def test_determine_validity_years(mock_current_app):
mock_current_app.config.get = Mock(return_value=2)
assert plugin.determine_validity_years(1) == 1
assert plugin.determine_validity_years(0) == 2
assert plugin.determine_validity_years(3) == 2
assert plugin.determine_validity_years(0) == 1
assert plugin.determine_validity_years(3) == 1
@patch("lemur.plugins.lemur_digicert.plugin.current_app")
def test_determine_end_date(mock_current_app):
mock_current_app.config.get = Mock(return_value=2)
mock_current_app.config.get = Mock(return_value=397) # 397 days validity
with freeze_time(time_to_freeze=arrow.get(2016, 11, 3).datetime):
assert arrow.get(2018, 11, 3) == plugin.determine_end_date(0)
assert arrow.get(2018, 5, 7) == plugin.determine_end_date(arrow.get(2018, 5, 7))
assert arrow.get(2018, 11, 3) == plugin.determine_end_date(arrow.get(2020, 5, 7))
assert arrow.get(2017, 12, 5) == plugin.determine_end_date(0) # 397 days from (2016, 11, 3)
assert arrow.get(2017, 12, 5) == plugin.determine_end_date(arrow.get(2017, 12, 5))
assert arrow.get(2017, 12, 5) == plugin.determine_end_date(arrow.get(2020, 5, 7))
@patch("lemur.plugins.lemur_digicert.plugin.current_app")
@ -52,7 +49,7 @@ def test_map_fields_with_validity_years(mock_current_app):
"owner": "bob@example.com",
"description": "test certificate",
"extensions": {"sub_alt_names": {"names": [x509.DNSName(x) for x in names]}},
"validity_years": 2
"validity_years": 1
}
expected = {
"certificate": {
@ -62,7 +59,7 @@ def test_map_fields_with_validity_years(mock_current_app):
"signature_hash": "sha256",
},
"organization": {"id": 111111},
"validity_years": 2,
"validity_years": 1,
}
assert expected == plugin.map_fields(options, CSR_STR)