diff options
Diffstat (limited to 'nixos/modules/services/security')
-rw-r--r-- | nixos/modules/services/security/clamav.nix | 80 | ||||
-rw-r--r-- | nixos/modules/services/security/fail2ban.nix | 150 | ||||
-rw-r--r-- | nixos/modules/services/security/fprot.nix | 88 | ||||
-rw-r--r-- | nixos/modules/services/security/frandom.nix | 31 | ||||
-rw-r--r-- | nixos/modules/services/security/tor.nix | 321 | ||||
-rw-r--r-- | nixos/modules/services/security/torify.nix | 69 | ||||
-rw-r--r-- | nixos/modules/services/security/torsocks.nix | 85 |
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 0000000000000..5ccb4927fcb7b --- /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 0000000000000..2b2a54ef40977 --- /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 0000000000000..9f1fc4ed6d8b4 --- /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 0000000000000..9aae7b33a4304 --- /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 0000000000000..2dafb4595c639 --- /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 0000000000000..1c158906a911c --- /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 0000000000000..d6974282a6b5c --- /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 + + }; + +} |