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
 | 
						|
      ];
 | 
						|
    };
 | 
						|
}
 |