diff --git a/lib/types.nix b/lib/types.nix
index 88fc90d0597..a334db5c724 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -256,6 +256,10 @@ rec {
functor = (defaultFunctor name) // { wrapped = elemType; };
};
+ nonEmptyListOf = elemType:
+ let list = addCheck (types.listOf elemType) (l: l != []);
+ in list // { description = "non-empty " + list.description; };
+
attrsOf = elemType: mkOptionType rec {
name = "attrsOf";
description = "attribute set of ${elemType.description}s";
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index fa4aeb22ae9..fed91756e76 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -88,6 +88,9 @@ let
${flip concatMapStrings v.map (p: ''
HiddenServicePort ${toString p.port} ${p.destination}
'')}
+ ${optionalString (v.authorizeClient != null) ''
+ HiddenServiceAuthorizeClient ${v.authorizeClient.authType} ${concatStringsSep "," v.authorizeClient.clientNames}
+ ''}
''))
+ cfg.extraConfig;
@@ -619,6 +622,33 @@ in
}));
};
+ authorizeClient = mkOption {
+ default = null;
+ description = "If configured, the hidden service is accessible for authorized clients only.";
+ type = types.nullOr (types.submodule ({config, ...}: {
+
+ options = {
+
+ authType = mkOption {
+ type = types.enum [ "basic" "stealth" ];
+ description = ''
+ Either "basic" for a general-purpose authorization protocol
+ or "stealth" for a less scalable protocol
+ that also hides service activity from unauthorized clients.
+ '';
+ };
+
+ clientNames = mkOption {
+ type = types.nonEmptyListOf (types.strMatching "[A-Za-z0-9+-_]+");
+ description = ''
+ Only clients that are listed here are authorized to access the hidden service.
+ Generated authorization data can be found in ${torDirectory}/onion/$name/hostname.
+ Clients need to put this authorization data in their configuration file using HidServAuth.
+ '';
+ };
+ };
+ }));
+ };
};
config = {