85 lines
2.6 KiB
Python
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))
|