nginx-configurator/nginx_configurator/certs.py
2023-11-04 23:08:15 +01:00

85 lines
2.6 KiB
Python

import os
import re
from pathlib import Path
from typing import List
from .templating import jinja
import glob
DOMAINS_RE = re.compile(
r"^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]{1,1}\.)*(xn--)?([a-z0-9][a-z0-9\-]{0,60}|[a-z0-9-]{1,30}\.[a-z]{2,})$"
)
def _walk_nginx_conf(nginx_conf: Path):
"""Recursively finds all configuration files of a nginx config"""
# this path is not necessarily correct... someone could have a weird prefix and
# conf file combination
conf_dir = nginx_conf.parent
stack = [nginx_conf]
while len(stack):
file = stack.pop()
conf = file.read_text()
yield file, conf
for include in re.finditer(r"(?:^|\n\s*|[{;]\s*)include (.+);", conf):
pattern = include.group(1)
for file in glob.glob(
pattern if pattern.startswith("/") else f"{conf_dir}/pattern"
):
stack.append(Path(file))
def gather_autossl_directives(nginx_conf: Path):
"""
Finds #AUTOSSL directives inside an nginx configuration. The server_name
must be on a separate line. (which it usually is)
"""
directives = []
for _, conf in _walk_nginx_conf(nginx_conf):
for directive in re.finditer(
r"(?:^|\n\s*|[{;]\s*)server_name (.*); *# *AUTOSSL *> *(\S+)", conf
):
domains, alias = directive.groups()
domains = domains.split()
if any(not re.match(DOMAINS_RE, domain) for domain in domains):
raise ValueError(
f"Cannot get SSL cert for \"{''.join(domains)}\". Invalid domains."
)
if not re.match(r"^[a-zA-Z0-9-_]$", alias):
raise ValueError(f'Invalid cert alias "{alias}"')
directives.append((domains, alias))
return directives
def get_site_files(nginx_dir: Path) -> List[Path]:
names = []
for file in (nginx_dir / "sites/auto").iterdir():
if file.is_file() and re.match(r"\d+-.+\.conf", file.name):
names.append(file)
for file in (nginx_dir / "sites/custom").iterdir():
if file.is_file() and re.match(r"\d+-.+\.conf", file.name):
names.append(file)
return names
def build_domains_txt(directives):
return (
"\n".join(" ".join(domains) + " > " + alias for domains, alias in directives)
+ "\n"
)
def generate_ssl_configs(dir: Path, cert_aliases: List[str]):
for alias in cert_aliases:
file = dir / (alias + ".conf")
template = jinja.get_template("ssl.conf")
file.write_text(template.render(alias=alias))