{tsuki/dosei}: set up wstunnel
This commit is contained in:
		@@ -111,7 +111,7 @@
 | 
				
			|||||||
      in [
 | 
					      in [
 | 
				
			||||||
        # (self: super: { pcloud = nonrecursive-unstable-pkgs.pcloud; })
 | 
					        # (self: super: { pcloud = nonrecursive-unstable-pkgs.pcloud; })
 | 
				
			||||||
        (self: super: {
 | 
					        (self: super: {
 | 
				
			||||||
          inherit (nonrecursive-unstable-pkgs) atuin;
 | 
					          inherit (nonrecursive-unstable-pkgs) atuin wstunnel;
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        (self: super: {
 | 
					        (self: super: {
 | 
				
			||||||
          mpv-unwrapped = super.mpv-unwrapped.override {
 | 
					          mpv-unwrapped = super.mpv-unwrapped.override {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
    ./services/avahi.nix
 | 
					    ./services/avahi.nix
 | 
				
			||||||
    ./services/docker.nix
 | 
					    ./services/docker.nix
 | 
				
			||||||
    ./services/jenkins.nix
 | 
					    ./services/jenkins.nix
 | 
				
			||||||
 | 
					    ./services/wstunnel.nix
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  boot.loader.systemd-boot.enable = true;
 | 
					  boot.loader.systemd-boot.enable = true;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								hosts/dosei/services/wstunnel.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								hosts/dosei/services/wstunnel.nix
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					{ config, ... }:
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  imports = [
 | 
				
			||||||
 | 
					    ../../../modules/wstunnel.nix
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					  disabledModules = [
 | 
				
			||||||
 | 
					    "services/networking/wstunnel.nix"
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # NOTE: Contains
 | 
				
			||||||
 | 
					  # - WSTUNNEL_HTTP_UPGRADE_PATH_PREFIX
 | 
				
			||||||
 | 
					  # - WSTUNNEL_RESTRICT_HTTP_UPGRADE_PATH_PREFIX
 | 
				
			||||||
 | 
					  sops.secrets."wstunnel/http-upgrade-path-prefix-envvars" = {
 | 
				
			||||||
 | 
					    sopsFile = ../../../secrets/common.yaml;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  services.wstunnel = {
 | 
				
			||||||
 | 
					    enable = true;
 | 
				
			||||||
 | 
					    clients."ws-tsuki" = {
 | 
				
			||||||
 | 
					      connectTo = "wss://ws.nani.wtf";
 | 
				
			||||||
 | 
					      localToRemote = [
 | 
				
			||||||
 | 
					        "tcp://10022:localhost:22"
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					      environmentFile = config.sops.secrets."wstunnel/http-upgrade-path-prefix-envvars".path;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -20,6 +20,7 @@
 | 
				
			|||||||
    ./services/taskserver.nix
 | 
					    ./services/taskserver.nix
 | 
				
			||||||
    ./services/vaultwarden.nix
 | 
					    ./services/vaultwarden.nix
 | 
				
			||||||
    ./services/vscode-server.nix
 | 
					    ./services/vscode-server.nix
 | 
				
			||||||
 | 
					    ./services/wstunnel.nix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ./services/scrapers/nhk-easy-news/default.nix
 | 
					    ./services/scrapers/nhk-easy-news/default.nix
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,6 +51,11 @@
 | 
				
			|||||||
      "osuchan".servers."localhost:${s ports.osuchan}" = { };
 | 
					      "osuchan".servers."localhost:${s ports.osuchan}" = { };
 | 
				
			||||||
      "plex".servers."localhost:${s ports.plex}" = { };
 | 
					      "plex".servers."localhost:${s ports.plex}" = { };
 | 
				
			||||||
      "vaultwarden".servers."unix:${sa.vaultwarden.newSocketAddress}" = { };
 | 
					      "vaultwarden".servers."unix:${sa.vaultwarden.newSocketAddress}" = { };
 | 
				
			||||||
 | 
					      "wstunnel".servers = let
 | 
				
			||||||
 | 
					        inherit (config.services.wstunnel.servers."ws-tsuki".listen) host port;
 | 
				
			||||||
 | 
					      in {
 | 
				
			||||||
 | 
					        "${host}:${s port}" = { };
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    virtualHosts = let
 | 
					    virtualHosts = let
 | 
				
			||||||
@@ -189,6 +194,8 @@
 | 
				
			|||||||
      #   };
 | 
					      #   };
 | 
				
			||||||
      # })
 | 
					      # })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (proxy ["ws"] "http://wstunnel" enableWebsockets)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      (host ["h7x4-stickers"] {})
 | 
					      (host ["h7x4-stickers"] {})
 | 
				
			||||||
      (host ["pingu-stickers"] {})
 | 
					      (host ["pingu-stickers"] {})
 | 
				
			||||||
    ]));
 | 
					    ]));
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								hosts/tsuki/services/wstunnel.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								hosts/tsuki/services/wstunnel.nix
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					{ config, ... }:
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  imports = [
 | 
				
			||||||
 | 
					    ../../../modules/wstunnel.nix
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					  disabledModules = [
 | 
				
			||||||
 | 
					    "services/networking/wstunnel.nix"
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # NOTE: Contains
 | 
				
			||||||
 | 
					  # - WSTUNNEL_HTTP_UPGRADE_PATH_PREFIX
 | 
				
			||||||
 | 
					  # - WSTUNNEL_RESTRICT_HTTP_UPGRADE_PATH_PREFIX
 | 
				
			||||||
 | 
					  sops.secrets."wstunnel/http-upgrade-path-prefix-envvars" = {
 | 
				
			||||||
 | 
					    sopsFile = ../../../secrets/common.yaml;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  services.wstunnel = {
 | 
				
			||||||
 | 
					    enable = true;
 | 
				
			||||||
 | 
					    servers."ws-tsuki" = {
 | 
				
			||||||
 | 
					      listen = {
 | 
				
			||||||
 | 
					        host = "127.0.0.1";
 | 
				
			||||||
 | 
					        port = 8789;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      enableHTTPS = false;
 | 
				
			||||||
 | 
					      environmentFile = config.sops.secrets."wstunnel/http-upgrade-path-prefix-envvars".path;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										469
									
								
								modules/wstunnel.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										469
									
								
								modules/wstunnel.nix
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,469 @@
 | 
				
			|||||||
 | 
					{ config
 | 
				
			||||||
 | 
					, lib
 | 
				
			||||||
 | 
					, pkgs
 | 
				
			||||||
 | 
					, ...
 | 
				
			||||||
 | 
					}:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let
 | 
				
			||||||
 | 
					  cfg = config.services.wstunnel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hostPortToString = { host, port }: "${host}:${toString port}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hostPortSubmodule = {
 | 
				
			||||||
 | 
					    options = {
 | 
				
			||||||
 | 
					      host = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "The hostname.";
 | 
				
			||||||
 | 
					        type = lib.types.str;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      port = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "The port.";
 | 
				
			||||||
 | 
					        type = lib.types.port;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  commonOptions = {
 | 
				
			||||||
 | 
					    enable = lib.mkEnableOption "this `wstunnel` instance." // {
 | 
				
			||||||
 | 
					      default = true;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    package = lib.mkPackageOption pkgs "wstunnel" { };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    autoStart =
 | 
				
			||||||
 | 
					      lib.mkEnableOption "starting this wstunnel instance automatically." // {
 | 
				
			||||||
 | 
					        default = true;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extraArgs = lib.mkOption {
 | 
				
			||||||
 | 
					      description = ''
 | 
				
			||||||
 | 
					        Extra command line arguments to pass to `wstunnel`.
 | 
				
			||||||
 | 
					        Attributes of the form `argName = true;` will be translated to `--argName`,
 | 
				
			||||||
 | 
					        and `argName = \"value\"` to `--argName value`.
 | 
				
			||||||
 | 
					      '';
 | 
				
			||||||
 | 
					      type = with lib.types; attrsOf (either str bool);
 | 
				
			||||||
 | 
					      default = { };
 | 
				
			||||||
 | 
					      example = {
 | 
				
			||||||
 | 
					        "someNewOption" = true;
 | 
				
			||||||
 | 
					        "someNewOptionWithValue" = "someValue";
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loggingLevel = lib.mkOption {
 | 
				
			||||||
 | 
					      description = ''
 | 
				
			||||||
 | 
					        Passed to --log-lvl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Control the log verbosity. i.e: TRACE, DEBUG, INFO, WARN, ERROR, OFF
 | 
				
			||||||
 | 
					        For more details, checkout [EnvFilter](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax)
 | 
				
			||||||
 | 
					      '';
 | 
				
			||||||
 | 
					      type = lib.types.nullOr lib.types.str;
 | 
				
			||||||
 | 
					      example = "INFO";
 | 
				
			||||||
 | 
					      default = null;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    environmentFile = lib.mkOption {
 | 
				
			||||||
 | 
					      description = ''
 | 
				
			||||||
 | 
					        Environment file to be passed to the systemd service.
 | 
				
			||||||
 | 
					        Useful for passing secrets to the service to prevent them from being
 | 
				
			||||||
 | 
					        world-readable in the Nix store.
 | 
				
			||||||
 | 
					        Note however that the secrets are passed to `wstunnel` through
 | 
				
			||||||
 | 
					        the command line, which makes them locally readable for all users of
 | 
				
			||||||
 | 
					        the system at runtime.
 | 
				
			||||||
 | 
					      '';
 | 
				
			||||||
 | 
					      type = lib.types.nullOr lib.types.path;
 | 
				
			||||||
 | 
					      default = null;
 | 
				
			||||||
 | 
					      example = "/var/lib/secrets/wstunnelSecrets";
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  serverSubmodule = { config, ... }: {
 | 
				
			||||||
 | 
					    options = commonOptions // {
 | 
				
			||||||
 | 
					      listen = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          Address and port to listen on.
 | 
				
			||||||
 | 
					          Setting the port to a value below 1024 will also give the process
 | 
				
			||||||
 | 
					          the required `CAP_NET_BIND_SERVICE` capability.
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.submodule hostPortSubmodule;
 | 
				
			||||||
 | 
					        default = {
 | 
				
			||||||
 | 
					          host = "0.0.0.0";
 | 
				
			||||||
 | 
					          port = if config.enableHTTPS then 443 else 80;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        defaultText = lib.literalExpression ''
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            host = "0.0.0.0";
 | 
				
			||||||
 | 
					            port = if enableHTTPS then 443 else 80;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      restrictTo = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          Accepted traffic will be forwarded only to this service.
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.listOf (lib.types.submodule hostPortSubmodule);
 | 
				
			||||||
 | 
					        default = [ ];
 | 
				
			||||||
 | 
					        example = [{
 | 
				
			||||||
 | 
					          host = "127.0.0.1";
 | 
				
			||||||
 | 
					          port = 51820;
 | 
				
			||||||
 | 
					        }];
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      enableHTTPS = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "Use HTTPS for the tunnel server.";
 | 
				
			||||||
 | 
					        type = lib.types.bool;
 | 
				
			||||||
 | 
					        default = true;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      tlsCertificate = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          TLS certificate to use instead of the hardcoded one in case of HTTPS connections.
 | 
				
			||||||
 | 
					          Use together with `tlsKey`.
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.path;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					        example = "/var/lib/secrets/cert.pem";
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      tlsKey = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          TLS key to use instead of the hardcoded on in case of HTTPS connections.
 | 
				
			||||||
 | 
					          Use together with `tlsCertificate`.
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.path;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					        example = "/var/lib/secrets/key.pem";
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      useACMEHost = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          Use a certificate generated by the NixOS ACME module for the given host.
 | 
				
			||||||
 | 
					          Note that this will not generate a new certificate - you will need to do so with `security.acme.certs`.
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.str;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					        example = "example.com";
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clientSubmodule = { config, ... }: {
 | 
				
			||||||
 | 
					    options = commonOptions // {
 | 
				
			||||||
 | 
					      connectTo = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "Server address and port to connect to.";
 | 
				
			||||||
 | 
					        type = lib.types.str;
 | 
				
			||||||
 | 
					        example = "https://wstunnel.server.com:8443";
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      localToRemote = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''Listen on local and forwards traffic from remote.'';
 | 
				
			||||||
 | 
					        type = lib.types.listOf (lib.types.str);
 | 
				
			||||||
 | 
					        default = [ ];
 | 
				
			||||||
 | 
					        example = [
 | 
				
			||||||
 | 
					          "tcp://1212:google.com:443"
 | 
				
			||||||
 | 
					          "unix:///tmp/wstunnel.sock:g.com:443"
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      remoteToLocal = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "Listen on remote and forwards traffic from local. Only tcp is supported";
 | 
				
			||||||
 | 
					        type = lib.types.listOf lib.types.str;
 | 
				
			||||||
 | 
					        default = [ ];
 | 
				
			||||||
 | 
					        example = [
 | 
				
			||||||
 | 
					          "tcp://1212:google.com:443"
 | 
				
			||||||
 | 
					          "unix://wstunnel.sock:g.com:443"
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      addNetBind = lib.mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      httpProxy = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          Proxy to use to connect to the wstunnel server (`USER:PASS@HOST:PORT`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          ::: {.warning}
 | 
				
			||||||
 | 
					          Passwords specified here will be world-readable in the Nix store!
 | 
				
			||||||
 | 
					          To pass a password to the service, point the `environmentFile` option
 | 
				
			||||||
 | 
					          to a file containing `PROXY_PASSWORD=<your-password-here>` and set
 | 
				
			||||||
 | 
					          this option to `<user>:$PROXY_PASSWORD@<host>:<port>`.
 | 
				
			||||||
 | 
					          Note however that this will also locally leak the passwords at
 | 
				
			||||||
 | 
					          runtime via e.g. /proc/<pid>/cmdline.
 | 
				
			||||||
 | 
					          :::
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.str;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      soMark = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          Mark network packets with the SO_MARK sockoption with the specified value.
 | 
				
			||||||
 | 
					          Setting this option will also enable the required `CAP_NET_ADMIN` capability
 | 
				
			||||||
 | 
					          for the systemd service.
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.ints.unsigned;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      upgradePathPrefix = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          Use a specific HTTP path prefix that will show up in the upgrade
 | 
				
			||||||
 | 
					          request to the `wstunnel` server.
 | 
				
			||||||
 | 
					          Useful when running `wstunnel` behind a reverse proxy.
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.str;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					        example = "wstunnel";
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      tlsSNI = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "Use this as the SNI while connecting via TLS. Useful for circumventing hostname-based firewalls.";
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.str;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      tlsVerifyCertificate = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "Whether to verify the TLS certificate of the server. It might be useful to set this to `false` when working with the `tlsSNI` option.";
 | 
				
			||||||
 | 
					        type = lib.types.bool;
 | 
				
			||||||
 | 
					        default = true;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # The original argument name `websocketPingFrequency` is a misnomer, as the frequency is the inverse of the interval.
 | 
				
			||||||
 | 
					      websocketPingInterval = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "Frequency at which the client will send websocket ping to the server.";
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.ints.unsigned;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      upgradeCredentials = lib.mkOption {
 | 
				
			||||||
 | 
					        description = ''
 | 
				
			||||||
 | 
					          Use these credentials to authenticate during the HTTP upgrade request
 | 
				
			||||||
 | 
					          (Basic authorization type, `USER:[PASS]`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          ::: {.warning}
 | 
				
			||||||
 | 
					          Passwords specified here will be world-readable in the Nix store!
 | 
				
			||||||
 | 
					          To pass a password to the service, point the `environmentFile` option
 | 
				
			||||||
 | 
					          to a file containing `HTTP_PASSWORD=<your-password-here>` and set this
 | 
				
			||||||
 | 
					          option to `<user>:$HTTP_PASSWORD`.
 | 
				
			||||||
 | 
					          Note however that this will also locally leak the passwords at runtime
 | 
				
			||||||
 | 
					          via e.g. /proc/<pid>/cmdline.
 | 
				
			||||||
 | 
					          :::
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					        type = lib.types.nullOr lib.types.str;
 | 
				
			||||||
 | 
					        default = null;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      customHeaders = lib.mkOption {
 | 
				
			||||||
 | 
					        description = "Custom HTTP headers to send during the upgrade request.";
 | 
				
			||||||
 | 
					        type = lib.types.attrsOf lib.types.str;
 | 
				
			||||||
 | 
					        default = { };
 | 
				
			||||||
 | 
					        example = {
 | 
				
			||||||
 | 
					          "X-Some-Header" = "some-value";
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  generateServerUnit = name: serverCfg: {
 | 
				
			||||||
 | 
					    name = "wstunnel-server-${name}";
 | 
				
			||||||
 | 
					    value =
 | 
				
			||||||
 | 
					      let
 | 
				
			||||||
 | 
					        certConfig = config.security.acme.certs.${serverCfg.useACMEHost};
 | 
				
			||||||
 | 
					      in
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        description = "wstunnel server - ${name}";
 | 
				
			||||||
 | 
					        requires = [ "network.target" "network-online.target" ];
 | 
				
			||||||
 | 
					        after = [ "network.target" "network-online.target" ];
 | 
				
			||||||
 | 
					        wantedBy = lib.optional serverCfg.autoStart "multi-user.target";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        environment.RUST_LOG = serverCfg.loggingLevel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        serviceConfig = {
 | 
				
			||||||
 | 
					          Type = "exec";
 | 
				
			||||||
 | 
					          EnvironmentFile =
 | 
				
			||||||
 | 
					            lib.optional (serverCfg.environmentFile != null) serverCfg.environmentFile;
 | 
				
			||||||
 | 
					          DynamicUser = true;
 | 
				
			||||||
 | 
					          SupplementaryGroups =
 | 
				
			||||||
 | 
					            lib.optional (serverCfg.useACMEHost != null) certConfig.group;
 | 
				
			||||||
 | 
					          PrivateTmp = true;
 | 
				
			||||||
 | 
					          AmbientCapabilities =
 | 
				
			||||||
 | 
					            lib.optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
 | 
				
			||||||
 | 
					          NoNewPrivileges = true;
 | 
				
			||||||
 | 
					          RestrictNamespaces = "uts ipc pid user cgroup";
 | 
				
			||||||
 | 
					          ProtectSystem = "strict";
 | 
				
			||||||
 | 
					          ProtectHome = true;
 | 
				
			||||||
 | 
					          ProtectKernelTunables = true;
 | 
				
			||||||
 | 
					          ProtectKernelModules = true;
 | 
				
			||||||
 | 
					          ProtectControlGroups = true;
 | 
				
			||||||
 | 
					          PrivateDevices = true;
 | 
				
			||||||
 | 
					          RestrictSUIDSGID = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          Restart = "on-failure";
 | 
				
			||||||
 | 
					          RestartSec = 2;
 | 
				
			||||||
 | 
					          RestartSteps = 20;
 | 
				
			||||||
 | 
					          RestartMaxDelaySec = "5min";
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        script = with serverCfg; ''
 | 
				
			||||||
 | 
					          ${lib.getExe package} \
 | 
				
			||||||
 | 
					            server \
 | 
				
			||||||
 | 
					            ${lib.cli.toGNUCommandLineShell { } (
 | 
				
			||||||
 | 
					              lib.recursiveUpdate
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                restrict-to = map hostPortToString restrictTo;
 | 
				
			||||||
 | 
					                # TODO: fix this upstream
 | 
				
			||||||
 | 
					                # tls-certificate = if useACMEHost != null
 | 
				
			||||||
 | 
					                #                   then "${certConfig.directory}/fullchain.pem"
 | 
				
			||||||
 | 
					                #                   else "${tlsCertificate}";
 | 
				
			||||||
 | 
					                # tls-private-key = if useACMEHost != null
 | 
				
			||||||
 | 
					                #                   then "${certConfig.directory}/key.pem"
 | 
				
			||||||
 | 
					                #                   else "${tlsKey}";
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              extraArgs
 | 
				
			||||||
 | 
					            )} \
 | 
				
			||||||
 | 
					            ${lib.escapeShellArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"}
 | 
				
			||||||
 | 
					        '';
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  generateClientUnit = name: clientCfg: {
 | 
				
			||||||
 | 
					    name = "wstunnel-client-${name}";
 | 
				
			||||||
 | 
					    value = {
 | 
				
			||||||
 | 
					      description = "wstunnel client - ${name}";
 | 
				
			||||||
 | 
					      requires = [ "network.target" "network-online.target" ];
 | 
				
			||||||
 | 
					      after = [ "network.target" "network-online.target" ];
 | 
				
			||||||
 | 
					      wantedBy = lib.optional clientCfg.autoStart "multi-user.target";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      environment.RUST_LOG = clientCfg.loggingLevel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      serviceConfig = {
 | 
				
			||||||
 | 
					        Type = "exec";
 | 
				
			||||||
 | 
					        EnvironmentFile =
 | 
				
			||||||
 | 
					          lib.optional (clientCfg.environmentFile != null) clientCfg.environmentFile;
 | 
				
			||||||
 | 
					        DynamicUser = true;
 | 
				
			||||||
 | 
					        PrivateTmp = true;
 | 
				
			||||||
 | 
					        AmbientCapabilities =
 | 
				
			||||||
 | 
					          (lib.optionals clientCfg.addNetBind [ "CAP_NET_BIND_SERVICE" ]) ++
 | 
				
			||||||
 | 
					          (lib.optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]);
 | 
				
			||||||
 | 
					        NoNewPrivileges = true;
 | 
				
			||||||
 | 
					        RestrictNamespaces = "uts ipc pid user cgroup";
 | 
				
			||||||
 | 
					        ProtectSystem = "strict";
 | 
				
			||||||
 | 
					        ProtectHome = true;
 | 
				
			||||||
 | 
					        ProtectKernelTunables = true;
 | 
				
			||||||
 | 
					        ProtectKernelModules = true;
 | 
				
			||||||
 | 
					        ProtectControlGroups = true;
 | 
				
			||||||
 | 
					        PrivateDevices = true;
 | 
				
			||||||
 | 
					        RestrictSUIDSGID = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Restart = "on-failure";
 | 
				
			||||||
 | 
					        RestartSec = 2;
 | 
				
			||||||
 | 
					        RestartSteps = 20;
 | 
				
			||||||
 | 
					        RestartMaxDelaySec = "5min";
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      script = with clientCfg; ''
 | 
				
			||||||
 | 
					        ${lib.getExe package} \
 | 
				
			||||||
 | 
					          client \
 | 
				
			||||||
 | 
					          ${lib.cli.toGNUCommandLineShell { } (
 | 
				
			||||||
 | 
					            lib.recursiveUpdate
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              local-to-remote = localToRemote;
 | 
				
			||||||
 | 
					              remote-to-local = remoteToLocal;
 | 
				
			||||||
 | 
					              http-headers = lib.mapAttrsToList (n: v: "${n}:${v}") customHeaders;
 | 
				
			||||||
 | 
					              http-proxy = httpProxy;
 | 
				
			||||||
 | 
					              socket-so-mark = soMark;
 | 
				
			||||||
 | 
					              http-upgrade-path-prefix = upgradePathPrefix;
 | 
				
			||||||
 | 
					              tls-sni-override = tlsSNI;
 | 
				
			||||||
 | 
					              tls-verify-certificate = tlsVerifyCertificate;
 | 
				
			||||||
 | 
					              websocket-ping-frequency-sec = websocketPingInterval;
 | 
				
			||||||
 | 
					              http-upgrade-credentials = upgradeCredentials;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            extraArgs
 | 
				
			||||||
 | 
					          )} \
 | 
				
			||||||
 | 
					          ${lib.escapeShellArg connectTo}
 | 
				
			||||||
 | 
					      '';
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					in
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  options.services.wstunnel = {
 | 
				
			||||||
 | 
					    enable = lib.mkEnableOption "wstunnel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    servers = lib.mkOption {
 | 
				
			||||||
 | 
					      description = "`wstunnel` servers to set up.";
 | 
				
			||||||
 | 
					      type = lib.types.attrsOf (lib.types.submodule serverSubmodule);
 | 
				
			||||||
 | 
					      default = { };
 | 
				
			||||||
 | 
					      example = {
 | 
				
			||||||
 | 
					        "wg-tunnel" = {
 | 
				
			||||||
 | 
					          listen = {
 | 
				
			||||||
 | 
					            host = "0.0.0.0";
 | 
				
			||||||
 | 
					            port = 8080;
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          enableHTTPS = true;
 | 
				
			||||||
 | 
					          tlsCertificate = "/var/lib/secrets/fullchain.pem";
 | 
				
			||||||
 | 
					          tlsKey = "/var/lib/secrets/key.pem";
 | 
				
			||||||
 | 
					          restrictTo = [{
 | 
				
			||||||
 | 
					            host = "127.0.0.1";
 | 
				
			||||||
 | 
					            port = 51820;
 | 
				
			||||||
 | 
					          }];
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clients = lib.mkOption {
 | 
				
			||||||
 | 
					      description = "`wstunnel` clients to set up.";
 | 
				
			||||||
 | 
					      type = lib.types.attrsOf (lib.types.submodule clientSubmodule);
 | 
				
			||||||
 | 
					      default = { };
 | 
				
			||||||
 | 
					      example = {
 | 
				
			||||||
 | 
					        "wg-tunnel" = {
 | 
				
			||||||
 | 
					          connectTo = "wss://wstunnel.server.com:8443";
 | 
				
			||||||
 | 
					          localToRemote = [
 | 
				
			||||||
 | 
					            "tcp://1212:google.com:443"
 | 
				
			||||||
 | 
					            "tcp://2:n.lan:4?proxy_protocol"
 | 
				
			||||||
 | 
					          ];
 | 
				
			||||||
 | 
					          remoteToLocal = [
 | 
				
			||||||
 | 
					            "socks5://[::1]:1212"
 | 
				
			||||||
 | 
					            "unix://wstunnel.sock:g.com:443"
 | 
				
			||||||
 | 
					          ];
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  config = lib.mkIf cfg.enable {
 | 
				
			||||||
 | 
					    systemd.services =
 | 
				
			||||||
 | 
					      (lib.mapAttrs' generateServerUnit (lib.filterAttrs (n: v: v.enable) cfg.servers)) //
 | 
				
			||||||
 | 
					      (lib.mapAttrs' generateClientUnit (lib.filterAttrs (n: v: v.enable) cfg.clients));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assertions =
 | 
				
			||||||
 | 
					      (lib.mapAttrsToList
 | 
				
			||||||
 | 
					        (name: serverCfg: {
 | 
				
			||||||
 | 
					          assertion =
 | 
				
			||||||
 | 
					            !(serverCfg.useACMEHost != null && serverCfg.tlsCertificate != null);
 | 
				
			||||||
 | 
					          message = ''
 | 
				
			||||||
 | 
					            Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive.
 | 
				
			||||||
 | 
					          '';
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        cfg.servers) ++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (lib.mapAttrsToList
 | 
				
			||||||
 | 
					        (name: serverCfg: {
 | 
				
			||||||
 | 
					          assertion =
 | 
				
			||||||
 | 
					            (serverCfg.tlsCertificate == null && serverCfg.tlsKey == null) ||
 | 
				
			||||||
 | 
					            (serverCfg.tlsCertificate != null && serverCfg.tlsKey != null);
 | 
				
			||||||
 | 
					          message = ''
 | 
				
			||||||
 | 
					            services.wstunnel.servers."${name}".tlsCertificate and services.wstunnel.servers."${name}".tlsKey need to be set together.
 | 
				
			||||||
 | 
					          '';
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        cfg.servers) ++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (lib.mapAttrsToList
 | 
				
			||||||
 | 
					        (name: clientCfg: {
 | 
				
			||||||
 | 
					          assertion = !(clientCfg.localToRemote == [ ] && clientCfg.remoteToLocal == [ ]);
 | 
				
			||||||
 | 
					          message = ''
 | 
				
			||||||
 | 
					            Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".remoteToLocal must be set.
 | 
				
			||||||
 | 
					          '';
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        cfg.clients);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  meta.maintainers = with lib.maintainers; [ alyaeanyx rvdp neverbehave ];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
nix:
 | 
					nix:
 | 
				
			||||||
    access-tokens: ENC[AES256_GCM,data:K1V98nx+w0uoOY9ONDxbaZT9jbEbMqpzyYWaSrQIYfo2bm1HLeTHPqp2rqRFIPu5gD/5SqY2FW4Pak92it4S7o9liiI=,iv:/c6Mr3WQsbW7nBaa5NIG3pzatSyC9UE5zDpKjuD/FG0=,tag:8V344qvOVrgh5XHlinuFyw==,type:str]
 | 
					    access-tokens: ENC[AES256_GCM,data:K1V98nx+w0uoOY9ONDxbaZT9jbEbMqpzyYWaSrQIYfo2bm1HLeTHPqp2rqRFIPu5gD/5SqY2FW4Pak92it4S7o9liiI=,iv:/c6Mr3WQsbW7nBaa5NIG3pzatSyC9UE5zDpKjuD/FG0=,tag:8V344qvOVrgh5XHlinuFyw==,type:str]
 | 
				
			||||||
 | 
					wstunnel:
 | 
				
			||||||
 | 
					    http-upgrade-path-prefix-envvars: ENC[AES256_GCM,data:aS7Kvpj9aHtaiKZiakDuvdiDcVYFMkYv9FIH060Dbkahk6v+2bbxzgKcRtnDnLlphtGlZD7yWRcbvlYiG7Y5mRNS1X5PkspQwFKKnwGGHiWgfun9yxB6VHvPdb4W4SNA8QfRmqH4XmJUfDSPmZfh5Ggzhy7/74avC0vfqKBvQ+ml4fjqTmdS6EkFGrrUwIXFrjiCqdxnNYmp8I/L1b22R5YoY/JTsc4mG6N9s3B75GvsYI2EDG4vQ7EMyktd2CHsXJgNFRQUM+GzBbkO4VvG,iv:EbuV/2L+p4A+aloC6uQYiFFF7Lsz5A5RTGMuHMqtTpI=,tag:DThZOERbXuUdDJso7ertbg==,type:str]
 | 
				
			||||||
ssh:
 | 
					ssh:
 | 
				
			||||||
    secret-config:
 | 
					    secret-config:
 | 
				
			||||||
        home: ENC[AES256_GCM,data:eUfhQb6yYYV3951sdwZpA1f8k+79mm1bMYY4EP+tn1g7DEHXG9XHYKPL3FLJMkaaXSWv5jbBZ3zrGodJPMH9VbcFOjvSdz9u56DnmyeR3S7Pwgj1YbELDn9akeVRpjcB1w2k8hn2vNIY1MV4vg==,iv:LQpS168sxPVegrlPJNZrVZE+GsZAMxRSl4EaHO6FFxg=,tag:w5SNj7LkYd+22SbLVbtsDQ==,type:str]
 | 
					        home: ENC[AES256_GCM,data:eUfhQb6yYYV3951sdwZpA1f8k+79mm1bMYY4EP+tn1g7DEHXG9XHYKPL3FLJMkaaXSWv5jbBZ3zrGodJPMH9VbcFOjvSdz9u56DnmyeR3S7Pwgj1YbELDn9akeVRpjcB1w2k8hn2vNIY1MV4vg==,iv:LQpS168sxPVegrlPJNZrVZE+GsZAMxRSl4EaHO6FFxg=,tag:w5SNj7LkYd+22SbLVbtsDQ==,type:str]
 | 
				
			||||||
@@ -47,8 +49,8 @@ sops:
 | 
				
			|||||||
            UllPc1JCTXoxUERMM05abjhnR0g0d2sK/wyBVH6Dxris4TF05POtYQbWj4DWOeID
 | 
					            UllPc1JCTXoxUERMM05abjhnR0g0d2sK/wyBVH6Dxris4TF05POtYQbWj4DWOeID
 | 
				
			||||||
            RAdf30dDVtmg4qPwsHiIQ8f10gA1DrgIrcae0JS5VZcRLRw5/4+g9Q==
 | 
					            RAdf30dDVtmg4qPwsHiIQ8f10gA1DrgIrcae0JS5VZcRLRw5/4+g9Q==
 | 
				
			||||||
            -----END AGE ENCRYPTED FILE-----
 | 
					            -----END AGE ENCRYPTED FILE-----
 | 
				
			||||||
    lastmodified: "2024-06-25T18:25:28Z"
 | 
					    lastmodified: "2024-06-28T13:06:09Z"
 | 
				
			||||||
    mac: ENC[AES256_GCM,data:GGjXTEHVHAWrr0QHc3O4bMpGi1wFge6AbK7XEwRiOqh4W1Zow2CEcfGZxW5TLLayfB9lXemeKtrZWsqBOCXtHkd670KbxxKInE3FvJbjME8ZODAMpknYX4BXBGt6ksC03Tm4ri1JIy1OxDVXG4qb8skNtna4YkIiUf+ErTihakA=,iv:YGKnVl9QCLLTqdQfpiTbv31vEGEoolzMWtyEFvJekYI=,tag:8j+dnOqHfupKTAl1GQ09Mg==,type:str]
 | 
					    mac: ENC[AES256_GCM,data:H19kxVh+pcjGhD78WHQYMGQ/0HY/F4NF6sYCvjn4hqPfFTJDDcVJ7QFxm2LL4Zz/+KNcI3qvnXO/g/MyaVxyJMyKC9LFwHT/0TwMRW1uHSBahPASFYvN0/h+6hp6TI9/DPeWjKEGk+1j1tU665YpnqYdOtRUfQEB02fmLf2jSiw=,iv:b0b4m/SlGNXBQ3ulLhbSHngSLZiFipPV+yAD6MG8vAo=,tag:B3oHJkWlFpY+g6dVkApDMw==,type:str]
 | 
				
			||||||
    pgp:
 | 
					    pgp:
 | 
				
			||||||
        - created_at: "2024-06-26T07:42:59Z"
 | 
					        - created_at: "2024-06-26T07:42:59Z"
 | 
				
			||||||
          enc: |-
 | 
					          enc: |-
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user