#!/usr/bin/python3 from pathlib import Path from nginx.config.api import (Section, Location, EmptyBlock, Comment, KeyValueOption, Block) 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_upstream(self, name="server", host="127.0.0.1", port=5555): upstream = Block(f"upstream %{name}", server=f"{host}:{port}") upstream.sections.add(self._config) self._config = upstream def add_logging(self, suffix="nginx"): if isinstance(self.domain, list): domain = self.domain[0].replace(".", "_") else: domain = domain.replace(".", "_") syslog = f"syslog:server=unix:/dev/log,tag={domain}_{suffix}," 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) def add_uwsgi_location(self, location="/", socket=None, auth=False): location = Location(location, uwsgi_pass=socket, include="uwsgi_params") if auth: location.sections.add(KeyValueOption("include", "auth.conf")) self._config.sections.add(location)