Merge pull request #3464 from hosseinsh/acme-preferred-chain
Support for selecting the ACME preferred chain
This commit is contained in:
commit
e5eba715b6
|
@ -871,6 +871,17 @@ account. See :ref:`Using a pre-existing ACME account <AcmeAccountReuse>` for mor
|
||||||
|
|
||||||
This is the registration for the ACME account, the most important part is the uri attribute (in JSON)
|
This is the registration for the ACME account, the most important part is the uri attribute (in JSON)
|
||||||
|
|
||||||
|
.. data:: ACME_PREFERRED_ISSUER
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
This is an optional parameter to indicate the preferred chain to retrieve from ACME when finalizing the order.
|
||||||
|
This is applicable to Let's Encrypts recent `migration https://letsencrypt.org/certificates/`_ to their
|
||||||
|
own root, where they provide two distinct certificate chains (fullchain_pem vs. alternative_fullchains_pem);
|
||||||
|
the main chain will be the long chain that is rooted in the expiring DTS root, whereas the alternative chain
|
||||||
|
is rooted in X1 root CA.
|
||||||
|
Select "X1" to get the shorter chain (currently alternative), leave blank or "DST Root CA X3" for the longer chain.
|
||||||
|
|
||||||
|
|
||||||
Active Directory Certificate Services Plugin
|
Active Directory Certificate Services Plugin
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ from acme import challenges, errors, messages
|
||||||
from acme.client import BackwardsCompatibleClientV2, ClientNetwork
|
from acme.client import BackwardsCompatibleClientV2, ClientNetwork
|
||||||
from acme.errors import TimeoutError
|
from acme.errors import TimeoutError
|
||||||
from acme.messages import Error as AcmeError
|
from acme.messages import Error as AcmeError
|
||||||
|
from certbot import crypto_util as acme_crypto_util
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from lemur.common.utils import generate_private_key
|
from lemur.common.utils import generate_private_key
|
||||||
|
@ -92,7 +93,8 @@ class AcmeHandler(object):
|
||||||
deadline = datetime.datetime.now() + datetime.timedelta(seconds=360)
|
deadline = datetime.datetime.now() + datetime.timedelta(seconds=360)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
orderr = acme_client.poll_and_finalize(order, deadline)
|
orderr = acme_client.poll_authorizations(order, deadline)
|
||||||
|
orderr = acme_client.finalize_order(orderr, deadline, fetch_alternative_chains=True)
|
||||||
|
|
||||||
except (AcmeError, TimeoutError):
|
except (AcmeError, TimeoutError):
|
||||||
sentry.captureException(extra={"order_url": str(order.uri)})
|
sentry.captureException(extra={"order_url": str(order.uri)})
|
||||||
|
@ -112,14 +114,23 @@ class AcmeHandler(object):
|
||||||
f"Successfully resolved Acme order: {order.uri}", exc_info=True
|
f"Successfully resolved Acme order: {order.uri}", exc_info=True
|
||||||
)
|
)
|
||||||
|
|
||||||
pem_certificate, pem_certificate_chain = self.extract_cert_and_chain(orderr.fullchain_pem)
|
pem_certificate, pem_certificate_chain = self.extract_cert_and_chain(orderr.fullchain_pem,
|
||||||
|
orderr.alternative_fullchains_pem)
|
||||||
|
|
||||||
current_app.logger.debug(
|
current_app.logger.debug(
|
||||||
"{0} {1}".format(type(pem_certificate), type(pem_certificate_chain))
|
"{0} {1}".format(type(pem_certificate), type(pem_certificate_chain))
|
||||||
)
|
)
|
||||||
return pem_certificate, pem_certificate_chain
|
return pem_certificate, pem_certificate_chain
|
||||||
|
|
||||||
def extract_cert_and_chain(self, fullchain_pem):
|
def extract_cert_and_chain(self, fullchain_pem, alternative_fullchains_pem, preferred_issuer=None):
|
||||||
|
|
||||||
|
if not preferred_issuer:
|
||||||
|
preferred_issuer = current_app.config.get("ACME_PREFERRED_ISSUER", None)
|
||||||
|
if preferred_issuer:
|
||||||
|
# returns first chain if not match
|
||||||
|
fullchain_pem = acme_crypto_util.find_chain_with_issuer([fullchain_pem] + alternative_fullchains_pem,
|
||||||
|
preferred_issuer)
|
||||||
|
|
||||||
pem_certificate = OpenSSL.crypto.dump_certificate(
|
pem_certificate = OpenSSL.crypto.dump_certificate(
|
||||||
OpenSSL.crypto.FILETYPE_PEM,
|
OpenSSL.crypto.FILETYPE_PEM,
|
||||||
OpenSSL.crypto.load_certificate(
|
OpenSSL.crypto.load_certificate(
|
||||||
|
@ -127,11 +138,6 @@ class AcmeHandler(object):
|
||||||
),
|
),
|
||||||
).decode()
|
).decode()
|
||||||
|
|
||||||
if current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA", False) \
|
|
||||||
and datetime.datetime.now() < datetime.datetime.strptime(
|
|
||||||
current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA_EXPIRATION_DATE", "17/03/21"), '%d/%m/%y'):
|
|
||||||
pem_certificate_chain = current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA")
|
|
||||||
else:
|
|
||||||
pem_certificate_chain = fullchain_pem[len(pem_certificate):].lstrip()
|
pem_certificate_chain = fullchain_pem[len(pem_certificate):].lstrip()
|
||||||
|
|
||||||
return pem_certificate, pem_certificate_chain
|
return pem_certificate, pem_certificate_chain
|
||||||
|
|
|
@ -119,8 +119,10 @@ class AcmeHttpChallenge(AcmeChallenge):
|
||||||
current_app.logger.info("Uploaded HTTP-01 challenge tokens, trying to poll and finalize the order")
|
current_app.logger.info("Uploaded HTTP-01 challenge tokens, trying to poll and finalize the order")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
finalized_orderr = acme_client.poll_and_finalize(orderr,
|
deadline = datetime.datetime.now() + datetime.timedelta(seconds=90)
|
||||||
datetime.datetime.now() + datetime.timedelta(seconds=90))
|
orderr = acme_client.poll_authorizations(orderr, deadline)
|
||||||
|
finalized_orderr = acme_client.finalize_order(orderr, deadline, fetch_alternative_chains=True)
|
||||||
|
|
||||||
except errors.ValidationError as validationError:
|
except errors.ValidationError as validationError:
|
||||||
for authz in validationError.failed_authzrs:
|
for authz in validationError.failed_authzrs:
|
||||||
for chall in authz.body.challenges:
|
for chall in authz.body.challenges:
|
||||||
|
@ -130,7 +132,8 @@ class AcmeHttpChallenge(AcmeChallenge):
|
||||||
ERROR_CODES[chall.error.code]))
|
ERROR_CODES[chall.error.code]))
|
||||||
raise Exception('Validation error occured, can\'t complete challenges. See logs for more information.')
|
raise Exception('Validation error occured, can\'t complete challenges. See logs for more information.')
|
||||||
|
|
||||||
pem_certificate, pem_certificate_chain = self.acme.extract_cert_and_chain(finalized_orderr.fullchain_pem)
|
pem_certificate, pem_certificate_chain = self.acme.extract_cert_and_chain(finalized_orderr.fullchain_pem,
|
||||||
|
finalized_orderr.alternative_fullchains_pem)
|
||||||
|
|
||||||
if len(deployed_challenges) != 0:
|
if len(deployed_challenges) != 0:
|
||||||
for token_path in deployed_challenges:
|
for token_path in deployed_challenges:
|
||||||
|
|
|
@ -5,6 +5,12 @@ from flask import Flask
|
||||||
from cryptography.x509 import DNSName
|
from cryptography.x509 import DNSName
|
||||||
from lemur.plugins.lemur_acme import acme_handlers
|
from lemur.plugins.lemur_acme import acme_handlers
|
||||||
|
|
||||||
|
from lemur.tests.vectors import (
|
||||||
|
ACME_CHAIN_SHORT_STR,
|
||||||
|
ACME_CHAIN_LONG_STR,
|
||||||
|
SAN_CERT_STR,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestAcmeHandler(unittest.TestCase):
|
class TestAcmeHandler(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -110,3 +116,18 @@ class TestAcmeHandler(unittest.TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
result, [options["common_name"], "test2.netflix.net"]
|
result, [options["common_name"], "test2.netflix.net"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_extract_cert_and_chain(self):
|
||||||
|
# expecting the short chain
|
||||||
|
leaf_pem, chain_pem = self.acme.extract_cert_and_chain(ACME_CHAIN_SHORT_STR,
|
||||||
|
[ACME_CHAIN_LONG_STR],
|
||||||
|
"(STAGING) Artificial Apricot R3")
|
||||||
|
self.assertEqual(leaf_pem, SAN_CERT_STR)
|
||||||
|
self.assertEqual(chain_pem, ACME_CHAIN_SHORT_STR[len(leaf_pem):].lstrip())
|
||||||
|
|
||||||
|
# expecting the long chain
|
||||||
|
leaf_pem, chain_pem = self.acme.extract_cert_and_chain(ACME_CHAIN_SHORT_STR,
|
||||||
|
[ACME_CHAIN_LONG_STR],
|
||||||
|
"(STAGING) Doctored Durian Root CA X3")
|
||||||
|
self.assertEqual(leaf_pem, SAN_CERT_STR)
|
||||||
|
self.assertEqual(chain_pem, ACME_CHAIN_LONG_STR[len(leaf_pem):].lstrip())
|
||||||
|
|
|
@ -146,7 +146,8 @@ Q9ePRFBCiXOQ6wPLoUhrrbZ8LpFUFYDXHMtYM7P9sc9IAWoONXREJaO08zgFtMp4
|
||||||
idWw1VrejtwclobqNMVtG3EiPUIpJGpbMcJgbiLSmKkrvQtGng==
|
idWw1VrejtwclobqNMVtG3EiPUIpJGpbMcJgbiLSmKkrvQtGng==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
"""
|
"""
|
||||||
mock_client.poll_and_finalize.return_value = mock_finalized_order
|
mock_finalized_order.alternative_fullchains_pem = [mock_finalized_order.fullchain_pem]
|
||||||
|
mock_client.finalize_order.return_value = mock_finalized_order
|
||||||
|
|
||||||
mock_acme.return_value = (mock_client, "")
|
mock_acme.return_value = (mock_client, "")
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,7 @@ ACME_EMAIL = "jim@example.com"
|
||||||
ACME_TEL = "4088675309"
|
ACME_TEL = "4088675309"
|
||||||
ACME_DIRECTORY_URL = "https://acme-v01.api.letsencrypt.org"
|
ACME_DIRECTORY_URL = "https://acme-v01.api.letsencrypt.org"
|
||||||
ACME_DISABLE_AUTORESOLVE = True
|
ACME_DISABLE_AUTORESOLVE = True
|
||||||
|
ACME_PREFERRED_ISSUER = "R3"
|
||||||
|
|
||||||
LDAP_AUTH = True
|
LDAP_AUTH = True
|
||||||
LDAP_BIND_URI = "ldap://localhost"
|
LDAP_BIND_URI = "ldap://localhost"
|
||||||
|
|
|
@ -587,3 +587,102 @@ zwKDoqAD+L4wEg8d890Zy2mbzJnDu2HQiMIROaBldKEAMQA=
|
||||||
"""
|
"""
|
||||||
|
|
||||||
CERT_CHAIN_PKCS7_PEM = CERT_CHAIN_PKCS7_STR.encode('utf-8')
|
CERT_CHAIN_PKCS7_PEM = CERT_CHAIN_PKCS7_STR.encode('utf-8')
|
||||||
|
|
||||||
|
ACME_CHAIN_LONG_STR = SAN_CERT_STR + """
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFWzCCA0OgAwIBAgIQTfQrldHumzpMLrM7jRBd1jANBgkqhkiG9w0BAQsFADBm
|
||||||
|
MQswCQYDVQQGEwJVUzEzMDEGA1UEChMqKFNUQUdJTkcpIEludGVybmV0IFNlY3Vy
|
||||||
|
aXR5IFJlc2VhcmNoIEdyb3VwMSIwIAYDVQQDExkoU1RBR0lORykgUHJldGVuZCBQ
|
||||||
|
ZWFyIFgxMB4XDTIwMDkwNDAwMDAwMFoXDTI1MDkxNTE2MDAwMFowWTELMAkGA1UE
|
||||||
|
BhMCVVMxIDAeBgNVBAoTFyhTVEFHSU5HKSBMZXQncyBFbmNyeXB0MSgwJgYDVQQD
|
||||||
|
Ex8oU1RBR0lORykgQXJ0aWZpY2lhbCBBcHJpY290IFIzMIIBIjANBgkqhkiG9w0B
|
||||||
|
AQEFAAOCAQ8AMIIBCgKCAQEAu6TR8+74b46mOE1FUwBrvxzEYLck3iasmKrcQkb+
|
||||||
|
gy/z9Jy7QNIAl0B9pVKp4YU76JwxF5DOZZhi7vK7SbCkK6FbHlyU5BiDYIxbbfvO
|
||||||
|
L/jVGqdsSjNaJQTg3C3XrJja/HA4WCFEMVoT2wDZm8ABC1N+IQe7Q6FEqc8NwmTS
|
||||||
|
nmmRQm4TQvr06DP+zgFK/MNubxWWDSbSKKTH5im5j2fZfg+j/tM1bGaczFWw8/lS
|
||||||
|
nukyn5J2L+NJYnclzkXoh9nMFnyPmVbfyDPOc4Y25aTzVoeBKXa/cZ5MM+WddjdL
|
||||||
|
biWvm19f1sYn1aRaAIrkppv7kkn83vcth8XCG39qC2ZvaQIDAQABo4IBEDCCAQww
|
||||||
|
DgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAS
|
||||||
|
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTecnpI3zHDplDfn4Uj31c3S10u
|
||||||
|
ZTAfBgNVHSMEGDAWgBS182Xy/rAKkh/7PH3zRKCsYyXDFDA2BggrBgEFBQcBAQQq
|
||||||
|
MCgwJgYIKwYBBQUHMAKGGmh0dHA6Ly9zdGcteDEuaS5sZW5jci5vcmcvMCsGA1Ud
|
||||||
|
HwQkMCIwIKAeoByGGmh0dHA6Ly9zdGcteDEuYy5sZW5jci5vcmcvMCIGA1UdIAQb
|
||||||
|
MBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCN
|
||||||
|
DLam9yN0EFxxn/3p+ruWO6n/9goCAM5PT6cC6fkjMs4uas6UGXJjr5j7PoTQf3C1
|
||||||
|
vuxiIGRJC6qxV7yc6U0X+w0Mj85sHI5DnQVWN5+D1er7mp13JJA0xbAbHa3Rlczn
|
||||||
|
y2Q82XKui8WHuWra0gb2KLpfboYj1Ghgkhr3gau83pC/WQ8HfkwcvSwhIYqTqxoZ
|
||||||
|
Uq8HIf3M82qS9aKOZE0CEmSyR1zZqQxJUT7emOUapkUN9poJ9zGc+FgRZvdro0XB
|
||||||
|
yphWXDaqMYph0DxW/10ig5j4xmmNDjCRmqIKsKoWA52wBTKKXK1na2ty/lW5dhtA
|
||||||
|
xkz5rVZFd4sgS4J0O+zm6d5GRkWsNJ4knotGXl8vtS3X40KXeb3A5+/3p0qaD215
|
||||||
|
Xq8oSNORfB2oI1kQuyEAJ5xvPTdfwRlyRG3lFYodrRg6poUBD/8fNTXMtzydpRgy
|
||||||
|
zUQZh/18F6B/iW6cbiRN9r2Hkh05Om+q0/6w0DdZe+8YrNpfhSObr/1eVZbKGMIY
|
||||||
|
qKmyZbBNu5ysENIK5MPc14mUeKmFjpN840VR5zunoU52lqpLDua/qIM8idk86xGW
|
||||||
|
xx2ml43DO/Ya/tVZVok0mO0TUjzJIfPqyvr455IsIut4RlCR9Iq0EDTve2/ZwCuG
|
||||||
|
hSjpTUFGSiQrR2JK2Evp+o6AETUkBCO1aw0PpQBPDQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFVDCCBDygAwIBAgIRAO1dW8lt+99NPs1qSY3Rs8cwDQYJKoZIhvcNAQELBQAw
|
||||||
|
cTELMAkGA1UEBhMCVVMxMzAxBgNVBAoTKihTVEFHSU5HKSBJbnRlcm5ldCBTZWN1
|
||||||
|
cml0eSBSZXNlYXJjaCBHcm91cDEtMCsGA1UEAxMkKFNUQUdJTkcpIERvY3RvcmVk
|
||||||
|
IER1cmlhbiBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQw
|
||||||
|
M1owZjELMAkGA1UEBhMCVVMxMzAxBgNVBAoTKihTVEFHSU5HKSBJbnRlcm5ldCBT
|
||||||
|
ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEiMCAGA1UEAxMZKFNUQUdJTkcpIFByZXRl
|
||||||
|
bmQgUGVhciBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALbagEdD
|
||||||
|
Ta1QgGBWSYkyMhscZXENOBaVRTMX1hceJENgsL0Ma49D3MilI4KS38mtkmdF6cPW
|
||||||
|
nL++fgehT0FbRHZgjOEr8UAN4jH6omjrbTD++VZneTsMVaGamQmDdFl5g1gYaigk
|
||||||
|
kmx8OiCO68a4QXg4wSyn6iDipKP8utsE+x1E28SA75HOYqpdrk4HGxuULvlr03wZ
|
||||||
|
GTIf/oRt2/c+dYmDoaJhge+GOrLAEQByO7+8+vzOwpNAPEx6LW+crEEZ7eBXih6V
|
||||||
|
P19sTGy3yfqK5tPtTdXXCOQMKAp+gCj/VByhmIr+0iNDC540gtvV303WpcbwnkkL
|
||||||
|
YC0Ft2cYUyHtkstOfRcRO+K2cZozoSwVPyB8/J9RpcRK3jgnX9lujfwA/pAbP0J2
|
||||||
|
UPQFxmWFRQnFjaq6rkqbNEBgLy+kFL1NEsRbvFbKrRi5bYy2lNms2NJPZvdNQbT/
|
||||||
|
2dBZKmJqxHkxCuOQFjhJQNeO+Njm1Z1iATS/3rts2yZlqXKsxQUzN6vNbD8KnXRM
|
||||||
|
EeOXUYvbV4lqfCf8mS14WEbSiMy87GB5S9ucSV1XUrlTG5UGcMSZOBcEUpisRPEm
|
||||||
|
QWUOTWIoDQ5FOia/GI+Ki523r2ruEmbmG37EBSBXdxIdndqrjy+QVAmCebyDx9eV
|
||||||
|
EGOIpn26bW5LKerumJxa/CFBaKi4bRvmdJRLAgMBAAGjgfEwge4wDgYDVR0PAQH/
|
||||||
|
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLXzZfL+sAqSH/s8ffNE
|
||||||
|
oKxjJcMUMB8GA1UdIwQYMBaAFAhX2onHolN5DE/d4JCPdLriJ3NEMDgGCCsGAQUF
|
||||||
|
BwEBBCwwKjAoBggrBgEFBQcwAoYcaHR0cDovL3N0Zy1kc3QzLmkubGVuY3Iub3Jn
|
||||||
|
LzAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8vc3RnLWRzdDMuYy5sZW5jci5vcmcv
|
||||||
|
MCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEB
|
||||||
|
CwUAA4IBAQB7tR8B0eIQSS6MhP5kuvGth+dN02DsIhr0yJtk2ehIcPIqSxRRmHGl
|
||||||
|
4u2c3QlvEpeRDp2w7eQdRTlI/WnNhY4JOofpMf2zwABgBWtAu0VooQcZZTpQruig
|
||||||
|
F/z6xYkBk3UHkjeqxzMN3d1EqGusxJoqgdTouZ5X5QTTIee9nQ3LEhWnRSXDx7Y0
|
||||||
|
ttR1BGfcdqHopO4IBqAhbkKRjF5zj7OD8cG35omywUbZtOJnftiI0nFcRaxbXo0v
|
||||||
|
oDfLD0S6+AC2R3tKpqjkNX6/91hrRFglUakyMcZU/xleqbv6+Lr3YD8PsBTub6lI
|
||||||
|
oZ2lS38fL18Aon458fbc0BPHtenfhKj5
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
"""
|
||||||
|
|
||||||
|
ACME_CHAIN_SHORT_STR = SAN_CERT_STR + """
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFWzCCA0OgAwIBAgIQTfQrldHumzpMLrM7jRBd1jANBgkqhkiG9w0BAQsFADBm
|
||||||
|
MQswCQYDVQQGEwJVUzEzMDEGA1UEChMqKFNUQUdJTkcpIEludGVybmV0IFNlY3Vy
|
||||||
|
aXR5IFJlc2VhcmNoIEdyb3VwMSIwIAYDVQQDExkoU1RBR0lORykgUHJldGVuZCBQ
|
||||||
|
ZWFyIFgxMB4XDTIwMDkwNDAwMDAwMFoXDTI1MDkxNTE2MDAwMFowWTELMAkGA1UE
|
||||||
|
BhMCVVMxIDAeBgNVBAoTFyhTVEFHSU5HKSBMZXQncyBFbmNyeXB0MSgwJgYDVQQD
|
||||||
|
Ex8oU1RBR0lORykgQXJ0aWZpY2lhbCBBcHJpY290IFIzMIIBIjANBgkqhkiG9w0B
|
||||||
|
AQEFAAOCAQ8AMIIBCgKCAQEAu6TR8+74b46mOE1FUwBrvxzEYLck3iasmKrcQkb+
|
||||||
|
gy/z9Jy7QNIAl0B9pVKp4YU76JwxF5DOZZhi7vK7SbCkK6FbHlyU5BiDYIxbbfvO
|
||||||
|
L/jVGqdsSjNaJQTg3C3XrJja/HA4WCFEMVoT2wDZm8ABC1N+IQe7Q6FEqc8NwmTS
|
||||||
|
nmmRQm4TQvr06DP+zgFK/MNubxWWDSbSKKTH5im5j2fZfg+j/tM1bGaczFWw8/lS
|
||||||
|
nukyn5J2L+NJYnclzkXoh9nMFnyPmVbfyDPOc4Y25aTzVoeBKXa/cZ5MM+WddjdL
|
||||||
|
biWvm19f1sYn1aRaAIrkppv7kkn83vcth8XCG39qC2ZvaQIDAQABo4IBEDCCAQww
|
||||||
|
DgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAS
|
||||||
|
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTecnpI3zHDplDfn4Uj31c3S10u
|
||||||
|
ZTAfBgNVHSMEGDAWgBS182Xy/rAKkh/7PH3zRKCsYyXDFDA2BggrBgEFBQcBAQQq
|
||||||
|
MCgwJgYIKwYBBQUHMAKGGmh0dHA6Ly9zdGcteDEuaS5sZW5jci5vcmcvMCsGA1Ud
|
||||||
|
HwQkMCIwIKAeoByGGmh0dHA6Ly9zdGcteDEuYy5sZW5jci5vcmcvMCIGA1UdIAQb
|
||||||
|
MBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCN
|
||||||
|
DLam9yN0EFxxn/3p+ruWO6n/9goCAM5PT6cC6fkjMs4uas6UGXJjr5j7PoTQf3C1
|
||||||
|
vuxiIGRJC6qxV7yc6U0X+w0Mj85sHI5DnQVWN5+D1er7mp13JJA0xbAbHa3Rlczn
|
||||||
|
y2Q82XKui8WHuWra0gb2KLpfboYj1Ghgkhr3gau83pC/WQ8HfkwcvSwhIYqTqxoZ
|
||||||
|
Uq8HIf3M82qS9aKOZE0CEmSyR1zZqQxJUT7emOUapkUN9poJ9zGc+FgRZvdro0XB
|
||||||
|
yphWXDaqMYph0DxW/10ig5j4xmmNDjCRmqIKsKoWA52wBTKKXK1na2ty/lW5dhtA
|
||||||
|
xkz5rVZFd4sgS4J0O+zm6d5GRkWsNJ4knotGXl8vtS3X40KXeb3A5+/3p0qaD215
|
||||||
|
Xq8oSNORfB2oI1kQuyEAJ5xvPTdfwRlyRG3lFYodrRg6poUBD/8fNTXMtzydpRgy
|
||||||
|
zUQZh/18F6B/iW6cbiRN9r2Hkh05Om+q0/6w0DdZe+8YrNpfhSObr/1eVZbKGMIY
|
||||||
|
qKmyZbBNu5ysENIK5MPc14mUeKmFjpN840VR5zunoU52lqpLDua/qIM8idk86xGW
|
||||||
|
xx2ml43DO/Ya/tVZVok0mO0TUjzJIfPqyvr455IsIut4RlCR9Iq0EDTve2/ZwCuG
|
||||||
|
hSjpTUFGSiQrR2JK2Evp+o6AETUkBCO1aw0PpQBPDQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
"""
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
bandit
|
bandit
|
||||||
black
|
black
|
||||||
coverage
|
coverage
|
||||||
|
certbot
|
||||||
factory-boy
|
factory-boy
|
||||||
Faker
|
Faker
|
||||||
fakeredis
|
fakeredis
|
||||||
|
|
|
@ -30,6 +30,7 @@ botocore==1.20.22
|
||||||
# boto3
|
# boto3
|
||||||
# moto
|
# moto
|
||||||
# s3transfer
|
# s3transfer
|
||||||
|
certbot==1.13.0
|
||||||
certifi==2020.12.5
|
certifi==2020.12.5
|
||||||
# via requests
|
# via requests
|
||||||
cffi==1.14.0
|
cffi==1.14.0
|
||||||
|
|
|
@ -7,6 +7,7 @@ asyncpool
|
||||||
boto3
|
boto3
|
||||||
botocore
|
botocore
|
||||||
celery[redis]==4.4.2 # need to first resolve the module not found error https://github.com/celery/celery/issues/6406
|
celery[redis]==4.4.2 # need to first resolve the module not found error https://github.com/celery/celery/issues/6406
|
||||||
|
certbot
|
||||||
certifi
|
certifi
|
||||||
certsrv
|
certsrv
|
||||||
CloudFlare
|
CloudFlare
|
||||||
|
|
|
@ -40,6 +40,7 @@ botocore==1.20.22
|
||||||
# s3transfer
|
# s3transfer
|
||||||
celery[redis]==4.4.2
|
celery[redis]==4.4.2
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
|
certbot==1.13.0
|
||||||
certifi==2020.12.5
|
certifi==2020.12.5
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
|
|
Loading…
Reference in New Issue