| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  | # A simple abstraction layer for almost all of my services' needs | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   config, | 
					
						
							|  |  |  |   lib, | 
					
						
							|  |  |  |   pkgs, | 
					
						
							|  |  |  |   ... | 
					
						
							|  |  |  | }: | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  | let | 
					
						
							|  |  |  |   cfg = config.my.services.nginx; | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |   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 = literalExpression ''
 | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               locations."/socket" = { | 
					
						
							|  |  |  |                 proxyPass = "http://127.0.0.1:8096/"; | 
					
						
							|  |  |  |                 proxyWebsockets = true; | 
					
						
							|  |  |  |               }; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           '';
 | 
					
						
							|  |  |  |           default = { }; | 
					
						
							|  |  |  |           description = ''
 | 
					
						
							|  |  |  |             Any extra configuration that should be applied to this virtual host. | 
					
						
							|  |  |  |           '';
 | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | in | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |   imports = [ ./sso ]; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |   options.my.services.nginx = with lib; { | 
					
						
							|  |  |  |     enable = mkEnableOption "Nginx"; | 
					
						
							|  |  |  |     acme = { | 
					
						
							|  |  |  |       credentialsFile = mkOption { | 
					
						
							|  |  |  |         type = types.str; | 
					
						
							|  |  |  |         example = "/var/lib/acme/creds.env"; | 
					
						
							|  |  |  |         description = ''
 | 
					
						
							|  |  |  |           INWX API key file as an 'EnvironmentFile' (see `systemd.exec(5)`) | 
					
						
							|  |  |  |         '';
 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     virtualHosts = mkOption { | 
					
						
							|  |  |  |       type = types.listOf virtualHostOption; | 
					
						
							|  |  |  |       default = [ ]; | 
					
						
							| 
									
										
										
										
											2023-06-22 20:54:16 +02:00
										 |  |  |       example = literalExpression ''
 | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |         [ | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             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 { | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |         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."; | 
					
						
							|  |  |  |               }; | 
					
						
							|  |  |  |               totpSecretFile = mkOption { | 
					
						
							|  |  |  |                 type = types.str; | 
					
						
							|  |  |  |                 example = "/var/lib/nginx-sso/alice/totp-secret.txt"; | 
					
						
							|  |  |  |                 description = "Path to file containing the user's TOTP secret."; | 
					
						
							|  |  |  |               }; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |             }; | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |           } | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2023-06-22 20:54:16 +02:00
										 |  |  |         example = literalExpression ''
 | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |           { | 
					
						
							|  |  |  |             alice = { | 
					
						
							|  |  |  |               passwordHashFile = "/var/lib/nginx-sso/alice/password-hash.txt"; | 
					
						
							|  |  |  |               totpSecretFile = "/var/lib/nginx-sso/alice/totp-secret.txt"; | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         '';
 | 
					
						
							|  |  |  |         description = "Definition of users"; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       groups = mkOption { | 
					
						
							|  |  |  |         type = with types; attrsOf (listOf str); | 
					
						
							| 
									
										
										
										
											2023-06-22 20:54:16 +02:00
										 |  |  |         example = literalExpression ''
 | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |           { | 
					
						
							|  |  |  |             root = [ "alice" ]; | 
					
						
							|  |  |  |             users = [ "alice" "bob" ]; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         '';
 | 
					
						
							|  |  |  |         description = "Groups of users"; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   config = lib.mkIf cfg.enable { | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |     assertions = lib.flip builtins.map cfg.virtualHosts ( | 
					
						
							|  |  |  |       { subdomain, ... }@args: | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |       let | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |         conflicts = [ | 
					
						
							|  |  |  |           "port" | 
					
						
							|  |  |  |           "root" | 
					
						
							|  |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |         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. | 
					
						
							|  |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     #      ++ ( | 
					
						
							|  |  |  |     #      let | 
					
						
							|  |  |  |     #        ports = lib.my.mapFilter | 
					
						
							|  |  |  |     #          (v: v != null) | 
					
						
							|  |  |  |     #          ({ port, ... }: port) | 
					
						
							|  |  |  |     #          cfg.virtualHosts; | 
					
						
							|  |  |  |     #        lib.unique ports; | 
					
						
							|  |  |  |     #        lib.compareLists ports | 
					
						
							|  |  |  |     #        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 | 
					
						
							|  |  |  |     #    ) | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |     ; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |     services = { | 
					
						
							|  |  |  |       nginx = { | 
					
						
							|  |  |  |         enable = true; | 
					
						
							|  |  |  |         statusPage = true; # For monitoring scraping. | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |         recommendedGzipSettings = true; | 
					
						
							|  |  |  |         recommendedOptimisation = true; | 
					
						
							|  |  |  |         recommendedTlsSettings = true; | 
					
						
							|  |  |  |         recommendedProxySettings = true; | 
					
						
							|  |  |  |         recommendedBrotliSettings = true; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |         # Only allow PFS-enabled ciphers with AES256 | 
					
						
							|  |  |  |         sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL"; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |         commonHttpConfig = ''
 | 
					
						
							|  |  |  |           # Add HSTS header with preloading to HTTPS requests. | 
					
						
							|  |  |  |           # Adding this header to HTTP requests is discouraged | 
					
						
							|  |  |  |           map $scheme $hsts_header { | 
					
						
							|  |  |  |               https   "max-age=31536000; includeSubdomains; preload"; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           add_header Strict-Transport-Security $hsts_header; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           # CORS header | 
					
						
							|  |  |  |           # some applications set it to wildcard, therefore this overrides it | 
					
						
							|  |  |  |           proxy_hide_header Access-Control-Allow-Origin; | 
					
						
							|  |  |  |           add_header Access-Control-Allow-Origin https://${config.networking.domain}; | 
					
						
							| 
									
										
										
										
											2023-04-13 20:14:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           # Minimize information leaked to other domains | 
					
						
							|  |  |  |           add_header 'Referrer-Policy' 'strict-origin-when-cross-origin'; | 
					
						
							| 
									
										
										
										
											2023-04-13 20:14:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           # Disable embedding as a frame | 
					
						
							|  |  |  |           add_header X-Frame-Options DENY; | 
					
						
							| 
									
										
										
										
											2023-04-13 20:14:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           # Prevent injection of code in other mime types (XSS Attacks) | 
					
						
							|  |  |  |           add_header X-Content-Type-Options nosniff; | 
					
						
							| 
									
										
										
										
											2023-04-13 20:14:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           # Enable XSS protection of the browser. | 
					
						
							|  |  |  |           # May be unnecessary when CSP is configured properly (see above) | 
					
						
							|  |  |  |           add_header X-XSS-Protection "1; mode=block"; | 
					
						
							| 
									
										
										
										
											2023-04-13 20:14:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           # This might create errors | 
					
						
							|  |  |  |           proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           # Enable CSP for your services. | 
					
						
							|  |  |  |           #add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always; | 
					
						
							|  |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |         virtualHosts = | 
					
						
							|  |  |  |           let | 
					
						
							|  |  |  |             genAttrs' = values: f: lib.listToAttrs (map f values); | 
					
						
							|  |  |  |             inherit (config.networking) domain; | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |             mkVHost = | 
					
						
							|  |  |  |               { subdomain, ... }@args: | 
					
						
							|  |  |  |               lib.nameValuePair "${subdomain}.${domain}" ( | 
					
						
							|  |  |  |                 lib.foldl lib.recursiveUpdate { } [ | 
					
						
							|  |  |  |                   # Base configuration | 
					
						
							|  |  |  |                   { | 
					
						
							|  |  |  |                     forceSSL = true; | 
					
						
							|  |  |  |                     useACMEHost = domain; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                   # Proxy to port | 
					
						
							|  |  |  |                   (lib.optionalAttrs (args.port != null) { | 
					
						
							|  |  |  |                     locations."/".proxyPass = "http://127.0.0.1:${toString args.port}"; | 
					
						
							|  |  |  |                     # TODO make ipv6 possible | 
					
						
							|  |  |  |                     # http://[::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}.${config.networking.domain}/login?go=$scheme://$http_host$request_uri | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |                       '';
 | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |                       "/" = { | 
					
						
							|  |  |  |                         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; | 
					
						
							|  |  |  |                           '';
 | 
					
						
							|  |  |  |                       }; | 
					
						
							|  |  |  |                       "/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; | 
					
						
							|  |  |  |                         '';
 | 
					
						
							|  |  |  |                       }; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |                     }; | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |                   }) | 
					
						
							|  |  |  |                 ] | 
					
						
							|  |  |  |               ); | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           in | 
					
						
							|  |  |  |           genAttrs' cfg.virtualHosts mkVHost; | 
					
						
							|  |  |  |         sso = { | 
					
						
							|  |  |  |           enable = true; | 
					
						
							|  |  |  |           configuration = { | 
					
						
							|  |  |  |             listen = { | 
					
						
							|  |  |  |               addr = "127.0.0.1"; | 
					
						
							|  |  |  |               inherit (cfg.sso) port; | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             audit_log = { | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |               target = [ "fd://stdout" ]; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |               events = [ | 
					
						
							|  |  |  |                 "access_denied" | 
					
						
							|  |  |  |                 "login_success" | 
					
						
							|  |  |  |                 "login_failure" | 
					
						
							|  |  |  |                 "logout" | 
					
						
							|  |  |  |                 "validate" | 
					
						
							|  |  |  |               ]; | 
					
						
							|  |  |  |               headers = [ | 
					
						
							|  |  |  |                 "x-origin-uri" | 
					
						
							|  |  |  |                 "x-application" | 
					
						
							|  |  |  |               ]; | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             cookie = { | 
					
						
							|  |  |  |               domain = ".${config.networking.domain}"; | 
					
						
							|  |  |  |               secure = true; | 
					
						
							|  |  |  |               authentication_key = { | 
					
						
							|  |  |  |                 _secret = cfg.sso.authKeyFile; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |               }; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |             }; | 
					
						
							|  |  |  |             login = { | 
					
						
							|  |  |  |               title = "Bühlers'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; }); | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |                   mfa = applyUsers ( | 
					
						
							|  |  |  |                     _: v: [ | 
					
						
							|  |  |  |                       { | 
					
						
							|  |  |  |                         provider = "totp"; | 
					
						
							|  |  |  |                         attributes = { | 
					
						
							|  |  |  |                           secret = { | 
					
						
							|  |  |  |                             _secret = v.totpSecretFile; | 
					
						
							|  |  |  |                           }; | 
					
						
							|  |  |  |                         }; | 
					
						
							|  |  |  |                       } | 
					
						
							|  |  |  |                     ] | 
					
						
							|  |  |  |                   ); | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |                   inherit (cfg.sso) groups; | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             acl = { | 
					
						
							|  |  |  |               rule_sets = [ | 
					
						
							|  |  |  |                 { | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |                   rules = [ | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                       field = "x-application"; | 
					
						
							|  |  |  |                       present = true; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   ]; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |                   allow = [ "@root" ]; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               ]; | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |           }; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |         }; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       # services.prometheus = lib.mkIf cfg.monitoring.enable { | 
					
						
							|  |  |  |       prometheus = { | 
					
						
							|  |  |  |         exporters.nginx = { | 
					
						
							|  |  |  |           enable = true; | 
					
						
							|  |  |  |           listenAddress = "127.0.0.1"; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         scrapeConfigs = [ | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             job_name = "nginx"; | 
					
						
							|  |  |  |             static_configs = [ | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |               { | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |                 targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.nginx.port}" ]; | 
					
						
							|  |  |  |                 labels = { | 
					
						
							|  |  |  |                   instance = config.networking.hostName; | 
					
						
							|  |  |  |                 }; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |               } | 
					
						
							|  |  |  |             ]; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  |           } | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       grafana.provision = { | 
					
						
							|  |  |  |         dashboards.settings.providers = [ | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             name = "Nginx"; | 
					
						
							|  |  |  |             options.path = pkgs.grafana-dashboards.nginx; | 
					
						
							|  |  |  |             disableDeletion = true; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:36:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |     my.services.nginx.virtualHosts = [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         subdomain = "login"; | 
					
						
							|  |  |  |         inherit (cfg.sso) port; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2023-12-07 00:40:36 +01:00
										 |  |  |     my.services.backup = { | 
					
						
							|  |  |  |       exclude = [ | 
					
						
							|  |  |  |         # fails often because the file changed | 
					
						
							|  |  |  |         "/var/log/nginx/access.log" | 
					
						
							|  |  |  |       ]; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-28 21:08:02 +02:00
										 |  |  |     networking.firewall.allowedTCPPorts = [ | 
					
						
							|  |  |  |       80 | 
					
						
							|  |  |  |       443 | 
					
						
							|  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |     # Nginx needs to be able to read the certificates | 
					
						
							|  |  |  |     users.users.nginx.extraGroups = [ "acme" ]; | 
					
						
							|  |  |  |     security.acme = { | 
					
						
							|  |  |  |       defaults.email = "server@buehler.rocks"; | 
					
						
							|  |  |  |       # this is specially needed for inwx and does not work without it | 
					
						
							|  |  |  |       defaults.dnsResolver = "ns.inwx.de"; | 
					
						
							|  |  |  |       acceptTerms = true; | 
					
						
							|  |  |  |       # Use DNS wildcard certificate | 
					
						
							|  |  |  |       certs = | 
					
						
							|  |  |  |         let | 
					
						
							| 
									
										
										
										
											2023-11-07 23:13:51 +01:00
										 |  |  |           inherit (config.networking) domain; | 
					
						
							| 
									
										
										
										
											2022-11-29 18:49:27 +01:00
										 |  |  |         in | 
					
						
							|  |  |  |         with pkgs; | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           "${domain}" = { | 
					
						
							|  |  |  |             extraDomainNames = [ "*.${domain}" ]; | 
					
						
							|  |  |  |             dnsProvider = "inwx"; | 
					
						
							|  |  |  |             inherit (cfg.acme) credentialsFile; | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } |