diff --git a/sysadmin/nginx-new-site.py b/sysadmin/nginx-new-site.py new file mode 100644 index 0000000..0c94dc9 --- /dev/null +++ b/sysadmin/nginx-new-site.py @@ -0,0 +1,140 @@ +#!/usr/bin/python3 + +from pathlib import Path + +from nginx.config.api import Section, Location, EmptyBlock, Comment, KeyValueOption, KeyMultiValueOption +from nginx.config.helpers import duplicate_options + +PROXY_OPTIONS = [ + ["Host", "$http_host"], + ["X-Real-IP", "$remote_addr"], + ["X-Forwarded-For", "$proxy_add_x_forwarded_for"], + ["X-Forwarded-Host", "$host,443"], + ["X-Forwarded-Server", "$host"], + ["X-Forwarded-Proto", "$scheme"], + ["X-Forwarded-Uri", "$request_uri"], + ["X-Forwarded-Ssl", "on"], + ["X-Real-IP", "$remote_addr"] +] + +FPM_OPTIONS = [ + ["HTTPS", "on"], + ["PATH_INFO", "$path_info"], + ["SCRIPT_FILENAME", "$document_root$fastcgi_script_name"], + ["modHeadersAvailable", "true"], + ["front_controller_active", "true"] +] + +PROXY_WEBSOCKETS_OPTIONS = [ + ["Upgrade", "$http_upgrade"], + ["Connection", "Upgrade"] + ] + + +class NginxSiteBuilder: + + def __init__(self, domain, site_number=0, root="/srv/www/htdocs", + authelia=True): + + self._filename = f"{site_number:02d}-{domain}.conf" + self._domain = domain + self._add_base_template(authelia) + self.add_logging() + + def __str__(self): + return str(self._config) + + __repr__ = __str__ + + def _add_base_template(self, authelia=True): + server = Section( + "server", + duplicate_options("listen", [["443 ssl http2"], + ["[::]:443 ssl http2"]]), + server_name=self.domain, + server_tokens="off" + ) + blocks = [["common_ssl.conf"]] + if authelia: + blocks.append(["authelia.conf"]) + includes = duplicate_options("include", blocks) + server.sections.add(includes) + + self._config = server + + @property + def filename(self): + return self._filename + + @property + def domain(self): + return self._domain + + @property + def config(self): + return self._config + + def write(self, path: Path): + destination = Path / self.filename + with destination.open("w") as handle: + handle.write(str(self._config)) + + def add_logging(self, suffix="nginx_"): + + syslog = f"syslog:server=unix:/dev/log,tag={self.domain}_nginx," + access = syslog + "severity=info,nohostname" + error = syslog + "severity=error,nohostname" + + logs = EmptyBlock( + access_log=access, + error_log=error) + self._config.sections.add(logs) + + def add_proxied_location(self, location="/", address="127.0.0.1", + port=8443, websocket=True, auth=True): + + location = Location(location, + proxy_pass=f"http://{address}:{port}", + gzip="off") + + if auth: + location.sections.add(KeyValueOption("include", "auth.conf")) + + proxy_options = duplicate_options( + "proxy_set_header", + PROXY_OPTIONS + ) + + if websocket: + comment = Comment(comment="Websockets") + proxy_options.sections.add(comment) + http_version = EmptyBlock(proxy_http_version="1.1") + websocket_cfg = duplicate_options( + "proxy_set_header", + PROXY_WEBSOCKETS_OPTIONS + ) + http_version.sections.add(websocket_cfg) + proxy_options.sections.add(http_version) + + location.sections.add(proxy_options) + self._config.sections.add(location) + + def add_php_try_files(self, location="/"): + + location = Location(location, + try_files=["$uri", "$uri/", + f"{location}index.php?$query_string"]) + + self._config.sections.add(location) + + def add_php_block(self): + + location = Location(r"~ ^(?.+?\.php)(?/.*)?$", + try_files="$script_name = 404", + include="fastcgi_params", + fastcgi_pass="unix:/run/php-fpm/php-fpm.sock", + fastcgi_hide_header="X-Powered-By") + + location.sections.add(duplicate_options("fastcgi_param", FPM_OPTIONS)) + + self._config.sections.add(location)