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/config/vpnc.nix41
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl21
-rw-r--r--nixos/modules/installer/tools/tools.nix1
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix3
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/misc/meta.nix63
-rw-r--r--nixos/modules/module-list.nix6
-rw-r--r--nixos/modules/programs/shadow.nix2
-rw-r--r--nixos/modules/programs/virtualbox.nix2
-rw-r--r--nixos/modules/security/pam.nix20
-rw-r--r--nixos/modules/security/setuid-wrappers.nix4
-rw-r--r--nixos/modules/services/misc/uhub.nix186
-rw-r--r--nixos/modules/services/network-filesystems/nfsd.nix13
-rw-r--r--nixos/modules/services/networking/atftpd.nix51
-rw-r--r--nixos/modules/services/networking/cjdns.nix316
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix18
-rw-r--r--nixos/modules/services/networking/nsd.nix241
-rw-r--r--nixos/modules/services/networking/openntpd.nix49
-rw-r--r--nixos/modules/services/networking/privoxy.nix76
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix38
-rw-r--r--nixos/modules/services/networking/znc.nix190
-rw-r--r--nixos/modules/services/security/clamav.nix4
-rw-r--r--nixos/modules/services/ttys/agetty.nix7
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/mediawiki.nix2
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix10
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix37
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl135
-rw-r--r--nixos/modules/system/boot/luksroot.nix67
-rw-r--r--nixos/modules/system/boot/modprobe.nix5
-rw-r--r--nixos/modules/tasks/filesystems/nfs.nix37
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix6
-rw-r--r--nixos/modules/tasks/network-interfaces.nix147
-rw-r--r--nixos/modules/virtualisation/docker-image.nix67
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix35
-rw-r--r--nixos/modules/virtualisation/openvswitch.nix117
36 files changed, 1337 insertions, 684 deletions
diff --git a/nixos/modules/config/vpnc.nix b/nixos/modules/config/vpnc.nix
new file mode 100644
index 00000000000..68d755232eb
--- /dev/null
+++ b/nixos/modules/config/vpnc.nix
@@ -0,0 +1,41 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.vpnc;
+  mkServiceDef = name: value:
+    {
+      name = "vpnc/${name}.conf";
+      value = { text = value; };
+    };
+
+in
+{
+  options = {
+    networking.vpnc = {
+      services = mkOption {
+       type = types.attrsOf types.str;
+       default = {};
+       example = {
+         test = 
+          ''
+           IPSec gateway 192.168.1.1 
+           IPSec ID someID
+           IPSec secret secretKey
+           Xauth username name
+           Xauth password pass
+          '';
+       };
+       description = 
+         ''
+           The names of cisco VPNs and their associated definitions
+         '';
+      };
+    };
+  };
+
+  config.environment.etc = mapAttrs' mkServiceDef cfg.services;
+}
+
+
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 93a348f2717..c507f7f979f 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -20,13 +20,6 @@ sub uniq {
     return @res;
 }
 
-sub runCommand {
-    my ($cmd) = @_;
-    open FILE, "$cmd 2>/dev/null |" or die "Failed to execute: $cmd\n";
-    my @ret = <FILE>;
-    close FILE;
-    return ($?, @ret);
-}
 
 # Process the command line.
 my $outDir = "/etc/nixos";
@@ -344,20 +337,6 @@ EOF
         }
     }
 
-	# Is this a btrfs filesystem?
-	if ($fsType eq "btrfs") {
-		my ($status, @info) = runCommand("btrfs subvol show $rootDir$mountPoint");
-		if ($status != 0) {
-			die "Failed to retreive subvolume info for $mountPoint";
-		}
-		my @subvols = join("", @info) =~ m/Name:[ \t\n]*([^ \t\n]*)/;
-		if ($#subvols > 0) {
-			die "Btrfs subvol name for $mountPoint listed multiple times in mount\n"
-		} elsif ($#subvols == 0) {
-			push @extraOptions, "subvol=$subvols[0]";
-		}
-	}
-
     # Emit the filesystem.
     $fileSystems .= <<EOF;
   fileSystems.\"$mountPoint\" =
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index 91a30695a7a..39da2f1f0be 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -38,7 +38,6 @@ let
   nixos-generate-config = makeProg {
     name = "nixos-generate-config";
     src = ./nixos-generate-config.pl;
-    path = [ pkgs.btrfsProgs ];
     perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
   };
 
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
index 49ec0899610..f68f8dc40aa 100644
--- a/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -10,9 +10,6 @@ with lib;
       ../profiles/clone-config.nix
     ];
 
-  # FIXME: UUID detection is currently broken
-  boot.loader.grub.fsIdentifier = "provided";
-
   # Allow mounting of shared folders.
   users.extraUsers.demo.extraGroups = [ "vboxsf" ];
 
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 513da5d50a1..efd8b253cd4 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -149,6 +149,7 @@
       radvd = 139;
       zookeeper = 140;
       dnsmasq = 141;
+      uhub = 142;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -268,6 +269,7 @@
       mlmmj = 135;
       riemann = 137;
       riemanndash = 138;
+      uhub = 142;
 
       # When adding a gid, make sure it doesn't match an existing uid. And don't use gids above 399!
 
diff --git a/nixos/modules/misc/meta.nix b/nixos/modules/misc/meta.nix
new file mode 100644
index 00000000000..22622706f2c
--- /dev/null
+++ b/nixos/modules/misc/meta.nix
@@ -0,0 +1,63 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  maintainer = mkOptionType {
+    name = "maintainer";
+    check = email: elem email (attrValues lib.maintainers);
+    merge = loc: defs: listToAttrs (singleton (nameValuePair (last defs).file (last defs).value));
+  };
+
+  listOfMaintainers = types.listOf maintainer // {
+    # Returns list of
+    #   { "module-file" = [
+    #        "maintainer1 <first@nixos.org>"
+    #        "maintainer2 <second@nixos.org>" ];
+    #   }
+    merge = loc: defs:
+      zipAttrs
+        (flatten (imap (n: def: imap (m: def':
+          maintainer.merge (loc ++ ["[${toString n}-${toString m}]"])
+            [{ inherit (def) file; value = def'; }]) def.value) defs));
+  };
+
+  docFile = types.path // {
+    # Returns tuples of
+    #   { file = "module location"; value = <path/to/doc.xml>; }
+    merge = loc: defs: defs;
+  };
+in
+
+{
+  options = {
+    meta = {
+
+      maintainers = mkOption {
+        type = listOfMaintainers;
+        internal = true;
+        default = [];
+        example = [ lib.maintainers.all ];
+        description = ''
+	  List of maintainers of each module.  This option should be defined at
+          most once per module.
+        '';
+      };
+
+      doc = mkOption {
+        type = docFile;
+        internal = true;
+        example = "./meta.xml";
+        description = ''
+	  Documentation prologe for the set of options of each module.  This
+          option should be defined at most once per module.
+        '';
+      };
+
+    };
+  };
+
+  config = {
+    meta.maintainers = singleton lib.maintainers.pierron;
+  };
+}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 853961804e5..76d1ed8a9d4 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -21,6 +21,7 @@
   ./config/system-environment.nix
   ./config/system-path.nix
   ./config/timezone.nix
+  ./config/vpnc.nix
   ./config/unix-odbc-drivers.nix
   ./config/users-groups.nix
   ./config/zram.nix
@@ -45,6 +46,7 @@
   ./misc/ids.nix
   ./misc/lib.nix
   ./misc/locate.nix
+  ./misc/meta.nix
   ./misc/nixpkgs.nix
   ./misc/passthru.nix
   ./misc/version.nix
@@ -168,6 +170,7 @@
   ./services/misc/siproxd.nix
   ./services/misc/svnserve.nix
   ./services/misc/synergy.nix
+  ./services/misc/uhub.nix
   ./services/misc/zookeeper.nix
   ./services/monitoring/apcupsd.nix
   ./services/monitoring/dd-agent.nix
@@ -190,6 +193,7 @@
   ./services/network-filesystems/rsyncd.nix
   ./services/network-filesystems/samba.nix
   ./services/networking/amuled.nix
+  ./services/networking/atftpd.nix
   ./services/networking/avahi-daemon.nix
   ./services/networking/bind.nix
   ./services/networking/bitlbee.nix
@@ -227,6 +231,7 @@
   ./services/networking/ntpd.nix
   ./services/networking/oidentd.nix
   ./services/networking/openfire.nix
+  ./services/networking/openntpd.nix
   ./services/networking/openvpn.nix
   ./services/networking/polipo.nix
   ./services/networking/prayer.nix
@@ -360,6 +365,7 @@
   ./virtualisation/docker.nix
   ./virtualisation/libvirtd.nix
   #./virtualisation/nova.nix
+  ./virtualisation/openvswitch.nix
   ./virtualisation/virtualbox-guest.nix
   #./virtualisation/xen-dom0.nix
 ]
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 5a467e112c2..5c2ea07c554 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -83,7 +83,7 @@ in
     security.pam.services =
       { chsh = { rootOK = true; };
         chfn = { rootOK = true; };
-        su = { rootOK = true; forwardXAuth = true; };
+        su = { rootOK = true; forwardXAuth = true; logFailures = true; };
         passwd = {};
         # Note: useradd, groupadd etc. aren't setuid root, so it
         # doesn't really matter what the PAM config says as long as it
diff --git a/nixos/modules/programs/virtualbox.nix b/nixos/modules/programs/virtualbox.nix
index e2dd76219eb..fec1a7b61f3 100644
--- a/nixos/modules/programs/virtualbox.nix
+++ b/nixos/modules/programs/virtualbox.nix
@@ -44,5 +44,5 @@ let virtualbox = config.boot.kernelPackages.virtualbox; in
         '';
     };
 
-  networking.interfaces.vboxnet0 = { ipAddress = "192.168.56.1"; prefixLength = 24; };
+  networking.interfaces.vboxnet0.ip4 = [ { address = "192.168.56.1"; prefixLength = 24; } ];
 }
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index b1b75a0068d..844a9da0eb4 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -126,12 +126,28 @@ let
         description = "Whether to show the message of the day.";
       };
 
+      makeHomeDir = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to try to create home directories for users
+          with <literal>$HOME</literal>s pointing to nonexistent
+          locations on session login.
+        '';
+      };
+
       updateWtmp = mkOption {
         default = false;
         type = types.bool;
         description = "Whether to update <filename>/var/log/wtmp</filename>.";
       };
 
+      logFailures = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to log authentication failures in <filename>/var/log/faillog</filename>.";
+      };
+
       text = mkOption {
         type = types.nullOr types.lines;
         description = "Contents of the PAM service file.";
@@ -159,6 +175,8 @@ let
           # Authentication management.
           ${optionalString cfg.rootOK
               "auth sufficient pam_rootok.so"}
+          ${optionalString cfg.logFailures
+              "auth required pam_tally.so"}
           ${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth)
               "auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=~/.ssh/authorized_keys:~/.ssh/authorized_keys2:/etc/ssh/authorized_keys.d/%u"}
           ${optionalString cfg.usbAuth
@@ -192,6 +210,8 @@ let
               "session ${
                 if config.boot.isContainer then "optional" else "required"
               } pam_loginuid.so"}
+          ${optionalString cfg.makeHomeDir
+              "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=/etc/skel umask=0022"}
           ${optionalString cfg.updateWtmp
               "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
           ${optionalString config.users.ldap.enable
diff --git a/nixos/modules/security/setuid-wrappers.nix b/nixos/modules/security/setuid-wrappers.nix
index 373afffd3fb..22dbdf6a6bf 100644
--- a/nixos/modules/security/setuid-wrappers.nix
+++ b/nixos/modules/security/setuid-wrappers.nix
@@ -77,7 +77,9 @@ in
   config = {
 
     security.setuidPrograms =
-      [ "fusermount" "wodim" "cdrdao" "growisofs" ];
+      [ "mount.nfs" "mount.nfs4" "mount.cifs"
+        "fusermount" "umount"
+        "wodim" "cdrdao" "growisofs" ];
 
     system.activationScripts.setuid =
       let
diff --git a/nixos/modules/services/misc/uhub.nix b/nixos/modules/services/misc/uhub.nix
new file mode 100644
index 00000000000..15071202b9c
--- /dev/null
+++ b/nixos/modules/services/misc/uhub.nix
@@ -0,0 +1,186 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.uhub;
+
+  uhubPkg = pkgs.uhub.override { tlsSupport = cfg.enableTLS; };
+
+  pluginConfig = ""
+  + optionalString cfg.plugins.authSqlite.enable ''
+    plugin ${uhubPkg.mod_auth_sqlite}/mod_auth_sqlite.so "file=${cfg.plugins.authSqlite.file}"
+  ''
+  + optionalString cfg.plugins.logging.enable ''
+    plugin ${uhubPkg.mod_logging}/mod_logging.so ${if cfg.plugins.logging.syslog then "syslog=true" else "file=${cfg.plugins.logging.file}"}
+  ''
+  + optionalString cfg.plugins.welcome.enable ''
+    plugin ${uhubPkg.mod_welcome}/mod_welcome.so "motd=${pkgs.writeText "motd.txt"  cfg.plugins.welcome.motd} rules=${pkgs.writeText "rules.txt" cfg.plugins.welcome.rules}"
+  ''
+  + optionalString cfg.plugins.history.enable ''
+    plugin ${uhubPkg.mod_chat_history}/mod_chat_history.so "history_max=${toString cfg.plugins.history.max} history_default=${toString cfg.plugins.history.default} history_connect=${toString cfg.plugins.history.connect}"
+  '';
+
+  uhubConfigFile = pkgs.writeText "uhub.conf" ''
+    file_acl=${pkgs.writeText "users.conf" cfg.aclConfig}
+    file_plugins=${pkgs.writeText "plugins.conf" pluginConfig}
+    server_bind_addr=${cfg.address}
+    server_port=${toString cfg.port}
+    ${lib.optionalString cfg.enableTLS "tls_enable=yes"}
+    ${cfg.hubConfig}
+  '';
+
+in
+
+{
+  options = {
+
+    services.uhub = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+	description = "Whether to enable the uhub ADC hub.";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 1511;
+	description = "TCP port to bind the hub to.";
+      };
+
+      address = mkOption {
+        type = types.string;
+        default = "any";
+	description = "Address to bind the hub to.";
+      };
+
+      enableTLS = mkOption {
+        type = types.bool;
+        default = false;
+	description = "Whether to enable TLS support.";
+      };
+
+      hubConfig = mkOption {
+        type = types.lines;
+        default = "";
+	description = "Contents of uhub configuration file.";
+      };
+
+      aclConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Contents of user ACL configuration file.";
+      };
+
+      plugins = {
+
+        authSqlite = {
+	  enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable the Sqlite authentication database plugin";
+	  };
+          file = mkOption {
+            type = types.string;
+            example = "/var/db/uhub-users";
+            description = "Path to user database. Use the uhub-passwd utility to create the database and add/remove users.";
+          };
+        };
+
+        logging = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable the logging plugin.";
+          };
+          file = mkOption {
+            type = types.string;
+            default = "";
+            description = "Path of log file.";
+          };
+          syslog = mkOption {
+            type = types.bool;
+            default = false;
+            description = "If true then the system log is used instead of writing to file.";
+          };
+        };
+
+        welcome = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable the welcome plugin.";
+          };
+          motd = mkOption {
+            default = "";
+            type = types.lines;
+            description = ''
+              Welcome message displayed to clients after connecting 
+              and with the <literal>!motd</literal> command.
+            '';
+          };
+          rules = mkOption {
+            default = "";
+            type = types.lines;
+            description = ''
+              Rules message, displayed to clients with the <literal>!rules</literal> command.
+            '';
+          };
+        };
+
+        history = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable the history plugin.";
+          };
+          max = mkOption {
+            type = types.int;
+            default = 200;
+            description = "The maximum number of messages to keep in history";
+          };
+          default = mkOption {
+            type = types.int;
+            default = 10;
+            description = "When !history is provided without arguments, then this default number of messages are returned.";
+          };
+          connect = mkOption {
+            type = types.int;
+            default = 5;
+            description = "The number of chat history messages to send when users connect (0 = do not send any history).";
+          };
+        };
+
+      };
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    users = {
+      extraUsers = singleton {
+        name = "uhub";
+        uid = config.ids.uids.uhub;
+      };
+      extraGroups = singleton {
+        name = "uhub";
+        gid = config.ids.gids.uhub;
+      };
+    };
+
+    systemd.services.uhub = {
+      description = "high performance peer-to-peer hub for the ADC network";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "notify";
+        ExecStart  = "${uhubPkg}/bin/uhub -c ${uhubConfigFile} -u uhub -g uhub -L";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+  };
+
+}
\ No newline at end of file
diff --git a/nixos/modules/services/network-filesystems/nfsd.nix b/nixos/modules/services/network-filesystems/nfsd.nix
index 2217fec3b0f..57d56cd7287 100644
--- a/nixos/modules/services/network-filesystems/nfsd.nix
+++ b/nixos/modules/services/network-filesystems/nfsd.nix
@@ -56,6 +56,14 @@ in
           default = false;
           description = "Whether to create the mount points in the exports file at startup time.";
         };
+
+        mountdPort = mkOption {
+          default = null;
+          example = 4002;
+          description = ''
+            Use fixed port for rpc.mountd, usefull if server is behind firewall.
+          '';
+        };
       };
 
     };
@@ -138,7 +146,10 @@ in
         restartTriggers = [ exports ];
 
         serviceConfig.Type = "forking";
-        serviceConfig.ExecStart = "@${pkgs.nfsUtils}/sbin/rpc.mountd rpc.mountd";
+        serviceConfig.ExecStart = ''
+          @${pkgs.nfsUtils}/sbin/rpc.mountd rpc.mountd \
+              ${if cfg.mountdPort != null then "-p ${toString cfg.mountdPort}" else ""}
+        '';
         serviceConfig.Restart = "always";
       };
 
diff --git a/nixos/modules/services/networking/atftpd.nix b/nixos/modules/services/networking/atftpd.nix
new file mode 100644
index 00000000000..ab9f8650f0f
--- /dev/null
+++ b/nixos/modules/services/networking/atftpd.nix
@@ -0,0 +1,51 @@
+# NixOS module for atftpd TFTP server
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.atftpd;
+
+in
+
+{
+
+  options = {
+
+    services.atftpd = {
+
+      enable = mkOption {
+        default = false;
+        type = types.uniq types.bool;
+        description = ''
+          Whenever to enable the atftpd TFTP server.
+        '';
+      };
+
+      root = mkOption {
+        default = "/var/empty";
+        type = types.uniq types.string;
+        description = ''
+          Document root directory for the atftpd.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.atftpd = {
+      description = "atftpd TFTP server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      # runs as nobody
+      serviceConfig.ExecStart = "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address 0.0.0.0 ${cfg.root}";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix
index 9306ffd5a18..0519172db91 100644
--- a/nixos/modules/services/networking/cjdns.nix
+++ b/nixos/modules/services/networking/cjdns.nix
@@ -1,13 +1,3 @@
-# You may notice the commented out sections in this file,
-# it would be great to configure cjdns from nix, but cjdns 
-# reads its configuration from stdin, including the private
-# key and admin password, all nested in a JSON structure.
-#
-# Until a good method of storing the keys outside the nix 
-# store and mixing them back into a string is devised
-# (without too much shell hackery), a skeleton of the
-# configuration building lies commented out.
-
 { config, lib, pkgs, ... }:
 
 with lib;
@@ -16,41 +6,35 @@ let
 
   cfg = config.services.cjdns;
 
-  /*
-  # can't keep keys and passwords in the nix store,
-  # but don't want to deal with this stdin quagmire.
-
-  cjdrouteConf = '' {
-    "admin": {"bind": "${cfg.admin.bind}", "password": "\${CJDNS_ADMIN}" },
-    "privateKey": "\${CJDNS_KEY}",
-
-    "interfaces": {
-    ''
-
-    + optionalString (cfg.interfaces.udp.bind.address != null) ''
-      "UDPInterface": [ {
-        "bind": "${cfg.interfaces.udp.bind.address}:"''
-	   ${if cfg.interfaces.upd.bind.port != null
-             then ${toString cfg.interfaces.udp.bind.port}
-	     else ${RANDOM}
-	   fi)
-      + '' } ]''
-
-    + (if cfg.interfaces.eth.bind != null then ''
-      "ETHInterface": [ {
-        "bind": "${cfg.interfaces.eth.bind}",
-        "beacon": ${toString cfg.interfaces.eth.beacon}
-      } ]
-    '' fi )
-    + ''
-    },
-    "router": { "interface": { "type": "TUNInterface" }, },
-    "security": [ { "setuser": "nobody" } ]
-    }
-    '';   
-
-    cjdrouteConfFile = pkgs.writeText "cjdroute.conf" cjdrouteConf
-    */
+  # would be nice to  merge 'cfg' with a //,
+  # but the json nesting is wacky.
+  cjdrouteConf = builtins.toJSON ( {
+    admin = {
+      bind = cfg.admin.bind;
+      password = "@CJDNS_ADMIN_PASSWORD@";
+    };
+    authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords;
+    interfaces = {
+      ETHInterface = if (cfg.ETHInterface.bind != "") then [ cfg.ETHInterface ] else [ ];
+      UDPInterface = if (cfg.UDPInterface.bind != "") then [ cfg.UDPInterface ] else [ ];
+    };
+
+    privateKey = "@CJDNS_PRIVATE_KEY@";
+
+    resetAfterInactivitySeconds = 100;
+
+    router = {
+      interface = { type = "TUNInterface"; };
+      ipTunnel = {
+        allowedConnections = [];
+        outgoingConnections = [];
+      };
+    };
+
+    security = [ { exemptAngel = 1; setuser = "nobody"; } ];
+
+  });
+
 in
 
 {
@@ -62,146 +46,180 @@ in
         type = types.bool;
 	default = false;
         description = ''
-          Enable this option to start a instance of the 
-          cjdns network encryption and and routing engine.
-          Configuration will be read from <literal>confFile</literal>.
+          Whether to enable the cjdns network encryption
+          and routing engine. A file at /etc/cjdns.keys will
+          be created if it does not exist to contain a random
+          secret key that your IPv6 address will be derived from.
         '';
       };
 
-      confFile = mkOption {
-	default = "/etc/cjdroute.conf";
-        description = ''
-          Configuration file to pipe to cjdroute.
+      authorizedPasswords = mkOption {
+        type = types.listOf types.str;
+	default = [ ];
+	example = [
+          "snyrfgkqsc98qh1y4s5hbu0j57xw5s0"
+	  "z9md3t4p45mfrjzdjurxn4wuj0d8swv"
+	  "49275fut6tmzu354pq70sr5b95qq0vj"
+        ];
+	description = ''
+	  Any remote cjdns nodes that offer these passwords on 
+	  connection will be allowed to route through this node.
         '';
       };
-
-      /*
+    
       admin = {
         bind = mkOption {
+          type = types.string;
 	  default = "127.0.0.1:11234";
 	  description = ''
             Bind the administration port to this address and port.
 	  '';
         };
+      };
 
-	passwordFile = mkOption {
-	  example = "/root/cjdns.adminPassword";
-	  description = ''
-	    File containing a password to the administration port.
+      UDPInterface = {
+        bind = mkOption {
+          type = types.string;
+	  default = "";
+          example = "192.168.1.32:43211";
+          description = ''
+	    Address and port to bind UDP tunnels to.
+	  '';
+ 	};
+        connectTo = mkOption {
+          type = types.attrsOf ( types.submodule (
+	    { options, ... }:
+            { options = {
+                # TODO make host an option, and add it to networking.extraHosts
+                password = mkOption {
+                  type = types.str;
+                  description = "Authorized password to the opposite end of the tunnel.";
+                };
+                publicKey = mkOption {
+                  type = types.str;
+                  description = "Public key at the opposite end of the tunnel.";
+                };
+              };
+            }
+          ));
+	  default = { };
+          example = {
+            "192.168.1.1:27313" = {
+	      password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
+              publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
+            };
+          };
+          description = ''
+	    Credentials for making UDP tunnels.
 	  '';
 	};
       };
 
-      keyFile = mkOption {
-        type = types.str;
-	example = "/root/cjdns.key";
-	description = ''
-	  Path to a file containing a cjdns private key on a single line.
-	'';
-      };
-      
-      passwordsFile = mkOption {
-        type = types.str;
-	default = null;
-	example = "/root/cjdns.authorizedPasswords";
-	description = ''
-	  A file containing a list of json dictionaries with passwords.
-	  For example:
-	    {"password": "s8xf5z7znl4jt05g922n3wpk75wkypk"},
-	    { "name": "nice guy",
-	      "password": "xhthk1mglz8tpjrbbvdlhyc092rhpx5"},
-	    {"password": "3qfxyhmrht7uwzq29pmhbdm9w4bnc8w"}
+      ETHInterface = {
+        bind = mkOption {
+	  default = "";
+	  example = "eth0";
+	  description = ''
+	    Bind to this device for native ethernet operation.
 	  '';
-	};
-
-      interfaces = {
-        udp = {
-	  bind = { 
-            address = mkOption {
-	      default = "0.0.0.0";
-	      description = ''
-	        Address to bind UDP tunnels to; disable by setting to null;
-	      '';
- 	    };
-	    port = mkOption {
-	      type = types.int;
-	      default = null;
-	      description = ''
-	        Port to bind UDP tunnels to.
-	        A port will be choosen at random if this is not set.
-	        This option is required to act as the server end of 
-	        a tunnel.
-	      '';
- 	    };
-	  };
-	};
+        };
 
-	eth = {
-	  bind = mkOption {
-	    default = null;
-	    example = "eth0";
-	    description = ''
-	      Bind to this device and operate with native wire format.
-	    '';
-	  };
-
-	  beacon = mkOption {
-	    default = 2;
-	    description = ''
-	      Auto-connect to other cjdns nodes on the same network.
-	      Options:
-	        0 -- Disabled.
-
-                1 -- Accept beacons, this will cause cjdns to accept incoming
-		     beacon messages and try connecting to the sender.
-
-		2 -- Accept and send beacons, this will cause cjdns to broadcast
-		     messages on the local network which contain a randomly
-		     generated per-session password, other nodes which have this
-                     set to 1 or 2 will hear the beacon messages and connect
-                     automatically.
-            '';
-	  };
-	  
-	  connectTo = mkOption {
-	    type = types.listOf types.str;
-	    default = [];
-	    description = ''
-	      Credentials for connecting look similar to UDP credientials
-              except they begin with the mac address, for example:
-              "01:02:03:04:05:06":{"password":"a","publicKey":"b"}
-	    '';
-	  };
+        beacon = mkOption {
+	  type = types.int;
+          default = 2;
+          description = ''
+            Auto-connect to other cjdns nodes on the same network.
+            Options:
+	      0: Disabled.
+              1: Accept beacons, this will cause cjdns to accept incoming
+                 beacon messages and try connecting to the sender.
+              2: Accept and send beacons, this will cause cjdns to broadcast
+                 messages on the local network which contain a randomly
+                 generated per-session password, other nodes which have this
+                 set to 1 or 2 will hear the beacon messages and connect
+                 automatically.
+          '';
         };
+
+        connectTo = mkOption {
+          type = types.attrsOf ( types.submodule (
+	    { options, ... }:
+            { options = {
+                password = mkOption {
+                  type = types.str;
+                  description = "Authorized password to the opposite end of the tunnel.";
+                };
+                publicKey = mkOption {
+                  type = types.str;
+                  description = "Public key at the opposite end of the tunnel.";
+                };
+              };
+            }
+          ));
+	  default = { };
+          example = {
+            "01:02:03:04:05:06" = {
+	      password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
+              publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
+            };
+          };
+	  description = ''
+	    Credentials for connecting look similar to UDP credientials
+            except they begin with the mac address.
+	  '';
+	};
       };
-      */
+
     };
+
   };
 
   config = mkIf config.services.cjdns.enable {
 
     boot.kernelModules = [ "tun" ];
 
-    /*
-    networking.firewall.allowedUDPPorts = mkIf (cfg.udp.bind.port != null) [
-      cfg.udp.bind.port
-    ];
-    */
+    # networking.firewall.allowedUDPPorts = ...
 
     systemd.services.cjdns = {
       description = "encrypted networking for everybody";
       wantedBy = [ "multi-user.target" ];
-      wants = [ "network.target" ];
-      before = [ "network.target" ];
-      path = [ pkgs.cjdns ];
+      after = [ "network-interfaces.target" ];
+
+      script = ''
+        source /etc/cjdns.keys
+        echo '${cjdrouteConf}' | sed \
+	  -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \
+          -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \
+            | ${pkgs.cjdns}/sbin/cjdroute
+      '';
 
       serviceConfig = {
         Type = "forking";
-	ExecStart = ''
-          ${pkgs.stdenv.shell} -c "${pkgs.cjdns}/sbin/cjdroute < ${cfg.confFile}"
-	'';
 	Restart = "on-failure";
       };
     };
+
+    system.activationScripts.cjdns = ''
+      grep -q "CJDNS_PRIVATE_KEY=" /etc/cjdns.keys || \
+        echo "CJDNS_PRIVATE_KEY=$(${pkgs.cjdns}/sbin/makekey)" \
+	  >> /etc/cjdns.keys
+
+      grep -q "CJDNS_ADMIN_PASSWORD=" /etc/cjdns.keys || \
+        echo "CJDNS_ADMIN_PASSWORD=$(${pkgs.coreutils}/bin/head -c 96 /dev/urandom | ${pkgs.coreutils}/bin/tr -dc A-Za-z0-9)" \
+	  >> /etc/cjdns.keys
+
+      chmod 600 /etc/cjdns.keys
+    '';
+
+    assertions = [
+      { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" );
+        message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
+      }
+      { assertion = config.networking.enableIPv6;
+        message = "networking.enableIPv6 must be enabled for CJDNS to work";
+      }
+    ];
+
   };
-}
+
+}
\ No newline at end of file
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index 89aa9bdb6b6..084ac69e8d5 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -11,7 +11,7 @@ let
   # Don't start dhcpcd on explicitly configured interfaces or on
   # interfaces that are part of a bridge, bond or sit device.
   ignoredInterfaces =
-    map (i: i.name) (filter (i: i.ipAddress != null) (attrValues config.networking.interfaces))
+    map (i: i.name) (filter (i: i.ip4 != [ ] || i.ipAddress != null) (attrValues config.networking.interfaces))
     ++ mapAttrsToList (i: _: i) config.networking.sits
     ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges))
     ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds))
@@ -64,7 +64,7 @@ let
       #    ${config.systemd.package}/bin/systemctl start ip-down.target
       #fi
 
-      ${config.networking.dhcpcd.runHook}
+      ${cfg.runHook}
     '';
 
 in
@@ -75,6 +75,18 @@ in
 
   options = {
 
+    networking.dhcpcd.persistent = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+          Whenever to leave interfaces configured on dhcpcd daemon
+          shutdown. Set to true if you have your root or store mounted
+          over the network or this machine accepts SSH connections
+          through DHCP interfaces and clients should be notified when
+          it shuts down.
+      '';
+    };
+
     networking.dhcpcd.denyInterfaces = mkOption {
       type = types.listOf types.str;
       default = [];
@@ -139,7 +151,7 @@ in
         serviceConfig =
           { Type = "forking";
             PIDFile = "/run/dhcpcd.pid";
-            ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet --config ${dhcpcdConf}";
+            ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}";
             ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind";
             Restart = "always";
           };
diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix
index db8cb122871..cacd52f130f 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -456,156 +456,131 @@ in
       };
 
 
-      ratelimit = mkOption {
-        type = types.submodule (
-          { options, ... }:
-          { options = {
-
-              enable = mkOption {
-                type        = types.bool;
-                default     = false;
-                description = ''
-                  Enable ratelimit capabilities.
-                '';
-              };
-
-              size = mkOption {
-                type        = types.int;
-                default     = 1000000;
-                description = ''
-                  Size of the hashtable. More buckets use more memory but lower
-                  the chance of hash hash collisions.
-                '';
-              };
+      ratelimit = {
+        enable = mkOption {
+          type        = types.bool;
+          default     = false;
+          description = ''
+            Enable ratelimit capabilities.
+          '';
+        };
 
-              ratelimit = mkOption {
-                type        = types.int;
-                default     = 200;
-                description = ''
-                  Max qps allowed from any query source.
-                  0 means unlimited. With an verbosity of 2 blocked and
-                  unblocked subnets will be logged.
-                '';
-              };
+        size = mkOption {
+          type        = types.int;
+          default     = 1000000;
+          description = ''
+            Size of the hashtable. More buckets use more memory but lower
+            the chance of hash hash collisions.
+          '';
+        };
 
-              whitelistRatelimit = mkOption {
-                type        = types.int;
-                default     = 2000;
-                description = ''
-                  Max qps allowed from whitelisted sources.
-                  0 means unlimited. Set the rrl-whitelist option for specific
-                  queries to apply this limit instead of the default to them.
-                '';
-              };
+        ratelimit = mkOption {
+          type        = types.int;
+          default     = 200;
+          description = ''
+            Max qps allowed from any query source.
+            0 means unlimited. With an verbosity of 2 blocked and
+            unblocked subnets will be logged.
+          '';
+        };
 
-              slip = mkOption {
-                type        = types.nullOr types.int;
-                default     = null;
-                description = ''
-                  Number of packets that get discarded before replying a SLIP response.
-                  0 disables SLIP responses. 1 will make every response a SLIP response.
-                '';
-              };
+        whitelistRatelimit = mkOption {
+          type        = types.int;
+          default     = 2000;
+          description = ''
+            Max qps allowed from whitelisted sources.
+            0 means unlimited. Set the rrl-whitelist option for specific
+            queries to apply this limit instead of the default to them.
+          '';
+        };
 
-              ipv4PrefixLength = mkOption {
-                type        = types.nullOr types.int;
-                default     = null;
-                description = ''
-                  IPv4 prefix length. Addresses are grouped by netblock.
-                '';
-              };
+        slip = mkOption {
+          type        = types.nullOr types.int;
+          default     = null;
+          description = ''
+            Number of packets that get discarded before replying a SLIP response.
+            0 disables SLIP responses. 1 will make every response a SLIP response.
+          '';
+        };
 
-              ipv6PrefixLength = mkOption {
-                type        = types.nullOr types.int;
-                default     = null;
-                description = ''
-                  IPv6 prefix length. Addresses are grouped by netblock.
-                '';
-              };
+        ipv4PrefixLength = mkOption {
+          type        = types.nullOr types.int;
+          default     = null;
+          description = ''
+            IPv4 prefix length. Addresses are grouped by netblock.
+          '';
+        };
 
-            };
-          });
-        default = {
+        ipv6PrefixLength = mkOption {
+          type        = types.nullOr types.int;
+          default     = null;
+          description = ''
+            IPv6 prefix length. Addresses are grouped by netblock.
+          '';
         };
-        example = {};
-        description = ''
-        '';
       };
 
 
-      remoteControl = mkOption {
-        type = types.submodule (
-          { config, options, ... }:
-          { options = {
-
-              enable = mkOption {
-                type        = types.bool;
-                default     = false;
-                description = ''
-                  Wheter to enable remote control via nsd-control(8).
-                '';
-              };
-
-              interfaces = mkOption {
-                type        = types.listOf types.str;
-                default     = [ "127.0.0.1" "::1" ];
-                description = ''
-                  Which interfaces NSD should bind to for remote control.
-                '';
-              };
-
-              port = mkOption {
-                type        = types.int;
-                default     = 8952;
-                description = ''
-                  Port number for remote control operations (uses TLS over TCP).
-                '';
-              };
+      remoteControl = {
+        enable = mkOption {
+          type        = types.bool;
+          default     = false;
+          description = ''
+            Wheter to enable remote control via nsd-control(8).
+          '';
+        };
 
-              serverKeyFile = mkOption {
-                type        = types.path;
-                default     = "/etc/nsd/nsd_server.key";
-                description = ''
-                  Path to the server private key, which is used by the server
-                  but not by nsd-control. This file is generated by nsd-control-setup.
-                '';
-              };
+        interfaces = mkOption {
+          type        = types.listOf types.str;
+          default     = [ "127.0.0.1" "::1" ];
+          description = ''
+            Which interfaces NSD should bind to for remote control.
+          '';
+        };
 
-              serverCertFile = mkOption {
-                type        = types.path;
-                default     = "/etc/nsd/nsd_server.pem";
-                description = ''
-                  Path to the server self signed certificate, which is used by the server
-                  but and by nsd-control. This file is generated by nsd-control-setup.
-                '';
-              };
+        port = mkOption {
+          type        = types.int;
+          default     = 8952;
+          description = ''
+            Port number for remote control operations (uses TLS over TCP).
+          '';
+        };
 
-              controlKeyFile = mkOption {
-                type        = types.path;
-                default     = "/etc/nsd/nsd_control.key";
-                description = ''
-                  Path to the client private key, which is used by nsd-control
-                  but not by the server. This file is generated by nsd-control-setup.
-                '';
-              };
+        serverKeyFile = mkOption {
+          type        = types.path;
+          default     = "/etc/nsd/nsd_server.key";
+          description = ''
+            Path to the server private key, which is used by the server
+            but not by nsd-control. This file is generated by nsd-control-setup.
+          '';
+        };
 
-              controlCertFile = mkOption {
-                type        = types.path;
-                default     = "/etc/nsd/nsd_control.pem";
-                description = ''
-                  Path to the client certificate signed with the server certificate.
-                  This file is used by nsd-control and generated by nsd-control-setup.
-                '';
-              };
+        serverCertFile = mkOption {
+          type        = types.path;
+          default     = "/etc/nsd/nsd_server.pem";
+          description = ''
+            Path to the server self signed certificate, which is used by the server
+            but and by nsd-control. This file is generated by nsd-control-setup.
+          '';
+        };
 
-            };
+        controlKeyFile = mkOption {
+          type        = types.path;
+          default     = "/etc/nsd/nsd_control.key";
+          description = ''
+            Path to the client private key, which is used by nsd-control
+            but not by the server. This file is generated by nsd-control-setup.
+          '';
+        };
 
-          });
-        default = {
+        controlCertFile = mkOption {
+          type        = types.path;
+          default     = "/etc/nsd/nsd_control.pem";
+          description = ''
+            Path to the client certificate signed with the server certificate.
+            This file is used by nsd-control and generated by nsd-control-setup.
+          '';
         };
-        example = {};
-        description = ''
-        '';
       };
 
 
diff --git a/nixos/modules/services/networking/openntpd.nix b/nixos/modules/services/networking/openntpd.nix
new file mode 100644
index 00000000000..bd8a7a04a2a
--- /dev/null
+++ b/nixos/modules/services/networking/openntpd.nix
@@ -0,0 +1,49 @@
+{ pkgs, lib, config, options, ... }:
+
+with lib;
+
+let
+  cfg = config.services.openntpd;
+
+  package = pkgs.openntpd.override {
+    privsepUser = "ntp";
+    privsepPath = "/var/empty";
+  };
+
+  cfgFile = pkgs.writeText "openntpd.conf" ''
+    ${concatStringsSep "\n" (map (s: "server ${s}") cfg.servers)}
+  '';
+in
+{
+  ###### interface
+
+  options.services.openntpd = {
+    enable = mkEnableOption "OpenNTP time synchronization server";
+
+    servers = mkOption {
+      default = config.services.ntp.servers;
+      type = types.listOf types.str;
+      inherit (options.services.ntp.servers) description;
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    services.ntp.enable = mkForce false;
+
+    users.extraUsers = singleton {
+      name = "ntp";
+      uid = config.ids.uids.ntp;
+      description = "OpenNTP daemon user";
+      home = "/var/empty";
+    };
+
+    systemd.services.openntpd = {
+      description = "OpenNTP Server";
+      wantedBy = [ "ip-up.target" ];
+      partOf = [ "ip-up.target" ];
+      serviceConfig.ExecStart = "${package}/sbin/ntpd -d -f ${cfgFile}";
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix
index 950112b2dab..94beb78ef5a 100644
--- a/nixos/modules/services/networking/privoxy.nix
+++ b/nixos/modules/services/networking/privoxy.nix
@@ -6,19 +6,18 @@ let
 
   inherit (pkgs) privoxy;
 
-  stateDir = "/var/spool/privoxy";
-
   privoxyUser = "privoxy";
 
-  privoxyFlags = "--no-daemon --user ${privoxyUser} ${privoxyCfg}";
-
-  privoxyCfg = pkgs.writeText "privoxy.conf" ''
-    listen-address  ${config.services.privoxy.listenAddress}
-    logdir          ${config.services.privoxy.logDir}
-    confdir         ${privoxy}/etc
-    filterfile      default.filter
+  cfg = config.services.privoxy;
 
-    ${config.services.privoxy.extraConfig}
+  confFile = pkgs.writeText "privoxy.conf" ''
+    user-manual ${privoxy}/share/doc/privoxy/user-manual
+    confdir ${privoxy}/etc/
+    listen-address  ${cfg.listenAddress}
+    enable-edit-actions ${if (cfg.enableEditActions == true) then "1" else "0"}
+    ${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles}
+    ${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles}
+    ${cfg.extraConfig}
   '';
 
 in
@@ -32,27 +31,51 @@ in
     services.privoxy = {
 
       enable = mkOption {
+        type = types.bool;
         default = false;
         description = ''
-          Whether to run the machine as a HTTP proxy server.
+          Whether to enable the Privoxy non-caching filtering proxy.
         '';
       };
 
       listenAddress = mkOption {
+        type = types.str;
         default = "127.0.0.1:8118";
         description = ''
           Address the proxy server is listening to.
         '';
       };
 
-      logDir = mkOption {
-        default = "/var/log/privoxy" ;
+      actionsFiles = mkOption {
+        type = types.listOf types.str;
+        example = [ "match-all.action" "default.action" "/etc/privoxy/user.action" ];
+        default = [ "match-all.action" "default.action" ];
+        description = ''
+          List of paths to Privoxy action files.
+          These paths may either be absolute or relative to the privoxy configuration directory.
+        '';
+      };
+
+      filterFiles = mkOption {
+        type = types.listOf types.str;
+        example = [ "default.filter" "/etc/privoxy/user.filter" ];
+        default = [ "default.filter" ];
+        description = ''
+          List of paths to Privoxy filter files.
+          These paths may either be absolute or relative to the privoxy configuration directory.
+        '';
+      };
+
+      enableEditActions = mkOption {
+        type = types.bool;
+        default = false;
         description = ''
-          Location for privoxy log files.
+          Whether or not the web-based actions file editor may be used.
         '';
       };
 
       extraConfig = mkOption {
+        type = types.lines;
         default = "" ;
         description = ''
           Extra configuration. Contents will be added verbatim to the configuration file.
@@ -62,33 +85,22 @@ in
 
   };
 
-
   ###### implementation
 
-  config = mkIf config.services.privoxy.enable {
+  config = mkIf cfg.enable {
   
-    environment.systemPackages = [ privoxy ];
-
     users.extraUsers = singleton
       { name = privoxyUser;
         uid = config.ids.uids.privoxy;
         description = "Privoxy daemon user";
-        home = stateDir;
       };
 
-    jobs.privoxy =
-      { name = "privoxy";
-
-        startOn = "startup";
-
-        preStart =
-          ''
-            mkdir -m 0755 -p ${stateDir}
-            chown ${privoxyUser} ${stateDir}
-          '';
-
-        exec = "${privoxy}/sbin/privoxy ${privoxyFlags}";
-      };
+    systemd.services.privoxy = {
+      description = "Filtering web proxy";
+      after = [ "network.target" "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.ExecStart = "${privoxy}/sbin/privoxy --no-daemon --user ${privoxyUser} ${confFile}";
+    };
 
   };
 
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index e4b29a0b909..379dec2e92c 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -144,6 +144,36 @@ in
         '';
       };
 
+      listenAddresses = mkOption {
+        type = types.listOf types.optionSet;
+        default = [];
+        example = [ { addr = "192.168.3.1"; port = 22; } { addr = "0.0.0.0"; port = 64022; } ];
+        description = ''
+          List of addresses and ports to listen on (ListenAddress directive
+          in config). If port is not specified for address sshd will listen
+          on all ports specified by <literal>ports</literal> option.
+          NOTE: this will override default listening on all local addresses and port 22.
+          NOTE: setting this option won't automatically enable given ports
+          in firewall configuration.
+        '';
+        options = {
+          addr = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            description = ''
+              Host, IPv4 or IPv6 address to listen to.
+            '';
+          };
+          port = mkOption {
+            type = types.nullOr types.int;
+            default = null;
+            description = ''
+              Port to listen to.
+            '';
+          };
+        };
+      };
+
       passwordAuthentication = mkOption {
         type = types.bool;
         default = true;
@@ -349,6 +379,10 @@ in
           Port ${toString port}
         '') cfg.ports}
 
+        ${concatMapStrings ({ port, addr }: ''
+          ListenAddress ${addr}${if port != null then ":" + toString port else ""}
+        '') cfg.listenAddresses}
+
         ${optionalString cfgc.setXAuthLocation ''
             XAuthLocation ${pkgs.xorg.xauth}/bin/xauth
         ''}
@@ -383,6 +417,10 @@ in
         assertion = (data.publicKey == null && data.publicKeyFile != null) ||
                     (data.publicKey != null && data.publicKeyFile == null);
         message = "knownHost ${name} must contain either a publicKey or publicKeyFile";
+      })
+      ++ flip map cfg.listenAddresses ({ addr, port }: {
+        assertion = addr != null;
+        message = "addr must be specified in each listenAddresses entry";
       });
 
   };
diff --git a/nixos/modules/services/networking/znc.nix b/nixos/modules/services/networking/znc.nix
index 2aa63c6e7df..9b26b2b3244 100644
--- a/nixos/modules/services/networking/znc.nix
+++ b/nixos/modules/services/networking/znc.nix
@@ -25,85 +25,6 @@ let
     paths = cfg.modulePackages;
   };
 
-  confOptions = { ... }: {
-    options = {
-      modules = mkOption {
-        type = types.listOf types.string;
-        default = [ "partyline" "webadmin" "adminlog" "log" ];
-        example = [ "partyline" "webadmin" "adminlog" "log" ];
-        description = ''
-          A list of modules to include in the `znc.conf` file.
-        '';
-      };
-
-      userModules = mkOption {
-        type = types.listOf types.string;
-        default = [ ];
-        example = [ "fish" "push" ];
-        description = ''
-          A list of user modules to include in the `znc.conf` file.
-        '';
-      };
-
-      userName = mkOption {
-        default = defaultUserName;
-        example = "johntron";
-        type = types.string;
-        description = ''
-          The user name to use when generating the `znc.conf` file.
-          This is the user name used by the user logging into the ZNC web admin. 
-        '';
-      };
-
-      nick = mkOption {
-        default = "znc-user";
-        example = "john";
-        type = types.string;
-        description = ''
-          The IRC nick to use when generating the `znc.conf` file.
-        '';
-      };
-
-      passBlock = mkOption {
-        default = defaultPassBlock;
-        example = "Must be the block generated by the `znc --makepass` command.";
-        type = types.string;
-        description = ''
-          The pass block to use when generating the `znc.conf` file.
-          This is the password used by the user logging into the ZNC web admin.
-          This is the block generated by the `znc --makepass` command.
-          !!! If not specified, please change this after starting the service. !!!
-        '';
-      };
-
-      port = mkOption {
-        default = 5000;
-        example = 5000;
-        type = types.int;
-        description = ''
-          Specifies the port on which to listen.
-        '';
-      };
- 
-      useSSL = mkOption {
-        default = true;
-        example = true;
-        type = types.bool;
-        description = ''
-          Indicates whether the ZNC server should use SSL when listening on the specified port.
-        '';
-      };
-
-      extraZncConf = mkOption {
-        default = "";
-        type = types.lines;
-        description = ''
-          Extra config to `znc.conf` file
-        '';
-      };
-    };
-  };
-
   # Keep znc.conf in nix store, then symlink or copy into `dataDir`, depending on `mutable`.
   mkZncConf = confOpts: ''
     // Also check http://en.znc.in/wiki/Configuration
@@ -211,18 +132,91 @@ in
         '';
       };
 
-      confOptions = mkOption {
-        default = {};
-        example = {
-          modules = [ "log" ];
-          userName = "john";
-          nick = "johntron";
+      /* TODO: add to the documentation of the current module:
+
+         Values to use when creating a `znc.conf` file.
+
+           confOptions = {
+             modules = [ "log" ];
+             userName = "john";
+             nick = "johntron";
+           };
+      */
+      confOptions = {
+        modules = mkOption {
+          type = types.listOf types.string;
+          default = [ "partyline" "webadmin" "adminlog" "log" ];
+          example = [ "partyline" "webadmin" "adminlog" "log" ];
+          description = ''
+            A list of modules to include in the `znc.conf` file.
+          '';
+        };
+
+        userModules = mkOption {
+          type = types.listOf types.string;
+          default = [ ];
+          example = [ "fish" "push" ];
+          description = ''
+            A list of user modules to include in the `znc.conf` file.
+          '';
+        };
+
+        userName = mkOption {
+          default = defaultUserName;
+          example = "johntron";
+          type = types.string;
+          description = ''
+            The user name to use when generating the `znc.conf` file.
+            This is the user name used by the user logging into the ZNC web admin.
+          '';
+        };
+
+        nick = mkOption {
+          default = "znc-user";
+          example = "john";
+          type = types.string;
+          description = ''
+            The IRC nick to use when generating the `znc.conf` file.
+          '';
+        };
+
+        passBlock = mkOption {
+          default = defaultPassBlock;
+          example = "Must be the block generated by the `znc --makepass` command.";
+          type = types.string;
+          description = ''
+            The pass block to use when generating the `znc.conf` file.
+            This is the password used by the user logging into the ZNC web admin.
+            This is the block generated by the `znc --makepass` command.
+            !!! If not specified, please change this after starting the service. !!!
+          '';
+        };
+
+        port = mkOption {
+          default = 5000;
+          example = 5000;
+          type = types.int;
+          description = ''
+            Specifies the port on which to listen.
+          '';
+        };
+
+        useSSL = mkOption {
+          default = true;
+          example = true;
+          type = types.bool;
+          description = ''
+            Indicates whether the ZNC server should use SSL when listening on the specified port.
+          '';
+        };
+
+        extraZncConf = mkOption {
+          default = "";
+          type = types.lines;
+          description = ''
+            Extra config to `znc.conf` file
+          '';
         };
-        type = types.optionSet;
-        description = ''
-          Values to use when creating a `znc.conf` file.
-        '';
-        options = confOptions; 
       };
 
       modulePackages = mkOption {
@@ -280,20 +274,16 @@ in
 
         # If mutable, regenerate conf file every time.
         ${optionalString (!cfg.mutable) ''
-          ${pkgs.coreutils}/echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
-          ${pkgs.coreutils}/rm -f ${cfg.dataDir}/configs/znc.conf
+          ${pkgs.coreutils}/bin/echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
+          ${pkgs.coreutils}/bin/rm -f ${cfg.dataDir}/configs/znc.conf
         ''}
 
         # Ensure essential files exist.
         if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
-          ${pkgs.coreutils}/bin/echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
-          ${if (!cfg.mutable)
-            then "${pkgs.coreutils}/bin/ln --force -s ${zncConfFile} ${cfg.dataDir}/.znc/configs/znc.conf"
-            else ''
-              ${pkgs.coreutils}/bin/cp --no-clobber ${zncConfFile} ${cfg.dataDir}/configs/znc.conf
-              ${pkgs.coreutils}/bin/chmod u+rw ${cfg.dataDir}/configs/znc.conf
-              ${pkgs.coreutils}/bin/chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
-            ''}
+            ${pkgs.coreutils}/bin/echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
+            ${pkgs.coreutils}/bin/cp --no-clobber ${zncConfFile} ${cfg.dataDir}/configs/znc.conf
+            ${pkgs.coreutils}/bin/chmod u+rw ${cfg.dataDir}/configs/znc.conf
+            ${pkgs.coreutils}/bin/chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
         fi
 
         if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index 057891a6047..a4d54301fc1 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -71,10 +71,10 @@ in
             mkdir -m 0755 -p ${stateDir}
             chown ${clamavUser}:${clamavGroup} ${stateDir}
           '';
-          exec = "${pkgs.clamav}/bin/freshclam --config-file=${pkgs.writeText "freshclam.conf" cfg.updater.config}";
+          exec = "${pkgs.clamav}/bin/freshclam --daemon --config-file=${pkgs.writeText "freshclam.conf" cfg.updater.config}";
       }; 
     };
 
   };
 
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index df21ebbd974..3958be33df2 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -66,6 +66,13 @@ with lib;
         restartIfChanged = false;
       };
 
+    systemd.services."console-getty" =
+      { serviceConfig.ExecStart = "@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login --keep-baud console 115200,38400,9600 $TERM";
+        serviceConfig.Restart = "always";
+        restartIfChanged = false;
+	enable = mkDefault config.boot.isContainer;
+      };
+
     environment.etc = singleton
       { # Friendly greeting on the virtual consoles.
         source = pkgs.writeText "issue" ''
diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
index 76c64f8cb29..bb066aa6c47 100644
--- a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
@@ -133,7 +133,7 @@ in
         RewriteEngine On
         RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
         RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
-        ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.serverConfig.servedDirs}
+        ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedDirs}
         RewriteRule ${if config.enableUploads
           then "!^/images"
           else "^.*\$"
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index c2f464014ae..2af249a8e96 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -5,7 +5,7 @@ with lib;
 let
 
   cfg = config.services.tomcat;
-  tomcat = pkgs.tomcat6;
+  tomcat = pkgs.tomcat7;
 in
 
 {
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
index 9ee4e0dc7cb..c7fbfa85e33 100644
--- a/nixos/modules/services/x11/display-managers/slim.nix
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -19,6 +19,7 @@ let
       reboot_cmd ${config.systemd.package}/sbin/shutdown -r now
       ${optionalString (cfg.defaultUser != null) ("default_user " + cfg.defaultUser)}
       ${optionalString cfg.autoLogin "auto_login yes"}
+      ${cfg.extraConfig}
     '';
 
   # Unpack the SLiM theme, or use the default.
@@ -89,6 +90,15 @@ in
         '';
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration options for SLiM login manager. Do not
+          add options that can be configured directly.
+        '';
+      };
+
     };
 
   };
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index bc9a155ac95..0cc060db8f9 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -6,8 +6,7 @@ let
 
   cfg = config.boot.loader.grub;
 
-  realGrub = if cfg.version == 1 then pkgs.grub
-    else pkgs.grub2.override { zfsSupport = cfg.zfsSupport; };
+  realGrub = if cfg.version == 1 then pkgs.grub else pkgs.grub2;
 
   grub =
     # Don't include GRUB if we're only generating a GRUB menu (e.g.,
@@ -26,12 +25,11 @@ let
       inherit (cfg)
         version extraConfig extraPerEntryConfig extraEntries
         extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout
-        default devices fsIdentifier;
+        default devices explicitBootRoot;
       path = (makeSearchPath "bin" [
-        pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils pkgs.btrfsProgs
-        pkgs.utillinux
+        pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils
       ]) + ":" + (makeSearchPath "sbin" [
-        pkgs.mdadm pkgs.utillinux
+        pkgs.mdadm
       ]);
     });
 
@@ -211,26 +209,12 @@ in
         '';
       };
 
-      fsIdentifier = mkOption {
-        default = "uuid";
-        type = types.addCheck types.str
-          (type: type == "uuid" || type == "label" || type == "provided");
-        description = ''
-          Determines how grub will identify devices when generating the
-          configuration file. A value of uuid / label signifies that grub
-          will always resolve the uuid or label of the device before using
-          it in the configuration. A value of provided means that grub will
-          use the device name as show in <command>df</command> or
-          <command>mount</command>. Note, zfs zpools / datasets are ignored
-          and will always be mounted using their labels.
-        '';
-      };
-
-      zfsSupport = mkOption {
-        default = false;
-        type = types.bool;
+      explicitBootRoot = mkOption {
+        default = "";
+        type = types.str;
         description = ''
-          Whether grub should be build against libzfs.
+          The relative path of /boot within the parent volume. Leave empty
+          if /boot is not a btrfs subvolume.
         '';
       };
 
@@ -276,9 +260,6 @@ in
           ${pkgs.coreutils}/bin/cp -pf "${v}" "/boot/${n}"
         '') config.boot.loader.grub.extraFiles);
 
-    assertions = [{ assertion = !cfg.zfsSupport || cfg.version == 2;
-                    message = "Only grub version 2 provides zfs support";}];
-
     })
 
   ];
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index d8ee8b50097..b4900358a5d 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -1,6 +1,5 @@
 use strict;
 use warnings;
-use Class::Struct;
 use XML::LibXML;
 use File::Basename;
 use File::Path;
@@ -28,14 +27,6 @@ sub writeFile {
     close FILE or die;
 }
 
-sub runCommand {
-    my ($cmd) = @_;
-    open FILE, "$cmd 2>/dev/null |" or die "Failed to execute: $cmd\n";
-    my @ret = <FILE>;
-    close FILE;
-    return ($?, @ret);
-}
-
 my $grub = get("grub");
 my $grubVersion = int(get("version"));
 my $extraConfig = get("extraConfig");
@@ -48,7 +39,7 @@ my $configurationLimit = int(get("configurationLimit"));
 my $copyKernels = get("copyKernels") eq "true";
 my $timeout = int(get("timeout"));
 my $defaultEntry = int(get("default"));
-my $fsIdentifier = get("fsIdentifier");
+my $explicitBootRoot = get("explicitBootRoot");
 $ENV{'PATH'} = get("path");
 
 die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
@@ -57,108 +48,22 @@ print STDERR "updating GRUB $grubVersion menu...\n";
 
 mkpath("/boot/grub", 0, 0700);
 
+
 # Discover whether /boot is on the same filesystem as / and
 # /nix/store.  If not, then all kernels and initrds must be copied to
-# /boot.
-if (stat("/boot")->dev != stat("/nix/store")->dev) {
+# /boot, and all paths in the GRUB config file must be relative to the
+# root of the /boot filesystem.  `$bootRoot' is the path to be
+# prepended to paths under /boot.
+my $bootRoot = "/boot";
+if (stat("/")->dev != stat("/boot")->dev) {
+    $bootRoot = "";
+    $copyKernels = 1;
+} elsif (stat("/boot")->dev != stat("/nix/store")->dev) {
     $copyKernels = 1;
 }
 
-# Discover information about the location of /boot
-struct(Fs => {
-    device => '$',
-    type => '$',
-    mount => '$',
-});
-sub GetFs {
-    my ($dir) = @_;
-    my ($status, @dfOut) = runCommand("df -T $dir");
-    if ($status != 0 || $#dfOut != 1) {
-        die "Failed to retrieve output about $dir from `df`";
-    }
-    my @boot = split(/[ \n\t]+/, $dfOut[1]);
-    return Fs->new(device => $boot[0], type => $boot[1], mount => $boot[6]);
-}
-struct (Grub => {
-    path => '$',
-    search => '$',
-});
-my $driveid = 1;
-sub GrubFs {
-    my ($dir) = @_;
-    my $fs = GetFs($dir);
-    my $path = "/" . substr($dir, length($fs->mount));
-    my $search = "";
-
-    if ($grubVersion > 1) {
-        # ZFS is completely separate logic as zpools are always identified by a label
-        # or custom UUID
-        if ($fs->type eq 'zfs') {
-            my $sid = index($fs->device, '/');
-
-            if ($sid < 0) {
-                $search = '--label ' . $fs->device;
-                $path = '/@' . $path;
-            } else {
-                $search = '--label ' . substr($fs->device, 0, $sid);
-                $path = '/' . substr($fs->device, $sid) . '/@' . $path;
-            }
-        } else {
-            my %types = ('uuid' => '--fs-uuid', 'label' => '--label');
-
-            if ($fsIdentifier eq 'provided') {
-                # If the provided dev is identifying the partition using a label or uuid,
-                # we should get the label / uuid and do a proper search
-                my @matches = $fs->device =~ m/\/dev\/disk\/by-(label|uuid)\/(.*)/;
-                if ($#matches > 1) {
-                    die "Too many matched devices"
-                } elsif ($#matches == 1) {
-                    $search = "$types{$matches[0]} $matches[1]"
-                }
-            } else {
-                # Determine the identifying type
-                $search = $types{$fsIdentifier} . ' ';
-
-                # Based on the type pull in the identifier from the system
-                my ($status, @devInfo) = runCommand("blkid -o export @{[$fs->device]}");
-                if ($status != 0) {
-                    die "Failed to get blkid info for @{[$fs->device]}";
-                }
-                my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/;
-                if ($#matches != 0) {
-                    die "Couldn't find a $types{$fsIdentifier} for @{[$fs->device]}\n"
-                }
-                $search .= $matches[0];
-            }
-
-            # BTRFS is a special case in that we need to fix the referrenced path based on subvolumes
-            if ($fs->type eq 'btrfs') {
-                my ($status, @info) = runCommand("btrfs subvol show @{[$fs->mount]}");
-                if ($status != 0) {
-                    die "Failed to retreive subvolume info for @{[$fs->mount]}";
-                }
-                my @subvols = join("", @info) =~ m/Name:[ \t\n]*([^ \t\n]*)/;
-                if ($#subvols > 0) {
-                    die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n"
-                } elsif ($#subvols == 0) {
-                    $path = "/$subvols[0]$path";
-                }
-            }
-        }
-        if (not $search eq "") {
-            $search = "search --set=drive$driveid " . $search;
-            $path = "(\$drive$driveid)$path";
-            $driveid += 1;
-        }
-    }
-    return Grub->new(path => $path, search => $search);
-}
-my $grubBoot = GrubFs("/boot");
-my $grubStore = GrubFs("/nix");
-
-# We don't need to copy if we can read the kernels directly
-if ($grubStore->search ne "") {
-	$copyKernels = 0;
+if ($explicitBootRoot ne "") {
+    $bootRoot = $explicitBootRoot;
 }
 
 # Generate the header.
@@ -171,14 +76,12 @@ if ($grubVersion == 1) {
     ";
     if ($splashImage) {
         copy $splashImage, "/boot/background.xpm.gz" or die "cannot copy $splashImage to /boot\n";
-        $conf .= "splashimage " . $grubBoot->path . "/background.xpm.gz\n";
+        $conf .= "splashimage $bootRoot/background.xpm.gz\n";
     }
 }
 
 else {
     $conf .= "
-        " . $grubBoot->search . "
-        " . $grubStore->search . "
         if [ -s \$prefix/grubenv ]; then
           load_env
         fi
@@ -199,7 +102,7 @@ else {
           set timeout=$timeout
         fi
 
-        if loadfont " . $grubBoot->path . "/grub/fonts/unicode.pf2; then
+        if loadfont $bootRoot/grub/fonts/unicode.pf2; then
           set gfxmode=640x480
           insmod gfxterm
           insmod vbe
@@ -213,7 +116,7 @@ else {
         copy $splashImage, "/boot/background.png" or die "cannot copy $splashImage to /boot\n";
         $conf .= "
             insmod png
-            if background_image " . $grubBoot->path . "/background.png; then
+            if background_image $bootRoot/background.png; then
               set color_normal=white/black
               set color_highlight=black/white
             else
@@ -235,7 +138,7 @@ mkpath("/boot/kernels", 0, 0755) if $copyKernels;
 
 sub copyToKernelsDir {
     my ($path) = @_;
-    return $grubStore->path . substr($path, length("/nix")) unless $copyKernels;
+    return $path unless $copyKernels;
     $path =~ /\/nix\/store\/(.*)/ or die;
     my $name = $1; $name =~ s/\//-/g;
     my $dst = "/boot/kernels/$name";
@@ -248,7 +151,7 @@ sub copyToKernelsDir {
         rename $tmp, $dst or die "cannot rename $tmp to $dst\n";
     }
     $copied{$dst} = 1;
-    return $grubBoot->path . "/kernels/$name";
+    return "$bootRoot/kernels/$name";
 }
 
 sub addEntry {
@@ -275,8 +178,6 @@ sub addEntry {
         $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n\n";
     } else {
         $conf .= "menuentry \"$name\" {\n";
-	$conf .= $grubBoot->search . "\n";
-	$conf .= $grubStore->search . "\n";
         $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
         $conf .= "  multiboot $xen $xenParams\n" if $xen;
         $conf .= "  " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
@@ -294,7 +195,7 @@ addEntry("NixOS - Default", $defaultConfig);
 $conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
 
 # extraEntries could refer to @bootRoot@, which we have to substitute
-$conf =~ s/\@bootRoot\@/$grubBoot->path/g;
+$conf =~ s/\@bootRoot\@/$bootRoot/g;
 
 # Emit submenus for all system profiles.
 sub addProfile {
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 68392e3cfe2..70ff1d588a3 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -342,40 +342,39 @@ in
               description = "Path where the ramfs used to update the LUKS key will be mounted in stage-1";
             };
 
-            storage = mkOption {
-              type = types.optionSet;
-              description = "Options related to the storing the salt";
-
-              options = {
-                device = mkOption {
-                  default = "/dev/sda1";
-                  type = types.path;
-                  description = ''
-                    An unencrypted device that will temporarily be mounted in stage-1.
-                    Must contain the current salt to create the challenge for this LUKS device.
-                  '';
-                };
-
-                fsType = mkOption {
-                  default = "vfat";
-                  type = types.string;
-                  description = "The filesystem of the unencrypted device";
-                };
-
-                mountPoint = mkOption {
-                  default = "/crypt-storage";
-                  type = types.string;
-                  description = "Path where the unencrypted device will be mounted in stage-1";
-                };
-
-                path = mkOption {
-                  default = "/crypt-storage/default";
-                  type = types.string;
-                  description = ''
-                    Absolute path of the salt on the unencrypted device with
-                    that device's root directory as "/".
-                  '';
-                };
+            /* TODO: Add to the documentation of the current module:
+
+               Options related to the storing the salt.
+            */
+            storage = {
+              device = mkOption {
+                default = "/dev/sda1";
+                type = types.path;
+                description = ''
+                  An unencrypted device that will temporarily be mounted in stage-1.
+                  Must contain the current salt to create the challenge for this LUKS device.
+                '';
+              };
+
+              fsType = mkOption {
+                default = "vfat";
+                type = types.string;
+                description = "The filesystem of the unencrypted device";
+              };
+
+              mountPoint = mkOption {
+                default = "/crypt-storage";
+                type = types.string;
+                description = "Path where the unencrypted device will be mounted in stage-1";
+              };
+
+              path = mkOption {
+                default = "/crypt-storage/default";
+                type = types.string;
+                description = ''
+                  Absolute path of the salt on the unencrypted device with
+                  that device's root directory as "/".
+                '';
               };
             };
           };
diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix
index 652eb046f50..eaf8cf1ecd6 100644
--- a/nixos/modules/system/boot/modprobe.nix
+++ b/nixos/modules/system/boot/modprobe.nix
@@ -77,6 +77,11 @@ with lib;
         '')}
         ${config.boot.extraModprobeConfig}
       '';
+    environment.etc."modprobe.d/usb-load-ehci-first.conf".text =
+      ''
+        softdep uhci_hcd pre: ehci_hcd
+        softdep ohci_hcd pre: ehci_hcd
+      '';
 
     environment.systemPackages = [ config.system.sbin.modprobe pkgs.kmod ];
 
diff --git a/nixos/modules/tasks/filesystems/nfs.nix b/nixos/modules/tasks/filesystems/nfs.nix
index e8c3d8ab56d..c902b9e0790 100644
--- a/nixos/modules/tasks/filesystems/nfs.nix
+++ b/nixos/modules/tasks/filesystems/nfs.nix
@@ -24,13 +24,37 @@ let
     Method = nsswitch
   '';
 
+  cfg = config.services.nfs;
+
 in
 
 {
+  ###### interface
+
+  options = {
+
+    services.nfs = {
+      statdPort = mkOption {
+        default = null;
+        example = 4000;
+        description = ''
+          Use fixed port for rpc.statd, usefull if NFS server is behind firewall.
+        '';
+      };
+      lockdPort = mkOption {
+        default = null;
+        example = 4001;
+        description = ''
+          Use fixed port for NFS lock manager kernel module (lockd/nlockmgr),
+          usefull if NFS server is behind firewall.
+        '';
+      };
+    };
+  };
 
   ###### implementation
 
-  config = mkIf (any (fs: fs == "nfs" || fs == "nfs4") config.boot.supportedFilesystems) {
+  config = mkIf (any (fs: fs == "nfs" || fs == "nfs4") config.boot.supportedFilesystems) ({
 
     services.rpcbind.enable = true;
 
@@ -60,7 +84,10 @@ in
           '';
 
         serviceConfig.Type = "forking";
-        serviceConfig.ExecStart = "@${pkgs.nfsUtils}/sbin/rpc.statd rpc.statd --no-notify";
+        serviceConfig.ExecStart = ''
+          @${pkgs.nfsUtils}/sbin/rpc.statd rpc.statd --no-notify \
+              ${if cfg.statdPort != null then "-p ${toString statdPort}" else ""}
+        '';
         serviceConfig.Restart = "always";
       };
 
@@ -90,5 +117,9 @@ in
         serviceConfig.Restart = "always";
       };
 
-  };
+  } // mkIf (cfg.lockdPort != null) {
+    boot.extraModprobeConfig = ''
+      options lockd nlm_udpport=${toString cfg.lockdPort} nlm_tcpport=${toString cfg.lockdPort}
+    '';
+  });
 }
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 1c4bbc16b49..d7deb44c407 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -133,7 +133,7 @@ in
       };
 
       boot.initrd = mkIf inInitrd {
-        kernelModules = [ "spl" "zfs" ];
+        kernelModules = [ "spl" "zfs" ] ;
         extraUtilsCommands =
           ''
             cp -v ${zfsPkg}/sbin/zfs $out/bin
@@ -148,10 +148,6 @@ in
           '';
       };
 
-      boot.loader.grub = mkIf inInitrd {
-        zfsSupport = true;
-      };
-
       systemd.services."zpool-import" = {
         description = "Import zpools";
         after = [ "systemd-udev-settle.service" ];
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 7dabe70f00c..2adb4bcfaba 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -10,6 +10,26 @@ let
   hasSits = cfg.sits != { };
   hasBonds = cfg.bonds != { };
 
+  addrOpts = v:
+    assert v == 4 || v == 6;
+    {
+      address = mkOption {
+        type = types.str;
+        description = ''
+          IPv${toString v} address of the interface.  Leave empty to configure the
+          interface using DHCP.
+        '';
+      };
+
+      prefixLength = mkOption {
+        type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
+        description = ''
+          Subnet mask of the interface, specified as the number of
+          bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
+        '';
+      };
+    };
+
   interfaceOpts = { name, ... }: {
 
     options = {
@@ -20,10 +40,36 @@ let
         description = "Name of the interface.";
       };
 
+      ip4 = mkOption {
+        default = [ ];
+        example = [
+          { address = "10.0.0.1"; prefixLength = 16; }
+          { address = "192.168.1.1"; prefixLength = 24; }
+        ];
+        type = types.listOf types.optionSet;
+        options = addrOpts 4;
+        description = ''
+          List of IPv4 addresses that will be statically assigned to the interface.
+        '';
+      };
+
+      ip6 = mkOption {
+        default = [ ];
+        example = [
+          { address = "fdfd:b3f0:482::1"; prefixLength = 48; }
+          { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; }
+        ];
+        type = types.listOf types.optionSet;
+        options = addrOpts 6;
+        description = ''
+          List of IPv6 addresses that will be statically assigned to the interface.
+        '';
+      };
+
       ipAddress = mkOption {
         default = null;
         example = "10.0.0.1";
-        type = types.nullOr (types.str);
+        type = types.nullOr types.str;
         description = ''
           IP address of the interface.  Leave empty to configure the
           interface using DHCP.
@@ -41,20 +87,16 @@ let
       };
 
       subnetMask = mkOption {
-        default = "";
-        example = "255.255.255.0";
-        type = types.str;
+        default = null;
         description = ''
-          Subnet mask of the interface, specified as a bitmask.
-          This is deprecated; use <option>prefixLength</option>
-          instead.
+          Defunct, supply the prefix length instead.
         '';
       };
 
       ipv6Address = mkOption {
         default = null;
         example = "2001:1470:fffd:2098::e006";
-        type = types.nullOr types.string;
+        type = types.nullOr types.str;
         description = ''
           IPv6 address of the interface.  Leave empty to configure the
           interface using NDP.
@@ -224,10 +266,10 @@ in
     networking.interfaces = mkOption {
       default = {};
       example =
-        { eth0 = {
-            ipAddress = "131.211.84.78";
-            subnetMask = "255.255.255.128";
-          };
+        { eth0.ip4 = [ {
+            address = "131.211.84.78";
+            prefixLength = 25;
+          } ];
         };
       description = ''
         The configuration for each network interface.  If
@@ -438,6 +480,12 @@ in
 
   config = {
 
+    assertions =
+      flip map interfaces (i: {
+        assertion = i.subnetMask == null;
+        message = "The networking.interfaces.${i.name}.subnetMask option is defunct. Use prefixLength instead.";
+      });
+
     boot.kernelModules = [ ]
       ++ optional cfg.enableIPv6 "ipv6"
       ++ optional hasVirtuals "tun"
@@ -534,12 +582,18 @@ in
         # network device, so it only gets started after the interface
         # has appeared, and it's stopped when the interface
         # disappears.
-        configureInterface = i: nameValuePair "${i.name}-cfg"
-          (let mask =
-                if i.prefixLength != null then toString i.prefixLength else
-                if i.subnetMask != "" then i.subnetMask else "32";
-               staticIPv6 = cfg.enableIPv6 && i.ipv6Address != null;
+        configureInterface = i:
+          let
+            ips = i.ip4 ++ optionals cfg.enableIPv6 i.ip6
+              ++ optional (i.ipAddress != null) {
+                address = i.ipAddress;
+                prefixLength = i.prefixLength;
+              } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) {
+                address = i.ipv6Address;
+                prefixLength = i.ipv6PrefixLength;
+              };
           in
+          nameValuePair "${i.name}-cfg"
           { description = "Configuration of ${i.name}";
             wantedBy = [ "network-interfaces.target" ];
             bindsTo = [ "sys-subsystem-net-devices-${i.name}.device" ];
@@ -562,36 +616,32 @@ in
                   echo "setting MTU to ${toString i.mtu}..."
                   ip link set "${i.name}" mtu "${toString i.mtu}"
                 ''
-              + optionalString (i.ipAddress != null)
+
+              # Ip Setup
+              +
                 ''
-                  cur=$(ip -4 -o a show dev "${i.name}" | awk '{print $4}')
-                  # Only do a flush/add if it's necessary.  This is
+                  curIps=$(ip -o a show dev "${i.name}" | awk '{print $4}')
+                  # Only do an add if it's necessary.  This is
                   # useful when the Nix store is accessed via this
                   # interface (e.g. in a QEMU VM test).
-                  if [ "$cur" != "${i.ipAddress}/${mask}" ]; then
-                    echo "configuring interface..."
-                    ip -4 addr flush dev "${i.name}"
-                    ip -4 addr add "${i.ipAddress}/${mask}" dev "${i.name}"
-                    restart_network_setup=true
-                  else
-                    echo "skipping configuring interface"
-                  fi
                 ''
-              + optionalString (staticIPv6)
+              + flip concatMapStrings (ips) (ip:
+                let
+                  address = "${ip.address}/${toString ip.prefixLength}";
+                in
                 ''
-                  # Only do a flush/add if it's necessary.  This is
-                  # useful when the Nix store is accessed via this
-                  # interface (e.g. in a QEMU VM test).
-                  if ! ip -6 -o a show dev "${i.name}" | grep "${i.ipv6Address}/${toString i.ipv6prefixLength}"; then
-                    echo "configuring interface..."
-                    ip -6 addr flush dev "${i.name}"
-                    ip -6 addr add "${i.ipv6Address}/${toString i.ipv6prefixLength}" dev "${i.name}"
-                    restart_network_setup=true
-                  else
-                    echo "skipping configuring interface"
+                  echo "checking ip ${address}..."
+                  if ! echo "$curIps" | grep "${address}" >/dev/null 2>&1; then
+                    if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then
+                      echo "added ip ${address}..."
+                      restart_network_setup=true
+                    elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
+                      echo "failed to add ${address}"
+                      exit 1
+                    fi
                   fi
-                ''
-              + optionalString (i.ipAddress != null || staticIPv6)
+                '')
+              + optionalString (ips != [ ])
                 ''
                   if [ restart_network_setup = true ]; then
                     # Ensure that the default gateway remains set.
@@ -608,7 +658,20 @@ in
                 ''
                   echo 1 > /proc/sys/net/ipv6/conf/${i.name}/proxy_ndp
                 '';
-          });
+            preStop =
+              ''
+                echo "releasing configured ip's..."
+              ''
+              + flip concatMapStrings (ips) (ip:
+                let
+                  address = "${ip.address}/${toString ip.prefixLength}";
+                in
+                ''
+                  echo -n "Deleting ${address}..."
+                  ip addr del "${address}" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed"
+                  echo ""
+                '');
+          };
 
         createTunDevice = i: nameValuePair "${i.name}"
           { description = "Virtual Network Interface ${i.name}";
diff --git a/nixos/modules/virtualisation/docker-image.nix b/nixos/modules/virtualisation/docker-image.nix
new file mode 100644
index 00000000000..13b861dc988
--- /dev/null
+++ b/nixos/modules/virtualisation/docker-image.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ pkgs2storeContents = l : map (x: { object = x; symlink = "none"; }) l;
+
+in {
+  # Create the tarball
+  system.build.dockerImage = import ../../lib/make-system-tarball.nix {
+    inherit (pkgs) stdenv perl xz pathsFromGraph;
+
+    contents = [];
+    extraArgs = "--owner=0";
+    storeContents = [
+      { object = config.system.build.toplevel + "/init";
+        symlink = "/bin/init";
+      }
+    ] ++ (pkgs2storeContents [ pkgs.stdenv ]);
+  };
+
+  boot.postBootCommands =
+    ''
+      # After booting, register the contents of the Nix store in the Nix
+      # database.
+      if [ -f /nix-path-registration ]; then
+        ${config.nix.package}/bin/nix-store --load-db < /nix-path-registration &&
+        rm /nix-path-registration
+      fi
+
+      # nixos-rebuild also requires a "system" profile and an
+      # /etc/NIXOS tag.
+      touch /etc/NIXOS
+      ${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
+
+      # Set virtualisation to docker
+      echo "docker" > /run/systemd/container 
+    '';
+
+
+  # docker image config
+  require = [
+    ../installer/cd-dvd/channel.nix
+    ../profiles/minimal.nix
+    ../profiles/clone-config.nix
+  ];
+
+  boot.isContainer = true;
+
+  # Iptables do not work in docker
+  networking.firewall.enable = false;
+
+  services.openssh.enable = true;
+
+  # Socket activated ssh presents problem in docker
+  services.openssh.startWhenNeeded = false;
+
+  # Allow the user to login as root without password
+  security.initialRootPassword = "";
+
+  # Some more help text.
+  services.mingetty.helpLine =
+    ''
+
+      Log in as "root" with an empty password.
+    '';
+}
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index d7d700d8841..318460f4c2c 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -7,6 +7,7 @@ with lib;
 let
 
   cfg = config.virtualisation.libvirtd;
+  vswitch = config.virtualisation.vswitch;
   configFile = pkgs.writeText "libvirtd.conf" ''
     unix_sock_group = "libvirtd"
     unix_sock_rw_perms = "0770"
@@ -56,6 +57,20 @@ in
           '';
       };
 
+    virtualisation.libvirtd.onShutdown =
+      mkOption {
+        type = types.enum ["shutdown" "suspend" ];
+        default = "suspend";
+        description =
+          ''
+            When shutting down / restarting the host what method should
+            be used to gracefully halt the guests. Setting to "shutdown"
+            will cause an ACPI shutdown of each guest. "suspend" will
+            attempt to save the state of the guests ready to restore on boot.
+          '';
+      };
+
+
   };
 
 
@@ -73,12 +88,17 @@ in
       { description = "Libvirt Virtual Machine Management Daemon";
 
         wantedBy = [ "multi-user.target" ];
-        after = [ "systemd-udev-settle.service" ];
+        after = [ "systemd-udev-settle.service" ]
+                ++ optional vswitch.enable "vswitchd.service";
 
-        path =
-          [ pkgs.bridge_utils pkgs.dmidecode pkgs.dnsmasq
+        path = [ 
+            pkgs.bridge_utils 
+            pkgs.dmidecode 
+            pkgs.dnsmasq
             pkgs.ebtables
-          ] ++ optional cfg.enableKVM pkgs.qemu_kvm;
+          ] 
+          ++ optional cfg.enableKVM pkgs.qemu_kvm
+          ++ optional vswitch.enable vswitch.package;
 
         preStart =
           ''
@@ -152,7 +172,12 @@ in
             ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests start || true
           '';
 
-        postStop = "${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests stop";
+        postStop = 
+            ''
+            export PATH=${pkgs.gettext}/bin:$PATH
+            export ON_SHUTDOWN=${cfg.onShutdown}
+            ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests stop
+            '';
 
         serviceConfig.Type = "oneshot";
         serviceConfig.RemainAfterExit = true;
diff --git a/nixos/modules/virtualisation/openvswitch.nix b/nixos/modules/virtualisation/openvswitch.nix
new file mode 100644
index 00000000000..c1579d94657
--- /dev/null
+++ b/nixos/modules/virtualisation/openvswitch.nix
@@ -0,0 +1,117 @@
+# Systemd services for openvswitch
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.virtualisation.vswitch;
+
+in
+
+{
+
+  options = {
+
+    virtualisation.vswitch.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description =
+        ''
+        Enable Open vSwitch. A configuration 
+        daemon (ovs-server) will be started.
+        '';
+    };
+
+
+    virtualisation.vswitch.package = mkOption {
+      type = types.package;
+      default = pkgs.openvswitch;
+      description =
+        ''
+        Open vSwitch package to use.
+        '';
+    };
+
+  };
+
+  config = mkIf cfg.enable (let 
+
+    # Where the communication sockets live
+    runDir = "/var/run/openvswitch";
+
+    # Where the config database live (can't be in nix-store)
+    stateDir = "/var/db/openvswitch";
+
+    # The path to the an initialized version of the database 
+    db = pkgs.stdenv.mkDerivation {
+      name = "vswitch.db";
+      unpackPhase = "true";
+      buildPhase = "true";
+      buildInputs = with pkgs; [
+        cfg.package
+      ];
+      installPhase = 
+        ''
+        ensureDir $out/
+        '';
+    };
+
+  in {
+
+    environment.systemPackages = [ cfg.package ]; 
+
+    boot.kernelModules = [ "tun" "openvswitch" ];
+
+    boot.extraModulePackages = [ cfg.package ];
+
+    systemd.services.ovsdb = {
+      description = "Open_vSwitch Database Server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "systemd-udev-settle.service" ];
+      wants = [ "vswitchd.service" ];
+      path = [ cfg.package ];
+      restartTriggers = [ db cfg.package ];
+      # Create the config database
+      preStart = 
+        ''
+        mkdir -p ${runDir}
+        mkdir -p /var/db/openvswitch
+        chmod +w /var/db/openvswitch
+        if [[ ! -e /var/db/openvswitch/conf.db ]]; then
+          ${cfg.package}/bin/ovsdb-tool create \
+            "/var/db/openvswitch/conf.db" \
+            "${cfg.package}/share/openvswitch/vswitch.ovsschema"
+        fi
+        chmod -R +w /var/db/openvswitch
+        '';
+      serviceConfig.ExecStart = 
+        ''
+        ${cfg.package}/bin/ovsdb-server \
+          --remote=punix:${runDir}/db.sock \
+          --private-key=db:Open_vSwitch,SSL,private_key \
+          --certificate=db:Open_vSwitch,SSL,certificate \
+          --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \
+          --unixctl=ovsdb.ctl.sock \
+          /var/db/openvswitch/conf.db
+        '';       
+      serviceConfig.Restart = "always";
+      serviceConfig.RestartSec = 3;
+      postStart =
+        ''
+        ${cfg.package}/bin/ovs-vsctl --timeout 3 --retry --no-wait init
+        '';
+
+    };
+
+    systemd.services.vswitchd = {
+      description = "Open_vSwitch Daemon";
+      bindsTo = [ "ovsdb.service" ];
+      after = [ "ovsdb.service" ];
+      path = [ cfg.package ];
+      serviceConfig.ExecStart = ''${cfg.package}/bin/ovs-vswitchd'';
+    };
+
+  });
+
+}