summary refs log tree commit diff
path: root/nixos/modules/services/security
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/security')
-rw-r--r--nixos/modules/services/security/clamav.nix80
-rw-r--r--nixos/modules/services/security/fail2ban.nix150
-rw-r--r--nixos/modules/services/security/fprot.nix88
-rw-r--r--nixos/modules/services/security/frandom.nix31
-rw-r--r--nixos/modules/services/security/tor.nix321
-rw-r--r--nixos/modules/services/security/torify.nix69
-rw-r--r--nixos/modules/services/security/torsocks.nix85
7 files changed, 824 insertions, 0 deletions
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
new file mode 100644
index 000000000000..5ccb4927fcb7
--- /dev/null
+++ b/nixos/modules/services/security/clamav.nix
@@ -0,0 +1,80 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  clamavUser = "clamav";
+  stateDir = "/var/lib/clamav";
+  clamavGroup = clamavUser;
+  cfg = config.services.clamav;
+in
+{
+  ###### interface
+
+  options = {
+
+    services.clamav = {
+      updater = {
+	enable = mkOption {
+	  default = false;
+	  description = ''
+	    Whether to enable automatic ClamAV virus definitions database updates.
+	  '';
+	};
+
+	frequency = mkOption {
+	  default = 12;
+	  description = ''
+	    Number of database checks per day.
+	  '';
+	};
+
+	config = mkOption {
+	  default = "";
+	  description = ''
+	    Extra configuration for freshclam. Contents will be added verbatim to the
+	    configuration file.
+	  '';
+	};
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.updater.enable {
+    environment.systemPackages = [ pkgs.clamav ];
+    users.extraUsers = singleton
+      { name = clamavUser;
+        uid = config.ids.uids.clamav;
+        description = "ClamAV daemon user";
+        home = stateDir;
+      };
+
+    users.extraGroups = singleton
+      { name = clamavGroup;
+        gid = config.ids.gids.clamav;
+      };
+
+    services.clamav.updater.config = ''
+      DatabaseDirectory ${stateDir}
+      Foreground yes
+      Checks ${toString cfg.updater.frequency}
+      DatabaseMirror database.clamav.net
+    '';
+
+    jobs = {
+      clamav_updater = {
+	name = "clamav-updater";
+          startOn = "started network-interfaces";
+          stopOn = "stopping network-interfaces";
+
+          preStart = ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${clamavUser}:${clamavGroup} ${stateDir}
+          '';
+          exec = "${pkgs.clamav}/bin/freshclam --config-file=${pkgs.writeText "freshclam.conf" cfg.updater.config}";
+      }; 
+    };
+
+  };
+
+}
\ No newline at end of file
diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix
new file mode 100644
index 000000000000..2b2a54ef4097
--- /dev/null
+++ b/nixos/modules/services/security/fail2ban.nix
@@ -0,0 +1,150 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.fail2ban;
+
+  fail2banConf = pkgs.writeText "fail2ban.conf" cfg.daemonConfig;
+
+  jailConf = pkgs.writeText "jail.conf"
+    (concatStringsSep "\n" (attrValues (flip mapAttrs cfg.jails (name: def:
+      optionalString (def != "") 
+        ''
+          [${name}]
+          ${def}
+        ''))));
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.fail2ban = {
+
+      daemonConfig = mkOption {
+        default =
+          ''
+            [Definition]
+            loglevel  = 3
+            logtarget = SYSLOG
+            socket    = /var/run/fail2ban/fail2ban.sock
+          '';
+        type = types.string;
+        description =
+          ''
+            The contents of Fail2ban's main configuration file.  It's
+            generally not necessary to change it.
+          '';
+      };
+
+      jails = mkOption {
+        default = { };
+        example =
+          { "apache-nohome-iptables" =
+              ''
+                # Block an IP address if it accesses a non-existent
+                # home directory more than 5 times in 10 minutes,
+                # since that indicates that it's scanning.
+                filter   = apache-nohome
+                action   = iptables-multiport[name=HTTP, port="http,https"]
+                logpath  = /var/log/httpd/error_log*
+                findtime = 600
+                bantime  = 600
+                maxretry = 5
+              '';
+          };
+        type = types.attrsOf types.string;
+        description =
+          ''
+            The configuration of each Fail2ban “jail”.  A jail
+            consists of an action (such as blocking a port using
+            <command>iptables</command>) that is triggered when a
+            filter applied to a log file triggers more than a certain
+            number of times in a certain time period.  Actions are
+            defined in <filename>/etc/fail2ban/action.d</filename>,
+            while filters are defined in
+            <filename>/etc/fail2ban/filter.d</filename>.
+          '';
+      };
+      
+    };
+
+  };
+
+  
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages = [ pkgs.fail2ban ];
+
+    environment.etc =
+      [ { source = fail2banConf;
+          target = "fail2ban/fail2ban.conf";
+        }
+        { source = jailConf;
+          target = "fail2ban/jail.conf";
+        }
+        { source = "${pkgs.fail2ban}/etc/fail2ban/action.d/*.conf";
+          target = "fail2ban/action.d";
+        }
+        { source = "${pkgs.fail2ban}/etc/fail2ban/filter.d/*.conf";
+          target = "fail2ban/filter.d";
+        }
+      ];
+
+    system.activationScripts.fail2ban =
+      ''
+        mkdir -p /var/run/fail2ban -m 0755
+      '';
+
+    systemd.services.fail2ban =
+      { description = "Fail2ban intrusion prevention system";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+      
+        restartTriggers = [ fail2banConf jailConf ];
+        path = [ pkgs.fail2ban pkgs.iptables ];
+        
+        serviceConfig =
+          { ExecStart = "${pkgs.fail2ban}/bin/fail2ban-server -f";
+            ReadOnlyDirectories = "/";
+            ReadWriteDirectories = "/var/run/fail2ban /var/tmp";
+            CapabilityBoundingSet="CAP_DAC_READ_SEARCH CAP_NET_ADMIN CAP_NET_RAW";
+          };
+
+        postStart =
+          ''
+            fail2ban-client reload
+          '';
+      };
+
+    # Add some reasonable default jails.  The special "DEFAULT" jail
+    # sets default values for all other jails.
+    services.fail2ban.jails.DEFAULT =
+      ''
+        ignoreip = 127.0.0.1/8
+        bantime  = 600
+        findtime = 600
+        maxretry = 3
+        backend  = auto
+      '';
+
+    # Block SSH if there are too many failing connection attempts.
+    services.fail2ban.jails."ssh-iptables" =
+      ''
+        filter   = sshd
+        action   = iptables[name=SSH, port=ssh, protocol=tcp]
+        logpath  = /var/log/warn
+        maxretry = 5
+      '';
+    
+  };
+
+}
diff --git a/nixos/modules/services/security/fprot.nix b/nixos/modules/services/security/fprot.nix
new file mode 100644
index 000000000000..9f1fc4ed6d8b
--- /dev/null
+++ b/nixos/modules/services/security/fprot.nix
@@ -0,0 +1,88 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  fprotUser = "fprot";
+  stateDir = "/var/lib/fprot";
+  fprotGroup = fprotUser;
+  cfg = config.services.fprot;
+in {
+  options = {
+
+    services.fprot = {
+      updater = {
+	enable = mkOption {
+	  default = false;
+	  description = ''
+	    Whether to enable automatic F-Prot virus definitions database updates.
+	  '';
+	};
+
+	productData = mkOption {
+	  default = "${pkgs.fprot}/opt/f-prot/product.data";
+	  description = ''
+	    product.data file. Defaults to the one supplied with installation package.
+	  '';
+	};
+
+	frequency = mkOption {
+	  default = 30;
+	  description = ''
+	    Update virus definitions every X minutes.
+	  '';
+	};
+
+	licenseKeyfile = mkOption {
+	  default = "${pkgs.fprot}/opt/f-prot/license.key";
+	  description = ''
+	    License keyfile. Defaults to the one supplied with installation package.
+	  '';
+	};
+
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.updater.enable {
+    environment.systemPackages = [ pkgs.fprot ];
+    environment.etc = singleton {
+      source = "${pkgs.fprot}/opt/f-prot/f-prot.conf";
+      target = "f-prot.conf";
+    };
+
+    users.extraUsers = singleton
+      { name = fprotUser;
+        uid = config.ids.uids.fprot;
+        description = "F-Prot daemon user";
+        home = stateDir;
+      };
+
+    users.extraGroups = singleton
+      { name = fprotGroup;
+        gid = config.ids.gids.fprot;
+      };
+
+    services.cron.systemCronJobs = [ "*/${toString cfg.updater.frequency} * * * * root start fprot-updater" ];
+
+    jobs = {
+      fprot_updater = {
+	name = "fprot-updater";
+	  task = true;
+
+	  # have to copy fpupdate executable because it insists on storing the virus database in the same dir
+          preStart = ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${fprotUser}:${fprotGroup} ${stateDir}
+	    cp ${pkgs.fprot}/opt/f-prot/fpupdate ${stateDir}
+	    ln -sf ${cfg.updater.productData} ${stateDir}/product.data
+          '';
+	  #setuid = fprotUser;
+	  #setgid = fprotGroup;
+          exec = "/var/lib/fprot/fpupdate --keyfile ${cfg.updater.licenseKeyfile}";
+      }; 
+    };
+
+ };
+
+}
\ No newline at end of file
diff --git a/nixos/modules/services/security/frandom.nix b/nixos/modules/services/security/frandom.nix
new file mode 100644
index 000000000000..9aae7b33a430
--- /dev/null
+++ b/nixos/modules/services/security/frandom.nix
@@ -0,0 +1,31 @@
+{pkgs, config, ...}:
+
+let kernel = config.boot.kernelPackages;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.frandom.enable = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        enable the /dev/frandom device (a very fast random number generator)
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.services.frandom.enable {
+    boot.kernelModules = [ "frandom" ];
+    boot.extraModulePackages = [ kernel.frandom ];
+    services.udev.packages = [ kernel.frandom ];
+  };
+
+}
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
new file mode 100644
index 000000000000..2dafb4595c63
--- /dev/null
+++ b/nixos/modules/services/security/tor.nix
@@ -0,0 +1,321 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) tor privoxy;
+
+  stateDir = "/var/lib/tor";
+  privoxyDir = stateDir+"/privoxy";
+
+  cfg = config.services.tor;
+
+  torUser = "tor";
+
+  opt = name: value: if value != "" then "${name} ${value}" else "";
+  optint = name: value: if value != 0 then "${name} ${toString value}" else "";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.tor = {
+
+      config = mkOption {
+        default = "";
+        description = ''
+          Extra configuration. Contents will be added verbatim to the
+          configuration file.
+        '';
+      };
+
+      client = {
+
+        enable = mkOption {
+          default = false;
+          description = ''
+            Whether to enable Tor daemon to route application connections.
+            You might want to disable this if you plan running a dedicated Tor relay.
+          '';
+        };
+
+        socksListenAddress = mkOption {
+          default = "127.0.0.1:9050";
+          example = "192.168.0.1:9100";
+          description = ''
+            Bind to this address to listen for connections from Socks-speaking
+            applications.
+          '';
+        };
+
+	socksListenAddressFaster = mkOption {
+          default = "127.0.0.1:9063";
+	  description = ''
+            Same as socksListenAddress but uses weaker circuit isolation to provide
+            performance suitable for a web browser.
+          '';
+        };
+
+        socksPolicy = mkOption {
+          default = "";
+          example = "accept 192.168.0.0/16, reject *";
+          description = ''
+            Entry policies to allow/deny SOCKS requests based on IP address.
+            First entry that matches wins. If no SocksPolicy is set, we accept
+            all (and only) requests from SocksListenAddress.
+          '';
+        };
+
+        privoxy = {
+
+          enable = mkOption {
+            default = true;
+            description = ''
+              Whether to enable a special instance of privoxy dedicated to Tor.
+              To have anonymity, protocols need to be scrubbed of identifying
+              information.
+              Most people using Tor want to anonymize their web traffic, so by
+              default we enable an special instance of privoxy specifically for
+              Tor.
+              However, if you are only going to use Tor only for other kinds of
+              traffic then you can disable this option.
+            '';
+          };
+
+          listenAddress = mkOption {
+            default = "127.0.0.1:8118";
+            description = ''
+              Address that Tor's instance of privoxy is listening to.
+              *This does not configure the standard NixOS instance of privoxy.*
+              This is for Tor connections only!
+              See services.privoxy.listenAddress to configure the standard NixOS
+              instace of privoxy.
+            '';
+          };
+
+          config = mkOption {
+            default = "";
+            description = ''
+              Extra configuration for Tor's instance of privoxy. Contents will be
+              added verbatim to the configuration file.
+              *This does not configure the standard NixOS instance of privoxy.*
+              This is for Tor connections only!
+              See services.privoxy.extraConfig to configure the standard NixOS
+              instace of privoxy.
+            '';
+          };
+
+        };
+
+      };
+
+      relay = {
+
+        enable = mkOption {
+          default = false;
+          description = ''
+            Whether to enable relaying TOR traffic for others.
+
+            See https://www.torproject.org/docs/tor-doc-relay for details.
+          '';
+        };
+
+        isBridge = mkOption {
+          default = false;
+          description = ''
+            Bridge relays (or "bridges" ) are Tor relays that aren't listed in the
+            main directory. Since there is no complete public list of them, even if an
+            ISP is filtering connections to all the known Tor relays, they probably
+            won't be able to block all the bridges.
+
+            A bridge relay can't be an exit relay.
+
+            You need to set relay.enable to true for this option to take effect.
+
+            The bridge is set up with an obfuscated transport proxy.
+
+            See https://www.torproject.org/bridges.html.en for more info.
+          '';
+        };
+
+        isExit = mkOption {
+          default = false;
+          description = ''
+            An exit relay allows Tor users to access regular Internet services.
+
+            Unlike running a non-exit relay, running an exit relay may expose
+            you to abuse complaints. See https://www.torproject.org/faq.html.en#ExitPolicies for more info.
+
+            You can specify which services Tor users may access via your exit relay using exitPolicy option.
+          '';
+        };
+
+        nickname = mkOption {
+          default = "anonymous";
+          description = ''
+            A unique handle for your TOR relay.
+          '';
+        };
+
+        bandwidthRate = mkOption {
+          default = 0;
+          example = 100;
+          description = ''
+            Specify this to limit the bandwidth usage of relayed (server)
+            traffic. Your own traffic is still unthrottled. Units: bytes/second.
+          '';
+        };
+
+        bandwidthBurst = mkOption {
+          default = cfg.relay.bandwidthRate;
+          example = 200;
+          description = ''
+            Specify this to allow bursts of the bandwidth usage of relayed (server)
+            traffic. The average usage will still be as specified in relayBandwidthRate.
+            Your own traffic is still unthrottled. Units: bytes/second.
+          '';
+        };
+
+        port = mkOption {
+          default = 9001;
+          description = ''
+            What port to advertise for Tor connections.
+          '';
+        };
+
+        listenAddress = mkOption {
+          default = "";
+          example = "0.0.0.0:9090";
+          description = ''
+            Set this if you need to listen on a port other than the one advertised
+            in relayPort (e.g. to advertise 443 but bind to 9090). You'll need to do
+            ipchains or other port forwsarding yourself to make this work.
+          '';
+        };
+
+        exitPolicy = mkOption {
+          default = "";
+          example = "accept *:6660-6667,reject *:*";
+          description = ''
+            A comma-separated list of exit policies. They're considered first
+            to last, and the first match wins. If you want to _replace_
+            the default exit policy, end this with either a reject *:* or an
+            accept *:*. Otherwise, you're _augmenting_ (prepending to) the
+            default exit policy. Leave commented to just use the default, which is
+            available in the man page or at https://www.torproject.org/documentation.html
+
+            Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses
+            for issues you might encounter if you use the default exit policy.
+
+            If certain IPs and ports are blocked externally, e.g. by your firewall,
+            you should update your exit policy to reflect this -- otherwise Tor
+            users will be told that those destinations are down.
+          '';
+        };
+
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.client.enable || cfg.relay.enable) (
+  mkAssert (cfg.relay.enable -> !(cfg.relay.isBridge && cfg.relay.isExit)) "
+    Can't be both an exit and a bridge relay at the same time
+  " {
+
+    users.extraUsers = singleton
+      { name = torUser;
+        uid = config.ids.uids.tor;
+        description = "Tor daemon user";
+        home = stateDir;
+      };
+
+    jobs = {
+      tor = { name = "tor";
+
+              startOn = "started network-interfaces";
+              stopOn = "stopping network-interfaces";
+
+              preStart = ''
+                mkdir -m 0755 -p ${stateDir}
+                chown ${torUser} ${stateDir}
+              '';
+              exec = "${tor}/bin/tor -f ${pkgs.writeText "torrc" cfg.config}";
+    }; }
+    // optionalAttrs (cfg.client.privoxy.enable && cfg.client.enable) {
+      torPrivoxy = { name = "tor-privoxy";
+
+                     startOn = "started network-interfaces";
+                     stopOn = "stopping network-interfaces";
+
+                     preStart = ''
+                       mkdir -m 0755 -p ${privoxyDir}
+                       chown ${torUser} ${privoxyDir}
+                     '';
+                     exec = "${privoxy}/sbin/privoxy --no-daemon --user ${torUser} ${pkgs.writeText "torPrivoxy.conf" cfg.client.privoxy.config}";
+    }; };
+
+      services.tor.config = ''
+        DataDirectory ${stateDir}
+        User ${torUser}
+      ''
+      + optionalString cfg.client.enable  ''
+        SOCKSPort ${cfg.client.socksListenAddress} IsolateDestAddr
+	SOCKSPort ${cfg.client.socksListenAddressFaster}
+        ${opt "SocksPolicy" cfg.client.socksPolicy}
+      ''
+      + optionalString cfg.relay.enable ''
+        ORPort ${toString cfg.relay.port}
+        ${opt "ORListenAddress" cfg.relay.listenAddress }
+        ${opt "Nickname" cfg.relay.nickname}
+        ${optint "RelayBandwidthRate" cfg.relay.bandwidthRate}
+        ${optint "RelayBandwidthBurst" cfg.relay.bandwidthBurst}
+        ${if cfg.relay.isExit then opt "ExitPolicy" cfg.relay.exitPolicy else "ExitPolicy reject *:*"}
+        ${if cfg.relay.isBridge then ''
+          BridgeRelay 1
+          ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed
+        '' else ""}
+      '';
+
+      services.tor.client.privoxy.config = ''
+        # Generally, this file goes in /etc/privoxy/config
+        #
+        # Tor listens as a SOCKS4a proxy here:
+        forward-socks4a / ${cfg.client.socksListenAddressFaster} .
+        confdir ${privoxy}/etc
+        logdir ${privoxyDir}
+        # actionsfile standard  # Internal purpose, recommended
+        actionsfile default.action   # Main actions file
+        actionsfile user.action      # User customizations
+        filterfile default.filter
+
+        # Don't log interesting things, only startup messages, warnings and errors
+        logfile logfile
+        #jarfile jarfile
+        #debug   0    # show each GET/POST/CONNECT request
+        debug   4096 # Startup banner and warnings
+        debug   8192 # Errors - *we highly recommended enabling this*
+
+        user-manual ${privoxy}/doc/privoxy/user-manual
+        listen-address  ${cfg.client.privoxy.listenAddress}
+        toggle  1
+        enable-remote-toggle 0
+        enable-edit-actions 0
+        enable-remote-http-toggle 0
+        buffer-limit 4096
+
+        # Extra config goes here
+      '';
+
+  });
+
+}
diff --git a/nixos/modules/services/security/torify.nix b/nixos/modules/services/security/torify.nix
new file mode 100644
index 000000000000..1c158906a911
--- /dev/null
+++ b/nixos/modules/services/security/torify.nix
@@ -0,0 +1,69 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+
+  cfg = config.services.tor;
+
+  torify = pkgs.writeTextFile {
+    name = "torify";
+    text = ''
+        #!${pkgs.stdenv.shell}
+        TSOCKS_CONF_FILE=${pkgs.writeText "tsocks.conf" cfg.torify.config} LD_PRELOAD="${pkgs.tsocks}/lib/libtsocks.so $LD_PRELOAD" "$@"
+    '';
+    executable = true;
+    destination = "/bin/torify";
+  };
+
+in
+
+{
+
+  ###### interface
+  
+  options = {
+  
+    services.tor.torify = {
+
+      enable = mkOption {
+        default = cfg.client.enable;
+        description = ''
+          Whether to build torify scipt to relay application traffic via TOR.
+        '';
+      };
+
+      server = mkOption {
+        default = "localhost:9050";
+        example = "192.168.0.20";
+        description = ''
+          IP address of TOR client to use.
+        '';
+      };
+
+      config = mkOption {
+        default = "";
+        description = ''
+          Extra configuration. Contents will be added verbatim to TSocks
+          configuration file.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.torify.enable {
+
+    environment.systemPackages = [ torify ];  # expose it to the users
+
+    services.tor.torify.config = ''
+      server = ${toString(head (splitString ":" cfg.torify.server))}
+      server_port = ${toString(tail (splitString ":" cfg.torify.server))}
+
+      local = 127.0.0.0/255.128.0.0
+      local = 127.128.0.0/255.192.0.0
+    '';
+  };
+
+}
diff --git a/nixos/modules/services/security/torsocks.nix b/nixos/modules/services/security/torsocks.nix
new file mode 100644
index 000000000000..d6974282a6b5
--- /dev/null
+++ b/nixos/modules/services/security/torsocks.nix
@@ -0,0 +1,85 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+
+  cfg = config.services.tor;
+
+  makeConfig = server: ''
+      server = ${toString(head (splitString ":" server))}
+      server_port = ${toString(tail (splitString ":" server))}
+
+      local = 127.0.0.0/255.128.0.0
+      local = 127.128.0.0/255.192.0.0
+      local = 169.254.0.0/255.255.0.0
+      local = 172.16.0.0/255.240.0.0
+      local = 192.168.0.0/255.255.0.0
+
+      ${cfg.torsocks.config}
+    '';
+  makeTorsocks = name: server: pkgs.writeTextFile {
+    name = name;
+    text = ''
+        #!${pkgs.stdenv.shell}
+        TORSOCKS_CONF_FILE=${pkgs.writeText "torsocks.conf" (makeConfig server)} LD_PRELOAD="${pkgs.torsocks}/lib/torsocks/libtorsocks.so $LD_PRELOAD" "$@"
+    '';
+    executable = true;
+    destination = "/bin/${name}";
+  };
+
+  torsocks = makeTorsocks "torsocks" cfg.torsocks.server;
+  torsocksFaster = makeTorsocks "torsocks-faster" cfg.torsocks.serverFaster;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.tor.torsocks = {
+
+      enable = mkOption {
+        default = cfg.client.enable;
+        description = ''
+          Whether to build torsocks scipt to relay application traffic via TOR.
+        '';
+      };
+
+      server = mkOption {
+        default = cfg.client.socksListenAddress;
+        example = "192.168.0.20:9050";
+        description = ''
+          IP address of TOR client to use.
+        '';
+      };
+
+      serverFaster = mkOption {
+        default = cfg.client.socksListenAddressFaster;
+        example = "192.168.0.20:9063";
+        description = ''
+          IP address of TOR client to use for applications like web browsers which
+	  need less circuit isolation to achive satisfactory performance.
+        '';
+      };
+
+      config = mkOption {
+        default = "";
+        description = ''
+          Extra configuration. Contents will be added verbatim to torsocks
+          configuration file.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.torsocks.enable {
+
+    environment.systemPackages = [ torsocks torsocksFaster ];  # expose it to the users
+
+  };
+
+}