# Web services configuration { config, pkgs, lib, ... }: { # Reverse proxy configuration services.nginx = { enable = true; recommendedTlsSettings = true; recommendedGzipSettings = true; recommendedProxySettings = true; recommendedOptimisation = true; recommendedBrotliSettings = true; clientMaxBodySize = "0"; sslCiphers = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK:!AES128"; sslProtocols = "TLSv1.2 TLSv1.3"; sslDhparam = "/var/lib/dhparams/nginx.pem"; commonHttpConfig = '' # Add HSTS header with preloading to HTTPS requests. add_header Strict-Transport-Security "max-age=31536000; includeSubdomains"; # Minimize information leaked to other domains add_header 'Referrer-Policy' 'strict-origin-when-cross-origin'; # Prevent injection of code in other mime types (XSS Attacks) add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Frame-Options SAMEORIGIN; # This might create errors proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; ''; virtualHosts = { "coolneng.duckdns.org" = { enableACME = true; forceSSL = true; # Redirect from legacy subdirectory URL to subdomain locations = { "/radicale/".return = "301 https://radicale.coolneng.duckdns.org"; "/syncthing/".return = "301 https://sync.coolneng.duckdns.org"; "/gitea/".extraConfig = "rewrite ^/gitea/(.*)$ https://git.coolneng.duckdns.org/$1 last;"; "/miniflux/".extraConfig = "rewrite ^/miniflux/(.*)$ https://rss.coolneng.duckdns.org/$1 last;"; # Delegation for Matrix "/.well-known/" = { alias = "${../well-known}" + "/"; extraConfig = '' ${config.services.nginx.commonHttpConfig} default_type application/json; add_header Access-Control-Allow-Origin * always; ''; }; }; }; "radicale.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; locations."/" = { proxyPass = "http://localhost:5232/"; extraConfig = '' proxy_set_header X-Script-Name /; proxy_pass_header Authorization; ''; }; }; "sync.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; locations."/".proxyPass = "http://localhost:8384/"; }; "git.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; locations."/" = { proxyPass = "http://localhost:3000/"; extraConfig = '' ${config.services.nginx.commonHttpConfig} # Disable embedding as a frame, except from the same origin add_header Content-Security-Policy "frame-src git.coolneng.duckdns.org; frame-ancestors git.coolneng.duckdns.org"; ''; }; }; "rss.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; locations."/".proxyPass = "http://localhost:8080/"; }; "matrix.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; listen = [ { addr = "0.0.0.0"; port = 8448; ssl = true; } { addr = "0.0.0.0"; port = 443; ssl = true; } ]; locations."~ ^(/_matrix|/_synapse/client)" = { proxyPass = "http://localhost:8008"; extraConfig = '' proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; ''; }; }; "element.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; locations."/".root = pkgs.element-web.override { conf.default_server_config = { "m.homeserver"."base_url" = "https://matrix.coolneng.duckdns.org"; "m.identity_server"."base_url" = "https://vector.im"; }; }; }; "wallabag.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; root = "${pkgs.wallabag}/web"; locations = { "/".tryFiles = "$uri /app.php$is_args$args"; "/assets".root = "${config.environment.variables.WALLABAG_DATA}/web"; "~ ^/app.php(/|$)" = { fastcgiParams = { SCRIPT_FILENAME = "${pkgs.wallabag}/web/$fastcgi_script_name"; DOCUMENT_ROOT = "${pkgs.wallabag}/web"; }; extraConfig = '' fastcgi_pass unix:${config.services.phpfpm.pools.wallabag.socket}; fastcgi_split_path_info ^(.+\.php)(/.*)$; include ${pkgs.nginx}/conf/fastcgi_params; internal; ''; }; }; }; "books.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; locations."/" = { proxyPass = "http://localhost:9000/"; proxyWebsockets = true; extraConfig = '' proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; ''; }; }; "grafana.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; locations."/" = { proxyPass = "http://localhost:9009/"; proxyWebsockets = true; }; }; "/nginx_status/" = { listen = [{ addr = "127.0.0.1"; port = 8282; }]; extraConfig = '' stub_status on; access_log off; allow 127.0.0.1; deny all; ''; }; "nightscout.coolneng.duckdns.org" = { enableACME = true; forceSSL = true; locations."/" = { proxyPass = "http://localhost:1337"; extraConfig = '' proxy_set_header X-Forwarded-For $remote_addr; ''; }; }; }; }; # ACME certs configuration security.acme = { acceptTerms = true; defaults.email = "akasroua@gmail.com"; }; # Generate dhparams security.dhparams = { enable = true; params.nginx.bits = 2048; }; # PostgreSQL databases configuration services.postgresql = { enable = true; package = pkgs.postgresql_15; authentication = lib.mkForce '' # Generated file; do not edit! # TYPE DATABASE USER ADDRESS METHOD local all all trust host all all 127.0.0.1/32 trust host all all ::1/128 trust ''; settings = { max_connections = "300"; shared_buffers = "512MB"; }; }; # Restart reverse proxy after services startup systemd.services.nginx.after = [ "gitea.service" "syncthing.service" "miniflux.service" "radicale.service" "dendrite.service" "phpfpm-wallabag.service" "systemd-tmpfiles-setup.service" "podman-openbooks.service" "podman-mqtt2prometheus.service" "podman-nightscout.service" ]; }