From fea553028bbfbe4e82588eb8e22b72d64973764b Mon Sep 17 00:00:00 2001
From: Julien Moutinho <julm@sourcephile.fr>
Date: Sat, 5 Sep 2020 08:23:00 +0200
Subject: [PATCH] tor: preparation

---
 flake.lock                                    |   8 +-
 flake.nix                                     |  14 +-
 machines/losurdo.nix                          |   2 +-
 machines/losurdo/fail2ban.nix                 |   2 +-
 machines/losurdo/networking.nix               |   5 +-
 machines/losurdo/networking/nftables.nix      | 219 +++--
 machines/losurdo/networking/tor.nix           |  62 ++
 .../losurdo/nginx/sourcephile.fr/losurdo.nix  |   3 +
 machines/losurdo/postgresql.nix               |  28 +-
 machines/losurdo/prosody.nix                  |   9 +-
 machines/losurdo/users.nix                    |   1 +
 machines/mermet/networking/nftables.nix       |  38 +-
 nixos/modules.nix                             |   4 +
 nixos/modules/services/security/tor.nix       | 799 ++++++++++++++++++
 nixos/profiles/hardware/dl10j.nix             |   6 +-
 nixpkgs/patches/fix-ld-nix-apparmor.diff      |  13 -
 nixpkgs/patches/fix-ld-nix.diff               |  66 --
 17 files changed, 1097 insertions(+), 182 deletions(-)
 create mode 100644 machines/losurdo/networking/tor.nix
 create mode 100644 nixos/modules/services/security/tor.nix
 delete mode 100644 nixpkgs/patches/fix-ld-nix-apparmor.diff
 delete mode 100644 nixpkgs/patches/fix-ld-nix.diff

diff --git a/flake.lock b/flake.lock
index 675f3ed..dd0d514 100644
--- a/flake.lock
+++ b/flake.lock
@@ -17,17 +17,17 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1598251641,
-        "narHash": "sha256-5XRLDKBbFEyVdwbI+pXAkLFgM03wAEZCvH1sr4uSFKI=",
+        "lastModified": 1599246614,
+        "narHash": "sha256-l2QyMGtf8H4jbgbAPefbevMeYOufTzbDAdj35gaaCoY=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "a332da8588aeea4feb9359d23f58d95520899e3c",
+        "rev": "e3e39aa84e9d8c381d9f420f08a57eccb6e0a285",
         "type": "github"
       },
       "original": {
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "a332da8588aeea4feb9359d23f58d95520899e3c",
+        "rev": "e3e39aa84e9d8c381d9f420f08a57eccb6e0a285",
         "type": "github"
       }
     },
diff --git a/flake.nix b/flake.nix
index 4d89519..466f1cb 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,5 +1,5 @@
 {
-inputs.nixpkgs.url = "github:NixOS/nixpkgs/a332da8588aeea4feb9359d23f58d95520899e3c";
+inputs.nixpkgs.url = "github:NixOS/nixpkgs/e3e39aa84e9d8c381d9f420f08a57eccb6e0a285";
 inputs.flake-utils.url = "github:numtide/flake-utils";
 inputs.shell = { type = "path"; path = "./shell"; flake = false; };
 inputs.secrets = { type = "path"; path = "./sec"; flake = false; };
@@ -25,7 +25,7 @@ outputs = inputs: let
     }
     { meta.description = "apparmor: fix and improve the service";
       url = "https://github.com/NixOS/nixpkgs/pull/93457.diff";
-      sha256 = "8QNOKDWNJiZujAvmDtZPZI5VRc+oh40IqjTYn/RCUi4=";
+      sha256 = "sha256-GxTZvaQrUxxV8vd7DT42k0xEXMQt2mAgEdiY3fsA8Gs=";
     }
     { meta.description = "nixos/security.gnupg: provisioning GnuPG-protected secrets through the Nix store";
       url = "https://github.com/NixOS/nixpkgs/pull/93659.diff";
@@ -33,11 +33,7 @@ outputs = inputs: let
     }
     { meta.description = "nixos/croc: init";
       url = "https://github.com/NixOS/nixpkgs/pull/93629.diff";
-      sha256 = "0fv3lpj244hvxyixxv4akrr70jv5wwbhb3kmbmd2yskx59a71rch";
-    }
-    { meta.description = "nixos/biboumi: init";
-      url = "https://github.com/NixOS/nixpkgs/pull/94917.diff";
-      sha256 = "sha256-kUYk6Lr31rXUkbGnnIfpls6v1Jw/EVF4giByoH4MfEk=";
+      sha256 = "sha256-hzs5Z9cYCo6aNFfylB37W+1neejV4/kVyM1QmqVhVww=";
     }
     { meta.description = "dovecot_fts_xapian: 1.3.1 -> 1.3.3";
       url = "https://github.com/NixOS/nixpkgs/pull/94938.diff";
@@ -45,7 +41,7 @@ outputs = inputs: let
     }
     { meta.description = "nixos-install: add support for flakes";
       url = "https://github.com/NixOS/nixpkgs/pull/95194.diff";
-      sha256 = "sha256-Y0KE9i+NFa3xLDdXXcVEAbpm//W7tCeOtKArgf2Dw2M=";
+      sha256 = "sha256-7EsqWguUWC4CQCMV0ZKQmjlsuLnQuR2/gI7Z/8sM8ww=";
     }
     { meta.description = "transmission: use freeformType on settings";
       url = "https://github.com/NixOS/nixpkgs/pull/96655.diff";
@@ -53,10 +49,8 @@ outputs = inputs: let
     }
   ];
   localNixpkgsPatches = [
-    #nixpkgs/patches/security.apparmor.diff
     #nixpkgs/patches/fix-ld-nix.diff
     #nixpkgs/patches/fix-ld-nix-apparmor.diff
-    #nixpkgs/patches/nixos-install.diff
   ];
   originPkgs = inputs.nixpkgs.legacyPackages."x86_64-linux";
   nixpkgs = originPkgs.applyPatches {
diff --git a/machines/losurdo.nix b/machines/losurdo.nix
index 5f1f9ef..8984cb7 100644
--- a/machines/losurdo.nix
+++ b/machines/losurdo.nix
@@ -30,7 +30,7 @@ modules = [
   losurdo/hardware.nix
   losurdo/networking.nix
   losurdo/nginx.nix
-  losurdo/postgresql.nix
+  #losurdo/postgresql.nix
   losurdo/prosody.nix
   (inputs.secrets + "/machines/losurdo/prosody.nix")
   losurdo/sanoid.nix
diff --git a/machines/losurdo/fail2ban.nix b/machines/losurdo/fail2ban.nix
index 79ae851..e1b2794 100644
--- a/machines/losurdo/fail2ban.nix
+++ b/machines/losurdo/fail2ban.nix
@@ -1,7 +1,7 @@
 { pkgs, lib, config, machines, ... }:
 {
 services.openssh.logLevel = "VERBOSE";
-services.postgresql.extraConfig = "log_line_prefix = '%h '";
+services.postgresql.settings.log_line_prefix = "%h ";
 /*
 systemd.services.nftables.postStart = '' systemctl reload fail2ban '';
 */
diff --git a/machines/losurdo/networking.nix b/machines/losurdo/networking.nix
index b6c3420..6f7ade2 100644
--- a/machines/losurdo/networking.nix
+++ b/machines/losurdo/networking.nix
@@ -1,6 +1,7 @@
 { pkgs, lib, config, machineName, machines, wireguard, ... }:
 with builtins;
 let
+  inherit (config) networking;
   #lanIPv4        = "192.168.1.215";
   lanNet         = "192.168.1.0/24";
   #lanIPv4Gateway = "192.168.1.1";
@@ -10,6 +11,7 @@ imports = [
   networking/nftables.nix
   networking/ssh.nix
   networking/wireguard.nix
+  networking/tor.nix
 ];
 
 boot.initrd.network = {
@@ -89,8 +91,9 @@ boot.kernel.sysctl = {
 networking = {
   hostName = machineName;
   domain = "sourcephile.fr";
-  
+
   useDHCP = false;
+  enableIPv6 = true;
   /*
   defaultGateway = {
     address = lanIPv4Gateway;
diff --git a/machines/losurdo/networking/nftables.nix b/machines/losurdo/networking/nftables.nix
index dc4a1c2..1e21912 100644
--- a/machines/losurdo/networking/nftables.nix
+++ b/machines/losurdo/networking/nftables.nix
@@ -2,6 +2,7 @@
 let
   inherit (builtins) hasAttr readFile;
   inherit (pkgs.lib) unlinesAttrs;
+  inherit (config) networking;
   inherit (config.users) users groups;
 in
 {
@@ -15,25 +16,21 @@ networking.nftables = {
     table inet filter {
       set lograte4 { type ipv4_addr; size 65535; flags dynamic; }
       set lograte6 { type ipv6_addr; size 65535; flags dynamic; }
+      chain block {
+        add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "block: "
+        add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "block: "
+        counter drop
+      }
       chain ping-flood {
         add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "ping-flood: "
         add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "ping-flood: "
         counter drop
       }
-      chain check-ping {
-        ip protocol icmp   icmp   type echo-request limit rate over 10/second burst 20 packets goto ping-flood
-        ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 10/second burst 20 packets goto ping-flood
-      }
       chain smurf {
         add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "smurf: "
         add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "smurf: "
         counter drop
       }
-      chain check-broadcast {
-        ip saddr 0.0.0.0/32 counter accept comment "DHCP broadcast"
-        fib saddr type broadcast counter goto smurf
-        ip saddr 224.0.0.0/4     counter goto smurf
-      }
       chain bogus-tcp {
         add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "bogus-tcp: "
         add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "bogus-tcp: "
@@ -70,15 +67,14 @@ networking.nftables = {
         #jump non-internet
 
         #ct state new add @connlimit { ip saddr ct count over 20 } counter tcp reject with tcp reset
-        
+
         # Some .nix append rules here with: add rule inet filter net2fw ...
       }
       chain fw2net {
-        ip daddr 224.0.0.0/4 udp dport 1900 counter accept comment "UPnP"
         tcp dport { 80, 443 } counter accept comment "HTTP"
         udp dport 123 skuid ${users.systemd-timesync.name} counter accept comment "NTP"
         tcp dport 9418 counter accept comment "Git"
-        
+
         # Some .nix append rules here with: add rule inet filter fw2net ...
       }
       chain intra2fw {
@@ -90,53 +86,171 @@ networking.nftables = {
       chain fwd-intra {
         # Some .nix append rules here with: add rule inet filter fwd-intra ...
       }
+      chain icmpv6 {
+        # Traffic That Must Not Be Dropped
+        # https://tools.ietf.org/html/rfc4890#section-4.4.1
+        ip6 nexthdr ipv6-icmp icmpv6 type destination-unreachable counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type packet-too-big counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type time-exceeded counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type parameter-problem counter accept
+
+        # Address Configuration and Router Selection messages
+        # (must be received with hop limit = 255)
+        ip6 nexthdr ipv6-icmp icmpv6 type nd-router-solicit ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type nd-router-advert ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type nd-neighbor-solicit ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type nd-neighbor-advert ip6 hoplimit 255 counter accept
+        # redirect messages provide a significant security risk,
+        # and administrators should take a case-by-case approach
+        # to whether firewalls, routers in general,
+        # and other nodes should accept these messages
+        #ip6 nexthdr ipv6-icmp icmpv6 type redirect ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type ind-neighbor-solicit ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type ind-neighbor-advert ip6 hoplimit 255 counter accept
+
+        # Link-local multicast receiver notification messages
+        # (must have link-local source address)
+        ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-query ip6 saddr fe80::/10 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-report ip6 saddr fe80::/10 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-done ip6 saddr fe80::/10 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type mld2-listener-report ip6 saddr fe80::/10 counter accept
+
+        # SEND Certificate Path notification messages
+        # (must be received with hop limit = 255)
+        ip6 nexthdr ipv6-icmp icmpv6 type 148 ip6 hoplimit 255 counter accept comment "certificate-path-solicitation"
+        ip6 nexthdr ipv6-icmp icmpv6 type 149 ip6 hoplimit 255 counter accept comment "certificate-path-advertisement"
+
+        # Multicast Router Discovery messages
+        # (must have link-local source address and hop limit = 1)
+        ip6 nexthdr ipv6-icmp icmpv6 type 151 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-advertisement"
+        ip6 nexthdr ipv6-icmp icmpv6 type 152 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-solicitation"
+        ip6 nexthdr ipv6-icmp icmpv6 type 153 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-termination"
+      }
 
       chain input {
         type filter hook input priority 0
         policy drop
-    
+
         iifname lo accept
-        
+
         jump check-tcp
-        jump check-ping
-        jump check-broadcast
-    
+
         ct state { established, related } accept
+
+        # (multicast) ping
+        ip protocol icmp icmp type echo-reply counter accept
+
+        ${lib.optionalString networking.enableIPv6 ''
+        # drop packets with rh0 headers
+        rt type 0 jump block
+        rt type 0 jump block
+        rt type 0 jump block
+
+        # (multicast) ping
+        ip6 nexthdr ipv6-icmp icmpv6 type echo-reply counter accept
+        ''}
+
         ct state invalid counter drop
-    
+
+        ip protocol icmp icmp type destination-unreachable counter accept
+        ip protocol icmp icmp type time-exceeded counter accept
+        ip protocol icmp icmp type parameter-problem counter accept
+        ip protocol icmp icmp type echo-request limit rate over 10/second burst 20 packets goto ping-flood
+        ip protocol icmp icmp type echo-request counter accept
+        # echo-reply is handled before invalid packets to allow multicast ping
+        # which do not have an associated connection.
+
+        #ip daddr 224.0.0.251 udp dport 5353 counter accept comment "mDNS"
+        #ip daddr 239.255.255.250 udp dport 1900 counter accept comment "UPnP"
+        #ip saddr 0.0.0.0/32 counter accept comment "DHCP"
+        #ip udp sport 67 udp dport 68 counter accept comment "DHCP"
+
+        ${lib.optionalString networking.enableIPv6 ''
+        # Traffic That Must Not Be Dropped
+        # https://tools.ietf.org/html/rfc4890#section-4.4.1
+        ip6 nexthdr ipv6-icmp icmpv6 type destination-unreachable counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type packet-too-big counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type time-exceeded counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type parameter-problem counter accept
+
+        # Connectivity checking messages
+        ip6 nexthdr ipv6-icmp icmpv6 type echo-request limit rate over 10/second burst 20 packets goto ping-flood
+        ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter accept
+        # echo-reply is handled before invalid packets to allow multicast ping
+        # which do not have an associated connection.
+
+        # Address Configuration and Router Selection messages
+        # (must be received with hop limit = 255)
+        ip6 nexthdr ipv6-icmp icmpv6 type nd-router-solicit ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type nd-router-advert ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type nd-neighbor-solicit ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type nd-neighbor-advert ip6 hoplimit 255 counter accept
+        # redirect messages provide a significant security risk,
+        # and administrators should take a case-by-case approach
+        # to whether firewalls, routers in general,
+        # and other nodes should accept these messages
+        #ip6 nexthdr ipv6-icmp icmpv6 type redirect ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type ind-neighbor-solicit ip6 hoplimit 255 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type ind-neighbor-advert ip6 hoplimit 255 counter accept
+
+        # Link-local multicast receiver notification messages
+        # (must have link-local source address)
+        ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-query ip6 saddr fe80::/10 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-report ip6 saddr fe80::/10 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-done ip6 saddr fe80::/10 counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type mld2-listener-report ip6 saddr fe80::/10 counter accept
+
+        # SEND Certificate Path notification messages
+        # (must be received with hop limit = 255)
+        ip6 nexthdr ipv6-icmp icmpv6 type 148 ip6 hoplimit 255 counter accept comment "certificate-path-solicitation"
+        ip6 nexthdr ipv6-icmp icmpv6 type 149 ip6 hoplimit 255 counter accept comment "certificate-path-advertisement"
+
+        # Multicast Router Discovery messages
+        # (must have link-local source address and hop limit = 1)
+        ip6 nexthdr ipv6-icmp icmpv6 type 151 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-advertisement"
+        ip6 nexthdr ipv6-icmp icmpv6 type 152 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-solicitation"
+        ip6 nexthdr ipv6-icmp icmpv6 type 153 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-termination"
+
+        ip6 daddr ff02::fb udp dport 5353 counter accept comment "mDNS"
+        ip6 daddr ff02::f udp dport 1900 counter accept comment "UPnP"
+        ''}
+
+        ip saddr 224.0.0.0/4 counter goto smurf
+        fib saddr type broadcast counter goto smurf
+
         # admin services
         tcp dport 22 counter accept comment "SSH"
         udp dport 60000-61000 counter accept comment "Mosh"
-    
-        # ICMP
-        ip protocol icmp icmp type echo-request counter accept
+
+        # Some .nix append gotos here with: add rule inet filter input iffname ... goto ...
+      }
+      chain forward {
+        type filter hook forward priority 0
+        policy drop
+
         ip protocol icmp icmp type destination-unreachable counter accept
-        ip protocol icmp icmp type router-solicitation counter accept
-        ip protocol icmp icmp type router-advertisement counter accept
         ip protocol icmp icmp type time-exceeded counter accept
         ip protocol icmp icmp type parameter-problem counter accept
-        ip protocol icmp log level warn prefix "net2fw: icmpv: " counter accept
-        #ip protocol icmp icmp type { echo-request, destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } counter accept
-
-        ip6 nexthdr icmpv6 icmpv6 type echo-request counter accept
-        ip6 nexthdr icmpv6 icmpv6 type nd-neighbor-solicit counter accept
-        ip6 nexthdr icmpv6 icmpv6 type nd-neighbor-advert counter accept
-        ip6 nexthdr icmpv6 icmpv6 type nd-router-solicit counter accept
-        ip6 nexthdr icmpv6 icmpv6 type nd-router-advert counter accept
-        ip6 nexthdr icmpv6 icmpv6 type mld-listener-query counter accept
-        ip6 nexthdr icmpv6 icmpv6 type mld-listener-report counter accept
-        ip6 nexthdr icmpv6 icmpv6 type mld-listener-reduction counter accept
-        ip6 nexthdr icmpv6 icmpv6 type destination-unreachable counter accept
-        ip6 nexthdr icmpv6 icmpv6 type packet-too-big counter accept
-        ip6 nexthdr icmpv6 icmpv6 type time-exceeded counter accept
-        ip6 nexthdr icmpv6 icmpv6 type parameter-problem counter accept
-        ip6 nexthdr icmpv6 icmpv6 type ind-neighbor-solicit counter accept
-        ip6 nexthdr icmpv6 icmpv6 type ind-neighbor-advert counter accept
-        ip6 nexthdr icmpv6 icmpv6 type mld2-listener-report counter accept
-        ip6 nexthdr icmpv6 log level warn prefix "net2fw: icmpv6: " counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit, nd-router-advert, mld-listener-query, mld-listener-report, mld-listener-reduction, destination-unreachable, packet-too-big, time-exceeded, parameter-problem, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } counter accept
+        ip protocol icmp icmp type echo-request counter accept
 
-        # Some .nix append gotos here with: add rule inet filter input iffname ... goto ...
+        ${lib.optionalString networking.enableIPv6 ''
+        # Traffic That Must Not Be Dropped
+        # https://tools.ietf.org/html/rfc4890#section-4.3.1
+        ip6 nexthdr ipv6-icmp icmpv6 type destination-unreachable counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type packet-too-big counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type time-exceeded counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type parameter-problem counter accept
+
+        ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type echo-reply counter accept
+
+        # Traffic That Normally Should Not Be Dropped
+        # https://tools.ietf.org/html/rfc4890#section-4.3.2
+        ip6 nexthdr ipv6-icmp icmpv6 type 144 counter accept comment "home-agent-address-discovery-request"
+        ip6 nexthdr ipv6-icmp icmpv6 type 145 counter accept comment "home-agent-address-discovery-reply"
+        ip6 nexthdr ipv6-icmp icmpv6 type 146 counter accept comment "mobile-prefix-solicitation"
+        ip6 nexthdr ipv6-icmp icmpv6 type 147 counter accept comment "mobile-prefix-advertisement"
+        ''}
       }
       chain output {
         type filter hook output priority 0
@@ -147,18 +261,23 @@ networking.nftables = {
         ct state { related, established } accept
         ct state invalid counter drop
 
-        # ICMP
         ip protocol icmp counter accept
-        ip6 nexthdr icmpv6 counter accept
+        ip daddr 224.0.0.0/4 udp dport 1900 counter accept comment "UPnP"
+
+        ${lib.optionalString networking.enableIPv6 ''
+        # Connectivity checking messages
+        ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter accept
+        ip6 nexthdr ipv6-icmp icmpv6 type echo-reply counter accept
+        jump icmpv6
+
+
+        ip6 nexthdr udp ip6 saddr fe80::/10 udp sport 547 ip6 daddr fe80::/10 udp dport 546 counter accept comment "DHCPv6"
+        ''}
 
         tcp dport 22 counter accept comment "SSH"
 
         # Some .nix append gotos here with: add rule inet filter output oifname ... goto ...
       }
-      chain forward {
-        type filter hook forward priority 0
-        policy drop
-      }
     }
   '';
 };
diff --git a/machines/losurdo/networking/tor.nix b/machines/losurdo/networking/tor.nix
new file mode 100644
index 0000000..21bbc8a
--- /dev/null
+++ b/machines/losurdo/networking/tor.nix
@@ -0,0 +1,62 @@
+{ pkgs, lib, config, ... }:
+let inherit (config) networking; in
+{
+environment.systemPackages = [
+  pkgs.tor
+];
+
+systemd.services.tor.serviceConfig.StateDirectory = [
+  "tor"
+  "tor/onion"
+  "tor/onion/${networking.domain}"
+];
+systemd.services.tor.serviceConfig.StateDirectoryMode = "0700";
+services.tor = {
+  enable = true;
+  enableGeoIP = false;
+  hiddenServices = {
+    "${networking.domain}/${networking.hostName}".map = [
+      { port = 22; }
+    ];
+  };
+  extraConfig = ''
+    ClientUseIPv4 1
+    ClientUseIPv6 1
+    ClientPreferIPv6ORPort 0
+  '';
+};
+/*
+# copy your onion folder
+boot.initrd.secrets = {
+  "/etc/tor/onion/bootup" = /home/tony/tor/onion; # maybe find a better spot to store this.
+};
+
+# copy tor to you initrd
+boot.initrd.extraUtilsCommands = ''
+  copy_bin_and_libs ${pkgs.tor}/bin/tor
+'';
+
+# start tor during boot process
+boot.initrd.network.postCommands = let
+  torRc = (pkgs.writeText "tor.rc" ''
+    DataDirectory /etc/tor
+    SOCKSPort 127.0.0.1:9050 IsolateDestAddr
+    SOCKSPort 127.0.0.1:9063
+    HiddenServiceDir /etc/tor/onion/bootup
+    HiddenServicePort 22 127.0.0.1:22
+  '');
+in ''
+  echo "tor: preparing onion folder"
+  # have to do this otherwise tor does not want to start
+  chmod -R 700 /etc/tor
+
+  echo "make sure localhost is up"
+  ip a a 127.0.0.1/8 dev lo
+  ip link set lo up
+
+  echo "tor: starting tor"
+  tor -f ${torRc} --verify-config
+  tor -f ${torRc} &
+'';
+*/
+}
diff --git a/machines/losurdo/nginx/sourcephile.fr/losurdo.nix b/machines/losurdo/nginx/sourcephile.fr/losurdo.nix
index 6b76546..a4d1579 100644
--- a/machines/losurdo/nginx/sourcephile.fr/losurdo.nix
+++ b/machines/losurdo/nginx/sourcephile.fr/losurdo.nix
@@ -7,6 +7,9 @@ let
   srv = "losurdo";
 in
 {
+services.tor.hiddenServices."${domain}/${srv}".map = [
+  { port = 443; toPort = 8443; }
+];
 services.nginx = {
   virtualHosts."${srv}" = {
     serverName = "${srv}.${domain}";
diff --git a/machines/losurdo/postgresql.nix b/machines/losurdo/postgresql.nix
index 3f89026..99a4ac4 100644
--- a/machines/losurdo/postgresql.nix
+++ b/machines/losurdo/postgresql.nix
@@ -27,22 +27,22 @@ services.postgresql = {
   enable = true;
   package = pkgs.postgresql_9_6;
   enableTCPIP = true;
-  extraConfig = ''
+  settings = {
     # ZFS is Copy on Write (CoW). As a result, it’s not possible
     # to have a torn page because a page can’t be partially written
     # without reverting to the previous copy.
-    full_page_writes = off
-    log_connections = true
-    log_disconnections = true
-    log_hostname = false
-    max_connections = 25
-    max_locks_per_transaction = 1024
-    password_encryption = on # FIXME: replace md5 by scram-sha-256, which requires postfix >= 11
-    ssl = on
-    ssl_cert_file = '/var/lib/acme/${networking.domain}/fullchain.pem'
-    ssl_key_file = '/var/lib/acme/${networking.domain}/key.pem'
-    unix_socket_permissions = 0770
-  '';
+    full_page_writes = false;
+    log_connections = true;
+    log_disconnections = true;
+    log_hostname = false;
+    max_connections = 25;
+    max_locks_per_transaction = 1024;
+    password_encryption = "on"; # FIXME: replace md5 by scram-sha-256, which requires postfix >= 11
+    ssl = true;
+    ssl_cert_file = '/var/lib/acme/${networking.domain}/fullchain.pem';
+    ssl_key_file = '/var/lib/acme/${networking.domain}/key.pem';
+    unix_socket_permissions = "0770";
+  };
   authentication = lib.mkForce ''
     # CONNECTION  DATABASE USER      AUTH  OPTIONS
     local         all      postgres  peer  map=admin
@@ -84,7 +84,7 @@ systemd.services.postgresql = {
       REVOKE ALL ON pg_catalog.pg_database     FROM public;
       REVOKE ALL ON pg_catalog.pg_tablespace   FROM public;
     EOF
-    
+
     pg_createdb () {
       local db=$1
       local owner=''${owner:-$db}
diff --git a/machines/losurdo/prosody.nix b/machines/losurdo/prosody.nix
index 1159d7e..d40640a 100644
--- a/machines/losurdo/prosody.nix
+++ b/machines/losurdo/prosody.nix
@@ -15,6 +15,11 @@ networking.nftables.ruleset = ''
   add rule inet filter net2fw tcp dport {${lib.concatMapStringsSep "," toString prosody.httpsPorts}} counter accept comment "XMPP HTTPS"
   add rule inet filter fw2net meta skuid ${prosody.user} counter accept comment "Prosody"
 '';
+services.tor.hiddenServices."${networking.domain}/xmpp".map = [
+  { port = 5222; }
+  { port = 5269; }
+  { port = 5000; }
+] ++ map (port: {inherit port;}) prosody.httpsPorts;
 users.groups.acme.members = [ prosody.user ];
 security.acme.certs."${networking.domain}" = {
   postRun = "systemctl reload prosody";
@@ -61,7 +66,7 @@ services.prosody = {
 
     turncredentials_host = "turn.${networking.domain}"
     turncredentials_port = 3478
-    
+
     --http_files_dir = "/var/lib/prosody/files"
     --http_external_url = "https://tmp.${networking.domain}:5281"
     --https_certificate = "/var/lib/acme/${networking.domain}/fullchain.pem"
@@ -72,7 +77,7 @@ services.prosody = {
     Component "proxy65.${networking.domain}" "proxy65"
       proxy65_address = "proxy65.${networking.domain}"
       proxy65_acl = { "${networking.domain}" }
-      
+
     Component "biboumi.${networking.domain}"
       component_secret = "useless-secret-on-loopback"
   '';
diff --git a/machines/losurdo/users.nix b/machines/losurdo/users.nix
index 7ae3eb9..6104bd5 100644
--- a/machines/losurdo/users.nix
+++ b/machines/losurdo/users.nix
@@ -20,6 +20,7 @@ networking.nftables.ruleset = lib.concatMapStringsSep "\n"
   ''tcp dport 5222 counter accept comment "XMPP"''
   ''tcp dport 11371 counter accept comment "HKP"''
   ''tcp dport {9009,9010,9011,9012,9013} counter accept comment "croc"''
+  #''ip protocol tcp counter accept comment "all"''
 ];
 
 users = {
diff --git a/machines/mermet/networking/nftables.nix b/machines/mermet/networking/nftables.nix
index fc03156..b0b2cb2 100644
--- a/machines/mermet/networking/nftables.nix
+++ b/machines/mermet/networking/nftables.nix
@@ -23,7 +23,7 @@ networking.nftables = {
       }
       chain check-ping {
         ip protocol icmp   icmp   type echo-request limit rate over 10/second burst 20 packets goto ping-flood
-        #ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 10/second burst 20 packets goto ping-flood
+        #ip6 nexthdr ipv6-icmp icmpv6 type echo-request limit rate over 10/second burst 20 packets goto ping-flood
       }
       chain smurf {
         add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "smurf: "
@@ -134,27 +134,27 @@ networking.nftables = {
         ip protocol icmp log level warn prefix "net2fw: icmpv: " counter accept
         #ip protocol icmp icmp type { echo-request, destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } counter accept
 
-        #ip6 nexthdr icmpv6 icmpv6 type echo-request counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type nd-neighbor-solicit counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type nd-neighbor-advert counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type nd-router-solicit counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type nd-router-advert counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type mld-listener-query counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type mld-listener-report counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type mld-listener-reduction counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type destination-unreachable counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type packet-too-big counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type time-exceeded counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type parameter-problem counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type ind-neighbor-solicit counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type ind-neighbor-advert counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type mld2-listener-report counter accept
-        #ip6 nexthdr icmpv6 log level warn prefix "net2fw: icmpv6: " counter accept
-        #ip6 nexthdr icmpv6 icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit, nd-router-advert, mld-listener-query, mld-listener-report, mld-listener-reduction, destination-unreachable, packet-too-big, time-exceeded, parameter-problem, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type nd-neighbor-solicit counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type nd-neighbor-advert counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type nd-router-solicit counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type nd-router-advert counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-query counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-report counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type mld-listener-reduction counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type destination-unreachable counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type packet-too-big counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type time-exceeded counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type parameter-problem counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type ind-neighbor-solicit counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type ind-neighbor-advert counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type mld2-listener-report counter accept
+        #ip6 nexthdr ipv6-icmp log level warn prefix "net2fw: icmpv6: " counter accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit, nd-router-advert, mld-listener-query, mld-listener-report, mld-listener-reduction, destination-unreachable, packet-too-big, time-exceeded, parameter-problem, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } counter accept
 
         # ICMP
         ip protocol icmp   icmp   type echo-request accept
-        #ip6 nexthdr icmpv6 icmpv6 type echo-request accept
+        #ip6 nexthdr ipv6-icmp icmpv6 type echo-request accept
 
         # Some .nix append gotos here with: add rule inet filter input iffname ... goto ...
       }
diff --git a/nixos/modules.nix b/nixos/modules.nix
index 28fae0a..1ccdf06 100644
--- a/nixos/modules.nix
+++ b/nixos/modules.nix
@@ -5,9 +5,11 @@
 imports = [
   modules/services/databases/openldap.nix
   modules/services/mail/public-inbox.nix
+  modules/services/security/tor.nix
   #modules/services/torrent/transmission.nix
   #modules/security/gnupg.nix
   #modules/services/networking/biboumi.nix
+  #modules/services/networking/croc.nix
   #/home/julm/src/nix/nixpkgs/.git-worktree/transmission/nixos/modules/services/torrent/transmission.nix
   #/home/julm/src/nix/nixpkgs/nixos/modules/services/torrent/transmission.nix
   #modules/services/mail/mlmmj.nix
@@ -15,8 +17,10 @@ imports = [
 disabledModules = [
   "services/mail/mlmmj.nix"
   "services/mail/public-inbox.nix"
+  "services/security/tor.nix"
   #"security/gnupg.nix"
   #"services/networking/biboumi.nix"
+  #"services/networking/croc.nix"
   #"services/torrent/transmission.nix"
 ];
 }
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
new file mode 100644
index 0000000..b4bc2f6
--- /dev/null
+++ b/nixos/modules/services/security/tor.nix
@@ -0,0 +1,799 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.tor;
+  torDirectory = "/var/lib/tor";
+  torRunDirectory = "/run/tor";
+
+  opt    = name: value: optionalString (value != null) "${name} ${value}";
+  optint = name: value: optionalString (value != null && value != 0)    "${name} ${toString value}";
+
+  isolationOptions = {
+    type = types.listOf (types.enum [
+      "IsolateClientAddr"
+      "IsolateSOCKSAuth"
+      "IsolateClientProtocol"
+      "IsolateDestPort"
+      "IsolateDestAddr"
+    ]);
+    default = [];
+    example = [
+      "IsolateClientAddr"
+      "IsolateSOCKSAuth"
+      "IsolateClientProtocol"
+      "IsolateDestPort"
+      "IsolateDestAddr"
+    ];
+    description = "Tor isolation options";
+  };
+
+
+  torRc = ''
+    User tor
+    DataDirectory ${torDirectory}
+    ${optionalString cfg.enableGeoIP ''
+      GeoIPFile ${cfg.package.geoip}/share/tor/geoip
+      GeoIPv6File ${cfg.package.geoip}/share/tor/geoip6
+    ''}
+
+    ${optint "ControlPort" cfg.controlPort}
+    ${optionalString cfg.controlSocket.enable "ControlPort unix:${torRunDirectory}/control GroupWritable RelaxDirModeCheck"}
+  ''
+  # Client connection config
+  + optionalString cfg.client.enable ''
+    SOCKSPort ${cfg.client.socksListenAddress} ${toString cfg.client.socksIsolationOptions}
+    SOCKSPort ${cfg.client.socksListenAddressFaster}
+    ${opt "SocksPolicy" cfg.client.socksPolicy}
+
+    ${optionalString cfg.client.transparentProxy.enable ''
+    TransPort ${cfg.client.transparentProxy.listenAddress} ${toString cfg.client.transparentProxy.isolationOptions}
+    ''}
+
+    ${optionalString cfg.client.dns.enable ''
+    DNSPort ${cfg.client.dns.listenAddress} ${toString cfg.client.dns.isolationOptions}
+    AutomapHostsOnResolve 1
+    AutomapHostsSuffixes ${concatStringsSep "," cfg.client.dns.automapHostsSuffixes}
+    ''}
+  ''
+  # Explicitly disable the SOCKS server if the client is disabled.  In
+  # particular, this makes non-anonymous hidden services possible.
+  + optionalString (! cfg.client.enable) ''
+  SOCKSPort 0
+  ''
+  # Relay config
+  + optionalString cfg.relay.enable ''
+    ORPort ${toString cfg.relay.port}
+    ${opt "Address" cfg.relay.address}
+    ${opt "Nickname" cfg.relay.nickname}
+    ${opt "ContactInfo" cfg.relay.contactInfo}
+
+    ${optint "RelayBandwidthRate" cfg.relay.bandwidthRate}
+    ${optint "RelayBandwidthBurst" cfg.relay.bandwidthBurst}
+    ${opt "AccountingMax" cfg.relay.accountingMax}
+    ${opt "AccountingStart" cfg.relay.accountingStart}
+
+    ${if (cfg.relay.role == "exit") then
+        opt "ExitPolicy" cfg.relay.exitPolicy
+      else
+        "ExitPolicy reject *:*"}
+
+    ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
+      BridgeRelay 1
+      ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${pkgs.obfs4}/bin/obfs4proxy managed
+      ExtORPort auto
+      ${optionalString (cfg.relay.role == "private-bridge") ''
+        ExtraInfoStatistics 0
+        PublishServerDescriptor 0
+      ''}
+    ''}
+  ''
+  # Hidden services
+  + concatStrings (flip mapAttrsToList cfg.hiddenServices (n: v: ''
+    HiddenServiceDir ${torDirectory}/onion/${v.name}
+    ${optionalString (v.version != null) "HiddenServiceVersion ${toString v.version}"}
+    ${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;
+
+  torRcFile = pkgs.writeText "torrc" torRc;
+
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ])
+    (mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.")
+    (mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.")
+  ];
+
+  options = {
+    services.tor = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the Tor daemon. By default, the daemon is run without
+          relay, exit, bridge or client connectivity.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.tor;
+        defaultText = "pkgs.tor";
+        example = literalExample "pkgs.tor";
+        description = ''
+          Tor package to use
+        '';
+      };
+
+      enableGeoIP = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whenever to configure Tor daemon to use GeoIP databases.
+
+          Disabling this will disable by-country statistics for
+          bridges and relays and some client and third-party software
+          functionality.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration. Contents will be added verbatim to the
+          configuration file at the end.
+        '';
+      };
+
+      controlPort = mkOption {
+        type = types.nullOr (types.either types.int types.str);
+        default = null;
+        example = 9051;
+        description = ''
+          If set, Tor will accept connections on the specified port
+          and allow them to control the tor process.
+        '';
+      };
+
+      controlSocket = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to enable Tor control socket. Control socket is created
+            in <literal>${torRunDirectory}/control</literal>
+          '';
+        };
+      };
+
+      client = {
+        enable = mkOption {
+          type = types.bool;
+          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 {
+          type = types.str;
+          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. Provides strong circuit
+            isolation, separate circuit per IP address.
+          '';
+        };
+
+        socksListenAddressFaster = mkOption {
+          type = types.str;
+          default = "127.0.0.1:9063";
+          example = "192.168.0.1:9101";
+          description = ''
+            Bind to this address to listen for connections from
+            Socks-speaking applications. Same as
+            <option>socksListenAddress</option> but uses weaker
+            circuit isolation to provide performance suitable for a
+            web browser.
+           '';
+         };
+
+        socksPolicy = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          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
+            <option>socksListenAddress</option>.
+          '';
+        };
+
+        socksIsolationOptions = mkOption (isolationOptions // {
+          default = ["IsolateDestAddr"];
+        });
+
+        transparentProxy = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable tor transparent proxy";
+          };
+
+          listenAddress = mkOption {
+            type = types.str;
+            default = "127.0.0.1:9040";
+            example = "192.168.0.1:9040";
+            description = ''
+              Bind transparent proxy to this address.
+            '';
+          };
+
+          isolationOptions = mkOption isolationOptions;
+        };
+
+        dns = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable tor dns resolver";
+          };
+
+          listenAddress = mkOption {
+            type = types.str;
+            default = "127.0.0.1:9053";
+            example = "192.168.0.1:9053";
+            description = ''
+              Bind tor dns to this address.
+            '';
+          };
+
+          isolationOptions = mkOption isolationOptions;
+
+          automapHostsSuffixes = mkOption {
+            type = types.listOf types.str;
+            default = [".onion" ".exit"];
+            example = [".onion"];
+            description = "List of suffixes to use with automapHostsOnResolve";
+          };
+        };
+
+        privoxy.enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to enable and configure the system Privoxy to use Tor's
+            faster port, suitable for HTTP.
+
+            To have anonymity, protocols need to be scrubbed of identifying
+            information, and this can be accomplished for HTTP by Privoxy.
+
+            Privoxy can also be useful for KDE torification. A good setup would be:
+            setting SOCKS proxy to the default Tor port, providing maximum
+            circuit isolation where possible; and setting HTTP proxy to Privoxy
+            to route HTTP traffic over faster, but less isolated port.
+          '';
+        };
+      };
+
+      relay = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to enable relaying TOR traffic for others.
+
+            See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
+            for details.
+
+            Setting this to true requires setting
+            <option>services.tor.relay.role</option>
+            and
+            <option>services.tor.relay.port</option>
+            options.
+          '';
+        };
+
+        role = mkOption {
+          type = types.enum [ "exit" "relay" "bridge" "private-bridge" ];
+          description = ''
+            Your role in Tor network. There're several options:
+
+            <variablelist>
+            <varlistentry>
+              <term><literal>exit</literal></term>
+              <listitem>
+                <para>
+                  An exit relay. This allows Tor users to access regular
+                  Internet services through your public IP.
+                </para>
+
+                <important><para>
+                  Running an exit relay may expose you to abuse
+                  complaints. See
+                  <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies" />
+                  for more info.
+                </para></important>
+
+                <para>
+                  You can specify which services Tor users may access via
+                  your exit relay using <option>exitPolicy</option> option.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>relay</literal></term>
+              <listitem>
+                <para>
+                  Regular relay. This allows Tor users to relay onion
+                  traffic to other Tor nodes, but not to public
+                  Internet.
+                </para>
+
+                <important><para>
+                  Note that some misconfigured and/or disrespectful
+                  towards privacy sites will block you even if your
+                  relay is not an exit relay. That is, just being listed
+                  in a public relay directory can have unwanted
+                  consequences.
+
+                  Which means you might not want to use
+                  this role if you browse public Internet from the same
+                  network as your relay, unless you want to write
+                  e-mails to those sites (you should!).
+                </para></important>
+
+                <para>
+                  See
+                  <link xlink:href="https://www.torproject.org/docs/tor-doc-relay.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>bridge</literal></term>
+              <listitem>
+                <para>
+                  Regular bridge. Works like a regular relay, but
+                  doesn't list you in the public relay directory and
+                  hides your Tor node behind obfs4proxy.
+                </para>
+
+                <para>
+                  Using this option will make Tor advertise your bridge
+                  to users through various mechanisms like
+                  <link xlink:href="https://bridges.torproject.org/" />, though.
+                </para>
+
+                <important>
+                  <para>
+                    WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVICE.
+                    Consult with your lawer when in doubt.
+                  </para>
+
+                  <para>
+                    This role should be safe to use in most situations
+                    (unless the act of forwarding traffic for others is
+                    a punishable offence under your local laws, which
+                    would be pretty insane as it would make ISP
+                    illegal).
+                  </para>
+                </important>
+
+                <para>
+                  See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>private-bridge</literal></term>
+              <listitem>
+                <para>
+                  Private bridge. Works like regular bridge, but does
+                  not advertise your node in any way.
+                </para>
+
+                <para>
+                  Using this role means that you won't contribute to Tor
+                  network in any way unless you advertise your node
+                  yourself in some way.
+                </para>
+
+                <para>
+                  Use this if you want to run a private bridge, for
+                  example because you'll give out your bridge address
+                  manually to your friends.
+                </para>
+
+                <para>
+                  Switching to this role after measurable time in
+                  "bridge" role is pretty useless as some Tor users
+                  would have learned about your node already. In the
+                  latter case you can still change
+                  <option>port</option> option.
+                </para>
+
+                <para>
+                  See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+            </variablelist>
+          '';
+        };
+
+        bridgeTransports = mkOption {
+          type = types.listOf types.str;
+          default = ["obfs4"];
+          example = ["obfs2" "obfs3" "obfs4" "scramblesuit"];
+          description = "List of pluggable transports";
+        };
+
+        nickname = mkOption {
+          type = types.str;
+          default = "anonymous";
+          description = ''
+            A unique handle for your TOR relay.
+          '';
+        };
+
+        contactInfo = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "admin@relay.com";
+          description = ''
+            Contact information for the relay owner (e.g. a mail
+            address and GPG key ID).
+          '';
+        };
+
+        accountingMax = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "450 GBytes";
+          description = ''
+            Specify maximum bandwidth allowed during an accounting period. This
+            allows you to limit overall tor bandwidth over some time period.
+            See the <literal>AccountingMax</literal> option by looking at the
+            tor manual <citerefentry><refentrytitle>tor</refentrytitle>
+            <manvolnum>1</manvolnum></citerefentry> for more.
+
+            Note this limit applies individually to upload and
+            download; if you specify <literal>"500 GBytes"</literal>
+            here, then you may transfer up to 1 TBytes of overall
+            bandwidth (500 GB upload, 500 GB download).
+          '';
+        };
+
+        accountingStart = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "month 1 1:00";
+          description = ''
+            Specify length of an accounting period. This allows you to limit
+            overall tor bandwidth over some time period. See the
+            <literal>AccountingStart</literal> option by looking at the tor
+            manual <citerefentry><refentrytitle>tor</refentrytitle>
+            <manvolnum>1</manvolnum></citerefentry> for more.
+          '';
+        };
+
+        bandwidthRate = mkOption {
+          type = types.nullOr types.int;
+          default = null;
+          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 {
+          type = types.nullOr types.int;
+          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.
+          '';
+        };
+
+        address = mkOption {
+          type    = types.nullOr types.str;
+          default = null;
+          example = "noname.example.com";
+          description = ''
+            The IP address or full DNS name for advertised address of your relay.
+            Leave unset and Tor will guess.
+          '';
+        };
+
+        port = mkOption {
+          type    = types.either types.int types.str;
+          example = 143;
+          description = ''
+            What port to advertise for Tor connections. This corresponds to the
+            <literal>ORPort</literal> section in the Tor manual; see
+            <citerefentry><refentrytitle>tor</refentrytitle>
+            <manvolnum>1</manvolnum></citerefentry> for more details.
+
+            At a minimum, you should just specify the port for the
+            relay to listen on; a common one like 143, 22, 80, or 443
+            to help Tor users who may have very restrictive port-based
+            firewalls.
+          '';
+        };
+
+        exitPolicy = mkOption {
+          type    = types.nullOr types.str;
+          default = null;
+          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
+            <link xlink:href="https://www.torproject.org/documentation.html" />.
+
+            Look at
+            <link xlink:href="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.
+          '';
+        };
+      };
+
+      hiddenServices = mkOption {
+        description = ''
+          A set of static hidden services that terminate their Tor
+          circuits at this node.
+
+          Every element in this set declares a virtual onion host.
+
+          You can specify your onion address by putting corresponding
+          private key to an appropriate place in ${torDirectory}.
+
+          For services without private keys in ${torDirectory} Tor
+          daemon will generate random key pairs (which implies random
+          onion addresses) on restart. The latter could take a while,
+          please be patient.
+
+          <note><para>
+            Hidden services can be useful even if you don't intend to
+            actually <emphasis>hide</emphasis> them, since they can
+            also be seen as a kind of NAT traversal mechanism.
+
+            E.g. the example will make your sshd, whatever runs on
+            "8080" and your mail server available from anywhere where
+            the Tor network is available (which, with the help from
+            bridges, is pretty much everywhere), even if both client
+            and server machines are behind NAT you have no control
+            over.
+          </para></note>
+        '';
+        default = {};
+        example = literalExample ''
+          { "my-hidden-service-example".map = [
+              { port = 22; }                # map ssh port to this machine's ssh
+              { port = 80; toPort = 8080; } # map http port to whatever runs on 8080
+              { port = "sip"; toHost = "mail.example.com"; toPort = "imap"; } # because we can
+            ];
+          }
+        '';
+        type = types.loaOf (types.submodule ({name, ...}: {
+          options = {
+
+             name = mkOption {
+               type = types.str;
+               description = ''
+                 Name of this tor hidden service.
+
+                 This is purely descriptive.
+
+                 After restarting Tor daemon you should be able to
+                 find your .onion address in
+                 <literal>${torDirectory}/onion/$name/hostname</literal>.
+               '';
+             };
+
+             map = mkOption {
+               default = [];
+               description = "Port mapping for this hidden service.";
+               type = types.listOf (types.submodule ({config, ...}: {
+                 options = {
+
+                   port = mkOption {
+                     type = types.either types.int types.str;
+                     example = 80;
+                     description = ''
+                       Hidden service port to "bind to".
+                     '';
+                   };
+
+                   destination = mkOption {
+                     internal = true;
+                     type = types.str;
+                     description = "Forward these connections where?";
+                   };
+
+                   toHost = mkOption {
+                     type = types.str;
+                     default = "127.0.0.1";
+                     description = "Mapping destination host.";
+                   };
+
+                   toPort = mkOption {
+                     type = types.either types.int types.str;
+                     example = 8080;
+                     description = "Mapping destination port.";
+                   };
+
+                 };
+
+                 config = {
+                   toPort = mkDefault config.port;
+                   destination = mkDefault "${config.toHost}:${toString config.toPort}";
+                 };
+               }));
+             };
+
+             authorizeClient = mkOption {
+               default = null;
+               description = "If configured, the hidden service is accessible for authorized clients only.";
+               type = types.nullOr (types.submodule ({...}: {
+
+                 options = {
+
+                   authType = mkOption {
+                     type = types.enum [ "basic" "stealth" ];
+                     description = ''
+                       Either <literal>"basic"</literal> for a general-purpose authorization protocol
+                       or <literal>"stealth"</literal> 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 <filename>${torDirectory}/onion/$name/hostname</filename>.
+                       Clients need to put this authorization data in their configuration file using <literal>HidServAuth</literal>.
+                     '';
+                   };
+                 };
+               }));
+             };
+
+             version = mkOption {
+               default = null;
+               description = "Rendezvous service descriptor version to publish for the hidden service. Currently, versions 2 and 3 are supported. (Default: 2)";
+               type = types.nullOr (types.enum [ 2 3 ]);
+             };
+          };
+
+          config = {
+            name = mkDefault name;
+          };
+        }));
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # Not sure if `cfg.relay.role == "private-bridge"` helps as tor
+    # sends a lot of stats
+    warnings = optional (cfg.relay.enable && cfg.hiddenServices != {})
+      ''
+        Running Tor hidden services on a public relay makes the
+        presence of hidden services visible through simple statistical
+        analysis of publicly available data.
+
+        You can safely ignore this warning if you don't intend to
+        actually hide your hidden services. In either case, you can
+        always create a container/VM with a separate Tor daemon instance.
+      '';
+
+    users.groups.tor.gid = config.ids.gids.tor;
+    users.users.tor =
+      { description = "Tor Daemon User";
+        createHome  = true;
+        home        = torDirectory;
+        group       = "tor";
+        uid         = config.ids.uids.tor;
+      };
+
+    # We have to do this instead of using RuntimeDirectory option in
+    # the service below because systemd has no way to set owners of
+    # RuntimeDirectory and putting this into the service below
+    # requires that service to relax it's sandbox since this needs
+    # writable /run
+    systemd.services.tor-init =
+      { description = "Tor Daemon Init";
+        wantedBy = [ "tor.service" ];
+        script = ''
+          install -m 0700 -o tor -g tor -d ${torDirectory} ${torDirectory}/onion
+          install -m 0750 -o tor -g tor -d ${torRunDirectory}
+        '';
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+      };
+
+    systemd.services.tor =
+      { description = "Tor Daemon";
+        path = [ pkgs.tor ];
+
+        wantedBy = [ "multi-user.target" ];
+        after    = [ "tor-init.service" "network.target" ];
+        restartTriggers = [ torRcFile ];
+
+        serviceConfig =
+          { Type         = "simple";
+            # Translated from the upstream contrib/dist/tor.service.in
+            ExecStartPre = "${cfg.package}/bin/tor -f ${torRcFile} --verify-config";
+            ExecStart    = "${cfg.package}/bin/tor -f ${torRcFile}";
+            ExecReload   = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+            KillSignal   = "SIGINT";
+            TimeoutSec   = 30;
+            Restart      = "on-failure";
+            LimitNOFILE  = 32768;
+
+            # Hardening
+            # this seems to unshare /run despite what systemd.exec(5) says
+            PrivateTmp              = mkIf (!cfg.controlSocket.enable) "yes";
+            PrivateDevices          = "yes";
+            ProtectHome             = "yes";
+            ProtectSystem           = "strict";
+            InaccessiblePaths       = "/home";
+            ReadOnlyPaths           = "/";
+            ReadWritePaths          = [ torDirectory torRunDirectory ];
+            NoNewPrivileges         = "yes";
+
+            # tor.service.in has this in, but this line it fails to spawn a namespace when using hidden services
+            #CapabilityBoundingSet   = "CAP_SETUID CAP_SETGID CAP_NET_BIND_SERVICE";
+          };
+      };
+
+    environment.systemPackages = [ cfg.package ];
+
+    services.privoxy = mkIf (cfg.client.enable && cfg.client.privoxy.enable) {
+      enable = true;
+      extraConfig = ''
+        forward-socks4a / ${cfg.client.socksListenAddressFaster} .
+        toggle  1
+        enable-remote-toggle 0
+        enable-edit-actions 0
+        enable-remote-http-toggle 0
+      '';
+    };
+  };
+}
diff --git a/nixos/profiles/hardware/dl10j.nix b/nixos/profiles/hardware/dl10j.nix
index a66f8ce..0801457 100644
--- a/nixos/profiles/hardware/dl10j.nix
+++ b/nixos/profiles/hardware/dl10j.nix
@@ -8,6 +8,11 @@ nix = {
 };
 powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
 
+# https://insanity.industries/post/sata-power-consumption/
+services.udev.extraRules = ''
+  SUBSYSTEM=="scsi_host", KERNEL=="host*", ATTR{link_power_management_policy}="med_power_with_dipm"
+'';
+
 boot.loader = {
   grub = {
     enable = true;
@@ -71,7 +76,6 @@ boot.kernelParams = [
 
 environment.systemPackages = with pkgs; [
   pciutils
-  #flashrom
   nvme-cli
 ];
 }
diff --git a/nixpkgs/patches/fix-ld-nix-apparmor.diff b/nixpkgs/patches/fix-ld-nix-apparmor.diff
deleted file mode 100644
index 7b756a1..0000000
--- a/nixpkgs/patches/fix-ld-nix-apparmor.diff
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/nixos/modules/config/malloc.nix b/nixos/modules/config/malloc.nix
-index 5c5752ef515..54ba3048fdb 100644
---- a/nixos/modules/config/malloc.nix
-+++ b/nixos/modules/config/malloc.nix
-@@ -90,7 +90,7 @@ in
-     security.apparmor.includes = {
-       "abstractions/base" = ''
-         r /etc/ld-nix.so.preload,
--        r ${config.environment.etc."ld-nix.so.preload".source},
-+        r ${config.environment.etc."ld-nix.${pkgs.glibc.src.outputHash}.so.preload".source},
-         mr ${providerLibPath},
-       '';
-     };
diff --git a/nixpkgs/patches/fix-ld-nix.diff b/nixpkgs/patches/fix-ld-nix.diff
deleted file mode 100644
index 0743c37..0000000
--- a/nixpkgs/patches/fix-ld-nix.diff
+++ /dev/null
@@ -1,66 +0,0 @@
-diff --git a/nixos/modules/config/malloc.nix b/nixos/modules/config/malloc.nix
-index 31a659ee83f..93089c220a2 100644
---- a/nixos/modules/config/malloc.nix
-+++ b/nixos/modules/config/malloc.nix
-@@ -84,7 +84,9 @@ in
-   };
- 
-   config = mkIf (cfg.provider != "libc") {
--    environment.etc."ld-nix.so.preload".text = ''
-+    # See comment in pkgs/development/libraries/glibc/common.nix
-+    # for an explanation about the name of this file.
-+    environment.etc."ld-nix.${pkgs.glibc.src.outputHash}.so.preload".text = ''
-       ${providerLibPath}
-     '';
-   };
-diff --git a/pkgs/development/libraries/glibc/common.nix b/pkgs/development/libraries/glibc/common.nix
-index 0b2f34c7b76..e1349c0076b 100644
---- a/pkgs/development/libraries/glibc/common.nix
-+++ b/pkgs/development/libraries/glibc/common.nix
-@@ -68,9 +68,6 @@ stdenv.mkDerivation ({
-       /* Don't use /etc/ld.so.cache, for non-NixOS systems.  */
-       ./dont-use-system-ld-so-cache.patch
- 
--      /* Don't use /etc/ld.so.preload, but /etc/ld-nix.so.preload.  */
--      ./dont-use-system-ld-so-preload.patch
--
-       /* The command "getconf CS_PATH" returns the default search path
-          "/bin:/usr/bin", which is inappropriate on NixOS machines. This
-          patch extends the search path by "/run/current-system/sw/bin". */
-@@ -128,6 +125,18 @@ stdenv.mkDerivation ({
-       # nscd needs libgcc, and we don't want it dynamically linked
-       # because we don't want it to depend on bootstrap-tools libs.
-       echo "LDFLAGS-nscd += -static-libgcc" >> nscd/Makefile
-+
-+      # Don't use /etc/ld.so.preload, but /etc/ld-nix.${sha256}.so.preload.
-+      # ${sha256} is included in the name
-+      # in order to only share a same .so.preload between the programs
-+      # built with that version of glibc,
-+      # otherwise it could break programs
-+      # requiring symbols from a more recent glibc
-+      # than the one loaded by shared libraries in that file.
-+      # This file may later be generated by a NixOS module,
-+      # using pkgs.glibc.src.outputHash instead of sha256
-+      substituteInPlace elf/rtld.c \
-+       --replace /etc/ld.so.preload /etc/ld-nix.${sha256}.so.preload
-     ''
-     # FIXME: find a solution for infinite recursion in cross builds.
-     # For now it's hopefully acceptable that IDN from libc doesn't reliably work.
-diff --git a/pkgs/development/libraries/glibc/dont-use-system-ld-so-preload.patch b/pkgs/development/libraries/glibc/dont-use-system-ld-so-preload.patch
-deleted file mode 100644
-index 894e2a11cf8..00000000000
---- a/pkgs/development/libraries/glibc/dont-use-system-ld-so-preload.patch
-+++ /dev/null
-@@ -1,12 +0,0 @@
--diff -ru glibc-2.20-orig/elf/rtld.c glibc-2.20/elf/rtld.c
----- glibc-2.20-orig/elf/rtld.c	2014-09-07 10:09:09.000000000 +0200
--+++ glibc-2.20/elf/rtld.c	2014-10-27 11:32:25.203043157 +0100
--@@ -1513,7 +1513,7 @@
--      open().  So we do this first.  If it succeeds we do almost twice
--      the work but this does not matter, since it is not for production
--      use.  */
---  static const char preload_file[] = "/etc/ld.so.preload";
--+  static const char preload_file[] = "/etc/ld-nix.so.preload";
--   if (__glibc_unlikely (__access (preload_file, R_OK) == 0))
--     {
--       /* Read the contents of the file.  */
-- 
2.47.2