{ config, lib, pkgs, ... }: let errorPages = pkgs.fetchgit { url = "https://git.ingolf-wagner.de/palo/http-errors.git"; rev = "74b8e4c1d9bbba3db6ad858b888e1867318af1f0"; sha256 = "0czdzafx4k76q773lyf3vsjm74g1995iz542dhw15kpy5xbivsrg"; }; error = { extraConfig = '' error_page 400 /errors/400.html; error_page 401 /errors/401.html; error_page 402 /errors/402.html; error_page 403 /errors/403.html; error_page 404 /errors/404.html; error_page 405 /errors/405.html; error_page 406 /errors/406.html; error_page 500 /errors/500.html; error_page 501 /errors/501.html; error_page 502 /errors/502.html; error_page 503 /errors/503.html; error_page 504 /errors/504.html; ''; locations."^~ /errors/" = { extraConfig = "internal;"; root = "${errorPages}/"; }; }; in { networking.firewall.allowedTCPPorts = [ 80 443 4443 config.services.taskserver.listenPort ]; networking.firewall.allowedUDPPorts = [ 80 443 4443 config.services.taskserver.listenPort ]; services.nginx = { enable = true; recommendedProxySettings = true; virtualHosts = { "git.ingolf-wagner.de" = { listen = [ { addr = "0.0.0.0"; port = 4443; ssl = true; } { addr = "0.0.0.0"; port = 80; ssl = false; } ]; forceSSL = true; enableACME = true; extraConfig = error.extraConfig; locations = { "/" = { proxyPass = "http://workhorse.private:3000"; extraConfig = '' if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # # Custom headers and headers various browsers *should* be OK with but aren't # add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; # # Tell client that this pre-flight info is valid for 20 days # add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } ''; }; } // error.locations; }; "paste.ingolf-wagner.de" = { listen = [ { addr = "0.0.0.0"; port = 4443; ssl = true; } { addr = "0.0.0.0"; port = 80; ssl = false; } ]; forceSSL = true; enableACME = true; extraConfig = error.extraConfig; locations = { "/" = { proxyPass = "http://workhorse.private:8000"; }; } // error.locations; }; "travel.ingolf-wagner.de" = { listen = [ { addr = "0.0.0.0"; port = 4443; ssl = true; } { addr = "0.0.0.0"; port = 80; ssl = false; } ]; forceSSL = true; enableACME = true; extraConfig = error.extraConfig; locations = { "/" = { root = "/srv/www/travel"; extraConfig = '' if (-d $request_filename) { rewrite [^/]$ $scheme://$http_host$request_uri/ permanent; } ''; }; } // error.locations; }; "tech.ingolf-wagner.de" = { listen = [ { addr = "0.0.0.0"; port = 4443; ssl = true; } { addr = "0.0.0.0"; port = 80; ssl = false; } ]; forceSSL = true; enableACME = true; extraConfig = error.extraConfig; locations = { "/" = { root = "/srv/www/tech"; extraConfig = '' if (-d $request_filename) { rewrite [^/]$ $scheme://$http_host$request_uri/ permanent; } ''; }; } // error.locations; }; "terranix.org" = { listen = [ { addr = "0.0.0.0"; port = 4443; ssl = true; } { addr = "0.0.0.0"; port = 80; ssl = false; } ]; forceSSL = true; enableACME = true; extraConfig = error.extraConfig; locations = { "/" = { root = "/srv/www/terranix"; extraConfig = '' if (-d $request_filename) { rewrite [^/]$ $scheme://$http_host$request_uri/ permanent; } ''; }; } // error.locations; }; #"chat.ingolf-wagner.de" = { # listen = [ # { # addr = "0.0.0.0"; # port = 4443; # ssl = true; # } # { # addr = "0.0.0.0"; # port = 80; # ssl = false; # } # ]; # forceSSL = true; # enableACME = true; # extraConfig = error.extraConfig; # locations = { # "/" = { # proxyPass = "http://chat.workhorse.private"; # proxyWebsockets = true; # extraConfig = '' # sub_filter "http://chat.ingolf-wagner.de" "https://chat.ingolf-wagner.de"; # sub_filter "chat.workhorse.private" "chat.ingolf-wagner.de"; # ''; # }; # } // error.locations; #}; "nextcloud.ingolf-wagner.de" = { listen = [ { addr = "0.0.0.0"; port = 4443; ssl = true; } { addr = "0.0.0.0"; port = 80; ssl = false; } ]; forceSSL = true; enableACME = true; extraConfig = error.extraConfig; locations = { "/" = { proxyPass = "http://nextcloud.workhorse.private"; extraConfig = '' sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de"; sub_filter "nextcloud.workhorse.private" "nextcloud.ingolf-wagner.de"; # used for view/edit office file via Office Online Server client_max_body_size 0; ''; }; "= /.well-known/carddav" = { priority = 210; extraConfig = "return 301 $scheme://$host/remote.php/dav;"; }; "= /.well-known/caldav" = { priority = 210; extraConfig = "return 301 $scheme://$host/remote.php/dav;"; }; "~ .(?:css|js|svg|gif)$" = { proxyPass = "http://nextcloud.workhorse.private$request_uri"; extraConfig = '' expires 6M; # Cache-Control policy borrowed from `.htaccess` access_log off; # Optional: Don't log access to assets sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de"; sub_filter "nextcloud.workhorse.private" "nextcloud.ingolf-wagner.de"; # used for view/edit office file via Office Online Server client_max_body_size 0; ''; }; "~ .woff2?$" = { proxyPass = "http://nextcloud.workhorse.private$request_uri"; extraConfig = '' expires 7d; # Cache-Control policy borrowed from `.htaccess` access_log off; # Optional: Don't log access to assets sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de"; sub_filter "nextcloud.workhorse.private" "nextcloud.ingolf-wagner.de"; # used for view/edit office file via Office Online Server client_max_body_size 0; ''; }; } // error.locations; }; "gaykraft.com" = { listen = [ { addr = "0.0.0.0"; port = 4443; ssl = true; } { addr = "0.0.0.0"; port = 80; ssl = false; } ]; forceSSL = true; enableACME = true; extraConfig = error.extraConfig; locations = { "/" = { root = "/srv/www/gaykraft"; }; } // error.locations; }; #"home.ingolf-wagner.de" = { # listen = [ # { # addr = "0.0.0.0"; # port = 4443; # ssl = true; # } # { # addr = "0.0.0.0"; # port = 80; # ssl = false; # } # ]; # extraConfig = '' # proxy_buffering off; # # client certificate # ssl_client_certificate ${}; # # make verification optional, so we can display a 403 message to those # # who fail authentication # ssl_verify_client optional; # ''; # forceSSL = true; # enableACME = true; # locations."/" = { # proxyPass = "http://pepe.private:8123"; # proxyWebsockets = true; # extraConfig = '' # # if the client-side certificate failed to authenticate, show a 403 # # message to the client # if ($ssl_client_verify != SUCCESS) { # return 403; # } # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection $connection_upgrade; # ''; # }; #}; }; }; services.sslh = { enable = true; listenAddresses = [ "0.0.0.0" ]; port = 443; verbose = true; transparent = true; # List of protocols # # Each protocol entry consists of: # name: name of the probe. These are listed on the command line (ssh -?), plus 'regex' and 'timeout'. # service: (optional) libwrap service name (see hosts_access(5)) # host, port: where to connect when this probe succeeds # log_level: 0 to turn off logging # 1 to log each incoming connection # keepalive: Should TCP keepalive be on or off for that # connection (default is off) # fork: Should a new process be forked for this protocol? # (only useful for sslh-select) # tfo_ok: Set to true if the server supports TCP FAST OPEN # # Probe-specific options: # (sslh will try each probe in order they are declared, and # connect to the first that matches.) # # tls: # sni_hostnames: list of FQDN for that target # alpn_protocols: list of ALPN protocols for that target, see: # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids # # if both sni_hostnames AND alpn_protocols are specified, both must match # # if neither are set, it is just checked whether this is the TLS protocol or not # # Obviously set the most specific probes # first, and if you use TLS with no ALPN/SNI # set it as the last TLS probe # regex: # regex_patterns: list of patterns to match for # that target. # # You can specify several of 'regex' and 'tls'. appendConfig = '' protocols: ( { name: "ssh"; service: "ssh"; host: "localhost"; port: "2222";}, { name: "tls"; host: "localhost"; port: "4443";}, { name: "tinc"; host: "localhost"; port: "655"; } ); ''; }; systemd.services."socat-proxy" = { wantedBy = [ "sslh.service" "multi-user.target" ]; after = [ "sslh.service" ]; script = '' ${pkgs.socat}/bin/socat TCP-LISTEN:2222,fork TCP:workhorse.private:2222 ''; }; systemd.services."socat-taskd" = { wantedBy = [ "multi-user.target" ]; script = let port = toString config.services.taskserver.listenPort; in '' ${pkgs.socat}/bin/socat TCP-LISTEN:${port},fork TCP:workhorse.private:${port} ''; }; }