{ config, lib, pkgs, ... }: let cfg = config.services.microsocks; cmd = if cfg.execWrapper != null then "${cfg.execWrapper} ${cfg.package}/bin/microsocks" else "${cfg.package}/bin/microsocks"; args = [ "-i" cfg.ip "-p" (toString cfg.port) ] ++ lib.optionals (cfg.authOnce) [ "-1" ] ++ lib.optionals (cfg.disableLogging) [ "-q" ] ++ lib.optionals (cfg.outgoingBindIp != null) [ "-b" cfg.outgoingBindIp ] ++ lib.optionals (cfg.authUsername != null) [ "-u" cfg.authUsername ]; in { options.services.microsocks = { enable = lib.mkEnableOption "Tiny, portable SOCKS5 server with very moderate resource usage"; user = lib.mkOption { default = "microsocks"; description = "User microsocks runs as."; type = lib.types.str; }; group = lib.mkOption { default = "microsocks"; description = "Group microsocks runs as."; type = lib.types.str; }; package = lib.mkPackageOption pkgs "microsocks" {}; ip = lib.mkOption { type = lib.types.str; default = "127.0.0.1"; description = '' IP on which microsocks should listen. Defaults to 127.0.0.1 for security reasons. ''; }; port = lib.mkOption { type = lib.types.port; default = 1080; description = "Port on which microsocks should listen."; }; disableLogging = lib.mkOption { type = lib.types.bool; default = false; description = "If true, microsocks will not log any messages to stdout/stderr."; }; authOnce = lib.mkOption { type = lib.types.bool; default = false; description = '' If true, once a specific ip address authed successfully with user/pass, it is added to a whitelist and may use the proxy without auth. ''; }; outgoingBindIp = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "Specifies which ip outgoing connections are bound to"; }; authUsername = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; example = "alice"; description = "Optional username to use for authentication."; }; authPasswordFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; example = "/run/secrets/microsocks-password"; description = "Path to a file containing the password for authentication."; }; execWrapper = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; example = '' ''${pkgs.mullvad-vpn}/bin/mullvad-exclude ''; description = '' An optional command to prepend to the microsocks command (such as proxychains, or a VPN exclude command). ''; }; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = (cfg.authUsername != null) == (cfg.authPasswordFile != null); message = "Need to set both authUsername and authPasswordFile for microsocks"; } ]; users = { users = lib.mkIf (cfg.user == "microsocks") { microsocks = { group = cfg.group; isSystemUser = true; }; }; groups = lib.mkIf (cfg.group == "microsocks") { microsocks = {}; }; }; systemd.services.microsocks = { enable = true; description = "a tiny socks server"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { User = cfg.user; Group = cfg.group; Restart = "on-failure"; RestartSec = 10; LoadCredential = lib.optionalString (cfg.authPasswordFile != null) "MICROSOCKS_PASSWORD_FILE:${cfg.authPasswordFile}"; MemoryDenyWriteExecute = true; SystemCallArchitectures = "native"; PrivateTmp = true; NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; ProtectKernelModules = true; ProtectKernelTunables = true; PrivateDevices = true; RestrictSUIDSGID = true; RestrictNamespaces = [ "cgroup" "ipc" "pid" "user" "uts" ]; }; script = if cfg.authPasswordFile != null then '' PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/MICROSOCKS_PASSWORD_FILE") ${cmd} ${lib.escapeShellArgs args} -P "$PASSWORD" '' else '' ${cmd} ${lib.escapeShellArgs args} ''; }; }; }