forked from jan-leila/nix-config
		
	
		
			
				
	
	
		
			176 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| {
 | |
|   lib,
 | |
|   config,
 | |
|   ...
 | |
| }: {
 | |
|   options.services.reverseProxy = {
 | |
|     enable = lib.mkEnableOption "turn on the reverse proxy";
 | |
|     openFirewall = lib.mkEnableOption "open the firewall";
 | |
|     refuseUnmatchedDomains = lib.mkOption {
 | |
|       type = lib.types.bool;
 | |
|       description = "refuse connections for domains that don't match any configured virtual hosts";
 | |
|       default = true;
 | |
|     };
 | |
|     ports = {
 | |
|       http = lib.mkOption {
 | |
|         type = lib.types.port;
 | |
|         description = "HTTP port for the reverse proxy";
 | |
|         default = 80;
 | |
|       };
 | |
|       https = lib.mkOption {
 | |
|         type = lib.types.port;
 | |
|         description = "HTTPS port for the reverse proxy";
 | |
|         default = 443;
 | |
|       };
 | |
|     };
 | |
|     acme = {
 | |
|       enable = lib.mkOption {
 | |
|         type = lib.types.bool;
 | |
|         description = "enable ACME certificate management";
 | |
|         default = true;
 | |
|       };
 | |
|       email = lib.mkOption {
 | |
|         type = lib.types.str;
 | |
|         description = "email address for ACME certificate registration";
 | |
|       };
 | |
|     };
 | |
|     services = lib.mkOption {
 | |
|       type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
 | |
|         options = {
 | |
|           target = lib.mkOption {
 | |
|             type = lib.types.str;
 | |
|             description = "what url will all traffic to this application be forwarded to";
 | |
|           };
 | |
|           domain = lib.mkOption {
 | |
|             type = lib.types.str;
 | |
|             description = "what is the default subdomain to be used for this application to be used for";
 | |
|             default = name;
 | |
|           };
 | |
|           extraDomains = lib.mkOption {
 | |
|             type = lib.types.listOf lib.types.str;
 | |
|             description = "extra domains that should be configured for this domain";
 | |
|             default = [];
 | |
|           };
 | |
|           settings = {
 | |
|             certificateRenewal.enable = lib.mkOption {
 | |
|               type = lib.types.bool;
 | |
|               description = "auto renew certificates";
 | |
|               default = true;
 | |
|             };
 | |
|             forceSSL.enable = lib.mkOption {
 | |
|               type = lib.types.bool;
 | |
|               description = "auto renew certificates";
 | |
|               default = true;
 | |
|             };
 | |
|             proxyHeaders = {
 | |
|               enable = lib.mkEnableOption "should we proxy headers";
 | |
|               timeout = lib.mkOption {
 | |
|                 type = lib.types.int;
 | |
|                 default = 60;
 | |
|               };
 | |
|             };
 | |
|             proxyWebsockets.enable = lib.mkEnableOption "should the default config proxy websockets";
 | |
|             forwardHeaders.enable = lib.mkEnableOption "should the default config contain forward headers";
 | |
|             noSniff.enable = lib.mkEnableOption "should the no sniff flags be set";
 | |
|             proxyBuffering.enable = lib.mkOption {
 | |
|               type = lib.types.bool;
 | |
|               description = "should proxy buffering be enabled";
 | |
|               default = true;
 | |
|             };
 | |
|             maxBodySize = lib.mkOption {
 | |
|               type = lib.types.nullOr lib.types.int;
 | |
|               description = "";
 | |
|               default = null;
 | |
|             };
 | |
|           };
 | |
|         };
 | |
|       }));
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   config = let
 | |
|     httpPort = config.services.reverseProxy.ports.http;
 | |
|     httpsPort = config.services.reverseProxy.ports.https;
 | |
|   in
 | |
|     lib.mkIf config.services.reverseProxy.enable {
 | |
|       security.acme = lib.mkIf config.services.reverseProxy.acme.enable {
 | |
|         acceptTerms = true;
 | |
|         defaults.email = config.services.reverseProxy.acme.email;
 | |
|       };
 | |
| 
 | |
|       services.nginx = {
 | |
|         enable = true;
 | |
|         virtualHosts = lib.mkMerge (
 | |
|           (lib.optionals config.services.reverseProxy.refuseUnmatchedDomains [
 | |
|             {
 | |
|               "_" = {
 | |
|                 default = true;
 | |
|                 serverName = "_";
 | |
|                 locations."/" = {
 | |
|                   extraConfig = ''
 | |
|                     return 444;
 | |
|                   '';
 | |
|                 };
 | |
|               };
 | |
|             }
 | |
|           ])
 | |
|           ++ lib.lists.flatten (
 | |
|             lib.attrsets.mapAttrsToList (
 | |
|               name: service: let
 | |
|                 hostConfig = {
 | |
|                   forceSSL = service.settings.forceSSL.enable;
 | |
|                   enableACME = service.settings.certificateRenewal.enable;
 | |
|                   locations = {
 | |
|                     "/" = {
 | |
|                       proxyPass = service.target;
 | |
|                       proxyWebsockets = service.settings.proxyWebsockets.enable;
 | |
|                       recommendedProxySettings = service.settings.forwardHeaders.enable;
 | |
|                       extraConfig = let
 | |
|                         # Client upload size configuration
 | |
|                         maxBodySizeConfig =
 | |
|                           lib.optionalString (service.settings.maxBodySize != null)
 | |
|                           "client_max_body_size ${toString service.settings.maxBodySize}M;";
 | |
| 
 | |
|                         # Security header configuration
 | |
|                         noSniffConfig =
 | |
|                           lib.optionalString service.settings.noSniff.enable
 | |
|                           "add_header X-Content-Type-Options nosniff;";
 | |
| 
 | |
|                         # Proxy buffering configuration
 | |
|                         proxyBufferingConfig =
 | |
|                           lib.optionalString (!service.settings.proxyBuffering.enable)
 | |
|                           "proxy_buffering off;";
 | |
| 
 | |
|                         # Proxy timeout configuration
 | |
|                         proxyTimeoutConfig =
 | |
|                           lib.optionalString service.settings.proxyHeaders.enable
 | |
|                           ''
 | |
|                             proxy_read_timeout ${toString service.settings.proxyHeaders.timeout}s;
 | |
|                             proxy_connect_timeout ${toString service.settings.proxyHeaders.timeout}s;
 | |
|                             proxy_send_timeout ${toString service.settings.proxyHeaders.timeout}s;
 | |
|                           '';
 | |
|                       in
 | |
|                         maxBodySizeConfig + noSniffConfig + proxyBufferingConfig + proxyTimeoutConfig;
 | |
|                     };
 | |
|                   };
 | |
|                 };
 | |
|               in (
 | |
|                 [
 | |
|                   {
 | |
|                     ${service.domain} = hostConfig;
 | |
|                   }
 | |
|                 ]
 | |
|                 ++ builtins.map (domain: {${domain} = hostConfig;})
 | |
|                 service.extraDomains
 | |
|               )
 | |
|             )
 | |
|             config.services.reverseProxy.services
 | |
|           )
 | |
|         );
 | |
|       };
 | |
|       networking.firewall.allowedTCPPorts = lib.mkIf config.services.reverseProxy.openFirewall [
 | |
|         httpPort
 | |
|         httpsPort
 | |
|       ];
 | |
|     };
 | |
| }
 |