mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Update dependencies and add Let's Encrypt DNS challenges support
This commit is contained in:
parent
7d473beefa
commit
6d46635a5e
27 changed files with 1548 additions and 1943 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -12,6 +12,7 @@
|
|||
- [FEATURE] Optimized the way the scheduler sends the configuration to the instances to make it faster and more reliable using a ThreadPoolExecutor
|
||||
- [FEATURE] Add the possibility to set a custom timezone for every service via the `TZ` environment variable (will apply to the logs and all date fields stored in the database). If not set, it will use the local timezone of the server.
|
||||
- [FEATURE] Add the possibility to run plugins job in async mode to avoid running them in order in the scheduler by setting the `async` key to `true` in the plugin job configuration (default is `false`)
|
||||
- [FEATURE] Add Let's Encrypt DNS challenges support !
|
||||
- [SCHEDULER] Refactor the scheduler to use the `BUNKERWEB_INSTANCES` (previously known as `OVERRIDE_INSTANCES`) environment variable instead of an integration specific system
|
||||
- [AUTOCONF] Add new `NAMESPACES` environment variable to allow setting the namespaces to watch for the autoconf feature which makes it possible to use multiple autoconf instances in the same cluster while keeping the configuration separated
|
||||
- [AUTOCONF] Add new `USE_KUBERNETES_FQDN` environment variable to allow using the full qualified domain name of the services in Kubernetes instead of the ip address for the hostname of instances (default is yes)
|
||||
|
|
@ -25,11 +26,23 @@
|
|||
- [DOCS] Updated docs for all new features and changes
|
||||
- [MISC] Review security headers in the `headers` plugin to improve security
|
||||
- [MISC] Updated context of `realip`'s `USE_PROXY_PROTOCOL` setting to `global` as it was always applied globally even if set only on a service
|
||||
- [DEPS] Updated coreruleset-v4 version to v4.7.0
|
||||
- [DEPS] Updated lua-resty-core version to v0.1.30
|
||||
- [DEPS] Updated lua-resty-lrucache version to v0.15
|
||||
- [DEPS] Updated Mbed TLS version to v3.6.2
|
||||
|
||||
## v1.5.11 - 2024/11/08
|
||||
|
||||
- [BUGFIX] Fix INTERCEPTED_ERROR_CODES to allow empty value
|
||||
- [UI] Fix missing settings when a service is published online
|
||||
- [UI] Fix instances always down in instances page
|
||||
- [AUTOCONF] Fix BW env vars not retrieved
|
||||
- [AUTOCONF] Fix deadlock on k8s events when there is no ingress
|
||||
- [LINUX] Increase default worker dict size to avoid crash on RPI
|
||||
- [MISC] Add WORKERLOCK_MEMORY_SIZE setting for worker dict size
|
||||
- [MISC] Add API_TIMEOUT and API_READ_TIMEOUT settings to control API timeouts
|
||||
- [DEPS] Updated coreruleset-v4 version to v4.8.0
|
||||
- [DEPS] Updated coreruleset-v3 version to v3.3.7
|
||||
|
||||
## v1.5.10 - 2024/08/17
|
||||
|
||||
- [UI] Fix setup wizard bug related to certificate
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
docker==7.1.0
|
||||
kubernetes==31.0.0
|
||||
pytz==2024.2
|
||||
urllib3<2.0.0
|
||||
|
|
|
|||
|
|
@ -241,10 +241,11 @@ six==1.16.0 \
|
|||
# via
|
||||
# kubernetes
|
||||
# python-dateutil
|
||||
urllib3==2.2.3 \
|
||||
--hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
|
||||
--hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9
|
||||
urllib3==1.26.20 \
|
||||
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
|
||||
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
|
||||
# via
|
||||
# -r requirements.in
|
||||
# docker
|
||||
# kubernetes
|
||||
# requests
|
||||
|
|
|
|||
|
|
@ -1,114 +1,239 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from itertools import chain
|
||||
from json import dumps
|
||||
from os import environ, getenv, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
from subprocess import DEVNULL, STDOUT, Popen, run, PIPE
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from re import MULTILINE, search
|
||||
from select import select
|
||||
from shutil import rmtree
|
||||
from subprocess import DEVNULL, PIPE, STDOUT, Popen, run
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from typing import Dict, Literal, Type, Union
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from logger import setup_logger # type: ignore
|
||||
from pydantic import ValidationError
|
||||
from models import (
|
||||
CloudflareProvider,
|
||||
DigitalOceanProvider,
|
||||
DnsimpleProvider,
|
||||
DnsMadeEasyProvider,
|
||||
GehirnProvider,
|
||||
GoogleProvider,
|
||||
LinodeProvider,
|
||||
LuaDnsProvider,
|
||||
NSOneProvider,
|
||||
OvhProvider,
|
||||
Rfc2136Provider,
|
||||
Route53Provider,
|
||||
SakuraCloudProvider,
|
||||
ScalewayProvider,
|
||||
WildcardGenerator,
|
||||
)
|
||||
|
||||
from common_utils import bytes_hash # type: ignore
|
||||
from jobs import Job # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
|
||||
LOGGER = setup_logger("LETS-ENCRYPT.new", getenv("LOG_LEVEL", "INFO"))
|
||||
CERTBOT_BIN = join(sep, "usr", "share", "bunkerweb", "deps", "python", "bin", "certbot")
|
||||
DEPS_PATH = join(sep, "usr", "share", "bunkerweb", "deps", "python")
|
||||
|
||||
LOGGER_CERTBOT = setup_logger("LETS-ENCRYPT.new.certbot", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
CERTBOT_BIN = join(sep, "usr", "share", "bunkerweb", "deps", "python", "bin", "certbot")
|
||||
|
||||
DATA_PATH = Path(sep, "var", "cache", "bunkerweb", "letsencrypt", "etc")
|
||||
LETS_ENCRYPT_JOBS_PATH = Path(sep, "usr", "share", "bunkerweb", "core", "letsencrypt", "jobs")
|
||||
LETS_ENCRYPT_WORK_DIR = join(sep, "var", "lib", "bunkerweb", "letsencrypt")
|
||||
LETS_ENCRYPT_LOGS_DIR = join(sep, "var", "log", "bunkerweb")
|
||||
PLUGIN_PATH = Path(sep, "usr", "share", "bunkerweb", "core", "letsencrypt")
|
||||
JOBS_PATH = PLUGIN_PATH.joinpath("jobs")
|
||||
CACHE_PATH = Path(sep, "var", "cache", "bunkerweb", "letsencrypt")
|
||||
DATA_PATH = CACHE_PATH.joinpath("etc")
|
||||
WORK_DIR = join(sep, "var", "lib", "bunkerweb", "letsencrypt")
|
||||
LOGS_DIR = join(sep, "var", "log", "bunkerweb", "letsencrypt")
|
||||
|
||||
|
||||
def certbot_new(domains: str, email: str, use_letsencrypt_staging: bool = False, *, force: bool = False) -> int:
|
||||
process = Popen(
|
||||
[
|
||||
CERTBOT_BIN,
|
||||
"certonly",
|
||||
"--config-dir",
|
||||
DATA_PATH.as_posix(),
|
||||
"--work-dir",
|
||||
LETS_ENCRYPT_WORK_DIR,
|
||||
"--logs-dir",
|
||||
LETS_ENCRYPT_LOGS_DIR,
|
||||
"--manual",
|
||||
"--preferred-challenges=http",
|
||||
"--manual-auth-hook",
|
||||
LETS_ENCRYPT_JOBS_PATH.joinpath("certbot-auth.py").as_posix(),
|
||||
"--manual-cleanup-hook",
|
||||
LETS_ENCRYPT_JOBS_PATH.joinpath("certbot-cleanup.py").as_posix(),
|
||||
"-n",
|
||||
"-d",
|
||||
domains,
|
||||
"--email",
|
||||
email,
|
||||
"--agree-tos",
|
||||
"--expand",
|
||||
]
|
||||
+ (["--staging"] if use_letsencrypt_staging else [])
|
||||
+ (["--force-renewal"] if force else []),
|
||||
stdin=DEVNULL,
|
||||
stderr=PIPE,
|
||||
universal_newlines=True,
|
||||
env=environ | {"PYTHONPATH": join(sep, "usr", "share", "bunkerweb", "deps", "python")},
|
||||
)
|
||||
def certbot_new(
|
||||
challenge_type: Literal["dns", "http"],
|
||||
domains: str,
|
||||
email: str,
|
||||
provider: str = None,
|
||||
credentials_path: Union[str, Path] = None,
|
||||
propagation: str = "default",
|
||||
staging: bool = False,
|
||||
force: bool = False,
|
||||
) -> int:
|
||||
if isinstance(credentials_path, str):
|
||||
credentials_path = Path(credentials_path)
|
||||
|
||||
# * Building the certbot command
|
||||
command = [
|
||||
CERTBOT_BIN,
|
||||
"certonly",
|
||||
"--config-dir",
|
||||
DATA_PATH.as_posix(),
|
||||
"--work-dir",
|
||||
WORK_DIR,
|
||||
"--logs-dir",
|
||||
LOGS_DIR,
|
||||
"-n",
|
||||
"-d",
|
||||
domains,
|
||||
"--email",
|
||||
email,
|
||||
"--agree-tos",
|
||||
"--expand",
|
||||
]
|
||||
|
||||
env = environ | {"PYTHONPATH": DEPS_PATH}
|
||||
|
||||
if challenge_type == "dns":
|
||||
# * Adding DNS challenge hooks
|
||||
command.append("--preferred-challenges=dns")
|
||||
|
||||
# * Adding the propagation time to the command
|
||||
if propagation != "default":
|
||||
if not propagation.isdigit():
|
||||
LOGGER.warning(f"Invalid propagation time : {propagation}, using provider's default...")
|
||||
else:
|
||||
command.extend([f"--dns-{provider}-propagation-seconds", propagation])
|
||||
|
||||
# * Adding the credentials to the command
|
||||
if provider == "route53":
|
||||
# ? Route53 credentials are different from the others, we need to add them to the environment
|
||||
with credentials_path.open("r") as file:
|
||||
for line in file:
|
||||
key, value = line.strip().split("=", 1)
|
||||
env[key] = value
|
||||
else:
|
||||
command.extend([f"--dns-{provider}-credentials", credentials_path.as_posix()])
|
||||
|
||||
# * Adding plugin argument
|
||||
if provider == "scaleway":
|
||||
# ? Scaleway plugin uses different arguments
|
||||
command.extend(["--authenticator", "dns-scaleway"])
|
||||
else:
|
||||
command.append(f"--dns-{provider}")
|
||||
elif challenge_type == "http":
|
||||
# * Adding HTTP challenge hooks
|
||||
command.extend(
|
||||
[
|
||||
"--manual",
|
||||
"--preferred-challenges=http",
|
||||
"--manual-auth-hook",
|
||||
JOBS_PATH.joinpath("certbot-auth.py").as_posix(),
|
||||
"--manual-cleanup-hook",
|
||||
JOBS_PATH.joinpath("certbot-cleanup.py").as_posix(),
|
||||
]
|
||||
)
|
||||
|
||||
if staging:
|
||||
command.append("--staging")
|
||||
|
||||
if force:
|
||||
command.append("--force-renewal")
|
||||
|
||||
current_date = datetime.now()
|
||||
process = Popen(command, stdin=DEVNULL, stderr=PIPE, universal_newlines=True, env=env)
|
||||
|
||||
while process.poll() is None:
|
||||
if process.stderr:
|
||||
for line in process.stderr:
|
||||
LOGGER_CERTBOT.info(line.strip())
|
||||
rlist, _, _ = select([process.stderr], [], [], 1) # 1-second timeout
|
||||
if rlist:
|
||||
for line in process.stderr:
|
||||
LOGGER_CERTBOT.info(line.strip())
|
||||
break
|
||||
|
||||
if datetime.now() - current_date > timedelta(seconds=5):
|
||||
LOGGER.info(
|
||||
"⏳ Still generating certificate(s)" + (" (this may take a while depending on the provider)" if challenge_type == "dns" else "") + "..."
|
||||
)
|
||||
current_date = datetime.now()
|
||||
|
||||
return process.returncode
|
||||
|
||||
|
||||
status = 0
|
||||
IS_MULTISITE = getenv("MULTISITE", "no") == "yes"
|
||||
|
||||
try:
|
||||
# Check if we're using let's encrypt
|
||||
use_letsencrypt = False
|
||||
is_multisite = getenv("MULTISITE", "no") == "yes"
|
||||
all_domains = getenv("SERVER_NAME", "").lower()
|
||||
server_names = all_domains.split(" ")
|
||||
servers = getenv("SERVER_NAME", "").lower() or []
|
||||
|
||||
if getenv("AUTO_LETS_ENCRYPT", "no") == "yes":
|
||||
use_letsencrypt = True
|
||||
elif is_multisite:
|
||||
for first_server in server_names:
|
||||
if first_server and getenv(f"{first_server}_AUTO_LETS_ENCRYPT", getenv("AUTO_LETS_ENCRYPT", "no")) == "yes":
|
||||
if isinstance(servers, str):
|
||||
servers = servers.split(" ")
|
||||
|
||||
if not servers:
|
||||
LOGGER.error("There are no server names, skipping generation...")
|
||||
sys_exit(0)
|
||||
|
||||
use_letsencrypt = False
|
||||
use_letsencrypt_dns = False
|
||||
|
||||
if not IS_MULTISITE:
|
||||
use_letsencrypt = getenv("AUTO_LETS_ENCRYPT", "no") == "yes"
|
||||
use_letsencrypt_dns = getenv("LETS_ENCRYPT_CHALLENGE", "http") == "dns"
|
||||
domains_server_names = {servers[0]: servers}
|
||||
else:
|
||||
domains_server_names = {}
|
||||
|
||||
for first_server in servers:
|
||||
if first_server and getenv(f"{first_server}_AUTO_LETS_ENCRYPT", "no") == "yes":
|
||||
use_letsencrypt = True
|
||||
break
|
||||
|
||||
if first_server and getenv(f"{first_server}_LETS_ENCRYPT_CHALLENGE", "http") == "dns":
|
||||
use_letsencrypt_dns = True
|
||||
|
||||
domains_server_names[first_server] = getenv(f"{first_server}_SERVER_NAME", first_server).lower()
|
||||
|
||||
if not use_letsencrypt:
|
||||
LOGGER.info("Let's Encrypt is not activated, skipping generation...")
|
||||
sys_exit(0)
|
||||
elif not all_domains:
|
||||
LOGGER.warning("There are no server names, skipping generation...")
|
||||
sys_exit(0)
|
||||
|
||||
provider_classes = {}
|
||||
|
||||
if use_letsencrypt_dns:
|
||||
provider_classes: Dict[
|
||||
str,
|
||||
Union[
|
||||
Type[CloudflareProvider],
|
||||
Type[DigitalOceanProvider],
|
||||
Type[DnsimpleProvider],
|
||||
Type[DnsMadeEasyProvider],
|
||||
Type[GehirnProvider],
|
||||
Type[GoogleProvider],
|
||||
Type[LinodeProvider],
|
||||
Type[LuaDnsProvider],
|
||||
Type[NSOneProvider],
|
||||
Type[OvhProvider],
|
||||
Type[Rfc2136Provider],
|
||||
Type[Route53Provider],
|
||||
Type[SakuraCloudProvider],
|
||||
Type[ScalewayProvider],
|
||||
],
|
||||
] = {
|
||||
"cloudflare": CloudflareProvider,
|
||||
"digitalocean": DigitalOceanProvider,
|
||||
"dnsimple": DnsimpleProvider,
|
||||
"dnsmadeeasy": DnsMadeEasyProvider,
|
||||
"gehirn": GehirnProvider,
|
||||
"google": GoogleProvider,
|
||||
"linode": LinodeProvider,
|
||||
"luadns": LuaDnsProvider,
|
||||
"nsone": NSOneProvider,
|
||||
"ovh": OvhProvider,
|
||||
"rfc2136": Rfc2136Provider,
|
||||
"route53": Route53Provider,
|
||||
"sakuracloud": SakuraCloudProvider,
|
||||
"scaleway": ScalewayProvider,
|
||||
}
|
||||
|
||||
JOB = Job(LOGGER)
|
||||
|
||||
# Restore Let's Encrypt data from db cache
|
||||
# ? Restore data from db cache of certbot-renew job
|
||||
JOB.restore_cache(job_name="certbot-renew")
|
||||
|
||||
domains_to_ask = {}
|
||||
# Multisite case
|
||||
if is_multisite:
|
||||
domains_server_names = {}
|
||||
|
||||
for first_server in server_names:
|
||||
if not first_server or getenv(f"{first_server}_AUTO_LETS_ENCRYPT", getenv("AUTO_LETS_ENCRYPT", "no")) != "yes":
|
||||
continue
|
||||
domains_server_names[first_server] = getenv(f"{first_server}_SERVER_NAME", first_server).lower()
|
||||
# Singlesite case
|
||||
else:
|
||||
domains_server_names = {server_names[0]: all_domains}
|
||||
|
||||
proc = run(
|
||||
[
|
||||
CERTBOT_BIN,
|
||||
|
|
@ -116,87 +241,285 @@ try:
|
|||
"--config-dir",
|
||||
DATA_PATH.as_posix(),
|
||||
"--work-dir",
|
||||
LETS_ENCRYPT_WORK_DIR,
|
||||
WORK_DIR,
|
||||
"--logs-dir",
|
||||
LETS_ENCRYPT_LOGS_DIR,
|
||||
LOGS_DIR,
|
||||
],
|
||||
stdin=DEVNULL,
|
||||
stdout=PIPE,
|
||||
stderr=STDOUT,
|
||||
text=True,
|
||||
env=environ | {"PYTHONPATH": join(sep, "usr", "share", "bunkerweb", "deps", "python")},
|
||||
env=environ | {"PYTHONPATH": DEPS_PATH},
|
||||
check=False,
|
||||
)
|
||||
stdout = proc.stdout
|
||||
|
||||
WILDCARD_GENERATOR = WildcardGenerator()
|
||||
credential_paths = set()
|
||||
generated_domains = set()
|
||||
domains_to_ask = {}
|
||||
|
||||
if proc.returncode != 0:
|
||||
LOGGER.error(f"Error while checking certificates :\n{proc.stdout}")
|
||||
domains_to_ask = {domain: True for domain in server_names}
|
||||
else:
|
||||
certificate_blocks = stdout.split("Certificate Name: ")[1:]
|
||||
for first_server, domains in domains_server_names.items():
|
||||
generated_domains.update(domains.split(" "))
|
||||
if getenv(f"{first_server}_USE_LETS_ENCRYPT_WILDCARD", getenv("USE_LETS_ENCRYPT_WILDCARD", "no")) == "yes":
|
||||
wildcards = WildcardGenerator.get_wildcards_from_domains((first_server,))
|
||||
first_server = wildcards[0].lstrip("*.")
|
||||
domains = set(wildcards)
|
||||
else:
|
||||
domains = set(domains.split(" "))
|
||||
|
||||
current_domains = search(rf"Domains: {first_server}(?P<domains>.*)\n\s*Expiry Date: (?P<expiry_date>.*)$$", stdout, MULTILINE)
|
||||
if not current_domains:
|
||||
domains_to_ask[first_server] = False
|
||||
certificate_block = None
|
||||
for block in certificate_blocks:
|
||||
if block.startswith(f"{first_server}\n"):
|
||||
certificate_block = block
|
||||
break
|
||||
|
||||
if not certificate_block:
|
||||
domains_to_ask[first_server] = True
|
||||
LOGGER.warning(f"Certificate block for {first_server} not found, asking new certificate...")
|
||||
continue
|
||||
elif set(f"{first_server}{current_domains.groupdict()['domains']}".strip().split(" ")) != set(domains.split(" ")):
|
||||
|
||||
try:
|
||||
cert_domains = search(r"Domains: (?P<domains>.*)\n\s*Expiry Date: (?P<expiry_date>.*)\n", certificate_block, MULTILINE)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"[{first_server}] Error while parsing certificate block: {e}")
|
||||
continue
|
||||
|
||||
if not cert_domains:
|
||||
LOGGER.error(f"[{first_server}] Failed to parse domains and expiry date from certificate block.")
|
||||
continue
|
||||
|
||||
cert_domains_list = cert_domains.group("domains").strip().split()
|
||||
cert_domains_set = set(cert_domains_list)
|
||||
|
||||
if cert_domains_set != domains:
|
||||
domains_to_ask[first_server] = True
|
||||
LOGGER.warning(f"Domains for {first_server} are not the same as in the certificate, asking new certificate...")
|
||||
domains_to_ask[first_server] = True
|
||||
continue
|
||||
elif "TEST_CERT" in current_domains.groupdict()["expiry_date"] and getenv(f"{first_server}_"):
|
||||
LOGGER.warning(f"Certificate environment (staging/production) changed for {first_server}, asking new certificate...")
|
||||
|
||||
use_letsencrypt_staging = getenv(f"{first_server}_USE_LETS_ENCRYPT_STAGING", getenv("USE_LETS_ENCRYPT_STAGING", "no")) == "yes"
|
||||
if ("TEST_CERT" in current_domains.groupdict()["expiry_date"] and not use_letsencrypt_staging) or (
|
||||
"TEST_CERT" not in current_domains.groupdict()["expiry_date"] and use_letsencrypt_staging
|
||||
):
|
||||
LOGGER.warning(f"Certificate environment (staging/production) changed for {first_server}, asking new certificate...")
|
||||
is_test_cert = "TEST_CERT" in cert_domains.group("expiry_date")
|
||||
|
||||
if (is_test_cert and not use_letsencrypt_staging) or (not is_test_cert and use_letsencrypt_staging):
|
||||
domains_to_ask[first_server] = True
|
||||
LOGGER.info(f"Certificates already exists for domain(s) {domains}")
|
||||
LOGGER.warning(f"Certificate environment (staging/production) changed for {first_server}, asking new certificate...")
|
||||
continue
|
||||
|
||||
letsencrypt_challenge = getenv(f"{first_server}_LETS_ENCRYPT_CHALLENGE", getenv("LETS_ENCRYPT_CHALLENGE", "http"))
|
||||
letsencrypt_provider = getenv(f"{first_server}_LETS_ENCRYPT_DNS_PROVIDER", getenv("LETS_ENCRYPT_DNS_PROVIDER", ""))
|
||||
current_provider = search(rf"DNS-01 challenge: {letsencrypt_provider}", certificate_block, MULTILINE)
|
||||
if letsencrypt_challenge == "dns":
|
||||
if letsencrypt_provider and (not current_provider or current_provider.group(1) != letsencrypt_provider):
|
||||
domains_to_ask[first_server] = True
|
||||
LOGGER.warning(f"Provider for {first_server} is not the same as in the certificate, asking new certificate...")
|
||||
continue
|
||||
elif current_provider and letsencrypt_challenge == "http":
|
||||
domains_to_ask[first_server] = True
|
||||
LOGGER.warning(f"{first_server} is no longer using DNS challenge, asking new certificate...")
|
||||
continue
|
||||
|
||||
domains_to_ask[first_server] = False
|
||||
LOGGER.info(f"[{first_server}] Certificates already exist for domain(s) {domains}, expiry date: {cert_domains.group('expiry_date')}")
|
||||
|
||||
for first_server, domains in domains_server_names.items():
|
||||
if first_server not in domains_to_ask:
|
||||
# * Getting all the necessary data
|
||||
data = {
|
||||
"email": getenv(f"{first_server}_EMAIL_LETS_ENCRYPT", getenv("EMAIL_LETS_ENCRYPT", "")) or f"contact@{first_server}",
|
||||
"challenge": getenv(f"{first_server}_LETS_ENCRYPT_CHALLENGE", getenv("LETS_ENCRYPT_CHALLENGE", "http")),
|
||||
"staging": getenv(f"{first_server}_USE_LETS_ENCRYPT_STAGING", getenv("USE_LETS_ENCRYPT_STAGING", "no")) == "yes",
|
||||
"use_wildcard": getenv(f"{first_server}_USE_LETS_ENCRYPT_WILDCARD", getenv("USE_LETS_ENCRYPT_WILDCARD", "no")) == "yes",
|
||||
"provider": getenv(f"{first_server}_LETS_ENCRYPT_DNS_PROVIDER", getenv("LETS_ENCRYPT_DNS_PROVIDER", "")),
|
||||
"propagation": getenv(f"{first_server}_LETS_ENCRYPT_DNS_PROPAGATION", getenv("LETS_ENCRYPT_DNS_PROPAGATION", "default")),
|
||||
"credential_items": {},
|
||||
}
|
||||
|
||||
if (not data["use_wildcard"] and not domains_to_ask.get(first_server)) or (
|
||||
data["use_wildcard"] and not domains_to_ask.get(WILDCARD_GENERATOR.get_wildcards_from_domains((first_server,))[0].lstrip("*."))
|
||||
):
|
||||
continue
|
||||
|
||||
real_email = getenv(f"{first_server}_EMAIL_LETS_ENCRYPT", getenv("EMAIL_LETS_ENCRYPT", f"contact@{first_server}"))
|
||||
if not real_email:
|
||||
real_email = f"contact@{first_server}"
|
||||
# * Getting the DNS provider data if necessary
|
||||
if data["challenge"] == "dns":
|
||||
credential_key = f"{first_server}_LETS_ENCRYPT_DNS_CREDENTIAL_ITEM" if IS_MULTISITE else "LETS_ENCRYPT_DNS_CREDENTIAL_ITEM"
|
||||
for env_key, env_value in environ.items():
|
||||
if env_value and env_key.startswith(credential_key):
|
||||
key, value = env_value.split(" ", 1)
|
||||
data["credential_items"][key.lower()] = value
|
||||
|
||||
use_letsencrypt_staging = getenv(f"{first_server}_USE_LETS_ENCRYPT_STAGING", getenv("USE_LETS_ENCRYPT_STAGING", "no")) == "yes"
|
||||
LOGGER.debug(f"Data for service {first_server} : {dumps(data)}")
|
||||
|
||||
LOGGER.info(f"Asking certificates for domain(s) : {domains} (email = {real_email}) to Let's Encrypt {'staging ' if use_letsencrypt_staging else ''}...")
|
||||
if certbot_new(domains.replace(" ", ","), real_email, use_letsencrypt_staging, force=domains_to_ask[first_server]) != 0:
|
||||
# * Checking if the DNS data is valid
|
||||
if data["challenge"] == "dns":
|
||||
if not data["provider"]:
|
||||
LOGGER.warning(
|
||||
f"No provider found for service {first_server} (available providers : {', '.join(provider_classes.keys())}), skipping certificate(s) generation..." # noqa: E501
|
||||
)
|
||||
continue
|
||||
elif data["provider"] not in provider_classes:
|
||||
LOGGER.warning(
|
||||
f"Provider {data['provider']} not found for service {first_server} (available providers : {', '.join(provider_classes.keys())}), skipping certificate(s) generation..." # noqa: E501
|
||||
)
|
||||
continue
|
||||
elif not data["credential_items"]:
|
||||
LOGGER.warning(f"No credentials items found for service {first_server} (you should have at least one), skipping certificate(s) generation...")
|
||||
continue
|
||||
|
||||
# * Validating the credentials
|
||||
try:
|
||||
provider = provider_classes[data["provider"]](**data["credential_items"])
|
||||
except ValidationError as ve:
|
||||
LOGGER.error(f"Error while validating credentials for service {first_server} :\n{ve}")
|
||||
continue
|
||||
|
||||
content = provider.get_formatted_credentials()
|
||||
else:
|
||||
content = b"http_challenge"
|
||||
|
||||
# * Adding the domains to Wildcard Generator if necessary
|
||||
file_type = provider.get_file_type() if data["challenge"] == "dns" else "txt"
|
||||
file_path = (first_server, f"credentials.{file_type}")
|
||||
if data["use_wildcard"]:
|
||||
group = f"{data['provider'] if data['challenge'] == 'dns' else 'http'}_{bytes_hash(content, algorithm='sha1')}"
|
||||
LOGGER.info(
|
||||
f"Service {first_server} is using wildcard, "
|
||||
+ ("the propagation time will be the provider's default and " if data["challenge"] == "dns" else "")
|
||||
+ "the email will be the same as the first domain that created the group..."
|
||||
)
|
||||
WILDCARD_GENERATOR.extend(group, domains.split(" "), data["email"], data["staging"])
|
||||
file_path = (f"{group}.{file_type}",)
|
||||
|
||||
# * Generating the credentials file
|
||||
credentials_path = CACHE_PATH.joinpath(*file_path)
|
||||
|
||||
if data["challenge"] == "dns":
|
||||
if not credentials_path.is_file():
|
||||
cached, err = JOB.cache_file(
|
||||
credentials_path.name, content, job_name="certbot-renew", service_id=first_server if not data["use_wildcard"] else ""
|
||||
)
|
||||
if not cached:
|
||||
LOGGER.error(f"Error while saving service {first_server}'s credentials file in cache : {err}")
|
||||
continue
|
||||
LOGGER.info(f"Successfully saved service {first_server}'s credentials file in cache")
|
||||
elif data["use_wildcard"]:
|
||||
LOGGER.info(f"Service {first_server}'s wildcard credentials file has already been generated")
|
||||
else:
|
||||
old_content = credentials_path.read_bytes()
|
||||
if old_content != content:
|
||||
LOGGER.warning(f"Service {first_server}'s credentials file is outdated, updating it...")
|
||||
cached, err = JOB.cache_file(credentials_path.name, content, job_name="certbot-renew", service_id=first_server)
|
||||
if not cached:
|
||||
LOGGER.error(f"Error while updating service {first_server}'s credentials file in cache : {err}")
|
||||
continue
|
||||
LOGGER.info(f"Successfully updated service {first_server}'s credentials file in cache")
|
||||
else:
|
||||
LOGGER.info(f"Service {first_server}'s credentials file is up to date")
|
||||
|
||||
credential_paths.add(credentials_path)
|
||||
credentials_path.chmod(0o600) # ? Setting the permissions to 600 (this is important to avoid warnings from certbot)
|
||||
|
||||
if data["use_wildcard"]:
|
||||
continue
|
||||
|
||||
domains = domains.replace(" ", ",")
|
||||
LOGGER.info(
|
||||
f"Asking certificates for domain(s) : {domains} (email = {data['email']}){' using staging' if data['staging'] else ''} with {data['challenge']} challenge..."
|
||||
)
|
||||
if (
|
||||
certbot_new(
|
||||
data["challenge"],
|
||||
domains,
|
||||
data["email"],
|
||||
data["provider"],
|
||||
credentials_path,
|
||||
data["propagation"],
|
||||
data["staging"],
|
||||
domains_to_ask[first_server],
|
||||
)
|
||||
!= 0
|
||||
):
|
||||
status = 2
|
||||
LOGGER.error(f"Certificate generation failed for domain(s) {domains} ...")
|
||||
continue
|
||||
else:
|
||||
status = 1 if status == 0 else status
|
||||
LOGGER.info(f"Certificate generation succeeded for domain(s) : {domains}")
|
||||
|
||||
# Remove old certificates
|
||||
generated_domains.update(domains.split(","))
|
||||
|
||||
# * Generating the wildcards if necessary
|
||||
wildcards = WILDCARD_GENERATOR.get_wildcards()
|
||||
if wildcards:
|
||||
for group, data in wildcards.items():
|
||||
if not data:
|
||||
continue
|
||||
# * Generating the certificate from the generated credentials
|
||||
provider = group.split("_", 1)[0]
|
||||
|
||||
email = data.pop("email")
|
||||
credentials_file = CACHE_PATH.joinpath(f"{group}.{provider_classes[provider].get_file_type() if provider in provider_classes else 'txt'}")
|
||||
for key, domains in data.items():
|
||||
if not domains:
|
||||
continue
|
||||
|
||||
staging = key == "staging"
|
||||
LOGGER.info(
|
||||
f"Asking wildcard certificates for domain(s) : {domains} (email = {email}){' using staging ' if staging else ''} with {'dns' if provider in provider_classes else 'http'} challenge..."
|
||||
)
|
||||
if (
|
||||
certbot_new(
|
||||
"dns" if provider in provider_classes else "http",
|
||||
domains,
|
||||
email,
|
||||
provider,
|
||||
credentials_file,
|
||||
staging=staging,
|
||||
)
|
||||
!= 0
|
||||
):
|
||||
status = 2
|
||||
LOGGER.error(f"Certificate generation failed for domain(s) {domains} ...")
|
||||
else:
|
||||
status = 1 if status == 0 else status
|
||||
LOGGER.info(f"Certificate generation succeeded for domain(s) : {domains}")
|
||||
|
||||
generated_domains.update(domains.split(","))
|
||||
else:
|
||||
LOGGER.info("No wildcard domains found, skipping wildcard certificate(s) generation...")
|
||||
|
||||
# * Clearing all missing credentials files
|
||||
for file in CACHE_PATH.glob("**/*"):
|
||||
if "etc" in file.parts or not file.is_file() or file.suffix not in (".ini", ".env", ".json"):
|
||||
continue
|
||||
# ? If the file is not in the wildcard groups, remove it
|
||||
if file not in credential_paths:
|
||||
LOGGER.debug(f"Removing old credentials file {file}")
|
||||
JOB.del_cache(file.name, job_name="certbot-renew", service_id=file.parent.name if file.parent.name != "letsencrypt" else "")
|
||||
|
||||
# * Clearing all no longer needed certificates
|
||||
if getenv("LETS_ENCRYPT_CLEAR_OLD_CERTS", "no") == "yes":
|
||||
LOGGER.info("Clear old certificates is activated, removing old / no longer used certificates...")
|
||||
for elem in chain(DATA_PATH.glob("archive/*"), DATA_PATH.glob("live/*"), DATA_PATH.glob("renewal/*")):
|
||||
if elem.name.replace(".conf", "") not in generated_domains and elem.name != "README":
|
||||
cert_name = elem.name.replace(".conf", "")
|
||||
if cert_name not in generated_domains and cert_name not in domains_to_ask and elem.name != "README":
|
||||
LOGGER.warning(f"Removing old certificate {elem}")
|
||||
if elem.is_dir():
|
||||
rmtree(elem, ignore_errors=True)
|
||||
else:
|
||||
elem.unlink(missing_ok=True)
|
||||
|
||||
# Save Let's Encrypt data to db cache
|
||||
# * Save data to db cache
|
||||
if DATA_PATH.is_dir() and list(DATA_PATH.iterdir()):
|
||||
cached, err = JOB.cache_dir(DATA_PATH, job_name="certbot-renew")
|
||||
if not cached:
|
||||
LOGGER.error(f"Error while saving Let's Encrypt data to db cache : {err}")
|
||||
LOGGER.error(f"Error while saving data to db cache : {err}")
|
||||
else:
|
||||
LOGGER.info("Successfully saved Let's Encrypt data to db cache")
|
||||
LOGGER.info("Successfully saved data to db cache")
|
||||
except SystemExit as e:
|
||||
status = e.code
|
||||
except BaseException as e:
|
||||
status = 3
|
||||
LOGGER.error(f"Exception while running certbot-new.py :\n{e}")
|
||||
except:
|
||||
status = 1
|
||||
LOGGER.exception("Exception while running certbot-new.py")
|
||||
|
||||
sys_exit(status)
|
||||
|
|
|
|||
|
|
@ -6,14 +6,7 @@ from pathlib import Path
|
|||
from subprocess import DEVNULL, PIPE, Popen
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
|
||||
for deps_path in [
|
||||
join(sep, "usr", "share", "bunkerweb", *paths)
|
||||
for paths in (
|
||||
("deps", "python"),
|
||||
("utils",),
|
||||
("db",),
|
||||
)
|
||||
]:
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
|
|
@ -21,25 +14,29 @@ from logger import setup_logger # type: ignore
|
|||
from jobs import Job # type: ignore
|
||||
|
||||
LOGGER = setup_logger("LETS-ENCRYPT.renew", getenv("LOG_LEVEL", "INFO"))
|
||||
LIB_PATH = Path(sep, "var", "lib", "bunkerweb", "letsencrypt")
|
||||
CERTBOT_BIN = join(sep, "usr", "share", "bunkerweb", "deps", "python", "bin", "certbot")
|
||||
DEPS_PATH = join(sep, "usr", "share", "bunkerweb", "deps", "python")
|
||||
|
||||
LOGGER_CERTBOT = setup_logger("LETS-ENCRYPT.renew.certbot", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
CERTBOT_BIN = join(sep, "usr", "share", "bunkerweb", "deps", "python", "bin", "certbot")
|
||||
|
||||
DATA_PATH = Path(sep, "var", "cache", "bunkerweb", "letsencrypt", "etc")
|
||||
LETS_ENCRYPT_WORK_DIR = join(sep, "var", "lib", "bunkerweb", "letsencrypt")
|
||||
LETS_ENCRYPT_LOGS_DIR = join(sep, "var", "log", "bunkerweb")
|
||||
CACHE_PATH = Path(sep, "var", "cache", "bunkerweb", "letsencrypt")
|
||||
DATA_PATH = CACHE_PATH.joinpath("etc")
|
||||
WORK_DIR = join(sep, "var", "lib", "bunkerweb", "letsencrypt")
|
||||
LOGS_DIR = join(sep, "var", "log", "bunkerweb", "letsencrypt")
|
||||
|
||||
try:
|
||||
# Check if we're using let's encrypt
|
||||
use_letsencrypt = False
|
||||
if getenv("MULTISITE", "no") == "yes":
|
||||
|
||||
if getenv("MULTISITE", "no") == "no":
|
||||
use_letsencrypt = getenv("AUTO_LETS_ENCRYPT", "no") == "yes"
|
||||
else:
|
||||
for first_server in getenv("SERVER_NAME", "").split(" "):
|
||||
if first_server and getenv(f"{first_server}_AUTO_LETS_ENCRYPT", "no") == "yes":
|
||||
use_letsencrypt = True
|
||||
break
|
||||
elif getenv("AUTO_LETS_ENCRYPT", "no") == "yes":
|
||||
use_letsencrypt = True
|
||||
|
||||
if not use_letsencrypt:
|
||||
LOGGER.info("Let's Encrypt is not activated, skipping renew...")
|
||||
|
|
@ -55,14 +52,14 @@ try:
|
|||
"--config-dir",
|
||||
DATA_PATH.as_posix(),
|
||||
"--work-dir",
|
||||
LETS_ENCRYPT_WORK_DIR,
|
||||
WORK_DIR,
|
||||
"--logs-dir",
|
||||
LETS_ENCRYPT_LOGS_DIR,
|
||||
LOGS_DIR,
|
||||
],
|
||||
stdin=DEVNULL,
|
||||
stderr=PIPE,
|
||||
universal_newlines=True,
|
||||
env=environ | {"PYTHONPATH": join(sep, "usr", "share", "bunkerweb", "deps", "python")},
|
||||
env=environ | {"PYTHONPATH": DEPS_PATH},
|
||||
)
|
||||
while process.poll() is None:
|
||||
if process.stderr:
|
||||
|
|
|
|||
235
src/common/core/letsencrypt/jobs/models.py
Normal file
235
src/common/core/letsencrypt/jobs/models.py
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from pathlib import Path
|
||||
from sys import path as sys_path
|
||||
from typing import Dict, List, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
# Define paths
|
||||
LIB_PATH = Path("/var/lib/bunkerweb/letsencrypt")
|
||||
PYTHON_PATH = LIB_PATH / "python"
|
||||
|
||||
# Add to sys.path if not already present
|
||||
python_path_str = PYTHON_PATH.as_posix()
|
||||
if python_path_str not in sys_path:
|
||||
sys_path.append(python_path_str)
|
||||
|
||||
|
||||
class Provider(BaseModel):
|
||||
"""Base class for DNS providers."""
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
def get_formatted_credentials(self) -> bytes:
|
||||
"""Return the formatted credentials to be written to a file."""
|
||||
return "\n".join(f"{key} = {value}" for key, value in self.model_dump(exclude={"file_type"}).items()).encode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def get_file_type() -> Literal["ini"]:
|
||||
"""Return the file type that the credentials should be written to."""
|
||||
return "ini"
|
||||
|
||||
|
||||
class CloudflareProvider(Provider):
|
||||
"""Cloudflare DNS provider."""
|
||||
|
||||
dns_cloudflare_api_token: str
|
||||
|
||||
|
||||
class DigitalOceanProvider(Provider):
|
||||
"""DigitalOcean DNS provider."""
|
||||
|
||||
dns_digitalocean_token: str
|
||||
|
||||
|
||||
class DnsimpleProvider(Provider):
|
||||
"""DNSimple DNS provider."""
|
||||
|
||||
dns_dnsimple_token: str
|
||||
|
||||
|
||||
class DnsMadeEasyProvider(Provider):
|
||||
"""DNS Made Easy DNS provider."""
|
||||
|
||||
dns_dnsmadeeasy_api_key: str
|
||||
dns_dnsmadeeasy_secret_key: str
|
||||
|
||||
|
||||
class GehirnProvider(Provider):
|
||||
"""Gehirn DNS provider."""
|
||||
|
||||
dns_gehirn_api_token: str
|
||||
dns_gehirn_api_secret: str
|
||||
|
||||
|
||||
class GoogleProvider(Provider):
|
||||
"""Google Cloud DNS provider."""
|
||||
|
||||
type: str = "service_account"
|
||||
project_id: str
|
||||
private_key_id: str
|
||||
private_key: str
|
||||
client_email: str
|
||||
client_id: str
|
||||
auth_uri: str = "https://accounts.google.com/o/oauth2/auth"
|
||||
token_uri: str = "https://accounts.google.com/o/oauth2/token"
|
||||
auth_provider_x509_cert_url: str = "https://www.googleapis.com/oauth2/v1/certs"
|
||||
client_x509_cert_url: str
|
||||
|
||||
def get_formatted_credentials(self) -> bytes:
|
||||
"""Return the formatted credentials in JSON format."""
|
||||
return self.model_dump_json(indent=2, exclude={"file_type"}).encode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def get_file_type() -> Literal["json"]:
|
||||
"""Return the file type that the credentials should be written to."""
|
||||
return "json"
|
||||
|
||||
|
||||
class LinodeProvider(Provider):
|
||||
"""Linode DNS provider."""
|
||||
|
||||
dns_linode_key: str
|
||||
dns_linode_version: str = "4"
|
||||
|
||||
|
||||
class LuaDnsProvider(Provider):
|
||||
"""LuaDns DNS provider."""
|
||||
|
||||
dns_luadns_email: str
|
||||
dns_luadns_token: str
|
||||
|
||||
|
||||
class NSOneProvider(Provider):
|
||||
"""NS1 DNS provider."""
|
||||
|
||||
dns_nsone_api_key: str
|
||||
|
||||
|
||||
class OvhProvider(Provider):
|
||||
"""OVH DNS provider."""
|
||||
|
||||
dns_ovh_endpoint: str = "ovh-eu"
|
||||
dns_ovh_application_key: str
|
||||
dns_ovh_application_secret: str
|
||||
dns_ovh_consumer_key: str
|
||||
|
||||
|
||||
class Rfc2136Provider(Provider):
|
||||
"""RFC 2136 DNS provider."""
|
||||
|
||||
dns_rfc2136_server: str
|
||||
dns_rfc2136_port: Optional[str] = None
|
||||
dns_rfc2136_name: str
|
||||
dns_rfc2136_secret: str
|
||||
dns_rfc2136_algorithm: str = "HMAC-MD5"
|
||||
dns_rfc2136_sign_query: str = "false"
|
||||
|
||||
def get_formatted_credentials(self) -> bytes:
|
||||
"""Return the formatted credentials, excluding defaults."""
|
||||
return "\n".join(f"{key} = {value}" for key, value in self.model_dump(exclude={"file_type"}, exclude_defaults=True).items()).encode("utf-8")
|
||||
|
||||
|
||||
class Route53Provider(Provider):
|
||||
"""AWS Route 53 DNS provider."""
|
||||
|
||||
aws_access_key_id: str
|
||||
aws_secret_access_key: str
|
||||
|
||||
def get_formatted_credentials(self) -> bytes:
|
||||
"""Return the formatted credentials in environment variable format."""
|
||||
return "\n".join(f"{key.upper()}={value!r}" for key, value in self.model_dump(exclude={"file_type"}).items()).encode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def get_file_type() -> Literal["env"]:
|
||||
"""Return the file type that the credentials should be written to."""
|
||||
return "env"
|
||||
|
||||
|
||||
class SakuraCloudProvider(Provider):
|
||||
"""Sakura Cloud DNS provider."""
|
||||
|
||||
dns_sakuracloud_api_token: str
|
||||
dns_sakuracloud_api_secret: str
|
||||
|
||||
|
||||
class ScalewayProvider(Provider):
|
||||
"""Scaleway DNS provider."""
|
||||
|
||||
dns_scaleway_application_token: str
|
||||
|
||||
|
||||
class WildcardGenerator:
|
||||
def __init__(self):
|
||||
self.__domain_groups = {}
|
||||
self.__wildcards = {}
|
||||
|
||||
def __generate_wildcards(self, staging: bool = False):
|
||||
self.__wildcards.clear()
|
||||
_type = "staging" if staging else "prod"
|
||||
|
||||
# * Loop through all the domains and generate wildcards
|
||||
for group, types in self.__domain_groups.items():
|
||||
if group not in self.__wildcards:
|
||||
self.__wildcards[group] = {"staging": set(), "prod": set(), "email": types["email"]}
|
||||
for domain in types[_type]:
|
||||
parts = domain.split(".")
|
||||
# ? Only take subdomains into account for wildcards generation
|
||||
if len(parts) > 2:
|
||||
suffix = ".".join(parts[1:])
|
||||
# ? If the suffix is not already in the wildcards, add it
|
||||
if suffix not in self.__wildcards[group][_type]:
|
||||
self.__wildcards[group][_type].add(f"*.{suffix}")
|
||||
self.__wildcards[group][_type].add(suffix)
|
||||
continue
|
||||
|
||||
# ? Add the raw domain to the wildcards
|
||||
self.__wildcards[group][_type].add(domain)
|
||||
|
||||
def extend(self, group: str, domains: List[str], email: str, staging: bool = False):
|
||||
if group not in self.__domain_groups:
|
||||
self.__domain_groups[group] = {"staging": set(), "prod": set(), "email": email}
|
||||
for domain in domains:
|
||||
if domain := domain.strip():
|
||||
self.__domain_groups[group]["staging" if staging else "prod"].add(domain)
|
||||
self.__generate_wildcards(staging)
|
||||
|
||||
def get_wildcards(self) -> Dict[str, Dict[Literal["staging", "prod", "email"], str]]:
|
||||
ret_data = {}
|
||||
for group, data in self.__wildcards.items():
|
||||
ret_data[group] = {"email": data["email"]}
|
||||
for _type, content in data.items():
|
||||
if _type in ("staging", "prod"):
|
||||
# ? Sort domains while favoring wildcards first
|
||||
ret_data[group][_type] = ",".join(sorted(content, key=lambda x: x[0] != "*"))
|
||||
return ret_data
|
||||
|
||||
@staticmethod
|
||||
def get_wildcards_from_domains(domains: List[str]) -> List[str]:
|
||||
wildcards = set()
|
||||
for domain in domains:
|
||||
parts = domain.split(".")
|
||||
# ? Only take subdomains into account for wildcards generation
|
||||
if len(parts) > 2:
|
||||
suffix = ".".join(parts[1:])
|
||||
# ? If the suffix is not already in the wildcards, add it
|
||||
if suffix not in wildcards:
|
||||
wildcards.add(f"*.{suffix}")
|
||||
wildcards.add(suffix)
|
||||
continue
|
||||
|
||||
# ? Add the raw domain to the wildcards
|
||||
wildcards.add(domain)
|
||||
return sorted(wildcards, key=lambda x: x[0] != "*")
|
||||
|
||||
|
||||
__all__ = (
|
||||
"CloudflareProvider",
|
||||
"DigitalOceanProvider",
|
||||
"GoogleProvider",
|
||||
"LinodeProvider",
|
||||
"OvhProvider",
|
||||
"Rfc2136Provider",
|
||||
"Route53Provider",
|
||||
"ScalewayProvider",
|
||||
)
|
||||
|
|
@ -43,6 +43,8 @@ end
|
|||
|
||||
function letsencrypt:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
local wildcard_servers = {}
|
||||
|
||||
if has_variable("AUTO_LETS_ENCRYPT", "yes") then
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
|
|
@ -50,26 +52,72 @@ function letsencrypt:init()
|
|||
end
|
||||
if multisite == "yes" then
|
||||
local vars
|
||||
vars, err = get_multiple_variables({ "AUTO_LETS_ENCRYPT", "SERVER_NAME" })
|
||||
vars, err = get_multiple_variables({
|
||||
"AUTO_LETS_ENCRYPT",
|
||||
"LETS_ENCRYPT_CHALLENGE",
|
||||
"LETS_ENCRYPT_DNS_PROVIDER",
|
||||
"USE_LETS_ENCRYPT_WILDCARD",
|
||||
"SERVER_NAME",
|
||||
})
|
||||
if not vars then
|
||||
return self:ret(false, "can't get AUTO_LETS_ENCRYPT variables : " .. err)
|
||||
return self:ret(false, "can't get required variables : " .. err)
|
||||
end
|
||||
local credential_items
|
||||
credential_items, err = get_multiple_variables({ "LETS_ENCRYPT_DNS_CREDENTIAL_ITEM" })
|
||||
if not credential_items then
|
||||
return self:ret(false, "can't get credential items : " .. err)
|
||||
end
|
||||
for server_name, multisite_vars in pairs(vars) do
|
||||
if multisite_vars["AUTO_LETS_ENCRYPT"] == "yes" and server_name ~= "global" then
|
||||
local check, data = read_files({
|
||||
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name .. "/fullchain.pem",
|
||||
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name .. "/privkey.pem",
|
||||
})
|
||||
if not check then
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
if
|
||||
multisite_vars["AUTO_LETS_ENCRYPT"] == "yes"
|
||||
and server_name ~= "global"
|
||||
and (
|
||||
multisite_vars["LETS_ENCRYPT_CHALLENGE"] == "http"
|
||||
or (
|
||||
multisite_vars["LETS_ENCRYPT_CHALLENGE"] == "dns"
|
||||
and multisite_vars["LETS_ENCRYPT_DNS_PROVIDER"] ~= ""
|
||||
and credential_items[server_name]
|
||||
)
|
||||
)
|
||||
then
|
||||
local data
|
||||
if multisite_vars["USE_LETS_ENCRYPT_WILDCARD"] == "yes" then
|
||||
for part in server_name:gmatch("%S+") do
|
||||
wildcard_servers[part] = true
|
||||
end
|
||||
local parts = {}
|
||||
for part in server_name:gmatch("[^.]+") do
|
||||
table.insert(parts, part)
|
||||
end
|
||||
server_name = table.concat(parts, ".", 2)
|
||||
data = self.datastore:get("plugin_letsencrypt_" .. server_name, true)
|
||||
else
|
||||
check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
for part in server_name:gmatch("%S+") do
|
||||
wildcard_servers[part] = false
|
||||
end
|
||||
end
|
||||
if not data then
|
||||
-- Load certificate
|
||||
local check
|
||||
check, data = read_files({
|
||||
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name .. "/fullchain.pem",
|
||||
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name .. "/privkey.pem",
|
||||
})
|
||||
if not check then
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
if multisite_vars["USE_LETS_ENCRYPT_WILDCARD"] == "yes" then
|
||||
check, err = self:load_data(data, server_name)
|
||||
else
|
||||
check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
end
|
||||
if not check then
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -80,9 +128,29 @@ function letsencrypt:init()
|
|||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local use_wildcard
|
||||
use_wildcard, err = get_variable("USE_LETS_ENCRYPT_WILDCARD", false)
|
||||
if not use_wildcard then
|
||||
return self:ret(false, "can't get USE_LETS_ENCRYPT_WILDCARD variable : " .. err)
|
||||
end
|
||||
server_name = server_name:match("%S+")
|
||||
if use_wildcard == "yes" then
|
||||
for part in server_name:gmatch("%S+") do
|
||||
wildcard_servers[part] = true
|
||||
end
|
||||
local parts = {}
|
||||
for part in server_name:gmatch("[^.]+") do
|
||||
table.insert(parts, part)
|
||||
end
|
||||
server_name = table.concat(parts, ".", 2)
|
||||
else
|
||||
for part in server_name:gmatch("%S+") do
|
||||
wildcard_servers[part] = false
|
||||
end
|
||||
end
|
||||
local check, data = read_files({
|
||||
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name:match("%S+") .. "/fullchain.pem",
|
||||
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name:match("%S+") .. "/privkey.pem",
|
||||
"/var/cache/bunkerweb/letsencrypt_dns/etc/live/" .. server_name .. "/fullchain.pem",
|
||||
"/var/cache/bunkerweb/letsencrypt_dns/etc/live/" .. server_name .. "/privkey.pem",
|
||||
})
|
||||
if not check then
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
|
|
@ -100,6 +168,12 @@ function letsencrypt:init()
|
|||
else
|
||||
ret_err = "let's encrypt is not used"
|
||||
end
|
||||
|
||||
local ok, err = self.datastore:set("plugin_letsencrypt_wildcard_servers", wildcard_servers, nil, true)
|
||||
if not ok then
|
||||
return self:ret(false, "error while setting wildcard servers into datastore : " .. err)
|
||||
end
|
||||
|
||||
return self:ret(ret_ok, ret_err)
|
||||
end
|
||||
|
||||
|
|
@ -108,6 +182,17 @@ function letsencrypt:ssl_certificate()
|
|||
if not server_name then
|
||||
return self:ret(false, "can't get server_name : " .. err)
|
||||
end
|
||||
local wildcard_servers, err = self.datastore:get("plugin_letsencrypt_wildcard_servers", true)
|
||||
if not wildcard_servers then
|
||||
return self:ret(false, "can't get wildcard servers : " .. err)
|
||||
end
|
||||
if wildcard_servers[server_name] then
|
||||
local parts = {}
|
||||
for part in server_name:gmatch("[^.]+") do
|
||||
table.insert(parts, part)
|
||||
end
|
||||
server_name = table.concat(parts, ".", 2)
|
||||
end
|
||||
local data
|
||||
data, err = self.datastore:get("plugin_letsencrypt_" .. server_name, true)
|
||||
if not data and err ~= "not found" then
|
||||
|
|
|
|||
|
|
@ -23,6 +23,70 @@
|
|||
"regex": "^([^@ \\t\\r\\n]+@[^@ \\t\\r\\n]+\\.[^@ \\t\\r\\n]+)?$",
|
||||
"type": "text"
|
||||
},
|
||||
"LETS_ENCRYPT_CHALLENGE": {
|
||||
"context": "multisite",
|
||||
"default": "http",
|
||||
"help": "The challenge type to use for Let's Encrypt (http or dns).",
|
||||
"id": "lets-encrypt-challenge",
|
||||
"label": "Challenge Type",
|
||||
"regex": "^(http|dns)$",
|
||||
"type": "select",
|
||||
"select": ["http", "dns"]
|
||||
},
|
||||
"LETS_ENCRYPT_DNS_PROVIDER": {
|
||||
"context": "multisite",
|
||||
"default": "",
|
||||
"help": "The DNS provider to use for DNS challenges.",
|
||||
"id": "auto-lets-encrypt-dns-provider",
|
||||
"label": "DNS Provider",
|
||||
"regex": "^(cloudflare|digitalocean|dnsimple|dnsmadeeasy|gehirn|google|linode|luadns|nsone|ovh|rfc2136|route53|sakuracloud|scaleway)?$",
|
||||
"type": "select",
|
||||
"select": [
|
||||
"",
|
||||
"cloudflare",
|
||||
"digitalocean",
|
||||
"dnsimple",
|
||||
"dnsmadeeasy",
|
||||
"gehirn",
|
||||
"google",
|
||||
"linode",
|
||||
"luadns",
|
||||
"nsone",
|
||||
"ovh",
|
||||
"rfc2136",
|
||||
"route53",
|
||||
"sakuracloud",
|
||||
"scaleway"
|
||||
]
|
||||
},
|
||||
"LETS_ENCRYPT_DNS_PROPAGATION": {
|
||||
"context": "multisite",
|
||||
"default": "default",
|
||||
"help": "The time to wait for DNS propagation in seconds for DNS challenges.",
|
||||
"id": "lets-encrypt-dns-propagation",
|
||||
"label": "DNS Propagation",
|
||||
"regex": "^(default|\\d+)$",
|
||||
"type": "text"
|
||||
},
|
||||
"LETS_ENCRYPT_DNS_CREDENTIAL_ITEM": {
|
||||
"context": "multisite",
|
||||
"default": "",
|
||||
"help": "Configuration item that will be added to the credentials.ini file for the DNS provider (e.g. 'cloudflare_api_token 123456') for DNS challenges.",
|
||||
"id": "lets-encrypt-dns-credential-item",
|
||||
"label": "Credential Item",
|
||||
"regex": "^(\\w+ .+)?$",
|
||||
"type": "password",
|
||||
"multiple": "lets-encrypt-dns-credential-item"
|
||||
},
|
||||
"USE_LETS_ENCRYPT_WILDCARD": {
|
||||
"context": "multisite",
|
||||
"default": "no",
|
||||
"help": "Create wildcard certificates for all domains. This allows a single certificate to secure multiple subdomains.",
|
||||
"id": "use-lets-encrypt-wildcard",
|
||||
"label": "Wildcard Certificates",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
"USE_LETS_ENCRYPT_STAGING": {
|
||||
"context": "multisite",
|
||||
"default": "no",
|
||||
|
|
|
|||
166
src/common/core/letsencrypt/ui/actions.py
Normal file
166
src/common/core/letsencrypt/ui/actions.py
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from io import BytesIO
|
||||
from logging import getLogger
|
||||
from os.path import sep
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
from tarfile import open as tar_open
|
||||
from traceback import format_exc
|
||||
from typing import Tuple
|
||||
from uuid import uuid4
|
||||
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
|
||||
|
||||
def extract_cache(folder_path, cache_files):
|
||||
folder_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for cache_file in cache_files:
|
||||
if cache_file["file_name"].endswith(".tgz") and cache_file["file_name"].startswith("folder:"):
|
||||
with tar_open(fileobj=BytesIO(cache_file["data"]), mode="r:gz") as tar:
|
||||
try:
|
||||
tar.extractall(folder_path, filter="fully_trusted")
|
||||
except TypeError:
|
||||
tar.extractall(folder_path)
|
||||
|
||||
|
||||
def retrieve_certificates_info(folder_paths: Tuple[Path, Path]) -> dict:
|
||||
certificates = {
|
||||
"domain": [],
|
||||
"common_name": [],
|
||||
"issuer": [],
|
||||
"issuer_server": [],
|
||||
"valid_from": [],
|
||||
"valid_to": [],
|
||||
"serial_number": [],
|
||||
"fingerprint": [],
|
||||
"version": [],
|
||||
"challenge": [],
|
||||
"authenticator": [],
|
||||
"key_type": [],
|
||||
}
|
||||
|
||||
for folder_path in folder_paths:
|
||||
for cert_file in folder_path.joinpath("live").glob("*/fullchain.pem"):
|
||||
domain = cert_file.parent.name
|
||||
certificates["domain"].append(domain)
|
||||
|
||||
# Default values
|
||||
cert_info = {
|
||||
"common_name": "Unknown",
|
||||
"issuer": "Unknown",
|
||||
"issuer_server": "Unknown",
|
||||
"valid_from": None,
|
||||
"valid_to": None,
|
||||
"serial_number": "Unknown",
|
||||
"fingerprint": "Unknown",
|
||||
"version": "Unknown",
|
||||
"challenge": "Unknown",
|
||||
"authenticator": "Unknown",
|
||||
"key_type": "Unknown",
|
||||
}
|
||||
|
||||
# * Parsing the certificate
|
||||
try:
|
||||
cert = x509.load_pem_x509_certificate(cert_file.read_bytes(), default_backend())
|
||||
|
||||
# ? Getting the subject
|
||||
subject = cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
|
||||
if subject:
|
||||
cert_info["common_name"] = subject[0].value
|
||||
|
||||
# ? Getting the issuer
|
||||
issuer = cert.issuer.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
|
||||
if issuer:
|
||||
cert_info["issuer"] = issuer[0].value
|
||||
|
||||
# ? Getting the validity period
|
||||
cert_info["valid_from"] = cert.not_valid_before.strftime("%d-%m-%Y %H:%M:%S UTC")
|
||||
cert_info["valid_to"] = cert.not_valid_after.strftime("%d-%m-%Y %H:%M:%S UTC")
|
||||
|
||||
# ? Getting the serial number
|
||||
cert_info["serial_number"] = str(cert.serial_number)
|
||||
|
||||
# ? Getting the fingerprint
|
||||
cert_info["fingerprint"] = cert.fingerprint(hashes.SHA256()).hex()
|
||||
|
||||
# ? Getting the version
|
||||
cert_info["version"] = cert.version.name
|
||||
except BaseException:
|
||||
print(f"Error while parsing certificate {cert_file}: {format_exc()}", flush=True)
|
||||
|
||||
# * Parsing the renewal configuration
|
||||
try:
|
||||
renewal_file = folder_path.joinpath("renewal", f"{domain}.conf")
|
||||
if renewal_file.exists():
|
||||
with renewal_file.open("r") as f:
|
||||
for line in f:
|
||||
if line.startswith("pref_challs = "):
|
||||
cert_info["challenge"] = line.split(" = ")[1].strip().split(",")[0]
|
||||
elif line.startswith("authenticator = "):
|
||||
cert_info["authenticator"] = line.split(" = ")[1].strip()
|
||||
elif line.startswith("server = "):
|
||||
cert_info["issuer_server"] = line.split(" = ")[1].strip()
|
||||
elif line.startswith("key_type = "):
|
||||
cert_info["key_type"] = line.split(" = ")[1].strip()
|
||||
except BaseException:
|
||||
print(f"Error while parsing renewal configuration {renewal_file}: {format_exc()}", flush=True)
|
||||
|
||||
# Append values to corresponding lists in certificates dictionary
|
||||
for key in cert_info:
|
||||
certificates[key].append(cert_info[key])
|
||||
|
||||
return certificates
|
||||
|
||||
|
||||
def pre_render(app, *args, **kwargs):
|
||||
logger = getLogger("UI")
|
||||
ret = {
|
||||
"list_certificates": {
|
||||
"data": {
|
||||
"domain": [],
|
||||
"common_name": [],
|
||||
"issuer": [],
|
||||
"issuer_server": [],
|
||||
"valid_from": [],
|
||||
"valid_to": [],
|
||||
"serial_number": [],
|
||||
"fingerprint": [],
|
||||
"version": [],
|
||||
"challenge": [],
|
||||
"authenticator": [],
|
||||
"key_type": [],
|
||||
},
|
||||
"order": {
|
||||
"column": 5,
|
||||
"dir": "asc",
|
||||
},
|
||||
"svg_color": "primary",
|
||||
"col-size": "col-12",
|
||||
},
|
||||
}
|
||||
|
||||
root_folder = Path(sep, "var", "tmp", "bunkerweb", "ui")
|
||||
try:
|
||||
# ? Fetching Let's Encrypt cache files
|
||||
regular_cache_files = kwargs["db"].get_jobs_cache_files(job_name="certbot-renew")
|
||||
|
||||
# ? Extracting cache files
|
||||
folder_path = root_folder.joinpath("letsencrypt", str(uuid4()))
|
||||
regular_le_folder = folder_path.joinpath("regular")
|
||||
extract_cache(regular_le_folder, regular_cache_files)
|
||||
|
||||
# ? We retrieve the certificates from the cache files by parsing the content of the .pem files
|
||||
ret["list_certificates"]["data"] = retrieve_certificates_info((regular_le_folder,))
|
||||
except BaseException as e:
|
||||
logger.debug(format_exc())
|
||||
logger.error(f"Failed to get Let's Encrypt certificates: {e}")
|
||||
ret["error"] = str(e)
|
||||
finally:
|
||||
if folder_path:
|
||||
rmtree(root_folder, ignore_errors=True)
|
||||
|
||||
return ret
|
||||
|
|
@ -1,380 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from itertools import chain
|
||||
from json import dumps
|
||||
from os import environ, getenv, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
from subprocess import DEVNULL, STDOUT, Popen
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from typing import Dict, List, Literal, Type, Union
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from common_utils import bytes_hash # type: ignore
|
||||
from jobs import Job # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
|
||||
LOGGER = setup_logger("LETS-ENCRYPT-DNS.new", getenv("LOG_LEVEL", "INFO"))
|
||||
LIB_PATH = Path(sep, "var", "lib", "bunkerweb", "letsencrypt_dns")
|
||||
|
||||
deps_path = LIB_PATH.joinpath("python").as_posix()
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
CERTBOT_BIN = LIB_PATH.joinpath("python", "bin", "certbot")
|
||||
|
||||
LOGGER_CERTBOT = setup_logger("LETS-ENCRYPT-DNS.new.certbot", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
PLUGIN_PATH = Path(sep, "usr", "share", "bunkerweb", "core", "letsencrypt_dns")
|
||||
JOBS_PATH = PLUGIN_PATH.joinpath("jobs")
|
||||
CACHE_PATH = Path(sep, "var", "cache", "bunkerweb", "letsencrypt_dns")
|
||||
DATA_PATH = CACHE_PATH.joinpath("etc")
|
||||
WORK_DIR = join(sep, "var", "lib", "bunkerweb", "letsencrypt_dns")
|
||||
LOGS_DIR = join(sep, "var", "log", "bunkerweb", "letsencrypt_dns")
|
||||
|
||||
|
||||
class WildcardGenerator:
|
||||
def __init__(self):
|
||||
self.__domain_groups = {}
|
||||
self.__wildcards = {}
|
||||
|
||||
def __generate_wildcards(self, staging: bool = False):
|
||||
self.__wildcards.clear()
|
||||
_type = "staging" if staging else "prod"
|
||||
|
||||
# * Loop through all the domains and generate wildcards
|
||||
for group, types in self.__domain_groups.items():
|
||||
if group not in self.__wildcards:
|
||||
self.__wildcards[group] = {"staging": set(), "prod": set(), "email": types["email"]}
|
||||
for domain in types[_type]:
|
||||
parts = domain.split(".")
|
||||
# ? Only take subdomains into account for wildcards generation
|
||||
if len(parts) > 2:
|
||||
suffix = ".".join(parts[1:])
|
||||
# ? If the suffix is not already in the wildcards, add it
|
||||
if suffix not in self.__wildcards[group][_type]:
|
||||
self.__wildcards[group][_type].add(f"*.{suffix}")
|
||||
self.__wildcards[group][_type].add(suffix)
|
||||
continue
|
||||
|
||||
# ? Add the raw domain to the wildcards
|
||||
self.__wildcards[group][_type].add(domain)
|
||||
|
||||
def extend(self, group: str, domains: List[str], email: str, staging: bool = False):
|
||||
if group not in self.__domain_groups:
|
||||
self.__domain_groups[group] = {"staging": set(), "prod": set(), "email": email}
|
||||
for domain in domains:
|
||||
if domain := domain.strip():
|
||||
self.__domain_groups[group]["staging" if staging else "prod"].add(domain)
|
||||
self.__generate_wildcards(staging)
|
||||
|
||||
def get_wildcards(self) -> Dict[str, Dict[Literal["staging", "prod", "email"], str]]:
|
||||
ret_data = {}
|
||||
for group, data in self.__wildcards.items():
|
||||
ret_data[group] = {"email": data["email"]}
|
||||
for _type, content in data.items():
|
||||
if _type in ("staging", "prod"):
|
||||
# ? Sort domains while favoring wildcards first
|
||||
ret_data[group][_type] = ",".join(sorted(content, key=lambda x: x[0] != "*"))
|
||||
return ret_data
|
||||
|
||||
|
||||
def certbot_new(provider: str, credentials_path: Union[str, Path], domains: str, email: str, propagation: str = "default", staging: bool = False) -> int:
|
||||
if isinstance(credentials_path, str):
|
||||
credentials_path = Path(credentials_path)
|
||||
|
||||
# * Building the certbot command
|
||||
command = [
|
||||
CERTBOT_BIN,
|
||||
"certonly",
|
||||
"--config-dir",
|
||||
DATA_PATH.as_posix(),
|
||||
"--work-dir",
|
||||
WORK_DIR,
|
||||
"--logs-dir",
|
||||
LOGS_DIR,
|
||||
"--preferred-challenges=dns",
|
||||
"-n",
|
||||
"-d",
|
||||
domains,
|
||||
"--email",
|
||||
email,
|
||||
"--agree-tos",
|
||||
"--expand",
|
||||
]
|
||||
|
||||
# * Adding the propagation time to the command
|
||||
if propagation != "default":
|
||||
if not propagation.isdigit():
|
||||
LOGGER.warning(f"Invalid propagation time : {propagation}, using provider's default...")
|
||||
else:
|
||||
command.extend([f"--dns-{provider}-propagation-seconds", propagation])
|
||||
|
||||
env = environ | {"PYTHONPATH": deps_path}
|
||||
|
||||
# * Adding the credentials to the command
|
||||
if provider == "route53":
|
||||
# ? Route53 credentials are different from the others, we need to add them to the environment
|
||||
with credentials_path.open("r") as file:
|
||||
for line in file:
|
||||
key, value = line.strip().split("=", 1)
|
||||
env[key] = value
|
||||
else:
|
||||
command.extend([f"--dns-{provider}-credentials", credentials_path.as_posix()])
|
||||
|
||||
# * Adding plugin argument
|
||||
if provider == "scaleway":
|
||||
# ? Scaleway plugin uses a different argument
|
||||
command.extend(["--authenticator", "dns-scaleway"])
|
||||
else:
|
||||
command.append(f"--dns-{provider}")
|
||||
|
||||
if staging:
|
||||
command.append("--staging")
|
||||
|
||||
current_date = datetime.now()
|
||||
process = Popen(command, stdin=DEVNULL, stderr=STDOUT, universal_newlines=True, env=env)
|
||||
while process.poll() is None:
|
||||
if datetime.now() - current_date > timedelta(seconds=5):
|
||||
LOGGER.info("⏳ Still generating certificate(s)...")
|
||||
current_date = datetime.now()
|
||||
return process.returncode
|
||||
|
||||
|
||||
IS_MULTISITE = getenv("MULTISITE", "no") == "yes"
|
||||
|
||||
try:
|
||||
servers = getenv("SERVER_NAME", "").lower() or []
|
||||
|
||||
if isinstance(servers, str):
|
||||
servers = servers.split(" ")
|
||||
|
||||
if not servers:
|
||||
LOGGER.error("There are no server names, skipping generation...")
|
||||
sys_exit(0)
|
||||
|
||||
use_letsencrypt_dns = False
|
||||
if not IS_MULTISITE:
|
||||
servers = [servers[0]]
|
||||
use_letsencrypt_dns = getenv("AUTO_LETS_ENCRYPT_DNS", "no") == "yes"
|
||||
else:
|
||||
for first_server in servers:
|
||||
if first_server and getenv(f"{first_server}_AUTO_LETS_ENCRYPT_DNS", "no") == "yes":
|
||||
use_letsencrypt_dns = True
|
||||
break
|
||||
|
||||
if not use_letsencrypt_dns:
|
||||
LOGGER.info("Let's Encrypt DNS is not activated, skipping generation...")
|
||||
sys_exit(0)
|
||||
elif not CERTBOT_BIN.is_file():
|
||||
LOGGER.error("Additional dependencies not installed, skipping certificate(s) generation...")
|
||||
sys_exit(2)
|
||||
|
||||
from pydantic import ValidationError
|
||||
from models import (
|
||||
CloudflareProvider,
|
||||
DigitalOceanProvider,
|
||||
GoogleProvider,
|
||||
LinodeProvider,
|
||||
OvhProvider,
|
||||
Rfc2136Provider,
|
||||
Route53Provider,
|
||||
ScalewayProvider,
|
||||
)
|
||||
|
||||
PROVIDER_CLASSES: Dict[
|
||||
str,
|
||||
Union[
|
||||
Type[CloudflareProvider],
|
||||
Type[DigitalOceanProvider],
|
||||
Type[GoogleProvider],
|
||||
Type[LinodeProvider],
|
||||
Type[OvhProvider],
|
||||
Type[Rfc2136Provider],
|
||||
Type[Route53Provider],
|
||||
Type[ScalewayProvider],
|
||||
],
|
||||
] = {
|
||||
"cloudflare": CloudflareProvider,
|
||||
"digitalocean": DigitalOceanProvider,
|
||||
"google": GoogleProvider,
|
||||
"linode": LinodeProvider,
|
||||
"ovh": OvhProvider,
|
||||
"rfc2136": Rfc2136Provider,
|
||||
"route53": Route53Provider,
|
||||
"scaleway": ScalewayProvider,
|
||||
}
|
||||
|
||||
JOB = Job(LOGGER)
|
||||
|
||||
# ? Restore data from db cache of dns-certbot-renew job
|
||||
JOB.restore_cache(job_name="dns-certbot-renew")
|
||||
|
||||
WILDCARD_GENERATOR = WildcardGenerator()
|
||||
credential_paths = set()
|
||||
generated_domains = set()
|
||||
|
||||
for first_server in servers:
|
||||
if getenv(f"{first_server}_AUTO_LETS_ENCRYPT_DNS", getenv("AUTO_LETS_ENCRYPT_DNS", "no")) == "no":
|
||||
LOGGER.info(f"Skipping certificate(s) generation for {first_server} because it is not enabled")
|
||||
continue
|
||||
elif getenv(f"{first_server}_AUTO_LETS_ENCRYPT", getenv("AUTO_LETS_ENCRYPT", "no")) == "yes":
|
||||
LOGGER.warning(f"Skipping certificate(s) generation for {first_server} because it is using regular Let's Encrypt")
|
||||
continue
|
||||
|
||||
# * Getting all the necessary data
|
||||
data = {
|
||||
"domains": getenv(f"{first_server}_SERVER_NAME", getenv("SERVER_NAME", "")).lower() or first_server,
|
||||
"email": getenv(f"{first_server}_LETS_ENCRYPT_DNS_EMAIL", getenv("LETS_ENCRYPT_DNS_EMAIL", "")) or f"contact@{first_server}",
|
||||
"staging": getenv(f"{first_server}_USE_LETS_ENCRYPT_DNS_STAGING", getenv("USE_LETS_ENCRYPT_DNS_STAGING", "no")) == "yes",
|
||||
"provider": getenv(f"{first_server}_LETS_ENCRYPT_DNS_PROVIDER", getenv("LETS_ENCRYPT_DNS_PROVIDER", "")),
|
||||
"use_wildcard": getenv(f"{first_server}_USE_LETS_ENCRYPT_DNS_WILDCARD", getenv("USE_LETS_ENCRYPT_DNS_WILDCARD", "no")) == "yes",
|
||||
"propagation": getenv(f"{first_server}_LETS_ENCRYPT_DNS_PROPAGATION", getenv("LETS_ENCRYPT_DNS_PROPAGATION", "default")),
|
||||
"credential_items": {},
|
||||
}
|
||||
for env_key, env_value in environ.items():
|
||||
if env_value and env_key.startswith(f"{first_server}_LETS_ENCRYPT_DNS_CREDENTIAL_ITEM" if IS_MULTISITE else "LETS_ENCRYPT_DNS_CREDENTIAL_ITEM"):
|
||||
key, value = env_value.split(" ", 1)
|
||||
data["credential_items"][key.lower()] = value
|
||||
|
||||
LOGGER.debug(f"Data for service {first_server} : {dumps(data)}")
|
||||
|
||||
# * Checking if the data is valid
|
||||
if not data["provider"]:
|
||||
LOGGER.warning(
|
||||
f"No provider found for service {first_server} (available providers : {', '.join(PROVIDER_CLASSES.keys())}), skipping certificate(s) generation..." # noqa: E501
|
||||
)
|
||||
continue
|
||||
elif not data["credential_items"]:
|
||||
LOGGER.warning(f"No credentials items found for service {first_server} (you should have at least one), skipping certificate(s) generation...")
|
||||
continue
|
||||
|
||||
# * Validating the credentials
|
||||
try:
|
||||
provider = PROVIDER_CLASSES[data["provider"]](**data["credential_items"])
|
||||
except ValidationError as ve:
|
||||
LOGGER.error(f"Error while validating credentials for service {first_server} :\n{ve}")
|
||||
continue
|
||||
|
||||
content = provider.get_formatted_credentials()
|
||||
|
||||
# * Adding the domains to Wildcard Generator if necessary
|
||||
file_path = (first_server, f"credentials.{provider.get_file_type()}")
|
||||
if data["use_wildcard"]:
|
||||
group = f"{data['provider']}_{bytes_hash(content, algorithm='sha1')}"
|
||||
LOGGER.info(
|
||||
f"Service {first_server} is using wildcard, the propagation time will be the provider's default "
|
||||
+ "and the email will be the same as the first domain that created the group..."
|
||||
)
|
||||
WILDCARD_GENERATOR.extend(group, data["domains"].strip().split(" "), data["email"], data["staging"])
|
||||
file_path = (f"{group}.{provider.get_file_type()}",)
|
||||
|
||||
# * Generating the credentials file
|
||||
credentials_path = CACHE_PATH.joinpath(*file_path)
|
||||
|
||||
if not credentials_path.is_file():
|
||||
cached, err = JOB.cache_file(
|
||||
credentials_path.name, content, job_name="dns-certbot-renew", service_id=first_server if not data["use_wildcard"] else ""
|
||||
)
|
||||
if not cached:
|
||||
LOGGER.error(f"Error while saving service {first_server}'s credentials file in cache : {err}")
|
||||
continue
|
||||
LOGGER.info(f"Successfully saved service {first_server}'s credentials file in cache")
|
||||
elif data["use_wildcard"]:
|
||||
LOGGER.info(f"Service {first_server}'s wildcard credentials file has already been generated")
|
||||
else:
|
||||
old_content = credentials_path.read_bytes()
|
||||
if old_content != content:
|
||||
LOGGER.warning(f"Service {first_server}'s credentials file is outdated, updating it...")
|
||||
cached, err = JOB.cache_file(credentials_path.name, content, job_name="dns-certbot-renew", service_id=first_server)
|
||||
if not cached:
|
||||
LOGGER.error(f"Error while updating service {first_server}'s credentials file in cache : {err}")
|
||||
continue
|
||||
LOGGER.info(f"Successfully updated service {first_server}'s credentials file in cache")
|
||||
else:
|
||||
LOGGER.info(f"Service {first_server}'s credentials file is up to date")
|
||||
|
||||
credential_paths.add(credentials_path)
|
||||
credentials_path.chmod(0o600) # ? Setting the permissions to 600 (this is important to avoid warnings from certbot)
|
||||
|
||||
if data["use_wildcard"]:
|
||||
continue
|
||||
|
||||
domains = data["domains"].replace(" ", ",")
|
||||
LOGGER.info(f"Asking certificates for domain(s) : {domains} (email = {data['email']}) {'using staging ' if data['staging'] else ''}...")
|
||||
if certbot_new(data["provider"], credentials_path, domains, data["email"], data["propagation"], data["staging"]) != 0:
|
||||
status = 2
|
||||
LOGGER.error(f"Certificate generation failed for domain(s) {data['domains']} ...")
|
||||
else:
|
||||
status = 1 if status == 0 else status
|
||||
LOGGER.info(f"Certificate generation succeeded for domain(s) : {data['domains']}")
|
||||
|
||||
generated_domains.update(data["domains"].split(","))
|
||||
|
||||
# * Generating the wildcards if necessary
|
||||
wildcards = WILDCARD_GENERATOR.get_wildcards()
|
||||
if wildcards:
|
||||
for group, data in wildcards.items():
|
||||
if not data:
|
||||
continue
|
||||
# * Generating the certificate from the generated credentials
|
||||
provider = group.split("_", 1)[0]
|
||||
email = data.pop("email")
|
||||
credentials_file = CACHE_PATH.joinpath(f"{group}.{PROVIDER_CLASSES[provider].get_file_type()}")
|
||||
for key, domains in data.items():
|
||||
if not domains:
|
||||
continue
|
||||
staging = key == "staging"
|
||||
LOGGER.info(f"Asking wildcard certificates for domain(s) : {domains} (email = {email}) {'using staging ' if staging else ''}...")
|
||||
if certbot_new(provider, credentials_file, domains, email, staging=staging) != 0:
|
||||
status = 2
|
||||
LOGGER.error(f"Certificate generation failed for domain(s) {domains} ...")
|
||||
else:
|
||||
status = 1 if status == 0 else status
|
||||
LOGGER.info(f"Certificate generation succeeded for domain(s) : {domains}")
|
||||
|
||||
generated_domains.update(domains.split(","))
|
||||
else:
|
||||
LOGGER.info("No wildcard domains found, skipping wildcard certificate(s) generation...")
|
||||
|
||||
# * Clearing all missing credentials files
|
||||
for file in CACHE_PATH.glob("**/*"):
|
||||
if "etc" in file.parts or not file.is_file() or file.suffix not in (".ini", ".env", ".json"):
|
||||
continue
|
||||
# ? If the file is not in the wildcard groups, remove it
|
||||
if file not in credential_paths:
|
||||
LOGGER.debug(f"Removing old credentials file {file}")
|
||||
JOB.del_cache(file.name, job_name="dns-certbot-renew", service_id=file.parent.name if file.parent.name != "letsencrypt_dns" else "")
|
||||
|
||||
# * Clearing all no longer needed certificates
|
||||
if getenv("LETS_ENCRYPT_DNS_CLEAR_OLD_CERTS", "no") == "yes":
|
||||
LOGGER.info("Clear old certificates is activated, removing old / no longer used certificates...")
|
||||
for elem in chain(DATA_PATH.glob("archive/*"), DATA_PATH.glob("live/*"), DATA_PATH.glob("renewal/*")):
|
||||
if elem.name.replace(".conf", "") not in generated_domains and elem.name != "README":
|
||||
LOGGER.warning(f"Removing old certificate {elem}")
|
||||
if elem.is_dir():
|
||||
rmtree(elem, ignore_errors=True)
|
||||
else:
|
||||
elem.unlink(missing_ok=True)
|
||||
|
||||
# * Save data to db cache
|
||||
if DATA_PATH.is_dir() and list(DATA_PATH.iterdir()):
|
||||
cached, err = JOB.cache_dir(DATA_PATH, job_name="dns-certbot-renew")
|
||||
if not cached:
|
||||
LOGGER.error(f"Error while saving data to db cache : {err}")
|
||||
else:
|
||||
LOGGER.info("Successfully saved data to db cache")
|
||||
except SystemExit as e:
|
||||
status = e.code
|
||||
except:
|
||||
status = 1
|
||||
LOGGER.exception("Exception while running certbot-new.py")
|
||||
|
||||
sys_exit(status)
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from os import environ, getenv, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from subprocess import DEVNULL, PIPE, Popen
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from traceback import format_exc
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from logger import setup_logger # type: ignore
|
||||
from jobs import Job # type: ignore
|
||||
|
||||
LOGGER = setup_logger("LETS-ENCRYPT-DNS.renew", getenv("LOG_LEVEL", "INFO"))
|
||||
LOGGER_CERTBOT = setup_logger("LETS-ENCRYPT-DNS.renew.certbot", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
LIB_PATH = Path(sep, "var", "lib", "bunkerweb", "letsencrypt_dns")
|
||||
PLUGIN_PATH = Path(sep, "usr", "share", "bunkerweb", "core", "letsencrypt_dns")
|
||||
JOBS_PATH = PLUGIN_PATH.joinpath("jobs")
|
||||
DATA_PATH = Path(sep, "var", "cache", "bunkerweb", "letsencrypt_dns", "etc")
|
||||
WORK_DIR = join(sep, "var", "lib", "bunkerweb", "letsencrypt_dns")
|
||||
LOGS_DIR = join(sep, "var", "log", "bunkerweb", "letsencrypt_dns")
|
||||
|
||||
deps_path = LIB_PATH.joinpath("python")
|
||||
CERTBOT_BIN = deps_path.joinpath("bin", "certbot")
|
||||
|
||||
try:
|
||||
# Check if we're using let's encrypt
|
||||
use_letsencrypt_dns = False
|
||||
if not getenv("MULTISITE", "no") == "yes":
|
||||
use_letsencrypt_dns = getenv("AUTO_LETS_ENCRYPT_DNS", "no") == "yes"
|
||||
else:
|
||||
for first_server in getenv("SERVER_NAME", "").split(" "):
|
||||
if first_server and getenv(f"{first_server}_AUTO_LETS_ENCRYPT_DNS", "no") == "yes":
|
||||
use_letsencrypt_dns = True
|
||||
break
|
||||
|
||||
if not use_letsencrypt_dns:
|
||||
LOGGER.info("Let's Encrypt DNS is not activated, skipping generation...")
|
||||
sys_exit(0)
|
||||
elif not CERTBOT_BIN.is_file():
|
||||
LOGGER.error("Additional dependencies not installed, skipping certificate(s) generation...")
|
||||
sys_exit(2)
|
||||
|
||||
JOB = Job(LOGGER)
|
||||
|
||||
process = Popen(
|
||||
[
|
||||
CERTBOT_BIN,
|
||||
"renew",
|
||||
"--no-random-sleep-on-renew",
|
||||
"--config-dir",
|
||||
DATA_PATH.as_posix(),
|
||||
"--work-dir",
|
||||
WORK_DIR,
|
||||
"--logs-dir",
|
||||
LOGS_DIR,
|
||||
],
|
||||
stdin=DEVNULL,
|
||||
stderr=PIPE,
|
||||
universal_newlines=True,
|
||||
env=environ | {"PYTHONPATH": deps_path.as_posix()},
|
||||
)
|
||||
while process.poll() is None:
|
||||
if process.stderr:
|
||||
for line in process.stderr:
|
||||
LOGGER_CERTBOT.info(line.strip())
|
||||
|
||||
if process.returncode != 0:
|
||||
status = 2
|
||||
LOGGER.error("Certificates renewal failed")
|
||||
|
||||
# Save Let's Encrypt DNS data to db cache
|
||||
if DATA_PATH.is_dir() and list(DATA_PATH.iterdir()):
|
||||
cached, err = JOB.cache_dir(DATA_PATH)
|
||||
if not cached:
|
||||
LOGGER.error(f"Error while saving Let's Encrypt DNS data to db cache : {err}")
|
||||
else:
|
||||
LOGGER.info("Successfully saved Let's Encrypt DNS data to db cache")
|
||||
except SystemExit as e:
|
||||
status = e.code
|
||||
except:
|
||||
status = 2
|
||||
LOGGER.error(f"Exception while running certbot-renew.py :\n{format_exc()}")
|
||||
|
||||
sys_exit(status)
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from os import environ, getenv, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
from subprocess import STDOUT, Popen, PIPE, run
|
||||
from sys import exit as sys_exit, path as sys_path, version_info
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from Database import Database # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
|
||||
LOGGER = setup_logger("LETS-ENCRYPT-DNS.install-deps", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
PLUGIN_PATH = Path(sep, "usr", "share", "bunkerweb", "core", "letsencrypt_dns")
|
||||
LIB_PATH = Path(sep, "var", "lib", "bunkerweb", "letsencrypt_dns")
|
||||
PIP_PATH = LIB_PATH.joinpath("pip")
|
||||
PYTHON_DEPS_PATH = LIB_PATH.joinpath("python")
|
||||
|
||||
try:
|
||||
|
||||
# * Check if we're using let's encrypt DNS
|
||||
all_domains = getenv("SERVER_NAME", "")
|
||||
|
||||
if not all_domains:
|
||||
LOGGER.warning("There are no server names, skipping additional dependencies installation...")
|
||||
sys_exit(0)
|
||||
|
||||
use_letsencrypt_dns = False
|
||||
is_multisite = getenv("MULTISITE", "no") == "yes"
|
||||
server_names = all_domains.split(" ")
|
||||
|
||||
if not is_multisite:
|
||||
use_letsencrypt_dns = getenv("AUTO_LETS_ENCRYPT_DNS", "no") == "yes"
|
||||
else:
|
||||
for first_server in server_names:
|
||||
if first_server and getenv(f"{first_server}_AUTO_LETS_ENCRYPT_DNS", getenv("AUTO_LETS_ENCRYPT_DNS", "no")) == "yes":
|
||||
use_letsencrypt_dns = True
|
||||
break
|
||||
|
||||
if not use_letsencrypt_dns:
|
||||
LOGGER.info("Let's Encrypt DNS is not activated, skipping additional dependencies installation...")
|
||||
sys_exit(0)
|
||||
|
||||
if PYTHON_DEPS_PATH.is_dir() and list(PYTHON_DEPS_PATH.iterdir()):
|
||||
LOGGER.info("Additional dependencies already installed, checking for updates...")
|
||||
|
||||
deps = {}
|
||||
with PLUGIN_PATH.joinpath("requirements.in").open("r") as f:
|
||||
for line in f:
|
||||
if (line := line.strip()) and not line.startswith("#"):
|
||||
package, version = line.split("==")
|
||||
deps[package] = version
|
||||
|
||||
all_deps_up_to_date = True
|
||||
process = Popen(
|
||||
["python3", "-m", "pip", "freeze", "--local"],
|
||||
stdout=PIPE,
|
||||
stderr=STDOUT,
|
||||
universal_newlines=True,
|
||||
env=environ | {"PYTHONPATH": PYTHON_DEPS_PATH.as_posix()},
|
||||
)
|
||||
while process.poll() is None:
|
||||
if process.stdout is not None:
|
||||
for line in process.stdout:
|
||||
if (line := line.strip()) and not line.startswith("#"):
|
||||
split = line.split("==")
|
||||
if len(split) != 2:
|
||||
continue
|
||||
package, version = split
|
||||
|
||||
if package in deps and deps[package] != version:
|
||||
LOGGER.info(f"⚠️ {package} is outdated: {version} -> {deps[package]}")
|
||||
all_deps_up_to_date = False
|
||||
|
||||
if process.returncode != 0:
|
||||
LOGGER.error("❌ Error while checking additional python dependencies, updating just in case...")
|
||||
elif all_deps_up_to_date:
|
||||
LOGGER.info("✅ All additional dependencies are up to date")
|
||||
sys_exit(0)
|
||||
else:
|
||||
LOGGER.warning("Some additional dependencies are outdated, updating...")
|
||||
rmtree(PYTHON_DEPS_PATH, ignore_errors=True)
|
||||
else:
|
||||
LOGGER.info("Deps path not found, installing additional dependencies...")
|
||||
|
||||
PYTHON_DEPS_PATH.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
pip_cmd = ["python3", "-m", "pip"]
|
||||
cmd_env = environ | {"PYTHONPATH": PYTHON_DEPS_PATH.as_posix()}
|
||||
|
||||
if PIP_PATH.joinpath("usr", "local", "bin").is_dir() and PIP_PATH.joinpath("usr", "local", "lib").is_dir():
|
||||
pip_cmd = [PIP_PATH.joinpath("usr", "local", "bin", "pip3").as_posix()]
|
||||
cmd_env["PYTHONPATH"] += ":" + PIP_PATH.joinpath("usr", "local", "lib", f"python{version_info.major}.{version_info.minor}", "site-packages").as_posix()
|
||||
else:
|
||||
process = run(pip_cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
|
||||
|
||||
if process.returncode != 0:
|
||||
LOGGER.warning("Pip is not installed, installing pip locally...")
|
||||
process = Popen(
|
||||
["python3", "-m", "ensurepip", "--root", PIP_PATH.as_posix()],
|
||||
stdout=PIPE,
|
||||
stderr=STDOUT,
|
||||
universal_newlines=True,
|
||||
)
|
||||
while process.poll() is None:
|
||||
if process.stdout is not None:
|
||||
for line in process.stdout:
|
||||
LOGGER.debug(line.strip())
|
||||
|
||||
if process.returncode != 0:
|
||||
LOGGER.error("❌ Error while ensuring pip is up to date")
|
||||
sys_exit(1)
|
||||
|
||||
LOGGER.info("✅ Pip installed successfully")
|
||||
|
||||
pip_cmd = [PIP_PATH.joinpath("usr", "local", "bin", "pip3").as_posix()]
|
||||
cmd_env["PYTHONPATH"] += (
|
||||
":" + PIP_PATH.joinpath("usr", "local", "lib", f"python{version_info.major}.{version_info.minor}", "site-packages").as_posix()
|
||||
)
|
||||
|
||||
LOGGER.info("Installing additional python dependencies...")
|
||||
current_date = datetime.now()
|
||||
process = Popen(
|
||||
pip_cmd
|
||||
+ [
|
||||
"install",
|
||||
"--no-cache-dir",
|
||||
"--require-hashes",
|
||||
"--ignore-installed",
|
||||
"--target",
|
||||
PYTHON_DEPS_PATH.as_posix(),
|
||||
"-r",
|
||||
PLUGIN_PATH.joinpath("requirements.txt").as_posix(),
|
||||
],
|
||||
stdout=PIPE,
|
||||
stderr=STDOUT,
|
||||
universal_newlines=True,
|
||||
env=cmd_env,
|
||||
)
|
||||
while process.poll() is None:
|
||||
if process.stdout is not None:
|
||||
for line in process.stdout:
|
||||
if datetime.now() - current_date > timedelta(seconds=5):
|
||||
LOGGER.info("⏳ Still installing additional python dependencies...")
|
||||
current_date = datetime.now()
|
||||
LOGGER.debug(line.strip())
|
||||
|
||||
if process.returncode != 0:
|
||||
LOGGER.error("❌ Error while installing additional python dependencies")
|
||||
sys_exit(1)
|
||||
|
||||
LOGGER.info("✅ Additional dependencies installed successfully")
|
||||
except SystemExit as e:
|
||||
status = e.code
|
||||
except:
|
||||
status = 1
|
||||
LOGGER.exception("Exception while running install-dependencies.py")
|
||||
|
||||
sys_exit(status)
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from os.path import sep
|
||||
from pathlib import Path
|
||||
from sys import path as sys_path
|
||||
from typing import Literal, Optional
|
||||
|
||||
LIB_PATH = Path(sep, "var", "lib", "bunkerweb", "letsencrypt_dns")
|
||||
|
||||
PYTHON_PATH = LIB_PATH.joinpath("python")
|
||||
if PYTHON_PATH.as_posix() not in sys_path:
|
||||
sys_path.append(PYTHON_PATH.as_posix())
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class Provider(BaseModel):
|
||||
"""Base class for DNS providers."""
|
||||
|
||||
# ? Allow extra fields in the model in case there are additional fields that are not defined in the model.
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
def get_formatted_credentials(self) -> bytes:
|
||||
"""Return the formatted credentials to be written to a file."""
|
||||
return "\n".join(f"{key} = {value}" for key, value in self.model_dump(exclude={"file_type"}).items()).encode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def get_file_type() -> Literal["ini"]:
|
||||
"""Return the file type that the credentials should be written to."""
|
||||
return "ini"
|
||||
|
||||
|
||||
class CloudflareProvider(Provider):
|
||||
dns_cloudflare_api_token: str
|
||||
|
||||
|
||||
class DigitalOceanProvider(Provider):
|
||||
dns_digitalocean_token: str
|
||||
|
||||
|
||||
class GoogleProvider(Provider):
|
||||
type: str = "service_account"
|
||||
project_id: str
|
||||
private_key_id: str
|
||||
private_key: str
|
||||
client_email: str
|
||||
client_id: str
|
||||
auth_uri: str = "https://accounts.google.com/o/oauth2/auth"
|
||||
token_uri: str = "https://accounts.google.com/o/oauth2/token"
|
||||
auth_provider_x509_cert_url: str = "https://www.googleapis.com/oauth2/v1/certs"
|
||||
client_x509_cert_url: str
|
||||
|
||||
def get_formatted_credentials(self) -> bytes:
|
||||
"""Return the formatted credentials to be written to a file."""
|
||||
return self.model_dump_json(indent=2, exclude={"file_type"}).encode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def get_file_type() -> Literal["json"]:
|
||||
"""Return the file type that the credentials should be written to."""
|
||||
return "json"
|
||||
|
||||
|
||||
class LinodeProvider(Provider):
|
||||
dns_linode_key: str
|
||||
dns_linode_version: str = "4"
|
||||
|
||||
|
||||
class OvhProvider(Provider):
|
||||
dns_ovh_endpoint: str = "ovh-eu"
|
||||
dns_ovh_application_key: str
|
||||
dns_ovh_application_secret: str
|
||||
dns_ovh_consumer_key: str
|
||||
|
||||
|
||||
class Rfc2136Provider(Provider):
|
||||
dns_rfc2136_server: str
|
||||
dns_rfc2136_port: Optional[str] = None
|
||||
dns_rfc2136_name: str
|
||||
dns_rfc2136_secret: str
|
||||
dns_rfc2136_algorithm: str = "HMAC-MD5"
|
||||
dns_rfc2136_sign_query: str = "false"
|
||||
|
||||
def get_formatted_credentials(self) -> bytes:
|
||||
"""Return the formatted credentials to be written to a file."""
|
||||
# ? Return the formatted credentials as a string. The default values are excluded as they are not required.
|
||||
return "\n".join(f"{key} = {value}" for key, value in self.model_dump(exclude={"file_type"}, exclude_defaults=True).items()).encode("utf-8")
|
||||
|
||||
|
||||
class Route53Provider(Provider):
|
||||
aws_access_key_id: str
|
||||
aws_secret_access_key: str
|
||||
|
||||
def get_formatted_credentials(self) -> bytes:
|
||||
"""Return the formatted credentials to be written to a file."""
|
||||
# ? Return the formatted credentials as a string. The keys are converted to uppercase and the values are represented as a string.
|
||||
return "\n".join(f"{key.upper()}={value!r}" for key, value in self.model_dump(exclude={"file_type"}).items()).encode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def get_file_type() -> Literal["env"]:
|
||||
"""Return the file type that the credentials should be written to."""
|
||||
return "env"
|
||||
|
||||
|
||||
class ScalewayProvider(Provider):
|
||||
dns_scaleway_application_token: str
|
||||
|
||||
|
||||
__ALL__ = (
|
||||
"CloudflareProvider",
|
||||
"DigitalOceanProvider",
|
||||
"GoogleProvider",
|
||||
"LinodeProvider",
|
||||
"OvhProvider",
|
||||
"Rfc2136Provider",
|
||||
"Route53Provider",
|
||||
"ScalewayProvider",
|
||||
)
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
local class = require("middleclass")
|
||||
local plugin = require("bunkerweb.plugin")
|
||||
local ssl = require("ngx.ssl")
|
||||
local utils = require("bunkerweb.utils")
|
||||
|
||||
local letsencrypt_dns = class("letsencrypt_dns", plugin)
|
||||
|
||||
-- luacheck: globals ngx
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local parse_pem_cert = ssl.parse_pem_cert
|
||||
local parse_pem_priv_key = ssl.parse_pem_priv_key
|
||||
local ssl_server_name = ssl.server_name
|
||||
local get_variable = utils.get_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local has_variable = utils.has_variable
|
||||
local has_not_variable = utils.has_not_variable
|
||||
local read_files = utils.read_files
|
||||
|
||||
function letsencrypt_dns:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "letsencrypt_dns", ctx)
|
||||
end
|
||||
|
||||
function letsencrypt_dns:set()
|
||||
local https_configured = self.variables["AUTO_LETS_ENCRYPT_DNS"]
|
||||
if https_configured == "yes" then
|
||||
self.ctx.bw.https_configured = "yes"
|
||||
end
|
||||
return self:ret(true, "set https_configured to " .. https_configured)
|
||||
end
|
||||
|
||||
function letsencrypt_dns:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if has_variable("AUTO_LETS_ENCRYPT_DNS", "yes") and has_not_variable("LETS_ENCRYPT_DNS_PROVIDER", "") then
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
return self:ret(false, "can't get MULTISITE variable : " .. err)
|
||||
end
|
||||
if multisite == "yes" then
|
||||
local vars
|
||||
vars, err = get_multiple_variables({
|
||||
"AUTO_LETS_ENCRYPT_DNS",
|
||||
"LETS_ENCRYPT_DNS_PROVIDER",
|
||||
"USE_LETS_ENCRYPT_DNS_WILDCARD",
|
||||
"SERVER_NAME",
|
||||
})
|
||||
if not vars then
|
||||
return self:ret(false, "can't get required variables : " .. err)
|
||||
end
|
||||
local credential_items
|
||||
credential_items, err = get_multiple_variables({ "LETS_ENCRYPT_DNS_CREDENTIAL_ITEM" })
|
||||
if not credential_items then
|
||||
return self:ret(false, "can't get credential items : " .. err)
|
||||
end
|
||||
for server_name, multisite_vars in pairs(vars) do
|
||||
if
|
||||
multisite_vars["AUTO_LETS_ENCRYPT_DNS"] == "yes"
|
||||
and multisite_vars["LETS_ENCRYPT_DNS_PROVIDER"] ~= ""
|
||||
and credential_items[server_name]
|
||||
and server_name ~= "global"
|
||||
then
|
||||
local data
|
||||
if multisite_vars["USE_LETS_ENCRYPT_DNS_WILDCARD"] == "yes" then
|
||||
local parts = {}
|
||||
for part in server_name:gmatch("[^.]+") do
|
||||
table.insert(parts, part)
|
||||
end
|
||||
server_name = table.concat(parts, ".", 2)
|
||||
data = self.datastore:get("plugin_letsencrypt_dns_" .. server_name, true)
|
||||
end
|
||||
if not data then
|
||||
-- Load certificate
|
||||
local check
|
||||
check, data = read_files({
|
||||
"/var/cache/bunkerweb/letsencrypt_dns/etc/live/" .. server_name .. "/fullchain.pem",
|
||||
"/var/cache/bunkerweb/letsencrypt_dns/etc/live/" .. server_name .. "/privkey.pem",
|
||||
})
|
||||
if not check then
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
if multisite_vars["USE_LETS_ENCRYPT_DNS_WILDCARD"] == "yes" then
|
||||
check, err = self:load_data(data, server_name)
|
||||
else
|
||||
check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
end
|
||||
if not check then
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local server_name
|
||||
server_name, err = get_variable("SERVER_NAME", false)
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local use_wildcard
|
||||
use_wildcard, err = get_variable("USE_LETS_ENCRYPT_DNS_WILDCARD", false)
|
||||
if not use_wildcard then
|
||||
return self:ret(false, "can't get USE_LETS_ENCRYPT_DNS_WILDCARD variable : " .. err)
|
||||
end
|
||||
server_name = server_name:match("%S+")
|
||||
if use_wildcard == "yes" then
|
||||
local parts = {}
|
||||
for part in server_name:gmatch("[^.]+") do
|
||||
table.insert(parts, part)
|
||||
end
|
||||
server_name = table.concat(parts, ".", 2)
|
||||
end
|
||||
local check, data = read_files({
|
||||
"/var/cache/bunkerweb/letsencrypt_dns/etc/live/" .. server_name .. "/fullchain.pem",
|
||||
"/var/cache/bunkerweb/letsencrypt_dns/etc/live/" .. server_name .. "/privkey.pem",
|
||||
})
|
||||
if not check then
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
check, err = self:load_data(data, server_name)
|
||||
if not check then
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
ret_err = "let's encrypt dns is not used"
|
||||
end
|
||||
return self:ret(ret_ok, ret_err)
|
||||
end
|
||||
|
||||
function letsencrypt_dns:ssl_certificate()
|
||||
local server_name, err = ssl_server_name()
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get server_name : " .. err)
|
||||
end
|
||||
local use_wildcard
|
||||
use_wildcard, err = get_variable("USE_LETS_ENCRYPT_DNS_WILDCARD", false)
|
||||
if not use_wildcard then
|
||||
return self:ret(false, "can't get USE_LETS_ENCRYPT_DNS_WILDCARD variable : " .. err)
|
||||
end
|
||||
if use_wildcard == "yes" then
|
||||
local parts = {}
|
||||
for part in server_name:gmatch("[^.]+") do
|
||||
table.insert(parts, part)
|
||||
end
|
||||
server_name = table.concat(parts, ".", 2)
|
||||
end
|
||||
local data
|
||||
data, err = self.datastore:get("plugin_letsencrypt_dns_" .. server_name, true)
|
||||
if not data and err ~= "not found" then
|
||||
return self:ret(
|
||||
false,
|
||||
"error while getting plugin_letsencrypt_dns_" .. server_name .. " from datastore : " .. err
|
||||
)
|
||||
elseif data then
|
||||
return self:ret(true, "certificate/key data found", data)
|
||||
end
|
||||
return self:ret(true, "let's encrypt dns is not used")
|
||||
end
|
||||
|
||||
function letsencrypt_dns:load_data(data, server_name)
|
||||
-- Load certificate
|
||||
local cert_chain, err = parse_pem_cert(data[1])
|
||||
if not cert_chain then
|
||||
return false, "error while parsing pem cert : " .. err
|
||||
end
|
||||
-- Load key
|
||||
local priv_key
|
||||
priv_key, err = parse_pem_priv_key(data[2])
|
||||
if not priv_key then
|
||||
return false, "error while parsing pem priv key : " .. err
|
||||
end
|
||||
-- Cache data
|
||||
for key in server_name:gmatch("%S+") do
|
||||
local cache_key = "plugin_letsencrypt_dns_" .. key
|
||||
local ok
|
||||
ok, err = self.datastore:set(cache_key, { cert_chain, priv_key }, nil, true)
|
||||
if not ok then
|
||||
return false, "error while setting data into datastore : " .. err
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return letsencrypt_dns
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
{
|
||||
"id": "letsencrypt_dns",
|
||||
"name": "Let's Encrypt DNS",
|
||||
"description": "Automatic creation, renewal and configuration of Let's Encrypt certificates using DNS challenges.",
|
||||
"version": "0.7",
|
||||
"stream": "yes",
|
||||
"settings": {
|
||||
"AUTO_LETS_ENCRYPT_DNS": {
|
||||
"context": "multisite",
|
||||
"default": "no",
|
||||
"help": "Activate automatic Let's Encrypt DNS.",
|
||||
"id": "auto-lets-encrypt-dns",
|
||||
"label": "Automatic Let's Encrypt Dns",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
"LETS_ENCRYPT_DNS_EMAIL": {
|
||||
"context": "multisite",
|
||||
"default": "",
|
||||
"help": "The email address to use for Let's Encrypt notifications.",
|
||||
"id": "lets-encrypt-dns-email",
|
||||
"label": "Email Address for Notifications",
|
||||
"regex": "^([^@ \\t\\r\\n]+@[^@ \\t\\r\\n]+\\.[^@ \\t\\r\\n]+)?$",
|
||||
"type": "text"
|
||||
},
|
||||
"USE_LETS_ENCRYPT_DNS_STAGING": {
|
||||
"context": "multisite",
|
||||
"default": "no",
|
||||
"help": "Use the Let's Encrypt staging environment.",
|
||||
"id": "use-lets-encrypt-dns-staging",
|
||||
"label": "Use Let's Encrypt DNS Staging",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
"LETS_ENCRYPT_DNS_PROVIDER": {
|
||||
"context": "multisite",
|
||||
"default": "",
|
||||
"help": "The DNS provider to use for DNS challenges.",
|
||||
"id": "auto-lets-encrypt-dns-provider",
|
||||
"label": "DNS Provider",
|
||||
"regex": "^(cloudflare|digitalocean|google|linode|ovh|rfc2136|route53|scaleway)?$",
|
||||
"type": "select",
|
||||
"select": [
|
||||
"",
|
||||
"cloudflare",
|
||||
"digitalocean",
|
||||
"google",
|
||||
"linode",
|
||||
"ovh",
|
||||
"rfc2136",
|
||||
"route53",
|
||||
"scaleway"
|
||||
]
|
||||
},
|
||||
"USE_LETS_ENCRYPT_DNS_WILDCARD": {
|
||||
"context": "multisite",
|
||||
"default": "yes",
|
||||
"help": "Create wildcard certificates for all domains using DNS challenges.",
|
||||
"id": "use-lets-encrypt-dns-wildcard",
|
||||
"label": "Wildcard Certificates",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
"LETS_ENCRYPT_DNS_PROPAGATION": {
|
||||
"context": "multisite",
|
||||
"default": "default",
|
||||
"help": "The time to wait for DNS propagation in seconds.",
|
||||
"id": "lets-encrypt-dns-propagation",
|
||||
"label": "DNS Propagation",
|
||||
"regex": "^(default|\\d+)$",
|
||||
"type": "text"
|
||||
},
|
||||
"LETS_ENCRYPT_DNS_CREDENTIAL_ITEM": {
|
||||
"context": "multisite",
|
||||
"default": "",
|
||||
"help": "Configuration item that will be added to the credentials.ini file for the DNS provider (e.g. 'cloudflare_api_token 123456').",
|
||||
"id": "lets-encrypt-dns-credential-item",
|
||||
"label": "Credential Item",
|
||||
"regex": "^(\\w+ .+)?$",
|
||||
"type": "password",
|
||||
"multiple": "lets-encrypt-dns-credential-item"
|
||||
},
|
||||
"LETS_ENCRYPT_DNS_CLEAR_OLD_CERTS": {
|
||||
"context": "global",
|
||||
"default": "no",
|
||||
"help": "Clear old certificates when renewing.",
|
||||
"id": "lets-encrypt-dns-clear-old-certs",
|
||||
"label": "Clear old certificates when they are no longer needed",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
}
|
||||
},
|
||||
"jobs": [
|
||||
{
|
||||
"name": "install-lets-encrypt-dns-dependencies",
|
||||
"file": "install-lets-encrypt-dns-dependencies.py",
|
||||
"every": "once",
|
||||
"reload": false
|
||||
},
|
||||
{
|
||||
"name": "dns-certbot-new",
|
||||
"file": "dns-certbot-new.py",
|
||||
"every": "once",
|
||||
"reload": false
|
||||
},
|
||||
{
|
||||
"name": "dns-certbot-renew",
|
||||
"file": "dns-certbot-renew.py",
|
||||
"every": "day",
|
||||
"reload": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
certbot-dns-cloudflare==2.11.0
|
||||
certbot-dns-digitalocean==2.11.0
|
||||
certbot-dns-google==2.11.0
|
||||
certbot-dns-linode==2.11.0
|
||||
certbot-dns-ovh==2.11.0
|
||||
certbot-dns-rfc2136==2.11.0
|
||||
certbot-dns-route53==2.11.0
|
||||
certbot-dns-scaleway==0.0.7
|
||||
pydantic==2.9.2
|
||||
|
|
@ -1,686 +0,0 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.9
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
acme==2.11.0 \
|
||||
--hash=sha256:23213ac3074a78862b219e0a30e141fd53238a8bdcf0668bd4dea59b28873fb8 \
|
||||
--hash=sha256:f4950015cf52ff0de12f37fc28034c7710aca63f64f1696253d2f6cb9f22645e
|
||||
# via
|
||||
# certbot
|
||||
# certbot-dns-cloudflare
|
||||
# certbot-dns-digitalocean
|
||||
# certbot-dns-google
|
||||
# certbot-dns-linode
|
||||
# certbot-dns-ovh
|
||||
# certbot-dns-rfc2136
|
||||
# certbot-dns-route53
|
||||
# certbot-dns-scaleway
|
||||
annotated-types==0.7.0 \
|
||||
--hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \
|
||||
--hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89
|
||||
# via pydantic
|
||||
attrs==24.2.0 \
|
||||
--hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \
|
||||
--hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2
|
||||
# via jsonlines
|
||||
beautifulsoup4==4.12.3 \
|
||||
--hash=sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051 \
|
||||
--hash=sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed
|
||||
# via dns-lexicon
|
||||
boto3==1.35.24 \
|
||||
--hash=sha256:97fcc1a14cbc759e4ba9535ced703a99fcf652c9c4b8dfcd06f292c80551684b \
|
||||
--hash=sha256:be7807f30f26d6c0057e45cfd09dad5968e664488bf4f9138d0bb7a0f6d8ed40
|
||||
# via certbot-dns-route53
|
||||
botocore==1.35.24 \
|
||||
--hash=sha256:1e59b0f14f4890c4f70bd6a58a634b9464bed1c4c6171f87c8795d974ade614b \
|
||||
--hash=sha256:eb9ccc068255cc3d24c36693fda6aec7786db05ae6c2b13bcba66dce6a13e2e3
|
||||
# via
|
||||
# boto3
|
||||
# s3transfer
|
||||
cachetools==5.5.0 \
|
||||
--hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \
|
||||
--hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a
|
||||
# via google-auth
|
||||
certbot==2.11.0 \
|
||||
--hash=sha256:257ae1cb0a534373ca50dd807c9ae96f27660e41379c45afb9b50cab0e6a7a97 \
|
||||
--hash=sha256:dc4e0a48bcb09448d60362170ca1047cc9a81966da0dd35135f2561f0ea7d5b1
|
||||
# via
|
||||
# certbot-dns-cloudflare
|
||||
# certbot-dns-digitalocean
|
||||
# certbot-dns-google
|
||||
# certbot-dns-linode
|
||||
# certbot-dns-ovh
|
||||
# certbot-dns-rfc2136
|
||||
# certbot-dns-route53
|
||||
# certbot-dns-scaleway
|
||||
certbot-dns-cloudflare==2.11.0 \
|
||||
--hash=sha256:2a3e06a692add6aacdc2dfe7fada695483f5fbf4fed073eabd36f3d7745f0c7a \
|
||||
--hash=sha256:42788044840328de1fe85ea32df1254823f1452e0479a60445fd364f8234a4a9
|
||||
# via -r requirements.in
|
||||
certbot-dns-digitalocean==2.11.0 \
|
||||
--hash=sha256:30e9543baef204e110dacb1e6adf9b1fd04777a14a204bd3cdbfb100c6f6e32a \
|
||||
--hash=sha256:d5166fc7eb3b3e8a8de4b43e7485d60eda4225db1d525b6f096949d1487e1c6c
|
||||
# via -r requirements.in
|
||||
certbot-dns-google==2.11.0 \
|
||||
--hash=sha256:6af70452913e472f74788e76375ff00a6c427e59c896d7b982732ba33a09a199 \
|
||||
--hash=sha256:de5fd15b4b60e652ea41e556fccf09376fdc7f881ec7544cb1e25176b2a1a5bf
|
||||
# via -r requirements.in
|
||||
certbot-dns-linode==2.11.0 \
|
||||
--hash=sha256:4727015830ff048e925d2acee26e9bd727e03cb3ceb29d228d45c6602b6f964f \
|
||||
--hash=sha256:60848af4c336928f0b069d350accae5abd5896118c36920247972b6759aa1ba6
|
||||
# via -r requirements.in
|
||||
certbot-dns-ovh==2.11.0 \
|
||||
--hash=sha256:1a9ccd1d987c0448dd9050a3ac43558569a6d887f2fcde148f2699c4dc624a26 \
|
||||
--hash=sha256:6be4feb03782bf2dc876319df0a54ee567241d777132546785b1f7c072e6a2df
|
||||
# via -r requirements.in
|
||||
certbot-dns-rfc2136==2.11.0 \
|
||||
--hash=sha256:413a80c09e3a00162d9f7833cb2f5ed3690ae0833e09be84c795b7ee5a357c4b \
|
||||
--hash=sha256:aaf9f6b387359734b4138f179f96f480889d9a4e2e44fae60c9ebe4d8715f567
|
||||
# via -r requirements.in
|
||||
certbot-dns-route53==2.11.0 \
|
||||
--hash=sha256:2492bd62fbe514a259d4a0b3455d576b267b9a82f26f396af136d9ec2a9c0ba8 \
|
||||
--hash=sha256:5fd11e3546175574ccc51aaeccb19860e00c633f9bbeb0c4d033bac5553b15bd
|
||||
# via -r requirements.in
|
||||
certbot-dns-scaleway==0.0.7 \
|
||||
--hash=sha256:999dda5b8689277facb77e1757b8a6b207baeecc0ded0c27aea2c51331affc92 \
|
||||
--hash=sha256:bc0833ed71a5cd314a93f8d02144c54e17066ee1e2b950ed868b14b2211f5e9e
|
||||
# via -r requirements.in
|
||||
certifi==2024.8.30 \
|
||||
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
|
||||
--hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9
|
||||
# via requests
|
||||
cffi==1.17.1 \
|
||||
--hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \
|
||||
--hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \
|
||||
--hash=sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1 \
|
||||
--hash=sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15 \
|
||||
--hash=sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36 \
|
||||
--hash=sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824 \
|
||||
--hash=sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8 \
|
||||
--hash=sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36 \
|
||||
--hash=sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17 \
|
||||
--hash=sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf \
|
||||
--hash=sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc \
|
||||
--hash=sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3 \
|
||||
--hash=sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed \
|
||||
--hash=sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702 \
|
||||
--hash=sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1 \
|
||||
--hash=sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8 \
|
||||
--hash=sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903 \
|
||||
--hash=sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6 \
|
||||
--hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \
|
||||
--hash=sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b \
|
||||
--hash=sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e \
|
||||
--hash=sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be \
|
||||
--hash=sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c \
|
||||
--hash=sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683 \
|
||||
--hash=sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9 \
|
||||
--hash=sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c \
|
||||
--hash=sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8 \
|
||||
--hash=sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1 \
|
||||
--hash=sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4 \
|
||||
--hash=sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655 \
|
||||
--hash=sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67 \
|
||||
--hash=sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595 \
|
||||
--hash=sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0 \
|
||||
--hash=sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65 \
|
||||
--hash=sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41 \
|
||||
--hash=sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6 \
|
||||
--hash=sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401 \
|
||||
--hash=sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6 \
|
||||
--hash=sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3 \
|
||||
--hash=sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16 \
|
||||
--hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 \
|
||||
--hash=sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e \
|
||||
--hash=sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4 \
|
||||
--hash=sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964 \
|
||||
--hash=sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c \
|
||||
--hash=sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576 \
|
||||
--hash=sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0 \
|
||||
--hash=sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3 \
|
||||
--hash=sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662 \
|
||||
--hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \
|
||||
--hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \
|
||||
--hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \
|
||||
--hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \
|
||||
--hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \
|
||||
--hash=sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5 \
|
||||
--hash=sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14 \
|
||||
--hash=sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d \
|
||||
--hash=sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9 \
|
||||
--hash=sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7 \
|
||||
--hash=sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382 \
|
||||
--hash=sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a \
|
||||
--hash=sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e \
|
||||
--hash=sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a \
|
||||
--hash=sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4 \
|
||||
--hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \
|
||||
--hash=sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87 \
|
||||
--hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b
|
||||
# via cryptography
|
||||
charset-normalizer==3.3.2 \
|
||||
--hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
|
||||
--hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \
|
||||
--hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \
|
||||
--hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \
|
||||
--hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \
|
||||
--hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \
|
||||
--hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \
|
||||
--hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \
|
||||
--hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \
|
||||
--hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \
|
||||
--hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \
|
||||
--hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \
|
||||
--hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \
|
||||
--hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \
|
||||
--hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \
|
||||
--hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \
|
||||
--hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \
|
||||
--hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \
|
||||
--hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \
|
||||
--hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \
|
||||
--hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \
|
||||
--hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \
|
||||
--hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \
|
||||
--hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \
|
||||
--hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \
|
||||
--hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \
|
||||
--hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \
|
||||
--hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \
|
||||
--hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \
|
||||
--hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \
|
||||
--hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \
|
||||
--hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \
|
||||
--hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \
|
||||
--hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \
|
||||
--hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \
|
||||
--hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \
|
||||
--hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \
|
||||
--hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \
|
||||
--hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \
|
||||
--hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \
|
||||
--hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \
|
||||
--hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \
|
||||
--hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \
|
||||
--hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \
|
||||
--hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \
|
||||
--hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \
|
||||
--hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \
|
||||
--hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \
|
||||
--hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \
|
||||
--hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \
|
||||
--hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \
|
||||
--hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \
|
||||
--hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \
|
||||
--hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \
|
||||
--hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \
|
||||
--hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \
|
||||
--hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \
|
||||
--hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \
|
||||
--hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \
|
||||
--hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \
|
||||
--hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \
|
||||
--hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \
|
||||
--hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \
|
||||
--hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \
|
||||
--hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \
|
||||
--hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \
|
||||
--hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \
|
||||
--hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \
|
||||
--hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \
|
||||
--hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \
|
||||
--hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \
|
||||
--hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \
|
||||
--hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \
|
||||
--hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \
|
||||
--hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \
|
||||
--hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \
|
||||
--hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \
|
||||
--hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \
|
||||
--hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \
|
||||
--hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \
|
||||
--hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \
|
||||
--hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \
|
||||
--hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \
|
||||
--hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \
|
||||
--hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \
|
||||
--hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \
|
||||
--hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \
|
||||
--hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \
|
||||
--hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \
|
||||
--hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561
|
||||
# via requests
|
||||
cloudflare==2.19.4 \
|
||||
--hash=sha256:3b6000a01a237c23bccfdf6d20256ea5111ec74a826ae9e74f9f0e5bb5b2383f
|
||||
# via certbot-dns-cloudflare
|
||||
configargparse==1.7 \
|
||||
--hash=sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b \
|
||||
--hash=sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1
|
||||
# via certbot
|
||||
configobj==5.0.9 \
|
||||
--hash=sha256:03c881bbf23aa07bccf1b837005975993c4ab4427ba57f959afdd9d1a2386848
|
||||
# via certbot
|
||||
cryptography==43.0.1 \
|
||||
--hash=sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494 \
|
||||
--hash=sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806 \
|
||||
--hash=sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d \
|
||||
--hash=sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062 \
|
||||
--hash=sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2 \
|
||||
--hash=sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4 \
|
||||
--hash=sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1 \
|
||||
--hash=sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85 \
|
||||
--hash=sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84 \
|
||||
--hash=sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042 \
|
||||
--hash=sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d \
|
||||
--hash=sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962 \
|
||||
--hash=sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2 \
|
||||
--hash=sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa \
|
||||
--hash=sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d \
|
||||
--hash=sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365 \
|
||||
--hash=sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96 \
|
||||
--hash=sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47 \
|
||||
--hash=sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d \
|
||||
--hash=sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d \
|
||||
--hash=sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c \
|
||||
--hash=sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb \
|
||||
--hash=sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277 \
|
||||
--hash=sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172 \
|
||||
--hash=sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034 \
|
||||
--hash=sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a \
|
||||
--hash=sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
# dns-lexicon
|
||||
# josepy
|
||||
# pyopenssl
|
||||
distro==1.9.0 \
|
||||
--hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \
|
||||
--hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2
|
||||
# via certbot
|
||||
dns-lexicon==3.18.0 \
|
||||
--hash=sha256:aabe320093b4f9a7f7e0e430551ae49c38c9cf99b45ef7e28da238c50106b1a0 \
|
||||
--hash=sha256:c2b1005a6621a2ec648131d96ec61304b90b98842af9ff62b1840ddf9d0e2c26
|
||||
# via
|
||||
# certbot-dns-linode
|
||||
# certbot-dns-ovh
|
||||
dnspython==2.6.1 \
|
||||
--hash=sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50 \
|
||||
--hash=sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc
|
||||
# via
|
||||
# certbot-dns-rfc2136
|
||||
# dns-lexicon
|
||||
filelock==3.16.1 \
|
||||
--hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \
|
||||
--hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435
|
||||
# via tldextract
|
||||
google-api-core==2.20.0 \
|
||||
--hash=sha256:ef0591ef03c30bb83f79b3d0575c3f31219001fc9c5cf37024d08310aeffed8a \
|
||||
--hash=sha256:f74dff1889ba291a4b76c5079df0711810e2d9da81abfdc99957bc961c1eb28f
|
||||
# via google-api-python-client
|
||||
google-api-python-client==2.146.0 \
|
||||
--hash=sha256:41f671be10fa077ee5143ee9f0903c14006d39dc644564f4e044ae96b380bf68 \
|
||||
--hash=sha256:b1e62c9889c5ef6022f11d30d7ef23dc55100300f0e8aaf8aa09e8e92540acad
|
||||
# via certbot-dns-google
|
||||
google-auth==2.35.0 \
|
||||
--hash=sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f \
|
||||
--hash=sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a
|
||||
# via
|
||||
# certbot-dns-google
|
||||
# google-api-core
|
||||
# google-api-python-client
|
||||
# google-auth-httplib2
|
||||
google-auth-httplib2==0.2.0 \
|
||||
--hash=sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05 \
|
||||
--hash=sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d
|
||||
# via google-api-python-client
|
||||
googleapis-common-protos==1.65.0 \
|
||||
--hash=sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63 \
|
||||
--hash=sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0
|
||||
# via google-api-core
|
||||
httplib2==0.22.0 \
|
||||
--hash=sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc \
|
||||
--hash=sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81
|
||||
# via
|
||||
# google-api-python-client
|
||||
# google-auth-httplib2
|
||||
idna==3.10 \
|
||||
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
|
||||
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
|
||||
# via
|
||||
# requests
|
||||
# tldextract
|
||||
importlib-metadata==8.5.0 \
|
||||
--hash=sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b \
|
||||
--hash=sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7
|
||||
# via
|
||||
# certbot
|
||||
# dns-lexicon
|
||||
jmespath==1.0.1 \
|
||||
--hash=sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 \
|
||||
--hash=sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe
|
||||
# via
|
||||
# boto3
|
||||
# botocore
|
||||
josepy==1.14.0 \
|
||||
--hash=sha256:308b3bf9ce825ad4d4bba76372cf19b5dc1c2ce96a9d298f9642975e64bd13dd \
|
||||
--hash=sha256:d2b36a30f316269f3242f4c2e45e15890784178af5ec54fa3e49cf9234ee22e0
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
jsonlines==4.0.0 \
|
||||
--hash=sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74 \
|
||||
--hash=sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55
|
||||
# via cloudflare
|
||||
jsonpickle==3.3.0 \
|
||||
--hash=sha256:287c12143f35571ab00e224fa323aa4b090d5a7f086f5f494d7ee9c7eb1a380a \
|
||||
--hash=sha256:ab467e601e5b1a1cd76f1819d014795165da071744ef30bf3786e9bc549de25a
|
||||
# via python-digitalocean
|
||||
mock==5.1.0 \
|
||||
--hash=sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744 \
|
||||
--hash=sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d
|
||||
# via certbot-dns-scaleway
|
||||
parsedatetime==2.6 \
|
||||
--hash=sha256:4cb368fbb18a0b7231f4d76119165451c8d2e35951455dfee97c62a87b04d455 \
|
||||
--hash=sha256:cb96edd7016872f58479e35879294258c71437195760746faffedb692aef000b
|
||||
# via certbot
|
||||
proto-plus==1.24.0 \
|
||||
--hash=sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445 \
|
||||
--hash=sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12
|
||||
# via google-api-core
|
||||
protobuf==5.28.2 \
|
||||
--hash=sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132 \
|
||||
--hash=sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f \
|
||||
--hash=sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece \
|
||||
--hash=sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0 \
|
||||
--hash=sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f \
|
||||
--hash=sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0 \
|
||||
--hash=sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276 \
|
||||
--hash=sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7 \
|
||||
--hash=sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3 \
|
||||
--hash=sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36 \
|
||||
--hash=sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d
|
||||
# via
|
||||
# google-api-core
|
||||
# googleapis-common-protos
|
||||
# proto-plus
|
||||
pyasn1==0.6.1 \
|
||||
--hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \
|
||||
--hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034
|
||||
# via
|
||||
# pyasn1-modules
|
||||
# rsa
|
||||
pyasn1-modules==0.4.1 \
|
||||
--hash=sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd \
|
||||
--hash=sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c
|
||||
# via google-auth
|
||||
pycparser==2.22 \
|
||||
--hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
|
||||
--hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
|
||||
# via cffi
|
||||
pydantic==2.9.2 \
|
||||
--hash=sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f \
|
||||
--hash=sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12
|
||||
# via -r requirements.in
|
||||
pydantic-core==2.23.4 \
|
||||
--hash=sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36 \
|
||||
--hash=sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05 \
|
||||
--hash=sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071 \
|
||||
--hash=sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327 \
|
||||
--hash=sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c \
|
||||
--hash=sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36 \
|
||||
--hash=sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29 \
|
||||
--hash=sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744 \
|
||||
--hash=sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d \
|
||||
--hash=sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec \
|
||||
--hash=sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e \
|
||||
--hash=sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e \
|
||||
--hash=sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577 \
|
||||
--hash=sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232 \
|
||||
--hash=sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863 \
|
||||
--hash=sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6 \
|
||||
--hash=sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368 \
|
||||
--hash=sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480 \
|
||||
--hash=sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2 \
|
||||
--hash=sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2 \
|
||||
--hash=sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6 \
|
||||
--hash=sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769 \
|
||||
--hash=sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d \
|
||||
--hash=sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2 \
|
||||
--hash=sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84 \
|
||||
--hash=sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166 \
|
||||
--hash=sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271 \
|
||||
--hash=sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5 \
|
||||
--hash=sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb \
|
||||
--hash=sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13 \
|
||||
--hash=sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323 \
|
||||
--hash=sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556 \
|
||||
--hash=sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665 \
|
||||
--hash=sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef \
|
||||
--hash=sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb \
|
||||
--hash=sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119 \
|
||||
--hash=sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126 \
|
||||
--hash=sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510 \
|
||||
--hash=sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b \
|
||||
--hash=sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87 \
|
||||
--hash=sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f \
|
||||
--hash=sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc \
|
||||
--hash=sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8 \
|
||||
--hash=sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21 \
|
||||
--hash=sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f \
|
||||
--hash=sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6 \
|
||||
--hash=sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658 \
|
||||
--hash=sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b \
|
||||
--hash=sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3 \
|
||||
--hash=sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb \
|
||||
--hash=sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59 \
|
||||
--hash=sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24 \
|
||||
--hash=sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9 \
|
||||
--hash=sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3 \
|
||||
--hash=sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd \
|
||||
--hash=sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753 \
|
||||
--hash=sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55 \
|
||||
--hash=sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad \
|
||||
--hash=sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a \
|
||||
--hash=sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605 \
|
||||
--hash=sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e \
|
||||
--hash=sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b \
|
||||
--hash=sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433 \
|
||||
--hash=sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8 \
|
||||
--hash=sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07 \
|
||||
--hash=sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728 \
|
||||
--hash=sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0 \
|
||||
--hash=sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327 \
|
||||
--hash=sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555 \
|
||||
--hash=sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64 \
|
||||
--hash=sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6 \
|
||||
--hash=sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea \
|
||||
--hash=sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b \
|
||||
--hash=sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df \
|
||||
--hash=sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e \
|
||||
--hash=sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd \
|
||||
--hash=sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068 \
|
||||
--hash=sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3 \
|
||||
--hash=sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040 \
|
||||
--hash=sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12 \
|
||||
--hash=sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916 \
|
||||
--hash=sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f \
|
||||
--hash=sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f \
|
||||
--hash=sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801 \
|
||||
--hash=sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231 \
|
||||
--hash=sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5 \
|
||||
--hash=sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8 \
|
||||
--hash=sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee \
|
||||
--hash=sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607
|
||||
# via pydantic
|
||||
pyopenssl==24.2.1 \
|
||||
--hash=sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95 \
|
||||
--hash=sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d
|
||||
# via
|
||||
# acme
|
||||
# josepy
|
||||
pyotp==2.9.0 \
|
||||
--hash=sha256:346b6642e0dbdde3b4ff5a930b664ca82abfa116356ed48cc42c7d6590d36f63 \
|
||||
--hash=sha256:81c2e5865b8ac55e825b0358e496e1d9387c811e85bb40e71a3b29b288963612
|
||||
# via dns-lexicon
|
||||
pyparsing==3.1.4 \
|
||||
--hash=sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c \
|
||||
--hash=sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032
|
||||
# via httplib2
|
||||
pyrfc3339==1.1 \
|
||||
--hash=sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4 \
|
||||
--hash=sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
python-dateutil==2.9.0.post0 \
|
||||
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
|
||||
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
|
||||
# via botocore
|
||||
python-digitalocean==1.17.0 \
|
||||
--hash=sha256:0032168e022e85fca314eb3f8dfaabf82087f2ed40839eb28f1eeeeca5afb1fa \
|
||||
--hash=sha256:107854fde1aafa21774e8053cf253b04173613c94531f75d5a039ad770562b24
|
||||
# via certbot-dns-digitalocean
|
||||
pytz==2024.2 \
|
||||
--hash=sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a \
|
||||
--hash=sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
# pyrfc3339
|
||||
pyyaml==6.0.2 \
|
||||
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
|
||||
--hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
|
||||
--hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
|
||||
--hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
|
||||
--hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
|
||||
--hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
|
||||
--hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
|
||||
--hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
|
||||
--hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
|
||||
--hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
|
||||
--hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
|
||||
--hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
|
||||
--hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
|
||||
--hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
|
||||
--hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
|
||||
--hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
|
||||
--hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
|
||||
--hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
|
||||
--hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
|
||||
--hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
|
||||
--hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
|
||||
--hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
|
||||
--hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
|
||||
--hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
|
||||
--hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
|
||||
--hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
|
||||
--hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
|
||||
--hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
|
||||
--hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
|
||||
--hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
|
||||
--hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
|
||||
--hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
|
||||
--hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
|
||||
--hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
|
||||
--hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
|
||||
--hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
|
||||
--hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
|
||||
--hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
|
||||
--hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
|
||||
--hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
|
||||
--hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
|
||||
--hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
|
||||
--hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
|
||||
--hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
|
||||
--hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
|
||||
--hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
|
||||
--hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
|
||||
--hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
|
||||
--hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
|
||||
--hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
|
||||
--hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
|
||||
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
|
||||
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
|
||||
# via
|
||||
# cloudflare
|
||||
# dns-lexicon
|
||||
requests==2.32.3 \
|
||||
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
|
||||
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
|
||||
# via
|
||||
# acme
|
||||
# certbot-dns-scaleway
|
||||
# cloudflare
|
||||
# dns-lexicon
|
||||
# google-api-core
|
||||
# python-digitalocean
|
||||
# requests-file
|
||||
# requests-mock
|
||||
# tldextract
|
||||
requests-file==2.1.0 \
|
||||
--hash=sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658 \
|
||||
--hash=sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c
|
||||
# via tldextract
|
||||
requests-mock==1.12.1 \
|
||||
--hash=sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563 \
|
||||
--hash=sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401
|
||||
# via certbot-dns-scaleway
|
||||
rsa==4.9 \
|
||||
--hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \
|
||||
--hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21
|
||||
# via google-auth
|
||||
s3transfer==0.10.2 \
|
||||
--hash=sha256:0711534e9356d3cc692fdde846b4a1e4b0cb6519971860796e6bc4c7aea00ef6 \
|
||||
--hash=sha256:eca1c20de70a39daee580aef4986996620f365c4e0fda6a86100231d62f1bf69
|
||||
# via importlib-metadata
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
setuptools==75.1.0 \
|
||||
--hash=sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2 \
|
||||
--hash=sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538
|
||||
# via boto3
|
||||
six==1.16.0 \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
|
||||
# via python-dateutil
|
||||
soupsieve==2.6 \
|
||||
--hash=sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb \
|
||||
--hash=sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9
|
||||
# via beautifulsoup4
|
||||
tldextract==5.1.2 \
|
||||
--hash=sha256:4dfc4c277b6b97fa053899fcdb892d2dc27295851ab5fac4e07797b6a21b2e46 \
|
||||
--hash=sha256:c9e17f756f05afb5abac04fe8f766e7e70f9fe387adb1859f0f52408ee060200
|
||||
# via dns-lexicon
|
||||
typing-extensions==4.12.2 \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
|
||||
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
|
||||
# via
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
uritemplate==4.1.1 \
|
||||
--hash=sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0 \
|
||||
--hash=sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e
|
||||
# via google-api-python-client
|
||||
urllib3==1.26.20 \
|
||||
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
|
||||
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
|
||||
# via
|
||||
# botocore
|
||||
# requests
|
||||
zipp==3.20.2 \
|
||||
--hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \
|
||||
--hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
# certbot-dns-cloudflare
|
||||
# certbot-dns-digitalocean
|
||||
# certbot-dns-google
|
||||
# certbot-dns-linode
|
||||
# certbot-dns-ovh
|
||||
# certbot-dns-rfc2136
|
||||
# certbot-dns-route53
|
||||
# certbot-dns-scaleway
|
||||
|
|
@ -4,3 +4,4 @@ python-dotenv==1.0.1
|
|||
pytz==2024.2
|
||||
redis==5.2.0
|
||||
requests==2.32.3
|
||||
urllib3<2.0.0
|
||||
|
|
|
|||
|
|
@ -210,7 +210,9 @@ requests==2.32.3 \
|
|||
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
|
||||
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
|
||||
# via -r requirements.in
|
||||
urllib3==2.2.3 \
|
||||
--hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
|
||||
--hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9
|
||||
# via requests
|
||||
urllib3==1.26.20 \
|
||||
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
|
||||
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
|
||||
# via
|
||||
# -r requirements.in
|
||||
# requests
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ pip-tools==7.4.1
|
|||
pip-upgrader==1.4.15
|
||||
setuptools==75.3.0
|
||||
tomli==2.0.2
|
||||
wheel==0.44.0
|
||||
urllib3<2.0.0
|
||||
wheel==0.45.0
|
||||
|
|
|
|||
|
|
@ -197,19 +197,21 @@ toposort==1.10 \
|
|||
--hash=sha256:bfbb479c53d0a696ea7402601f4e693c97b0367837c8898bc6471adfca37a6bd \
|
||||
--hash=sha256:cbdbc0d0bee4d2695ab2ceec97fe0679e9c10eab4b2a87a9372b929e70563a87
|
||||
# via pip-compile-multi
|
||||
urllib3==2.2.3 \
|
||||
--hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
|
||||
--hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9
|
||||
# via requests
|
||||
wheel==0.44.0 \
|
||||
--hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \
|
||||
--hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49
|
||||
urllib3==1.26.20 \
|
||||
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
|
||||
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
|
||||
# via
|
||||
# -r requirements-deps.in
|
||||
# requests
|
||||
wheel==0.45.0 \
|
||||
--hash=sha256:52f0baa5e6522155090a09c6bd95718cc46956d1b51d537ea5454249edb671c7 \
|
||||
--hash=sha256:a57353941a3183b3d5365346b567a260a0602a0f8a635926a7dede41b94c674a
|
||||
# via
|
||||
# -r requirements-deps.in
|
||||
# pip-tools
|
||||
zipp==3.20.2 \
|
||||
--hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \
|
||||
--hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29
|
||||
zipp==3.21.0 \
|
||||
--hash=sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4 \
|
||||
--hash=sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931
|
||||
# via
|
||||
# -r requirements-deps.in
|
||||
# pip-tools
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
pip==24.3.1
|
||||
pip-tools==7.4.1
|
||||
setuptools==75.3.0
|
||||
wheel==0.44.0
|
||||
urllib3<2.0.0
|
||||
wheel==0.45.0
|
||||
|
|
|
|||
|
|
@ -48,15 +48,19 @@ tomli==2.0.2 \
|
|||
# via
|
||||
# build
|
||||
# pip-tools
|
||||
wheel==0.44.0 \
|
||||
--hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \
|
||||
--hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49
|
||||
urllib3==1.26.20 \
|
||||
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
|
||||
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
|
||||
# via -r requirements.in
|
||||
wheel==0.45.0 \
|
||||
--hash=sha256:52f0baa5e6522155090a09c6bd95718cc46956d1b51d537ea5454249edb671c7 \
|
||||
--hash=sha256:a57353941a3183b3d5365346b567a260a0602a0f8a635926a7dede41b94c674a
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pip-tools
|
||||
zipp==3.20.2 \
|
||||
--hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \
|
||||
--hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29
|
||||
zipp==3.21.0 \
|
||||
--hash=sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4 \
|
||||
--hash=sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pip-tools
|
||||
|
|
|
|||
|
|
@ -1,6 +1,21 @@
|
|||
certbot==3.0.0
|
||||
certbot-dns-cloudflare==3.0.0
|
||||
certbot-dns-digitalocean==3.0.0
|
||||
certbot-dns-dnsimple==3.0.0
|
||||
certbot-dns-dnsmadeeasy==3.0.0
|
||||
certbot-dns-gehirn==3.0.0
|
||||
certbot-dns-google==3.0.0
|
||||
certbot-dns-linode==3.0.0
|
||||
certbot-dns-luadns==3.0.0
|
||||
certbot-dns-nsone==3.0.0
|
||||
certbot-dns-ovh==3.0.0
|
||||
certbot-dns-rfc2136==3.0.0
|
||||
certbot-dns-route53==3.0.0
|
||||
certbot-dns-sakuracloud==3.0.0
|
||||
certbot-dns-scaleway==0.0.7
|
||||
cryptography==43.0.3
|
||||
maxminddb==2.6.2
|
||||
pydantic==2.9.2
|
||||
python-magic==0.4.27
|
||||
requests==2.32.3
|
||||
schedule==1.2.2
|
||||
|
|
|
|||
|
|
@ -7,10 +7,122 @@
|
|||
acme==3.0.0 \
|
||||
--hash=sha256:2bb48ad117b4190600e9fb5c372e3b658cd320bc445d5dba493be20db490d9e4 \
|
||||
--hash=sha256:e8ebff13b8ae85d2a700466a01c01ce68da72cec8e82031fd94b9aeb4966c0f1
|
||||
# via certbot
|
||||
# via
|
||||
# certbot
|
||||
# certbot-dns-cloudflare
|
||||
# certbot-dns-digitalocean
|
||||
# certbot-dns-dnsimple
|
||||
# certbot-dns-dnsmadeeasy
|
||||
# certbot-dns-gehirn
|
||||
# certbot-dns-google
|
||||
# certbot-dns-linode
|
||||
# certbot-dns-luadns
|
||||
# certbot-dns-nsone
|
||||
# certbot-dns-ovh
|
||||
# certbot-dns-rfc2136
|
||||
# certbot-dns-route53
|
||||
# certbot-dns-sakuracloud
|
||||
# certbot-dns-scaleway
|
||||
annotated-types==0.7.0 \
|
||||
--hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \
|
||||
--hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89
|
||||
# via pydantic
|
||||
attrs==24.2.0 \
|
||||
--hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \
|
||||
--hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2
|
||||
# via jsonlines
|
||||
beautifulsoup4==4.12.3 \
|
||||
--hash=sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051 \
|
||||
--hash=sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed
|
||||
# via dns-lexicon
|
||||
boto3==1.35.57 \
|
||||
--hash=sha256:9edf49640c79a05b0a72f4c2d1e24dfc164344b680535a645f455ac624dc3680 \
|
||||
--hash=sha256:db58348849a5af061f0f5ec9c3b699da5221ca83354059fdccb798e3ddb6b62a
|
||||
# via certbot-dns-route53
|
||||
botocore==1.35.57 \
|
||||
--hash=sha256:92ddd02469213766872cb2399269dd20948f90348b42bf08379881d5e946cc34 \
|
||||
--hash=sha256:d96306558085baf0bcb3b022d7a8c39c93494f031edb376694d2b2dcd0e81327
|
||||
# via
|
||||
# boto3
|
||||
# s3transfer
|
||||
cachetools==5.5.0 \
|
||||
--hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \
|
||||
--hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a
|
||||
# via google-auth
|
||||
certbot==3.0.0 \
|
||||
--hash=sha256:18b01f12b6278d19bad416fb9435a7e192b37a5081528473031193502f920c97 \
|
||||
--hash=sha256:480014af69ac507c1ff4b892d23159a98d8d2ff3f70aedc3e6e56cf170878f5b
|
||||
# via
|
||||
# -r requirements.in
|
||||
# certbot-dns-cloudflare
|
||||
# certbot-dns-digitalocean
|
||||
# certbot-dns-dnsimple
|
||||
# certbot-dns-dnsmadeeasy
|
||||
# certbot-dns-gehirn
|
||||
# certbot-dns-google
|
||||
# certbot-dns-linode
|
||||
# certbot-dns-luadns
|
||||
# certbot-dns-nsone
|
||||
# certbot-dns-ovh
|
||||
# certbot-dns-rfc2136
|
||||
# certbot-dns-route53
|
||||
# certbot-dns-sakuracloud
|
||||
# certbot-dns-scaleway
|
||||
certbot-dns-cloudflare==3.0.0 \
|
||||
--hash=sha256:5a2540997a676e7bbb426953734d29dddb4cee8940b85fa4c5c019b88647f995 \
|
||||
--hash=sha256:e7b008c9631c44d1124905ff9e6027ee7ea265b2140b37d245d1a321c9d2c686
|
||||
# via -r requirements.in
|
||||
certbot-dns-digitalocean==3.0.0 \
|
||||
--hash=sha256:17eaee8033686a85414ee248c705402cd5c80c9b2bb2fd27019e08852dce618d \
|
||||
--hash=sha256:56febedb1ec16338fb72444c2a56ebaeabccb624c32dad97fc4b7070d79d07ac
|
||||
# via -r requirements.in
|
||||
certbot-dns-dnsimple==3.0.0 \
|
||||
--hash=sha256:4bb52867286741c46298aa2b49d1e45141c8b674fd4ddf7d6561b3c464e043b9 \
|
||||
--hash=sha256:e8609d66614dab0a70a464a4f3a1f886863435bbdf50039651b4c42c5d80ba8b
|
||||
# via -r requirements.in
|
||||
certbot-dns-dnsmadeeasy==3.0.0 \
|
||||
--hash=sha256:9753c369e779eb15465915d215a536e47cc5de124a554a8bba6e33dfc7324b7c \
|
||||
--hash=sha256:9d986c6a2bbde6c42c24eb3cdfa7791194ef88a71dcf4e5f22e3907017b8d0ae
|
||||
# via -r requirements.in
|
||||
certbot-dns-gehirn==3.0.0 \
|
||||
--hash=sha256:b1ee3a12cc70afeeb060ce9e34f367b3b4d3b211a7ca1e60a04865c75598edbd \
|
||||
--hash=sha256:f612e2a5f5e9327365272e88aa8eb9a544d2dc4d0869bc628296addc678ad782
|
||||
# via -r requirements.in
|
||||
certbot-dns-google==3.0.0 \
|
||||
--hash=sha256:12a3bfce2f1a18406a6e333c46420a7a33c0fe0e23c49017a0cc6c9bc305e84d \
|
||||
--hash=sha256:b2206dfaaa7bc34b64f9f3876ff0844bd96c7c702564c61456a2f3b21ac78786
|
||||
# via -r requirements.in
|
||||
certbot-dns-linode==3.0.0 \
|
||||
--hash=sha256:32bbbc54aee72b1a0d075f4c18cda5a5e54d4f672a8e060f825c04ec332ab179 \
|
||||
--hash=sha256:d0c43c01542b1b49b147055204a6950da972268d173734c6cbfe4ac679f9bfa8
|
||||
# via -r requirements.in
|
||||
certbot-dns-luadns==3.0.0 \
|
||||
--hash=sha256:0895a1f8a5dc2cbcd4793a8f4fa8fa3d42494b8402216500dbbfdc3287bc538f \
|
||||
--hash=sha256:fc914ad69b4d16f637153b80a40afc559354189899b45329c0d993dab8f84b1e
|
||||
# via -r requirements.in
|
||||
certbot-dns-nsone==3.0.0 \
|
||||
--hash=sha256:11831fe028355e7d5b7ad7ef51c268afbc3d316c330e586fcb8af05a61efba81 \
|
||||
--hash=sha256:504c167bccaa746d74ae3c078e5e2cbc7d4998ddb0074f3829e930239f1745b0
|
||||
# via -r requirements.in
|
||||
certbot-dns-ovh==3.0.0 \
|
||||
--hash=sha256:49e05c4a23acbc1bd6ed82e6d9fec2b1e9190ee89480ec975b0535b479894015 \
|
||||
--hash=sha256:7f115b2428c6e68aa35b29220ceeebdd2d3ac28131e361edaf6589c46203d45c
|
||||
# via -r requirements.in
|
||||
certbot-dns-rfc2136==3.0.0 \
|
||||
--hash=sha256:08219637dac3b8c9215e76855ddd98fde7bc2f100fb3c89c49bf11c530abb0c5 \
|
||||
--hash=sha256:140b02d5a2db3fef9c88d00c59a39ef34b8447b83228db17224f3a5d54c518aa
|
||||
# via -r requirements.in
|
||||
certbot-dns-route53==3.0.0 \
|
||||
--hash=sha256:4d234462963ed56a4161bda039c24bd8c984e999f42c26fb2e18263416220ecf \
|
||||
--hash=sha256:ea47ff36513478e28c590276c3605b4f592d9b9938d36f2231484c63b38249e2
|
||||
# via -r requirements.in
|
||||
certbot-dns-sakuracloud==3.0.0 \
|
||||
--hash=sha256:635eac33dc3c99e2eabb0ab124007d23559d54ac7bfd473712b917ee7e17f07e \
|
||||
--hash=sha256:65cea7b612fe62badfe3fe0ded67cb2a6613ced68bbdc55a5c55c722e8cc6844
|
||||
# via -r requirements.in
|
||||
certbot-dns-scaleway==0.0.7 \
|
||||
--hash=sha256:999dda5b8689277facb77e1757b8a6b207baeecc0ded0c27aea2c51331affc92 \
|
||||
--hash=sha256:bc0833ed71a5cd314a93f8d02144c54e17066ee1e2b950ed868b14b2211f5e9e
|
||||
# via -r requirements.in
|
||||
certifi==2024.8.30 \
|
||||
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
|
||||
|
|
@ -192,6 +304,9 @@ charset-normalizer==3.4.0 \
|
|||
--hash=sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079 \
|
||||
--hash=sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482
|
||||
# via requests
|
||||
cloudflare==2.19.4 \
|
||||
--hash=sha256:3b6000a01a237c23bccfdf6d20256ea5111ec74a826ae9e74f9f0e5bb5b2383f
|
||||
# via certbot-dns-cloudflare
|
||||
configargparse==1.7 \
|
||||
--hash=sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b \
|
||||
--hash=sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1
|
||||
|
|
@ -231,26 +346,97 @@ cryptography==43.0.3 \
|
|||
# -r requirements.in
|
||||
# acme
|
||||
# certbot
|
||||
# dns-lexicon
|
||||
# josepy
|
||||
# pyopenssl
|
||||
distro==1.9.0 \
|
||||
--hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \
|
||||
--hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2
|
||||
# via certbot
|
||||
dns-lexicon==3.18.0 \
|
||||
--hash=sha256:aabe320093b4f9a7f7e0e430551ae49c38c9cf99b45ef7e28da238c50106b1a0 \
|
||||
--hash=sha256:c2b1005a6621a2ec648131d96ec61304b90b98842af9ff62b1840ddf9d0e2c26
|
||||
# via
|
||||
# certbot-dns-dnsimple
|
||||
# certbot-dns-dnsmadeeasy
|
||||
# certbot-dns-gehirn
|
||||
# certbot-dns-linode
|
||||
# certbot-dns-luadns
|
||||
# certbot-dns-nsone
|
||||
# certbot-dns-ovh
|
||||
# certbot-dns-sakuracloud
|
||||
dnspython==2.7.0 \
|
||||
--hash=sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86 \
|
||||
--hash=sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1
|
||||
# via
|
||||
# certbot-dns-rfc2136
|
||||
# dns-lexicon
|
||||
filelock==3.16.1 \
|
||||
--hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \
|
||||
--hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435
|
||||
# via tldextract
|
||||
google-api-core==2.22.0 \
|
||||
--hash=sha256:26f8d76b96477db42b55fd02a33aae4a42ec8b86b98b94969b7333a2c828bf35 \
|
||||
--hash=sha256:a6652b6bd51303902494998626653671703c420f6f4c88cfd3f50ed723e9d021
|
||||
# via google-api-python-client
|
||||
google-api-python-client==2.151.0 \
|
||||
--hash=sha256:4427b2f47cd88b0355d540c2c52215f68c337f3bc9d6aae1ceeae4525977504c \
|
||||
--hash=sha256:a9d26d630810ed4631aea21d1de3e42072f98240aaf184a8a1a874a371115034
|
||||
# via certbot-dns-google
|
||||
google-auth==2.36.0 \
|
||||
--hash=sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb \
|
||||
--hash=sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1
|
||||
# via
|
||||
# certbot-dns-google
|
||||
# google-api-core
|
||||
# google-api-python-client
|
||||
# google-auth-httplib2
|
||||
google-auth-httplib2==0.2.0 \
|
||||
--hash=sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05 \
|
||||
--hash=sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d
|
||||
# via google-api-python-client
|
||||
googleapis-common-protos==1.65.0 \
|
||||
--hash=sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63 \
|
||||
--hash=sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0
|
||||
# via google-api-core
|
||||
httplib2==0.22.0 \
|
||||
--hash=sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc \
|
||||
--hash=sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81
|
||||
# via
|
||||
# google-api-python-client
|
||||
# google-auth-httplib2
|
||||
idna==3.10 \
|
||||
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
|
||||
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
|
||||
# via requests
|
||||
# via
|
||||
# requests
|
||||
# tldextract
|
||||
importlib-metadata==8.5.0 \
|
||||
--hash=sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b \
|
||||
--hash=sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7
|
||||
# via certbot
|
||||
# via
|
||||
# certbot
|
||||
# dns-lexicon
|
||||
jmespath==1.0.1 \
|
||||
--hash=sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 \
|
||||
--hash=sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe
|
||||
# via
|
||||
# boto3
|
||||
# botocore
|
||||
josepy==1.14.0 \
|
||||
--hash=sha256:308b3bf9ce825ad4d4bba76372cf19b5dc1c2ce96a9d298f9642975e64bd13dd \
|
||||
--hash=sha256:d2b36a30f316269f3242f4c2e45e15890784178af5ec54fa3e49cf9234ee22e0
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
jsonlines==4.0.0 \
|
||||
--hash=sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74 \
|
||||
--hash=sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55
|
||||
# via cloudflare
|
||||
jsonpickle==3.4.2 \
|
||||
--hash=sha256:2efa2778859b6397d5804b0a98d52cd2a7d9a70fcb873bc5a3ca5acca8f499ba \
|
||||
--hash=sha256:fd6c273278a02b3b66e3405db3dd2f4dbc8f4a4a3123bfcab3045177c6feb9c3
|
||||
# via python-digitalocean
|
||||
maxminddb==2.6.2 \
|
||||
--hash=sha256:058ca89789bc1770fe58d02a88272ca91dabeef9f3fe0011fe506484355f1804 \
|
||||
--hash=sha256:05e873eb82281cef6e787bd40bd1d58b2e496a21b3689346f0d0420988b3cbb1 \
|
||||
|
|
@ -322,26 +508,171 @@ maxminddb==2.6.2 \
|
|||
--hash=sha256:f412a54f87ef9083911c334267188d3d1b14f2591eac94b94ca32528f21d5f25 \
|
||||
--hash=sha256:fb38aa94e76a87785b654c035f9f3ee39b74a98e9beea9a10b1aa62abdcc4cbd
|
||||
# via -r requirements.in
|
||||
mock==5.1.0 \
|
||||
--hash=sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744 \
|
||||
--hash=sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d
|
||||
# via certbot-dns-scaleway
|
||||
parsedatetime==2.6 \
|
||||
--hash=sha256:4cb368fbb18a0b7231f4d76119165451c8d2e35951455dfee97c62a87b04d455 \
|
||||
--hash=sha256:cb96edd7016872f58479e35879294258c71437195760746faffedb692aef000b
|
||||
# via certbot
|
||||
proto-plus==1.25.0 \
|
||||
--hash=sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961 \
|
||||
--hash=sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91
|
||||
# via google-api-core
|
||||
protobuf==5.28.3 \
|
||||
--hash=sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24 \
|
||||
--hash=sha256:135658402f71bbd49500322c0f736145731b16fc79dc8f367ab544a17eab4535 \
|
||||
--hash=sha256:27b246b3723692bf1068d5734ddaf2fccc2cdd6e0c9b47fe099244d80200593b \
|
||||
--hash=sha256:3e6101d095dfd119513cde7259aa703d16c6bbdfae2554dfe5cfdbe94e32d548 \
|
||||
--hash=sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584 \
|
||||
--hash=sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b \
|
||||
--hash=sha256:70585a70fc2dd4818c51287ceef5bdba6387f88a578c86d47bb34669b5552c36 \
|
||||
--hash=sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135 \
|
||||
--hash=sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868 \
|
||||
--hash=sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687 \
|
||||
--hash=sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed
|
||||
# via
|
||||
# google-api-core
|
||||
# googleapis-common-protos
|
||||
# proto-plus
|
||||
pyasn1==0.6.1 \
|
||||
--hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \
|
||||
--hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034
|
||||
# via
|
||||
# pyasn1-modules
|
||||
# rsa
|
||||
pyasn1-modules==0.4.1 \
|
||||
--hash=sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd \
|
||||
--hash=sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c
|
||||
# via google-auth
|
||||
pycparser==2.22 \
|
||||
--hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
|
||||
--hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
|
||||
# via cffi
|
||||
pydantic==2.9.2 \
|
||||
--hash=sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f \
|
||||
--hash=sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12
|
||||
# via -r requirements.in
|
||||
pydantic-core==2.23.4 \
|
||||
--hash=sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36 \
|
||||
--hash=sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05 \
|
||||
--hash=sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071 \
|
||||
--hash=sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327 \
|
||||
--hash=sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c \
|
||||
--hash=sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36 \
|
||||
--hash=sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29 \
|
||||
--hash=sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744 \
|
||||
--hash=sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d \
|
||||
--hash=sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec \
|
||||
--hash=sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e \
|
||||
--hash=sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e \
|
||||
--hash=sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577 \
|
||||
--hash=sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232 \
|
||||
--hash=sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863 \
|
||||
--hash=sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6 \
|
||||
--hash=sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368 \
|
||||
--hash=sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480 \
|
||||
--hash=sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2 \
|
||||
--hash=sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2 \
|
||||
--hash=sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6 \
|
||||
--hash=sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769 \
|
||||
--hash=sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d \
|
||||
--hash=sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2 \
|
||||
--hash=sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84 \
|
||||
--hash=sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166 \
|
||||
--hash=sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271 \
|
||||
--hash=sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5 \
|
||||
--hash=sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb \
|
||||
--hash=sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13 \
|
||||
--hash=sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323 \
|
||||
--hash=sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556 \
|
||||
--hash=sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665 \
|
||||
--hash=sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef \
|
||||
--hash=sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb \
|
||||
--hash=sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119 \
|
||||
--hash=sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126 \
|
||||
--hash=sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510 \
|
||||
--hash=sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b \
|
||||
--hash=sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87 \
|
||||
--hash=sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f \
|
||||
--hash=sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc \
|
||||
--hash=sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8 \
|
||||
--hash=sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21 \
|
||||
--hash=sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f \
|
||||
--hash=sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6 \
|
||||
--hash=sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658 \
|
||||
--hash=sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b \
|
||||
--hash=sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3 \
|
||||
--hash=sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb \
|
||||
--hash=sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59 \
|
||||
--hash=sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24 \
|
||||
--hash=sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9 \
|
||||
--hash=sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3 \
|
||||
--hash=sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd \
|
||||
--hash=sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753 \
|
||||
--hash=sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55 \
|
||||
--hash=sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad \
|
||||
--hash=sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a \
|
||||
--hash=sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605 \
|
||||
--hash=sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e \
|
||||
--hash=sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b \
|
||||
--hash=sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433 \
|
||||
--hash=sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8 \
|
||||
--hash=sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07 \
|
||||
--hash=sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728 \
|
||||
--hash=sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0 \
|
||||
--hash=sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327 \
|
||||
--hash=sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555 \
|
||||
--hash=sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64 \
|
||||
--hash=sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6 \
|
||||
--hash=sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea \
|
||||
--hash=sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b \
|
||||
--hash=sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df \
|
||||
--hash=sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e \
|
||||
--hash=sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd \
|
||||
--hash=sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068 \
|
||||
--hash=sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3 \
|
||||
--hash=sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040 \
|
||||
--hash=sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12 \
|
||||
--hash=sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916 \
|
||||
--hash=sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f \
|
||||
--hash=sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f \
|
||||
--hash=sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801 \
|
||||
--hash=sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231 \
|
||||
--hash=sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5 \
|
||||
--hash=sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8 \
|
||||
--hash=sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee \
|
||||
--hash=sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607
|
||||
# via pydantic
|
||||
pyopenssl==24.2.1 \
|
||||
--hash=sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95 \
|
||||
--hash=sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d
|
||||
# via
|
||||
# acme
|
||||
# josepy
|
||||
pyotp==2.9.0 \
|
||||
--hash=sha256:346b6642e0dbdde3b4ff5a930b664ca82abfa116356ed48cc42c7d6590d36f63 \
|
||||
--hash=sha256:81c2e5865b8ac55e825b0358e496e1d9387c811e85bb40e71a3b29b288963612
|
||||
# via dns-lexicon
|
||||
pyparsing==3.2.0 \
|
||||
--hash=sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84 \
|
||||
--hash=sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c
|
||||
# via httplib2
|
||||
pyrfc3339==2.0.1 \
|
||||
--hash=sha256:30b70a366acac3df7386b558c21af871522560ed7f3f73cf344b8c2cbb8b0c9d \
|
||||
--hash=sha256:e47843379ea35c1296c3b6c67a948a1a490ae0584edfcbdea0eaffb5dd29960b
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
python-dateutil==2.9.0.post0 \
|
||||
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
|
||||
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
|
||||
# via botocore
|
||||
python-digitalocean==1.17.0 \
|
||||
--hash=sha256:0032168e022e85fca314eb3f8dfaabf82087f2ed40839eb28f1eeeeca5afb1fa \
|
||||
--hash=sha256:107854fde1aafa21774e8053cf253b04173613c94531f75d5a039ad770562b24
|
||||
# via certbot-dns-digitalocean
|
||||
python-magic==0.4.27 \
|
||||
--hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \
|
||||
--hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3
|
||||
|
|
@ -352,12 +683,93 @@ pytz==2024.2 \
|
|||
# via
|
||||
# acme
|
||||
# certbot
|
||||
pyyaml==6.0.2 \
|
||||
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
|
||||
--hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
|
||||
--hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
|
||||
--hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
|
||||
--hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
|
||||
--hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
|
||||
--hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
|
||||
--hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
|
||||
--hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
|
||||
--hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
|
||||
--hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
|
||||
--hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
|
||||
--hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
|
||||
--hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
|
||||
--hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
|
||||
--hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
|
||||
--hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
|
||||
--hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
|
||||
--hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
|
||||
--hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
|
||||
--hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
|
||||
--hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
|
||||
--hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
|
||||
--hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
|
||||
--hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
|
||||
--hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
|
||||
--hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
|
||||
--hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
|
||||
--hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
|
||||
--hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
|
||||
--hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
|
||||
--hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
|
||||
--hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
|
||||
--hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
|
||||
--hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
|
||||
--hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
|
||||
--hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
|
||||
--hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
|
||||
--hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
|
||||
--hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
|
||||
--hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
|
||||
--hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
|
||||
--hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
|
||||
--hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
|
||||
--hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
|
||||
--hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
|
||||
--hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
|
||||
--hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
|
||||
--hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
|
||||
--hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
|
||||
--hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
|
||||
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
|
||||
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
|
||||
# via
|
||||
# cloudflare
|
||||
# dns-lexicon
|
||||
requests==2.32.3 \
|
||||
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
|
||||
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
|
||||
# via
|
||||
# -r requirements.in
|
||||
# acme
|
||||
# certbot-dns-scaleway
|
||||
# cloudflare
|
||||
# dns-lexicon
|
||||
# google-api-core
|
||||
# python-digitalocean
|
||||
# requests-file
|
||||
# requests-mock
|
||||
# tldextract
|
||||
requests-file==2.1.0 \
|
||||
--hash=sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658 \
|
||||
--hash=sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c
|
||||
# via tldextract
|
||||
requests-mock==1.12.1 \
|
||||
--hash=sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563 \
|
||||
--hash=sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401
|
||||
# via certbot-dns-scaleway
|
||||
rsa==4.9 \
|
||||
--hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \
|
||||
--hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21
|
||||
# via google-auth
|
||||
s3transfer==0.10.3 \
|
||||
--hash=sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d \
|
||||
--hash=sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c
|
||||
# via boto3
|
||||
schedule==1.2.2 \
|
||||
--hash=sha256:15fe9c75fe5fd9b9627f3f19cc0ef1420508f9f9a46f45cd0769ef75ede5f0b7 \
|
||||
--hash=sha256:5bef4a2a0183abf44046ae0d164cadcac21b1db011bdd8102e4a0c1e91e06a7d
|
||||
|
|
@ -368,13 +780,51 @@ setuptools==75.3.0 \
|
|||
--hash=sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd \
|
||||
--hash=sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686
|
||||
# via -r requirements.in
|
||||
urllib3==2.2.3 \
|
||||
--hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
|
||||
--hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9
|
||||
# via requests
|
||||
zipp==3.20.2 \
|
||||
--hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \
|
||||
--hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29
|
||||
six==1.16.0 \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
|
||||
# via python-dateutil
|
||||
soupsieve==2.6 \
|
||||
--hash=sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb \
|
||||
--hash=sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9
|
||||
# via beautifulsoup4
|
||||
tldextract==5.1.3 \
|
||||
--hash=sha256:78de310cc2ca018692de5ddf320f9d6bd7c5cf857d0fd4f2175f0cdf4440ea75 \
|
||||
--hash=sha256:d43c7284c23f5dc8a42fd0fee2abede2ff74cc622674e4cb07f514ab3330c338
|
||||
# via dns-lexicon
|
||||
typing-extensions==4.12.2 \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
|
||||
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
|
||||
# via
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
uritemplate==4.1.1 \
|
||||
--hash=sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0 \
|
||||
--hash=sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e
|
||||
# via google-api-python-client
|
||||
urllib3==1.26.20 \
|
||||
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
|
||||
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
|
||||
# via
|
||||
# botocore
|
||||
# requests
|
||||
zipp==3.21.0 \
|
||||
--hash=sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4 \
|
||||
--hash=sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
# certbot-dns-cloudflare
|
||||
# certbot-dns-digitalocean
|
||||
# certbot-dns-dnsimple
|
||||
# certbot-dns-dnsmadeeasy
|
||||
# certbot-dns-gehirn
|
||||
# certbot-dns-google
|
||||
# certbot-dns-linode
|
||||
# certbot-dns-luadns
|
||||
# certbot-dns-nsone
|
||||
# certbot-dns-ovh
|
||||
# certbot-dns-rfc2136
|
||||
# certbot-dns-route53
|
||||
# certbot-dns-sakuracloud
|
||||
# certbot-dns-scaleway
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@ python_dateutil==2.9.0.post0
|
|||
qrcode==8.0
|
||||
regex==2024.11.6
|
||||
user_agents==2.2.0
|
||||
werkzeug==3.1.2
|
||||
werkzeug==3.1.3
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ beautifulsoup4==4.12.3 \
|
|||
--hash=sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051 \
|
||||
--hash=sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed
|
||||
# via -r requirements.in
|
||||
blinker==1.8.2 \
|
||||
--hash=sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01 \
|
||||
--hash=sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83
|
||||
blinker==1.9.0 \
|
||||
--hash=sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf \
|
||||
--hash=sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc
|
||||
# via
|
||||
# flask
|
||||
# flask-principal
|
||||
|
|
@ -404,9 +404,9 @@ user-agents==2.2.0 \
|
|||
--hash=sha256:a98c4dc72ecbc64812c4534108806fb0a0b3a11ec3fd1eafe807cee5b0a942e7 \
|
||||
--hash=sha256:d36d25178db65308d1458c5fa4ab39c9b2619377010130329f3955e7626ead26
|
||||
# via -r requirements.in
|
||||
werkzeug==3.1.2 \
|
||||
--hash=sha256:4f7d1a5de312c810a8a2c6f0b47e9f6a7cffb7c8322def35e4d4d9841ff85597 \
|
||||
--hash=sha256:f471a4cd167233077e9d2a8190c3471c5bc520c636a9e3c1e9300c33bced03bc
|
||||
werkzeug==3.1.3 \
|
||||
--hash=sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e \
|
||||
--hash=sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746
|
||||
# via
|
||||
# -r requirements.in
|
||||
# flask
|
||||
|
|
@ -415,9 +415,9 @@ wtforms==3.2.1 \
|
|||
--hash=sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4 \
|
||||
--hash=sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682
|
||||
# via flask-wtf
|
||||
zipp==3.20.2 \
|
||||
--hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \
|
||||
--hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29
|
||||
zipp==3.21.0 \
|
||||
--hash=sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4 \
|
||||
--hash=sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931
|
||||
# via importlib-metadata
|
||||
zope-event==5.0 \
|
||||
--hash=sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26 \
|
||||
|
|
|
|||
Loading…
Reference in a new issue