summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/programs/oblogout.nix16
-rw-r--r--nixos/modules/programs/zsh/zsh.nix10
-rw-r--r--nixos/modules/rename.nix3
-rw-r--r--nixos/modules/security/pam.nix4
-rw-r--r--nixos/modules/services/continuous-integration/hail.nix61
-rw-r--r--nixos/modules/services/hardware/tlp.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/unifi-exporter.nix1
-rw-r--r--nixos/modules/services/networking/firefox/sync-server.nix52
-rw-r--r--nixos/modules/services/security/tor.nix434
-rw-r--r--nixos/modules/services/web-servers/caddy.nix14
-rw-r--r--nixos/modules/services/web-servers/varnish/default.nix44
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/maxx.nix25
-rw-r--r--nixos/modules/virtualisation/containers.nix1
15 files changed, 513 insertions, 157 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 881cafe05ad4..625b1d3a852c 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -165,6 +165,7 @@
   ./services/continuous-integration/buildbot/master.nix
   ./services/continuous-integration/buildbot/worker.nix
   ./services/continuous-integration/buildkite-agent.nix
+  ./services/continuous-integration/hail.nix
   ./services/continuous-integration/hydra/default.nix
   ./services/continuous-integration/gitlab-runner.nix
   ./services/continuous-integration/gocd-agent/default.nix
diff --git a/nixos/modules/programs/oblogout.nix b/nixos/modules/programs/oblogout.nix
index 79a8ddb7ce37..720c29b1eaee 100644
--- a/nixos/modules/programs/oblogout.nix
+++ b/nixos/modules/programs/oblogout.nix
@@ -27,6 +27,7 @@ in
         type = types.int;
         default = 70;
         description = ''
+          Opacity percentage of Cairo rendered backgrounds.
         '';
       };
 
@@ -34,6 +35,7 @@ in
         type = types.str;
         default = "black";
         description = ''
+          Colour name or hex code (#ffffff) of the background color.
         '';
       };
 
@@ -41,6 +43,9 @@ in
         type = types.str;
         default = "simplistic";
         description = ''
+          Icon theme for the buttons, must be in the themes folder of
+          the package, or in
+          <filename>~/.themes/&lt;name&gt;/oblogout/</filename>.
         '';
       };
 
@@ -48,6 +53,7 @@ in
         type = types.str;
         default =  "cancel, logout, restart, shutdown, suspend, hibernate";
         description = ''
+          List and order of buttons to show.
         '';
       };
 
@@ -55,6 +61,7 @@ in
         type = types.str;
         default =  "Escape";
         description = ''
+          Cancel logout/shutdown shortcut.
         '';
       };
 
@@ -62,6 +69,7 @@ in
         type = types.str;
         default = "S";
         description = ''
+          Shutdown shortcut.
         '';
       };
 
@@ -69,6 +77,7 @@ in
         type = types.str;
         default = "R";
         description = ''
+          Restart shortcut.
         '';
       };
 
@@ -76,6 +85,7 @@ in
         type = types.str;
         default = "U";
         description = ''
+          Suspend shortcut.
         '';
       };
 
@@ -83,6 +93,7 @@ in
         type = types.str;
         default = "L";
         description = ''
+          Logout shortcut.
         '';
       };
 
@@ -90,6 +101,7 @@ in
         type = types.str;
         default = "K";
         description = ''
+          Lock session shortcut.
         '';
       };
 
@@ -97,6 +109,7 @@ in
         type = types.str;
         default =  "H";
         description = ''
+          Hibernate shortcut.
         '';
       };
 
@@ -104,6 +117,7 @@ in
         type = types.str;
         default = "openbox --exit";
         description = ''
+          Command to logout.
         '';
       };
 
@@ -111,6 +125,7 @@ in
         type = types.str;
         default = "";
         description = ''
+          Command to lock screen.
         '';
       };
 
@@ -118,6 +133,7 @@ in
         type = types.str;
         default = "";
         description = ''
+          Command to switch user.
         '';
       };
     };
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index a055291282c9..ee61e2d2382c 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -158,6 +158,11 @@ in
 
         HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
 
+        # Tell zsh how to find installed completions
+        for p in ''${(z)NIX_PROFILES}; do
+          fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions)
+        done
+
         ${optionalString cfg.enableCompletion "autoload -U compinit && compinit"}
 
         ${optionalString (cfg.enableAutosuggestions)
@@ -172,11 +177,6 @@ in
 
         ${cfg.promptInit}
 
-        # Tell zsh how to find installed completions
-        for p in ''${(z)NIX_PROFILES}; do
-          fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions)
-        done
-
         # Read system-wide modifications.
         if test -f /etc/zshrc.local; then
           . /etc/zshrc.local
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 08146d1f5687..2079ed544aee 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -25,6 +25,7 @@ with lib;
     (mkRenamedOptionModule [ "services" "sslh" "host" ] [ "services" "sslh" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "statsd" "host" ] [ "services" "statsd" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "subsonic" "host" ] [ "services" "subsonic" "listenAddress" ])
+    (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ])
     (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ])
 
     (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ])
@@ -195,6 +196,8 @@ with lib;
     (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "")
     (mkRemovedOptionModule [ "services" "printing" "cupsFilesConf" ] "")
     (mkRemovedOptionModule [ "services" "printing" "cupsdConf" ] "")
+    (mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.")
+    (mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.")
     (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ]
       "See the 16.09 release notes for more information.")
     (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 5632500df2e0..ede4ace5ed03 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -281,7 +281,7 @@ let
                 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
               ${optionalString cfg.enableKwallet
                 ("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
-                 " kwalletd=${pkgs.libsForQt5.kwallet}/bin/kwalletd5")}
+                 " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
             '') + ''
           ${optionalString cfg.unixAuth
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
@@ -350,7 +350,7 @@ let
               "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"}
           ${optionalString (cfg.enableKwallet)
               ("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
-               " kwalletd=${pkgs.libsForQt5.kwallet}/bin/kwalletd5")}
+               " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
         '');
     };
 
diff --git a/nixos/modules/services/continuous-integration/hail.nix b/nixos/modules/services/continuous-integration/hail.nix
new file mode 100644
index 000000000000..5d0c3f7b4ab3
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/hail.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+  cfg = config.services.hail;
+in {
+
+
+  ###### interface
+
+  options.services.hail = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables the Hail Auto Update Service. Hail can automatically deploy artifacts
+        built by a Hydra Continous Integration server. A common use case is to provide
+        continous deployment for single services or a full NixOS configuration.'';
+    };
+    profile = mkOption {
+      type = types.str;
+      default = "hail-profile";
+      description = "The name of the Nix profile used by Hail.";
+    };
+    hydraJobUri = mkOption {
+      type = types.str;
+      description = "The URI of the Hydra Job.";
+    };
+    netrc = mkOption {
+      type = types.nullOr types.path;
+      description = "The netrc file to use when fetching data from Hydra.";
+      default = null;
+    };
+    package = mkOption {
+      type = types.package;
+      default = pkgs.haskellPackages.hail;
+      defaultText = "pkgs.haskellPackages.hail";
+      description = "Hail package to use.";
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.hail = {
+      description = "Hail Auto Update Service";
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = with pkgs; [ nix ];
+      environment = {
+        HOME = "/var/lib/empty";
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/hail --profile ${cfg.profile} --job-uri ${cfg.hydraJobUri}"
+          + lib.optionalString (cfg.netrc != null) " --netrc-file ${cfg.netrc}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/hardware/tlp.nix b/nixos/modules/services/hardware/tlp.nix
index 3b108c87edd2..68425822a884 100644
--- a/nixos/modules/services/hardware/tlp.nix
+++ b/nixos/modules/services/hardware/tlp.nix
@@ -57,6 +57,8 @@ in
     powerManagement.scsiLinkPolicy = null;
     powerManagement.cpuFreqGovernor = null;
 
+    systemd.sockets."systemd-rfkill".enable = false;
+
     systemd.services = {
       "systemd-rfkill@".enable = false;
       "systemd-rfkill".enable = false;
diff --git a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
index e3059e485098..0a56d6ae95a5 100644
--- a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
+++ b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
@@ -83,6 +83,7 @@ in {
       description = "Prometheus exporter for UniFi Controller metrics";
       unitConfig.Documentation = "https://github.com/mdlayher/unifi_exporter";
       wantedBy = [ "multi-user.target" ];
+      after = optional config.services.unifi.enable "unifi.service";
       serviceConfig = {
         User = "nobody";
         Restart = "always";
diff --git a/nixos/modules/services/networking/firefox/sync-server.nix b/nixos/modules/services/networking/firefox/sync-server.nix
index c1a14931429a..a9f3fd65d76b 100644
--- a/nixos/modules/services/networking/firefox/sync-server.nix
+++ b/nixos/modules/services/networking/firefox/sync-server.nix
@@ -4,6 +4,10 @@ with lib;
 
 let
   cfg = config.services.firefox.syncserver;
+
+  defaultDbLocation = "/var/db/firefox-sync-server/firefox-sync-server.db";
+  defaultSqlUri = "sqlite:///${defaultDbLocation}";
+
   syncServerIni = pkgs.writeText "syncserver.ini" ''
     [DEFAULT]
     overrides = ${cfg.privateConfig}
@@ -25,6 +29,7 @@ let
     backend = tokenserver.verifiers.LocalVerifier
     audiences = ${removeSuffix "/" cfg.publicUrl}
   '';
+
 in
 
 {
@@ -65,6 +70,18 @@ in
         '';
       };
 
+      user = mkOption {
+        type = types.str;
+        default = "syncserver";
+        description = "User account under which syncserver runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "syncserver";
+        description = "Group account under which syncserver runs.";
+      };
+
       publicUrl = mkOption {
         type = types.str;
         default = "http://localhost:5000/";
@@ -85,7 +102,7 @@ in
 
       sqlUri = mkOption {
         type = types.str;
-        default = "sqlite:////var/db/firefox-sync-server.db";
+        default = defaultSqlUri;
         example = "postgresql://scott:tiger@localhost/test";
         description = ''
           The location of the database. This URL is composed of
@@ -126,16 +143,45 @@ in
       description = "Firefox Sync Server";
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.coreutils syncServerEnv ];
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        PermissionsStartOnly = true;
+      };
+
       preStart = ''
         if ! test -e ${cfg.privateConfig}; then
-          umask u=rwx,g=x,o=x
-          mkdir -p $(dirname ${cfg.privateConfig})
+          mkdir -m 700 -p $(dirname ${cfg.privateConfig})
           echo  > ${cfg.privateConfig} '[syncserver]'
           echo >> ${cfg.privateConfig} "secret = $(head -c 20 /dev/urandom | sha1sum | tr -d ' -')"
         fi
+        chown ${cfg.user}:${cfg.group} ${cfg.privateConfig}
+      '' + optionalString (cfg.sqlUri == defaultSqlUri) ''
+        if ! test -e $(dirname ${defaultDbLocation}); then
+          mkdir -m 700 -p $(dirname ${defaultDbLocation})
+          chown ${cfg.user}:${cfg.group} $(dirname ${defaultDbLocation})
+        fi
+        # Move previous database file if it exists
+        oldDb="/var/db/firefox-sync-server.db"
+        if test -f $oldDb; then
+          mv $oldDb ${defaultDbLocation}
+          chown ${cfg.user}:${cfg.group} ${defaultDbLocation}
+        fi
       '';
       serviceConfig.ExecStart = "${syncServerEnv}/bin/paster serve ${syncServerIni}";
     };
 
+    users.extraUsers = optionalAttrs (cfg.user == "syncserver")
+      (singleton {
+        name = "syncserver";
+        group = cfg.group;
+        isSystemUser = true;
+      });
+
+    users.extraGroups = optionalAttrs (cfg.group == "syncserver")
+      (singleton {
+        name = "syncserver";
+      });
   };
 }
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index 3f1450ebfbd7..04b065f6ae4b 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -7,7 +7,7 @@ let
   torDirectory = "/var/lib/tor";
 
   opt    = name: value: optionalString (value != null) "${name} ${value}";
-  optint = name: value: optionalString (value != 0)    "${name} ${toString value}";
+  optint = name: value: optionalString (value != null && value != 0)    "${name} ${toString value}";
 
   torRc = ''
     User tor
@@ -17,7 +17,7 @@ let
       GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6
     ''}
 
-    ${optint "ControlPort" cfg.controlPort}
+    ${optint "ControlPort" (toString cfg.controlPort)}
   ''
   # Client connection config
   + optionalString cfg.client.enable  ''
@@ -27,7 +27,8 @@ let
   ''
   # Relay config
   + optionalString cfg.relay.enable ''
-    ORPort ${cfg.relay.portSpec}
+    ORPort ${toString cfg.relay.port}
+    ${opt "Address" cfg.relay.address}
     ${opt "Nickname" cfg.relay.nickname}
     ${opt "ContactInfo" cfg.relay.contactInfo}
 
@@ -36,31 +37,32 @@ let
     ${opt "AccountingMax" cfg.relay.accountingMax}
     ${opt "AccountingStart" cfg.relay.accountingStart}
 
-    ${if cfg.relay.isExit then
+    ${if (cfg.relay.role == "exit") then
         opt "ExitPolicy" cfg.relay.exitPolicy
       else
         "ExitPolicy reject *:*"}
 
-    ${optionalString cfg.relay.isBridge ''
+    ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
       BridgeRelay 1
       ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed
+      ExtORPort auto
+      ${optionalString (cfg.relay.role == "private-bridge") ''
+        ExtraInfoStatistics 0
+        PublishServerDescriptor 0
+      ''}
     ''}
   ''
-  + hiddenServices
+  # Hidden services
+  + concatStrings (flip mapAttrsToList cfg.hiddenServices (n: v: ''
+    HiddenServiceDir ${torDirectory}/onion/${v.name}
+    ${flip concatMapStrings v.map (p: ''
+      HiddenServicePort ${toString p.port} ${p.destination}
+    '')}
+  ''))
   + cfg.extraConfig;
 
-  hiddenServices = concatStrings (mapAttrsToList (hiddenServiceDir: hs:
-    let
-      hsports = concatStringsSep "\n" (map mkHiddenServicePort hs.hiddenServicePorts);
-    in
-      "HiddenServiceDir ${hiddenServiceDir}\n${hsports}\n${hs.extraConfig}\n"
-    ) cfg.hiddenServices);
-
-    mkHiddenServicePort = hsport: let
-      trgt = optionalString (hsport.target != null) (" " + hsport.target);
-    in "HiddenServicePort ${toString hsport.virtualPort}${trgt}";
-
   torRcFile = pkgs.writeText "torrc" torRc;
+
 in
 {
   options = {
@@ -96,8 +98,8 @@ in
       };
 
       controlPort = mkOption {
-        type = types.int;
-        default = 0;
+        type = types.nullOr (types.either types.int types.str);
+        default = null;
         example = 9051;
         description = ''
           If set, Tor will accept connections on the specified port
@@ -133,9 +135,10 @@ in
           example = "192.168.0.1:9101";
           description = ''
             Bind to this address to listen for connections from
-            Socks-speaking applications. Same as socksListenAddress
-            but uses weaker circuit isolation to provide performance
-            suitable for a web browser.
+            Socks-speaking applications. Same as
+            <option>socksListenAddress</option> but uses weaker
+            circuit isolation to provide performance suitable for a
+            web browser.
            '';
          };
 
@@ -145,9 +148,9 @@ in
           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
+            address. First entry that matches wins. If no SocksPolicy
             is set, we accept all (and only) requests from
-            SocksListenAddress.
+            <option>socksListenAddress</option>.
           '';
         };
 
@@ -176,45 +179,147 @@ in
           description = ''
             Whether to enable relaying TOR traffic for others.
 
-            See https://www.torproject.org/docs/tor-doc-relay for details.
-          '';
-        };
-
-        isBridge = mkOption {
-          type = types.bool;
-          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.
+            See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
+            for details.
 
-            The bridge is set up with an obfuscated transport proxy.
-
-            See https://www.torproject.org/bridges.html.en for more info.
+            Setting this to true requires setting
+            <option>services.tor.relay.role</option>
+            and
+            <option>services.tor.relay.port</option>
+            options.
           '';
         };
 
-        isExit = mkOption {
-          type = types.bool;
-          default = false;
+        role = mkOption {
+          type = types.enum [ "exit" "relay" "bridge" "private-bridge" ];
           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.
+            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 obfsproxy.
+                </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 ADVISE.
+                    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>
           '';
         };
 
@@ -268,8 +373,8 @@ in
         };
 
         bandwidthRate = mkOption {
-          type = types.int;
-          default = 0;
+          type = types.nullOr types.int;
+          default = null;
           example = 100;
           description = ''
             Specify this to limit the bandwidth usage of relayed (server)
@@ -278,7 +383,7 @@ in
         };
 
         bandwidthBurst = mkOption {
-          type = types.int;
+          type = types.nullOr types.int;
           default = cfg.relay.bandwidthRate;
           example = 200;
           description = ''
@@ -288,9 +393,19 @@ in
           '';
         };
 
-        portSpec = mkOption {
-          type    = types.str;
-          example = "143";
+        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
@@ -313,13 +428,15 @@ in
             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
+            _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
+            <link xlink:href="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.
+            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
@@ -330,79 +447,122 @@ in
       };
 
       hiddenServices = mkOption {
-        type = types.attrsOf (types.submodule ({
-          options = {
-            hiddenServicePorts = mkOption {
-              type = types.listOf (types.submodule {
-                options = {
-                  virtualPort = mkOption {
-                    type = types.int;
-                    example = 80;
-                    description = "Virtual port.";
-                  };
-                  target = mkOption {
-                    type = types.nullOr types.str;
-                    default = null;
-                    example = "127.0.0.1:8080";
-                    description = ''
-                      Target virtual Port shall be mapped to.
-
-                      You may override the target port, address, or both by
-                      specifying a target of addr, port, addr:port, or
-                      unix:path. (You can specify an IPv6 target as
-                      [addr]:port. Unix paths may be quoted, and may use
-                      standard C escapes.)
-                    '';
-                  };
-                };
-              });
-              example = [ { virtualPort = 80; target = "127.0.0.1:8080"; } { virtualPort = 6667; } ];
-              description = ''
-                If target is <literal>null</literal> the virtual port is mapped
-                to the same port on 127.0.0.1 over TCP. You may use
-                <literal>target</literal> to overwrite this behaviour (see
-                description of target).
-
-                This corresponds to the <literal>HiddenServicePort VIRTPORT
-                [TARGET]</literal> option by looking at the tor manual
-                <citerefentry><refentrytitle>tor</refentrytitle>
-                <manvolnum>1</manvolnum></citerefentry> for more information.
-              '';
-            };
-            extraConfig = mkOption {
-              type = types.str;
-              default = "";
-              description = ''
-                Extra configuration. Contents will be added in the current
-                hidden service context.
-              '';
-            };
-          };
-        }));
+        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 = {
-          "/var/lib/tor/webserver" = {
-            hiddenServicePorts = [ { virtualPort = 80; } ];
+        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, config, ...}: {
+          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}";
+                 };
+               }));
+             };
+
           };
-        };
-        description = ''
-          Configure hidden services.
 
-          Please consult the tor manual
-          <citerefentry><refentrytitle>tor</refentrytitle>
-          <manvolnum>1</manvolnum></citerefentry> for a more detailed
-          explanation. (search for 'HIDDEN').
-        '';
+          config = {
+            name = mkDefault name;
+          };
+        }));
       };
     };
   };
 
   config = mkIf cfg.enable {
-    assertions = singleton
-      { message = "Can't be both an exit and a bridge relay at the same time";
-        assertion =
-          cfg.relay.enable -> !(cfg.relay.isBridge && cfg.relay.isExit);
-      };
+    # 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.extraGroups.tor.gid = config.ids.gids.tor;
     users.extraUsers.tor =
@@ -422,9 +582,13 @@ in
         restartTriggers = [ torRcFile ];
 
         # Translated from the upstream contrib/dist/tor.service.in
+        preStart = ''
+          install -o tor -g tor -d ${torDirectory}/onion
+          ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config
+        '';
+
         serviceConfig =
           { Type         = "simple";
-            ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config";
             ExecStart    = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0";
             ExecReload   = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
             KillSignal   = "SIGINT";
diff --git a/nixos/modules/services/web-servers/caddy.nix b/nixos/modules/services/web-servers/caddy.nix
index ee32a1c86d4d..d8efa24bc6d5 100644
--- a/nixos/modules/services/web-servers/caddy.nix
+++ b/nixos/modules/services/web-servers/caddy.nix
@@ -5,12 +5,22 @@ with lib;
 let
   cfg = config.services.caddy;
   configFile = pkgs.writeText "Caddyfile" cfg.config;
-in
-{
+in {
   options.services.caddy = {
     enable = mkEnableOption "Caddy web server";
 
     config = mkOption {
+      default = "";
+      example = ''
+        example.com {
+        gzip
+        minify
+        log syslog
+
+        root /srv/http
+        }
+      '';
+      type = types.lines;
       description = "Verbatim Caddyfile to use";
     };
 
diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix
index 5433db3b91c8..c3bc065d4651 100644
--- a/nixos/modules/services/web-servers/varnish/default.nix
+++ b/nixos/modules/services/web-servers/varnish/default.nix
@@ -7,14 +7,10 @@ with lib;
 {
   options = {
     services.varnish = {
-      enable = mkOption {
-        default = false;
-        description = "
-          Enable the Varnish Server.
-        ";
-      };
+      enable = mkEnableOption "Varnish Server";
 
       http_address = mkOption {
+        type = types.str;
         default = "*:6081";
         description = "
           HTTP listen address and port.
@@ -22,17 +18,37 @@ with lib;
       };
 
       config = mkOption {
+        type = types.lines;
         description = "
           Verbatim default.vcl configuration.
         ";
       };
 
       stateDir = mkOption {
+        type = types.path;
         default = "/var/spool/varnish/${config.networking.hostName}";
         description = "
           Directory holding all state for Varnish to run.
         ";
       };
+
+      extraModules = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "[ pkgs.varnish-geoip ]";
+        description = "
+          Varnish modules (except 'std').
+        ";
+      };
+
+      extraCommandLine = mkOption {
+        type = types.str;
+        default = "";
+        example = "-s malloc,256M";
+        description = "
+          Command line switches for varnishd (run 'varnishd -?' to get list of options)
+        ";
+      };
     };
 
   };
@@ -42,6 +58,7 @@ with lib;
     systemd.services.varnish = {
       description = "Varnish";
       wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
       preStart = ''
         mkdir -p ${cfg.stateDir}
         chown -R varnish:varnish ${cfg.stateDir}
@@ -49,8 +66,19 @@ with lib;
       postStop = ''
         rm -rf ${cfg.stateDir}
       '';
-      serviceConfig.ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -u varnish";
-      serviceConfig.Type = "forking";
+      serviceConfig = {
+        Type = "simple";
+        PermissionsStartOnly = true;
+        ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -F ${cfg.extraCommandLine}"
+          + optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([pkgs.varnish] ++ cfg.extraModules)}' -r vmod_path";
+        Restart = "always";
+        RestartSec = "5s";
+        User = "varnish";
+        Group = "varnish";
+        AmbientCapabilities = "cap_net_bind_service";
+        NoNewPrivileges = true;
+        LimitNOFILE = 131072;
+      };
     };
 
     environment.systemPackages = [ pkgs.varnish ];
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index c207aab5de0a..4b57d9641f50 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -19,7 +19,7 @@ in
   # E.g., if Plasma 5 is enabled, it supersedes xterm.
   imports = [
     ./none.nix ./xterm.nix ./xfce.nix ./plasma5.nix ./lumina.nix
-    ./lxqt.nix ./enlightenment.nix ./gnome3.nix ./kodi.nix
+    ./lxqt.nix ./enlightenment.nix ./gnome3.nix ./kodi.nix ./maxx.nix
   ];
 
   options = {
diff --git a/nixos/modules/services/x11/desktop-managers/maxx.nix b/nixos/modules/services/x11/desktop-managers/maxx.nix
new file mode 100644
index 000000000000..d7bd2fc5eb0c
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/maxx.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.maxx;
+in {
+  options.services.xserver.desktopManager.maxx = {
+    enable = mkEnableOption "MaXX desktop environment";
+  };
+
+  config = mkIf (xcfg.enable && cfg.enable) {
+    environment.systemPackages = [ pkgs.maxx ];
+
+    services.xserver.desktopManager.session = [
+    { name = "MaXX";
+      start = ''
+        exec ${pkgs.maxx}/opt/MaXX/etc/skel/Xsession.dt
+      '';
+    }];
+  };
+
+  meta.maintainers = [ maintainers.gnidorah ];
+}
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 6adb2c1681a2..001c6473a98e 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -120,7 +120,6 @@ let
 
       # Run systemd-nspawn without startup notification (we'll
       # wait for the container systemd to signal readiness).
-      EXIT_ON_REBOOT=1 \
       exec ${config.systemd.package}/bin/systemd-nspawn \
         --keep-unit \
         -M "$INSTANCE" -D "$root" $extraFlags \