improved LE certificates checks and fix missing full SERVER_NAME when MULTISITE=no

This commit is contained in:
florian 2023-08-05 18:56:31 +02:00
parent 843c023707
commit 5df2a74caf
No known key found for this signature in database
GPG key ID: 3D80806F12602A7C
4 changed files with 52 additions and 23 deletions

View file

@ -3,12 +3,13 @@
from os import _exit, environ, getenv, sep from os import _exit, environ, getenv, sep
from os.path import join from os.path import join
from pathlib import Path from pathlib import Path
from subprocess import DEVNULL, STDOUT, run from subprocess import DEVNULL, STDOUT, run, PIPE
from sys import exit as sys_exit, path as sys_path from sys import exit as sys_exit, path as sys_path
from traceback import format_exc from traceback import format_exc
from tarfile import open as tar_open from tarfile import open as tar_open
from io import BytesIO from io import BytesIO
from shutil import rmtree from shutil import rmtree
from re import findall, MULTILINE
for deps_path in [ for deps_path in [
join(sep, "usr", "share", "bunkerweb", *paths) join(sep, "usr", "share", "bunkerweb", *paths)
@ -54,15 +55,43 @@ def certbot_new(
"--email", "--email",
email, email,
"--agree-tos", "--agree-tos",
"--expand"
] ]
+ (["--staging"] if getenv("USE_LETS_ENCRYPT_STAGING", "no") == "yes" else []), + (["--staging"] if getenv("USE_LETS_ENCRYPT_STAGING", "no") == "yes" else []),
stdin=DEVNULL, stdin=DEVNULL,
stderr=STDOUT, stderr=STDOUT,
env=environ.copy() env=environ.copy()
| {"PYTHONPATH": join(sep, "usr", "share", "bunkerweb", "deps", "python")}, | {"PYTHONPATH": join(sep, "usr", "share", "bunkerweb", "deps", "python")},
check=True,
).returncode ).returncode
def certbot_check_domains(domains: list[str], letsencrypt_path: Path) -> int:
proc = run(
[
join(sep, "usr", "share", "bunkerweb", "deps", "python", "bin", "certbot"),
"certificates",
"--config-dir",
str(letsencrypt_path.joinpath("etc")),
"--work-dir",
join(sep, "var", "lib", "bunkerweb", "letsencrypt"),
"--logs-dir",
join(sep, "var", "log", "bunkerweb"),
],
stdin=DEVNULL,
stdout=PIPE,
stderr=STDOUT,
text=True,
env=environ.copy()
)
if proc.returncode != 0:
logger.error(f"Error while checking certificates :\n{proc.stdout}")
return 2
first_needed_domain = domains[0]
needed_domains = set(domains)
for raw_domains in findall(r"^ Domains: (.*)$", proc.stdout, MULTILINE):
current_domains = raw_domains.split(" ")
if current_domains[0] == first_needed_domain and set(current_domains) == needed_domains:
return 1
return 0
status = 0 status = 0
@ -133,7 +162,7 @@ try:
logger.info("No Let's Encrypt data found in db cache") logger.info("No Let's Encrypt data found in db cache")
# Multisite case # Multisite case
if getenv("MULTISITE", "no") == "yes": if getenv("MULTISITE", "no") == "yes" and getenv("SERVER_NAME"):
for first_server in getenv("SERVER_NAME", "").split(" "): for first_server in getenv("SERVER_NAME", "").split(" "):
if ( if (
not first_server not first_server
@ -145,11 +174,8 @@ try:
): ):
continue continue
domains = getenv(f"{first_server}_SERVER_NAME", first_server).replace( domains = getenv(f"{first_server}_SERVER_NAME", first_server)
" ", "," if certbot_check_domains(domains.split(" "), letsencrypt_path) == 1:
)
if letsencrypt_path.joinpath(first_server, "cert.pem").exists():
logger.info( logger.info(
f"Certificates already exists for domain(s) {domains}", f"Certificates already exists for domain(s) {domains}",
) )
@ -166,13 +192,13 @@ try:
f"Asking certificates for domains : {domains} (email = {real_email}) ...", f"Asking certificates for domains : {domains} (email = {real_email}) ...",
) )
if ( if (
certbot_new(domains, real_email, letsencrypt_path, letsencrypt_job_path) certbot_new(domains.replace(" ", ","), real_email, letsencrypt_path, letsencrypt_job_path)
!= 0 != 0
): ):
logger.error( logger.error(
f"Certificate generation failed for domain(s) {domains} ...", f"Certificate generation failed for domain(s) {domains} ...",
) )
_exit(2) continue
else: else:
status = 1 status = 1
logger.info( logger.info(
@ -182,10 +208,12 @@ try:
# Singlesite case # Singlesite case
elif getenv("AUTO_LETS_ENCRYPT", "no") == "yes" and getenv("SERVER_NAME"): elif getenv("AUTO_LETS_ENCRYPT", "no") == "yes" and getenv("SERVER_NAME"):
first_server = getenv("SERVER_NAME", "").split(" ")[0] first_server = getenv("SERVER_NAME", "").split(" ")[0]
domains = getenv("SERVER_NAME", "").replace(" ", ",") domains = getenv("SERVER_NAME", "")
if letsencrypt_path.joinpath("etc", "live", first_server, "cert.pem").exists(): if certbot_check_domains(domains.split(" "), letsencrypt_path) == 1:
logger.info(f"Certificates already exists for domain(s) {domains}") logger.info(
f"Certificates already exists for domain(s) {domains}",
)
else: else:
real_email = getenv("EMAIL_LETS_ENCRYPT", f"contact@{first_server}") real_email = getenv("EMAIL_LETS_ENCRYPT", f"contact@{first_server}")
if not real_email: if not real_email:
@ -195,7 +223,7 @@ try:
f"Asking certificates for domain(s) : {domains} (email = {real_email}) ...", f"Asking certificates for domain(s) : {domains} (email = {real_email}) ...",
) )
if ( if (
certbot_new(domains, real_email, letsencrypt_path, letsencrypt_job_path) certbot_new(domains.replace(" ", ","), real_email, letsencrypt_path, letsencrypt_job_path)
!= 0 != 0
): ):
status = 2 status = 2

View file

@ -149,7 +149,7 @@ try:
logger.error( logger.error(
f"Certificates renewal for {first_server} failed", f"Certificates renewal for {first_server} failed",
) )
elif getenv("AUTO_LETS_ENCRYPT", "no") == "yes" and not getenv("SERVER_NAME", ""): elif getenv("AUTO_LETS_ENCRYPT", "no") == "yes" and getenv("SERVER_NAME", ""):
first_server = getenv("SERVER_NAME", "").split(" ")[0] first_server = getenv("SERVER_NAME", "").split(" ")[0]
if letsencrypt_path.joinpath("etc", "live", first_server, "cert.pem").exists(): if letsencrypt_path.joinpath("etc", "live", first_server, "cert.pem").exists():
if renew(first_server, letsencrypt_path) != 0: if renew(first_server, letsencrypt_path) != 0:

View file

@ -659,7 +659,7 @@ class Database:
) )
) )
config.pop("SERVER_NAME", None) #config.pop("SERVER_NAME", None)
for key, value in config.items(): for key, value in config.items():
suffix = 0 suffix = 0
@ -921,6 +921,7 @@ class Database:
} }
) )
if config["MULTISITE"] == "yes":
servers = " ".join(service.id for service in session.query(Services).all()) servers = " ".join(service.id for service in session.query(Services).all())
config["SERVER_NAME"] = ( config["SERVER_NAME"] = (
servers servers

View file

@ -164,7 +164,7 @@ class LinuxTest(Test):
def _debug_fail(self): def _debug_fail(self):
self.docker_exec( self.docker_exec(
self.__distro, self.__distro,
"cat /var/log/nginx/access.log ; cat /var/log/nginx/error.log ; journalctl -u bunkerweb --no-pager", "cat /var/log/bunkerweb/access.log ; cat /var/log/bunkerweb/error.log ; journalctl -u bunkerweb --no-pager",
) )
@staticmethod @staticmethod