diff options
Diffstat (limited to '')
-rw-r--r-- | hosts/tahoe/services.nix | 14 | ||||
-rw-r--r-- | modules/services/gitea/default.nix | 12 | ||||
-rw-r--r-- | modules/services/nginx/default.nix | 291 |
3 files changed, 13 insertions, 304 deletions
diff --git a/hosts/tahoe/services.nix b/hosts/tahoe/services.nix index 8846eae..8481bd0 100644 --- a/hosts/tahoe/services.nix +++ b/hosts/tahoe/services.nix @@ -19,19 +19,7 @@ in { }; rclone = { enable = true; }; traefik = { enable = false; }; - nginx = { - enable = true; - acme = { credentialsFile = secrets."acme/dns-key".path; }; - sso = { - authKeyFile = secrets."sso/auth-key".path; - users = { - fcuny = { - passwordHashFile = secrets."sso/fcuny/password-hash".path; - }; - }; - groups = { root = [ "fcuny" ]; }; - }; - }; + nginx = { enable = true; }; transmission = { enable = true; }; metrics-exporter = { enable = true; }; backup = { diff --git a/modules/services/gitea/default.nix b/modules/services/gitea/default.nix index 6828439..6259c30 100644 --- a/modules/services/gitea/default.nix +++ b/modules/services/gitea/default.nix @@ -37,10 +37,14 @@ in { }; }; - my.services.nginx.virtualHosts = [{ - subdomain = "gitea"; - port = 8002; - }]; + services.nginx.virtualHosts."git.fcuny.net" = { + forceSSL = true; + enableACME = true; + locations."/" = { + proxyPass = "http://127.0.0.1:8002"; + proxyWebsockets = true; + }; + }; my.services.backup = { paths = [ cfg.stateDir ]; }; }; diff --git a/modules/services/nginx/default.nix b/modules/services/nginx/default.nix index 0020111..9fb5cbd 100644 --- a/modules/services/nginx/default.nix +++ b/modules/services/nginx/default.nix @@ -1,177 +1,8 @@ -# A simple abstraction layer for almost all of my services' needs { config, lib, pkgs, ... }: -let - cfg = config.my.services.nginx; - virtualHostOption = with lib; - types.submodule { - options = { - subdomain = mkOption { - type = types.str; - example = "dev"; - description = '' - Which subdomain, under config.networking.domain, to use - for this virtual host. - ''; - }; - port = mkOption { - type = with types; nullOr port; - default = null; - example = 8080; - description = '' - Which port to proxy to, through 127.0.0.1, for this virtual host. - This option is incompatible with `root`. - ''; - }; - root = mkOption { - type = with types; nullOr path; - default = null; - example = "/var/www/blog"; - description = '' - The root folder for this virtual host. This option is incompatible - with `port`. - ''; - }; - sso = { enable = mkEnableOption "SSO authentication"; }; - extraConfig = mkOption { - type = types.attrs; # FIXME: forward type of virtualHosts - example = litteralExample '' - { - locations."/socket" = { - proxyPass = "http://127.0.0.1:8096/"; - proxyWebsockets = true; - }; - } - ''; - default = { }; - description = '' - Any extra configuration that should be applied to this virtual host. - ''; - }; - }; - }; +let cfg = config.my.services.nginx; in { - imports = [ ./sso ]; - options.my.services.nginx = with lib; { - enable = mkEnableOption "Nginx"; - monitoring = { - enable = my.mkDisableOption "monitoring through grafana and prometheus"; - }; - virtualHosts = mkOption { - type = types.listOf virtualHostOption; - default = [ ]; - example = litteralExample '' - [ - { - subdomain = "gitea"; - port = 8080; - } - { - subdomain = "dev"; - root = "/var/www/dev"; - } - { - subdomain = "jellyfin"; - port = 8096; - extraConfig = { - locations."/socket" = { - proxyPass = "http://127.0.0.1:8096/"; - proxyWebsockets = true; - }; - }; - } - ] - ''; - description = '' - List of virtual hosts to set-up using default settings. - ''; - }; - sso = { - authKeyFile = mkOption { - type = types.str; - example = "/var/lib/nginx-sso/auth-key.txt"; - description = '' - Path to the auth key. - ''; - }; - subdomain = mkOption { - type = types.str; - default = "login"; - example = "auth"; - description = "Which subdomain, to use for SSO."; - }; - port = mkOption { - type = types.port; - default = 8082; - example = 8080; - description = "Port to use for internal webui."; - }; - users = mkOption { - type = types.attrsOf (types.submodule { - options = { - passwordHashFile = mkOption { - type = types.str; - example = "/var/lib/nginx-sso/alice/password-hash.txt"; - description = "Path to file containing the user's password hash."; - }; - }; - }); - example = litteralExample '' - { - alice = { - passwordHashFile = "/var/lib/nginx-sso/alice/password-hash.txt"; - }; - } - ''; - description = "Definition of users"; - }; - groups = mkOption { - type = with types; attrsOf (listOf str); - example = litteralExample '' - { - root = [ "alice" ]; - users = [ "alice" "bob" ]; - } - ''; - description = "Groups of users"; - }; - }; - }; + options.my.services.nginx = with lib; { enable = mkEnableOption "Nginx"; }; config = lib.mkIf cfg.enable { - assertions = [ ] ++ (lib.flip builtins.map cfg.virtualHosts - ({ subdomain, ... }@args: - let - conflicts = [ "port" "root" ]; - optionsNotNull = builtins.map (v: args.${v} != null) conflicts; - optionsSet = lib.filter lib.id optionsNotNull; - in { - assertion = builtins.length optionsSet == 1; - message = '' - Subdomain '${subdomain}' must have exactly one of ${ - lib.concatStringsSep ", " (builtins.map (v: "'${v}'") conflicts) - } configured. - ''; - })) ++ (let - ports = lib.my.mapFilter (v: v != null) ({ port, ... }: port) - cfg.virtualHosts; - portCounts = lib.my.countValues ports; - nonUniquesCounts = lib.filterAttrs (_: v: v != 1) portCounts; - nonUniques = builtins.attrNames nonUniquesCounts; - mkAssertion = port: { - assertion = false; - message = "Port ${port} cannot appear in multiple virtual hosts."; - }; - in map mkAssertion nonUniques) ++ (let - subs = map ({ subdomain, ... }: subdomain) cfg.virtualHosts; - subsCounts = lib.my.countValues subs; - nonUniquesCounts = lib.filterAttrs (_: v: v != 1) subsCounts; - nonUniques = builtins.attrNames nonUniquesCounts; - mkAssertion = v: { - assertion = false; - message = '' - Subdomain '${v}' cannot appear in multiple virtual hosts. - ''; - }; - in map mkAssertion nonUniques); services.nginx = { enable = true; statusPage = true; # For monitoring scraping. @@ -179,133 +10,19 @@ in { recommendedOptimisation = true; recommendedTlsSettings = true; recommendedProxySettings = true; - virtualHosts = let - domain = "fcuny.net"; - mkVHost = ({ subdomain, ... }@args: - lib.nameValuePair "${subdomain}.${domain}" (lib.my.recursiveMerge [ - # Base configuration - { - forceSSL = true; - useACMEHost = domain; - } - # Proxy to port - (lib.optionalAttrs (args.port != null) { - locations."/".proxyPass = - "http://127.0.0.1:${toString args.port}"; - }) - # Serve filesystem content - (lib.optionalAttrs (args.root != null) { inherit (args) root; }) - # VHost specific configuration - args.extraConfig - # SSO configuration - (lib.optionalAttrs args.sso.enable { - extraConfig = (args.extraConfig.extraConfig or "") + '' - error_page 401 = @error401; - ''; - locations."@error401".return = '' - 302 https://${cfg.sso.subdomain}.fcuny.net/login?go=$scheme://$http_host$request_uri - ''; - locations."/" = { - extraConfig = (args.extraConfig.locations."/".extraConfig or "") - + '' - # Use SSO - auth_request /sso-auth; - # Set username through header - auth_request_set $username $upstream_http_x_username; - proxy_set_header X-User $username; - # Renew SSO cookie on request - auth_request_set $cookie $upstream_http_set_cookie; - add_header Set-Cookie $cookie; - ''; - }; - locations."/sso-auth" = { - proxyPass = "http://localhost:${toString cfg.sso.port}/auth"; - extraConfig = '' - # Do not allow requests from outside - internal; - # Do not forward the request body - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - # Set X-Application according to subdomain for matching - proxy_set_header X-Application "${subdomain}"; - # Set origin URI for matching - proxy_set_header X-Origin-URI $request_uri; - ''; - }; - }) - ])); - in lib.my.genAttrs' cfg.virtualHosts mkVHost; - sso = { - enable = true; - configuration = { - listen = { - addr = "127.0.0.1"; - inherit (cfg.sso) port; - }; - audit_log = { - target = [ "fd://stdout" ]; - events = [ - "access_denied" - "login_success" - "login_failure" - "logout" - "validate" - ]; - headers = [ "x-origin-uri" "x-application" ]; - }; - cookie = { - domain = ".fcuny.net"; - secure = true; - authentication_key = { _secret = cfg.sso.authKeyFile; }; - }; - login = { - title = "fcuny.net's SSO"; - default_method = "simple"; - hide_mfa_field = false; - names = { simple = "Username / Password"; }; - }; - providers = { - simple = let applyUsers = lib.flip lib.mapAttrs cfg.sso.users; - in { - users = applyUsers (_: v: { _secret = v.passwordHashFile; }); - inherit (cfg.sso) groups; - }; - }; - acl = { - rule_sets = [{ - rules = [{ - field = "x-application"; - present = true; - }]; - allow = [ "@root" ]; - }]; - }; - }; - }; }; - my.services.nginx.virtualHosts = [{ - subdomain = "login"; - inherit (cfg.sso) port; - }]; - networking.firewall.allowedTCPPorts = [ 80 443 ]; # Nginx needs to be able to read the certificates users.users.nginx.extraGroups = [ "acme" ]; security.acme = { - defaults.email = "franck@fcuny.net"; + email = "franck@fcuny.net"; acceptTerms = true; }; - services.grafana.provision.dashboards = lib.mkIf cfg.monitoring.enable [{ - name = "NGINX"; - options.path = pkgs.nur.repos.alarsyo.grafanaDashboards.nginx; - disableDeletion = true; - }]; - - services.prometheus = lib.mkIf cfg.monitoring.enable { + services.prometheus = { exporters.nginx = { enable = true; listenAddress = "127.0.0.1"; |