Merge branch 'master' into ilabun/optimize-certificates-sql
This commit is contained in:
commit
50091cca1d
@ -22,7 +22,7 @@ Lemur
|
||||
Lemur manages TLS certificate creation. While not able to issue certificates itself, Lemur acts as a broker between CAs
|
||||
and environments providing a central portal for developers to issue TLS certificates with 'sane' defaults.
|
||||
|
||||
It works on CPython 3.5. 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
|
||||
|
64
docker/Dockerfile-src
Normal file
64
docker/Dockerfile-src
Normal file
@ -0,0 +1,64 @@
|
||||
FROM alpine:3.8
|
||||
|
||||
ARG VERSION
|
||||
ENV VERSION master
|
||||
|
||||
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 \
|
||||
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=$(urlContextPath) && \
|
||||
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"]
|
@ -36,7 +36,7 @@ fi
|
||||
# fi
|
||||
|
||||
echo " # Running init"
|
||||
su lemur -s /bin/bash -c "cd /opt/lemur/lemur; python3 /opt/lemur/lemur/manage.py init -p ${LEMUR_ADMIN_PASSWORD}"
|
||||
su lemur -s /bin/bash -c "cd /opt/lemur/lemur; lemur init -p ${LEMUR_ADMIN_PASSWORD}"
|
||||
echo " # Done"
|
||||
|
||||
# echo "Creating user"
|
||||
@ -47,11 +47,13 @@ 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 python3 /opt/lemur/lemur/manage.py notify expirations" > /etc/crontabs/lemur_notify
|
||||
echo "${cron_sync} lemur python3 /opt/lemur/lemur/manage.py source sync -s all" > /etc/crontabs/lemur_sync
|
||||
echo "${cron_revoked} lemur python3 /opt/lemur/lemur/manage.py certificate check_revoked" > /etc/crontabs/lemur_revoked
|
||||
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 "$@"
|
||||
|
@ -16,12 +16,16 @@ LEMUR_WHITELISTED_DOMAINS = []
|
||||
LEMUR_EMAIL = ''
|
||||
LEMUR_SECURITY_TEAM_EMAIL = []
|
||||
|
||||
ALLOW_CERT_DELETION = os.environ.get('ALLOW_CERT_DELETION') == "True"
|
||||
|
||||
LEMUR_DEFAULT_COUNTRY = repr(os.environ.get('LEMUR_DEFAULT_COUNTRY',''))
|
||||
LEMUR_DEFAULT_STATE = repr(os.environ.get('LEMUR_DEFAULT_STATE',''))
|
||||
LEMUR_DEFAULT_LOCATION = repr(os.environ.get('LEMUR_DEFAULT_LOCATION',''))
|
||||
LEMUR_DEFAULT_ORGANIZATION = repr(os.environ.get('LEMUR_DEFAULT_ORGANIZATION',''))
|
||||
LEMUR_DEFAULT_ORGANIZATIONAL_UNIT = repr(os.environ.get('LEMUR_DEFAULT_ORGANIZATIONAL_UNIT',''))
|
||||
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 = []
|
||||
|
||||
|
@ -7,7 +7,7 @@ pidfile = /tmp/supervisord.pid
|
||||
|
||||
[program:lemur]
|
||||
environment=LEMUR_CONF=/home/lemur/.lemur/lemur.conf.py
|
||||
command=/usr/bin/python3 manage.py start -b 0.0.0.0:8000
|
||||
command=lemur start -b 0.0.0.0:8000
|
||||
user=lemur
|
||||
directory=/opt/lemur/lemur
|
||||
stdout_logfile=/dev/stdout
|
||||
@ -24,6 +24,7 @@ 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
|
||||
|
@ -286,6 +286,178 @@ def rotate(endpoint_name, new_certificate_name, old_certificate_name, message, c
|
||||
)
|
||||
|
||||
|
||||
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",
|
||||
|
@ -146,7 +146,8 @@ class CertificateInputSchema(CertificateCreationSchema):
|
||||
data["extensions"]["subAltNames"] = {"names": []}
|
||||
elif not data["extensions"]["subAltNames"].get("names"):
|
||||
data["extensions"]["subAltNames"]["names"] = []
|
||||
data["extensions"]["subAltNames"]["names"] += csr_sans
|
||||
|
||||
data["extensions"]["subAltNames"]["names"] = csr_sans
|
||||
return missing.convert_validity_years(data)
|
||||
|
||||
|
||||
|
@ -631,7 +631,8 @@ def certificate_reissue():
|
||||
|
||||
|
||||
@celery.task(soft_time_limit=3600)
|
||||
def certificate_rotate():
|
||||
def certificate_rotate(**kwargs):
|
||||
|
||||
"""
|
||||
This celery task rotates certificates which are reissued but having endpoints attached to the replaced cert
|
||||
:return:
|
||||
@ -641,6 +642,7 @@ def certificate_rotate():
|
||||
if celery.current_task:
|
||||
task_id = celery.current_task.request.id
|
||||
|
||||
region = kwargs.get("region")
|
||||
log_data = {
|
||||
"function": function,
|
||||
"message": "rotating certificates",
|
||||
@ -654,7 +656,11 @@ def certificate_rotate():
|
||||
|
||||
current_app.logger.debug(log_data)
|
||||
try:
|
||||
cli_certificate.rotate(None, None, None, None, True)
|
||||
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)
|
||||
|
@ -14,7 +14,7 @@ import re
|
||||
import hvac
|
||||
from flask import current_app
|
||||
|
||||
from lemur.common.defaults import common_name
|
||||
from lemur.common.defaults import common_name, country, state, location, organizational_unit, organization
|
||||
from lemur.common.utils import parse_certificate
|
||||
from lemur.plugins.bases import DestinationPlugin
|
||||
from lemur.plugins.bases import SourcePlugin
|
||||
@ -58,7 +58,7 @@ class VaultSourcePlugin(SourcePlugin):
|
||||
"helpMessage": "Authentication method to use",
|
||||
},
|
||||
{
|
||||
"name": "tokenFile/VaultRole",
|
||||
"name": "tokenFileOrVaultRole",
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"validation": "^([a-zA-Z0-9/._-]+/?)+$",
|
||||
@ -94,7 +94,7 @@ class VaultSourcePlugin(SourcePlugin):
|
||||
body = ""
|
||||
url = self.get_option("vaultUrl", options)
|
||||
auth_method = self.get_option("authenticationMethod", options)
|
||||
auth_key = self.get_option("tokenFile/vaultRole", options)
|
||||
auth_key = self.get_option("tokenFileOrVaultRole", options)
|
||||
mount = self.get_option("vaultMount", options)
|
||||
path = self.get_option("vaultPath", options)
|
||||
obj_name = self.get_option("objectName", options)
|
||||
@ -185,7 +185,7 @@ class VaultDestinationPlugin(DestinationPlugin):
|
||||
"helpMessage": "Authentication method to use",
|
||||
},
|
||||
{
|
||||
"name": "tokenFile/VaultRole",
|
||||
"name": "tokenFileOrVaultRole",
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"validation": "^([a-zA-Z0-9/._-]+/?)+$",
|
||||
@ -202,15 +202,15 @@ class VaultDestinationPlugin(DestinationPlugin):
|
||||
"name": "vaultPath",
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"validation": "^([a-zA-Z0-9._-]+/?)+$",
|
||||
"helpMessage": "Must be a valid Vault secrets path",
|
||||
"validation": "^(([a-zA-Z0-9._-]+|{(CN|OU|O|L|S|C)})+/?)+$",
|
||||
"helpMessage": "Must be a valid Vault secrets path. Support vars: {CN|OU|O|L|S|C}",
|
||||
},
|
||||
{
|
||||
"name": "objectName",
|
||||
"type": "str",
|
||||
"required": False,
|
||||
"validation": "[0-9a-zA-Z.:_-]+",
|
||||
"helpMessage": "Name to bundle certs under, if blank use cn",
|
||||
"validation": "^([0-9a-zA-Z.:_-]+|{(CN|OU|O|L|S|C)})+$",
|
||||
"helpMessage": "Name to bundle certs under, if blank use {CN}. Support vars: {CN|OU|O|L|S|C}",
|
||||
},
|
||||
{
|
||||
"name": "bundleChain",
|
||||
@ -241,11 +241,12 @@ class VaultDestinationPlugin(DestinationPlugin):
|
||||
:param cert_chain:
|
||||
:return:
|
||||
"""
|
||||
cname = common_name(parse_certificate(body))
|
||||
cert = parse_certificate(body)
|
||||
cname = common_name(cert)
|
||||
|
||||
url = self.get_option("vaultUrl", options)
|
||||
auth_method = self.get_option("authenticationMethod", options)
|
||||
auth_key = self.get_option("tokenFile/vaultRole", options)
|
||||
auth_key = self.get_option("tokenFileOrVaultRole", options)
|
||||
mount = self.get_option("vaultMount", options)
|
||||
path = self.get_option("vaultPath", options)
|
||||
bundle = self.get_option("bundleChain", options)
|
||||
@ -285,10 +286,27 @@ class VaultDestinationPlugin(DestinationPlugin):
|
||||
|
||||
client.secrets.kv.default_kv_version = api_version
|
||||
|
||||
if obj_name:
|
||||
path = "{0}/{1}".format(path, obj_name)
|
||||
else:
|
||||
path = "{0}/{1}".format(path, cname)
|
||||
t_path = path.format(
|
||||
CN=cname,
|
||||
OU=organizational_unit(cert),
|
||||
O=organization(cert), # noqa: E741
|
||||
L=location(cert),
|
||||
S=state(cert),
|
||||
C=country(cert)
|
||||
)
|
||||
if not obj_name:
|
||||
obj_name = '{CN}'
|
||||
|
||||
f_obj_name = obj_name.format(
|
||||
CN=cname,
|
||||
OU=organizational_unit(cert),
|
||||
O=organization(cert), # noqa: E741
|
||||
L=location(cert),
|
||||
S=state(cert),
|
||||
C=country(cert)
|
||||
)
|
||||
|
||||
path = "{0}/{1}".format(t_path, f_obj_name)
|
||||
|
||||
secret = get_secret(client, mount, path)
|
||||
secret["data"][cname] = {}
|
||||
|
@ -33,7 +33,7 @@ readme-renderer==25.0 # via twine
|
||||
requests-toolbelt==0.9.1 # via twine
|
||||
requests==2.23.0 # via requests-toolbelt, twine
|
||||
secretstorage==3.1.2 # via keyring
|
||||
six==1.14.0 # via bleach, cryptography, readme-renderer, virtualenv
|
||||
six==1.15.0 # via bleach, cryptography, readme-renderer, virtualenv
|
||||
toml==0.10.0 # via pre-commit
|
||||
tqdm==4.45.0 # via twine
|
||||
twine==3.1.1 # via -r requirements-dev.in
|
||||
|
@ -16,8 +16,8 @@ babel==2.8.0 # via sphinx
|
||||
bcrypt==3.1.7 # via -r requirements.txt, flask-bcrypt, paramiko
|
||||
billiard==3.6.3.0 # via -r requirements.txt, celery
|
||||
blinker==1.4 # via -r requirements.txt, flask-mail, flask-principal, raven
|
||||
boto3==1.13.11 # via -r requirements.txt
|
||||
botocore==1.16.11 # via -r requirements.txt, boto3, s3transfer
|
||||
boto3==1.13.18 # via -r requirements.txt
|
||||
botocore==1.16.18 # via -r requirements.txt, boto3, s3transfer
|
||||
celery[redis]==4.4.2 # via -r requirements.txt
|
||||
certifi==2020.4.5.1 # via -r requirements.txt, requests
|
||||
certsrv==2.1.1 # via -r requirements.txt
|
||||
@ -42,7 +42,7 @@ flask-sqlalchemy==2.4.1 # via -r requirements.txt, flask-migrate
|
||||
flask==1.1.2 # via -r requirements.txt, flask-bcrypt, flask-cors, flask-mail, flask-migrate, flask-principal, flask-restful, flask-script, flask-sqlalchemy, raven
|
||||
future==0.18.2 # via -r requirements.txt, cloudflare
|
||||
gunicorn==20.0.4 # via -r requirements.txt
|
||||
hvac==0.10.1 # via -r requirements.txt
|
||||
hvac==0.10.3 # via -r requirements.txt
|
||||
idna==2.9 # via -r requirements.txt, requests
|
||||
imagesize==1.2.0 # via sphinx
|
||||
inflection==0.4.0 # via -r requirements.txt
|
||||
@ -87,7 +87,7 @@ requests-toolbelt==0.9.1 # via -r requirements.txt, acme
|
||||
requests[security]==2.23.0 # via -r requirements.txt, acme, certsrv, cloudflare, hvac, requests-toolbelt, sphinx
|
||||
retrying==1.3.3 # via -r requirements.txt
|
||||
s3transfer==0.3.3 # via -r requirements.txt, boto3
|
||||
six==1.14.0 # via -r requirements.txt, acme, bcrypt, cryptography, flask-cors, flask-restful, hvac, josepy, jsonlines, packaging, pynacl, pyopenssl, python-dateutil, retrying, sphinxcontrib-httpdomain, sqlalchemy-utils
|
||||
six==1.15.0 # via -r requirements.txt, acme, bcrypt, cryptography, flask-cors, flask-restful, hvac, josepy, jsonlines, packaging, pynacl, pyopenssl, python-dateutil, retrying, sphinxcontrib-httpdomain, sqlalchemy-utils
|
||||
snowballstemmer==2.0.0 # via sphinx
|
||||
sphinx-rtd-theme==0.4.3 # via -r requirements-docs.in
|
||||
sphinx==3.0.3 # via -r requirements-docs.in, sphinx-rtd-theme, sphinxcontrib-httpdomain
|
||||
@ -98,7 +98,7 @@ sphinxcontrib-httpdomain==1.7.0 # via -r requirements-docs.in
|
||||
sphinxcontrib-jsmath==1.0.1 # via sphinx
|
||||
sphinxcontrib-qthelp==1.0.3 # via sphinx
|
||||
sphinxcontrib-serializinghtml==1.1.4 # via sphinx
|
||||
sqlalchemy-utils==0.36.5 # via -r requirements.txt
|
||||
sqlalchemy-utils==0.36.6 # via -r requirements.txt
|
||||
sqlalchemy==1.3.16 # via -r requirements.txt, alembic, flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils
|
||||
tabulate==0.8.7 # via -r requirements.txt
|
||||
twofish==0.3.0 # via -r requirements.txt, pyjks
|
||||
|
@ -10,9 +10,9 @@ aws-sam-translator==1.22.0 # via cfn-lint
|
||||
aws-xray-sdk==2.5.0 # via moto
|
||||
bandit==1.6.2 # via -r requirements-tests.in
|
||||
black==19.10b0 # via -r requirements-tests.in
|
||||
boto3==1.13.11 # via aws-sam-translator, moto
|
||||
boto3==1.13.18 # via aws-sam-translator, moto
|
||||
boto==2.49.0 # via moto
|
||||
botocore==1.16.11 # via aws-xray-sdk, boto3, moto, s3transfer
|
||||
botocore==1.16.18 # via aws-xray-sdk, boto3, moto, s3transfer
|
||||
certifi==2020.4.5.1 # via requests
|
||||
cffi==1.14.0 # via cryptography
|
||||
cfn-lint==0.29.5 # via moto
|
||||
@ -72,7 +72,7 @@ requests==2.23.0 # via docker, moto, requests-mock, responses
|
||||
responses==0.10.12 # via moto
|
||||
rsa==4.0 # via python-jose
|
||||
s3transfer==0.3.3 # via boto3
|
||||
six==1.14.0 # via aws-sam-translator, bandit, cfn-lint, cryptography, docker, ecdsa, fakeredis, freezegun, jsonschema, moto, packaging, pyrsistent, python-dateutil, python-jose, requests-mock, responses, stevedore, websocket-client
|
||||
six==1.15.0 # via aws-sam-translator, bandit, cfn-lint, cryptography, docker, ecdsa, fakeredis, freezegun, jsonschema, moto, packaging, pyrsistent, python-dateutil, python-jose, requests-mock, responses, stevedore, websocket-client
|
||||
smmap==3.0.2 # via gitdb
|
||||
sortedcontainers==2.1.0 # via fakeredis
|
||||
sshpubkeys==3.1.0 # via moto
|
||||
|
@ -14,8 +14,8 @@ asyncpool==1.0 # via -r requirements.in
|
||||
bcrypt==3.1.7 # via flask-bcrypt, paramiko
|
||||
billiard==3.6.3.0 # via celery
|
||||
blinker==1.4 # via flask-mail, flask-principal, raven
|
||||
boto3==1.13.11 # via -r requirements.in
|
||||
botocore==1.16.11 # via -r requirements.in, boto3, s3transfer
|
||||
boto3==1.13.18 # via -r requirements.in
|
||||
botocore==1.16.18 # via -r requirements.in, boto3, s3transfer
|
||||
celery[redis]==4.4.2 # via -r requirements.in
|
||||
certifi==2020.4.5.1 # via -r requirements.in, requests
|
||||
certsrv==2.1.1 # via -r requirements.in
|
||||
@ -40,7 +40,7 @@ flask-sqlalchemy==2.4.1 # via -r requirements.in, flask-migrate
|
||||
flask==1.1.2 # via -r requirements.in, flask-bcrypt, flask-cors, flask-mail, flask-migrate, flask-principal, flask-restful, flask-script, flask-sqlalchemy, raven
|
||||
future==0.18.2 # via -r requirements.in, cloudflare
|
||||
gunicorn==20.0.4 # via -r requirements.in
|
||||
hvac==0.10.1 # via -r requirements.in
|
||||
hvac==0.10.3 # via -r requirements.in
|
||||
idna==2.9 # via requests
|
||||
inflection==0.4.0 # via -r requirements.in
|
||||
itsdangerous==1.1.0 # via flask
|
||||
@ -81,8 +81,8 @@ requests-toolbelt==0.9.1 # via acme
|
||||
requests[security]==2.23.0 # via -r requirements.in, acme, certsrv, cloudflare, hvac, requests-toolbelt
|
||||
retrying==1.3.3 # via -r requirements.in
|
||||
s3transfer==0.3.3 # via boto3
|
||||
six==1.14.0 # via -r requirements.in, acme, bcrypt, cryptography, flask-cors, flask-restful, hvac, josepy, jsonlines, pynacl, pyopenssl, python-dateutil, retrying, sqlalchemy-utils
|
||||
sqlalchemy-utils==0.36.5 # via -r requirements.in
|
||||
six==1.15.0 # via -r requirements.in, acme, bcrypt, cryptography, flask-cors, flask-restful, hvac, josepy, jsonlines, pynacl, pyopenssl, python-dateutil, retrying, sqlalchemy-utils
|
||||
sqlalchemy-utils==0.36.6 # via -r requirements.in
|
||||
sqlalchemy==1.3.16 # via alembic, flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils
|
||||
tabulate==0.8.7 # via -r requirements.in
|
||||
twofish==0.3.0 # via pyjks
|
||||
|
Loading…
Reference in New Issue
Block a user