summary refs log tree commit diff
path: root/nixos/modules/config
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/config')
-rw-r--r--nixos/modules/config/fonts/corefonts.nix32
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix58
-rw-r--r--nixos/modules/config/fonts/fontdir.nix75
-rw-r--r--nixos/modules/config/fonts/fonts.nix49
-rw-r--r--nixos/modules/config/fonts/ghostscript.nix32
-rw-r--r--nixos/modules/config/gnu.nix45
-rw-r--r--nixos/modules/config/i18n.nix84
-rw-r--r--nixos/modules/config/krb5.nix204
-rw-r--r--nixos/modules/config/ldap.nix246
-rw-r--r--nixos/modules/config/networking.nix88
-rw-r--r--nixos/modules/config/no-x-libs.nix23
-rw-r--r--nixos/modules/config/nsswitch.nix63
-rw-r--r--nixos/modules/config/power-management.nix108
-rw-r--r--nixos/modules/config/pulseaudio.nix148
-rw-r--r--nixos/modules/config/shells-environment.nix179
-rw-r--r--nixos/modules/config/swap.nix124
-rw-r--r--nixos/modules/config/sysctl.nix69
-rw-r--r--nixos/modules/config/system-path.nix142
-rw-r--r--nixos/modules/config/timezone.nix36
-rw-r--r--nixos/modules/config/unix-odbc-drivers.nix34
-rw-r--r--nixos/modules/config/users-groups.nix315
21 files changed, 2154 insertions, 0 deletions
diff --git a/nixos/modules/config/fonts/corefonts.nix b/nixos/modules/config/fonts/corefonts.nix
new file mode 100644
index 00000000000..7de95200cfa
--- /dev/null
+++ b/nixos/modules/config/fonts/corefonts.nix
@@ -0,0 +1,32 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    fonts = {
+
+      enableCoreFonts = mkOption {
+        default = false;
+        description = ''
+          Whether to include Microsoft's proprietary Core Fonts.  These fonts
+          are redistributable, but only verbatim, among other restrictions.
+          See <link xlink:href="http://corefonts.sourceforge.net/eula.htm"/>
+          for details.
+       '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.fonts.enableCoreFonts {
+
+    fonts.extraFonts = [ pkgs.corefonts ];
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
new file mode 100644
index 00000000000..6e0fdaf4b74
--- /dev/null
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -0,0 +1,58 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    fonts = {
+
+      enableFontConfig = mkOption { # !!! should be enableFontconfig
+        default = true;
+        description = ''
+          If enabled, a Fontconfig configuration file will be built
+          pointing to a set of default fonts.  If you don't care about
+          running X11 applications or any other program that uses
+          Fontconfig, you can turn this option off and prevent a
+          dependency on all those fonts.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.fonts.enableFontConfig {
+
+    # Bring in the default (upstream) fontconfig configuration.
+    environment.etc."fonts/fonts.conf".source =
+      pkgs.makeFontsConf { fontDirectories = config.fonts.fonts; };
+
+    environment.etc."fonts/conf.d/00-nixos.conf".text =
+      ''
+        <?xml version='1.0'?>
+        <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+        <fontconfig>
+
+          <!-- Set the default hinting style to "slight". -->
+          <match target="font">
+            <edit mode="assign" name="hintstyle">
+              <const>hintslight</const>
+            </edit>
+          </match>
+
+        </fontconfig>
+      '';
+
+    # FIXME: This variable is no longer needed, but we'll keep it
+    # around for a while for applications linked against old
+    # fontconfig builds.
+    environment.variables.FONTCONFIG_FILE = "/etc/fonts/fonts.conf";
+
+    environment.systemPackages = [ pkgs.fontconfig ];
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fontdir.nix b/nixos/modules/config/fonts/fontdir.nix
new file mode 100644
index 00000000000..a4f69809b2a
--- /dev/null
+++ b/nixos/modules/config/fonts/fontdir.nix
@@ -0,0 +1,75 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  fontDirs = config.fonts.fonts;
+
+  localDefs = with pkgs.builderDefs; pkgs.builderDefs.passthru.function rec {
+    src = "";/* put a fetchurl here */
+    buildInputs = [pkgs.xorg.mkfontdir pkgs.xorg.mkfontscale];
+    inherit fontDirs;
+    installPhase = fullDepEntry ("
+    list='';
+    for i in ${toString fontDirs} ; do
+      if [ -d \$i/ ]; then
+        list=\"\$list \$i\";
+      fi;
+    done
+    list=\$(find \$list -name fonts.dir -o -name '*.ttf' -o -name '*.otf');
+    fontDirs='';
+    for i in \$list ; do
+      fontDirs=\"\$fontDirs \$(dirname \$i)\";
+    done;
+    mkdir -p \$out/share/X11-fonts/;
+    find \$fontDirs -type f -o -type l | while read i; do
+      j=\"\${i##*/}\"
+      if ! test -e \"\$out/share/X11-fonts/\${j}\"; then
+        ln -s \"\$i\" \"\$out/share/X11-fonts/\${j}\";
+      fi;
+    done;
+    cd \$out/share/X11-fonts/
+    rm fonts.dir
+    rm fonts.scale
+    rm fonts.alias
+    mkfontdir
+    mkfontscale
+    cat \$( find ${pkgs.xorg.fontalias}/ -name fonts.alias) >fonts.alias
+  ") ["minInit" "addInputs"];
+  };
+
+  x11Fonts = with localDefs; stdenv.mkDerivation rec {
+    name = "X11-fonts";
+    builder = writeScript (name + "-builder")
+      (textClosure localDefs
+        [installPhase doForceShare doPropagate]);
+  };
+
+in
+
+{
+
+  options = {
+
+    fonts = {
+
+      enableFontDir = mkOption {
+        default = false;
+        description = ''
+          Whether to create a directory with links to all fonts in
+          <filename>/run/current-system/sw/share/X11-fonts</filename>.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf config.fonts.enableFontDir {
+
+    environment.systemPackages = [ x11Fonts ];
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fonts.nix b/nixos/modules/config/fonts/fonts.nix
new file mode 100644
index 00000000000..f43784f6d03
--- /dev/null
+++ b/nixos/modules/config/fonts/fonts.nix
@@ -0,0 +1,49 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    fonts = {
+
+      # TODO: find another name for it.
+      fonts = mkOption {
+        default = [
+          # - the user's .fonts directory
+          "~/.fonts"
+          # - the user's current profile
+          "~/.nix-profile/lib/X11/fonts"
+          "~/.nix-profile/share/fonts"
+          # - the default profile
+          "/nix/var/nix/profiles/default/lib/X11/fonts"
+          "/nix/var/nix/profiles/default/share/fonts"
+        ];
+        description = "List of primary font paths.";
+        apply = list: list ++ [
+          # - a few statically built locations
+          pkgs.xorg.fontbhttf
+          pkgs.xorg.fontbhlucidatypewriter100dpi
+          pkgs.xorg.fontbhlucidatypewriter75dpi
+          pkgs.ttf_bitstream_vera
+          pkgs.freefont_ttf
+          pkgs.liberation_ttf
+          pkgs.xorg.fontbh100dpi
+          pkgs.xorg.fontmiscmisc
+          pkgs.xorg.fontcursormisc
+        ]
+        ++ config.fonts.extraFonts;
+      };
+
+      extraFonts = mkOption {
+        default = [];
+        example = [ pkgs.dejavu_fonts ];
+        description = "List of packages with additional fonts.";
+      };
+
+    };
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/ghostscript.nix b/nixos/modules/config/fonts/ghostscript.nix
new file mode 100644
index 00000000000..9ef00396808
--- /dev/null
+++ b/nixos/modules/config/fonts/ghostscript.nix
@@ -0,0 +1,32 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    fonts = {
+
+      enableGhostscriptFonts = mkOption {
+        default = false;
+        description = ''
+          Whether to add the fonts provided by Ghostscript (such as
+          various URW fonts and the “Base-14” Postscript fonts) to the
+          list of system fonts, making them available to X11
+          applications.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.fonts.enableGhostscriptFonts {
+
+    fonts.extraFonts = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
+
+  };
+
+}
diff --git a/nixos/modules/config/gnu.nix b/nixos/modules/config/gnu.nix
new file mode 100644
index 00000000000..1a69083a206
--- /dev/null
+++ b/nixos/modules/config/gnu.nix
@@ -0,0 +1,45 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options = {
+    gnu = mkOption {
+      default = false;
+      description =
+        '' When enabled, GNU software is chosen by default whenever a there is
+           a choice between GNU and non-GNU software (e.g., GNU lsh
+           vs. OpenSSH).
+        '';
+    };
+  };
+
+  config = mkIf config.gnu {
+
+    environment.systemPackages = with pkgs;
+      # TODO: Adjust `requiredPackages' from `system-path.nix'.
+      # TODO: Add Inetutils once it has the new `ifconfig'.
+      [ parted
+        #fdisk  # XXX: GNU fdisk currently fails to build and it's redundant
+                # with the `parted' command.
+        nano zile
+        texinfo # for the stand-alone Info reader
+      ]
+      ++ stdenv.lib.optional (!stdenv.isArm) grub2;
+
+
+    # GNU GRUB, where available.
+    boot.loader.grub.enable = !pkgs.stdenv.isArm;
+    boot.loader.grub.version = 2;
+
+    # GNU lsh.
+    services.openssh.enable = false;
+    services.lshd.enable = true;
+    services.xserver.startOpenSSHAgent = false;
+    services.xserver.startGnuPGAgent = true;
+
+    # TODO: GNU dico.
+    # TODO: GNU Inetutils' inetd.
+    # TODO: GNU Pies.
+  };
+}
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
new file mode 100644
index 00000000000..c3e39717258
--- /dev/null
+++ b/nixos/modules/config/i18n.nix
@@ -0,0 +1,84 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  glibcLocales = pkgs.glibcLocales.override {
+    allLocales = any (x: x == "all") config.i18n.supportedLocales;
+    locales = config.i18n.supportedLocales;
+  };
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    i18n = {
+      defaultLocale = mkOption {
+        default = "en_US.UTF-8";
+        example = "nl_NL.UTF-8";
+        description = "
+          The default locale.  It determines the language for program
+          messages, the format for dates and times, sort order, and so on.
+          It also determines the character set, such as UTF-8.
+        ";
+      };
+
+      supportedLocales = mkOption {
+        default = ["all"];
+        example = ["en_US.UTF-8/UTF-8" "nl_NL.UTF-8/UTF-8" "nl_NL/ISO-8859-1"];
+        description = ''
+          List of locales that the system should support.  The value
+          <literal>"all"</literal> means that all locales supported by
+          Glibc will be installed.  A full list of supported locales
+          can be found at <link
+          xlink:href="http://sourceware.org/cgi-bin/cvsweb.cgi/libc/localedata/SUPPORTED?cvsroot=glibc"/>.
+        '';
+      };
+
+      consoleFont = mkOption {
+        default = "lat9w-16";
+        example = "LatArCyrHeb-16";
+        description = "
+          The font used for the virtual consoles.  Leave empty to use
+          whatever the <command>setfont</command> program considers the
+          default font.
+        ";
+      };
+
+      consoleKeyMap = mkOption {
+        default = "us";
+        example = "fr";
+        description = "
+          The keyboard mapping table for the virtual consoles.
+        ";
+        type = types.uniq types.string;
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages = [ glibcLocales ];
+
+    environment.variables.LANG = config.i18n.defaultLocale;
+
+    # ‘/etc/locale.conf’ is used by systemd.
+    environment.etc = singleton
+      { target = "locale.conf";
+        source = pkgs.writeText "locale.conf"
+          ''
+            LANG=${config.i18n.defaultLocale}
+          '';
+      };
+
+  };
+}
diff --git a/nixos/modules/config/krb5.nix b/nixos/modules/config/krb5.nix
new file mode 100644
index 00000000000..3323046ac5b
--- /dev/null
+++ b/nixos/modules/config/krb5.nix
@@ -0,0 +1,204 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.krb5;
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    krb5 = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable Kerberos V.";
+      };
+
+      defaultRealm = mkOption {
+        default = "ATENA.MIT.EDU";
+        description = "Default realm.";
+      };
+
+      domainRealm = mkOption {
+        default = "atena.mit.edu";
+        description = "Default domain realm.";
+      };
+
+      kdc = mkOption {
+        default = "kerberos.mit.edu";
+        description = "Kerberos Domain Controller";
+      };
+
+      kerberosAdminServer = mkOption {
+        default = "kerberos.mit.edu";
+        description = "Kerberos Admin Server";
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.krb5.enable {
+
+    environment.systemPackages = [ pkgs.krb5 ];
+
+    environment.etc."krb5.conf".text =
+      ''
+        [libdefaults]
+            default_realm = ${cfg.defaultRealm}
+            encrypt = true
+
+        # The following krb5.conf variables are only for MIT Kerberos.
+            krb4_config = /etc/krb.conf
+            krb4_realms = /etc/krb.realms
+            kdc_timesync = 1
+            ccache_type = 4
+            forwardable = true
+            proxiable = true
+
+        # The following encryption type specification will be used by MIT Kerberos
+        # if uncommented.  In general, the defaults in the MIT Kerberos code are
+        # correct and overriding these specifications only serves to disable new
+        # encryption types as they are added, creating interoperability problems.
+
+        #   default_tgs_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
+        #   default_tkt_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
+        #   permitted_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
+
+        # The following libdefaults parameters are only for Heimdal Kerberos.
+            v4_instance_resolve = false
+            v4_name_convert = {
+                host = {
+                    rcmd = host
+                    ftp = ftp
+                }
+                plain = {
+                    something = something-else
+                }
+            }
+            fcc-mit-ticketflags = true
+
+        [realms]
+            ${cfg.defaultRealm} = {
+                kdc = ${cfg.kdc}
+                admin_server = ${cfg.kerberosAdminServer}
+                #kpasswd_server = ${cfg.kerberosAdminServer}
+            }
+            ATHENA.MIT.EDU = {
+                kdc = kerberos.mit.edu:88
+                kdc = kerberos-1.mit.edu:88
+                kdc = kerberos-2.mit.edu:88
+                admin_server = kerberos.mit.edu
+                default_domain = mit.edu
+            }
+            MEDIA-LAB.MIT.EDU = {
+                kdc = kerberos.media.mit.edu
+                admin_server = kerberos.media.mit.edu
+            }
+            ZONE.MIT.EDU = {
+                kdc = casio.mit.edu
+                kdc = seiko.mit.edu
+                admin_server = casio.mit.edu
+            }
+            MOOF.MIT.EDU = {
+                kdc = three-headed-dogcow.mit.edu:88
+                kdc = three-headed-dogcow-1.mit.edu:88
+                admin_server = three-headed-dogcow.mit.edu
+            }
+            CSAIL.MIT.EDU = {
+                kdc = kerberos-1.csail.mit.edu
+                kdc = kerberos-2.csail.mit.edu
+                admin_server = kerberos.csail.mit.edu
+                default_domain = csail.mit.edu
+                krb524_server = krb524.csail.mit.edu
+            }
+            IHTFP.ORG = {
+                kdc = kerberos.ihtfp.org
+                admin_server = kerberos.ihtfp.org
+            }
+            GNU.ORG = {
+                kdc = kerberos.gnu.org
+                kdc = kerberos-2.gnu.org
+                kdc = kerberos-3.gnu.org
+                admin_server = kerberos.gnu.org
+            }
+            1TS.ORG = {
+                kdc = kerberos.1ts.org
+                admin_server = kerberos.1ts.org
+            }
+            GRATUITOUS.ORG = {
+                kdc = kerberos.gratuitous.org
+                admin_server = kerberos.gratuitous.org
+            }
+            DOOMCOM.ORG = {
+                kdc = kerberos.doomcom.org
+                admin_server = kerberos.doomcom.org
+            }
+            ANDREW.CMU.EDU = {
+                kdc = vice28.fs.andrew.cmu.edu
+                kdc = vice2.fs.andrew.cmu.edu
+                kdc = vice11.fs.andrew.cmu.edu
+                kdc = vice12.fs.andrew.cmu.edu
+                admin_server = vice28.fs.andrew.cmu.edu
+                default_domain = andrew.cmu.edu
+            }
+            CS.CMU.EDU = {
+                kdc = kerberos.cs.cmu.edu
+                kdc = kerberos-2.srv.cs.cmu.edu
+                admin_server = kerberos.cs.cmu.edu
+            }
+            DEMENTIA.ORG = {
+                kdc = kerberos.dementia.org
+                kdc = kerberos2.dementia.org
+                admin_server = kerberos.dementia.org
+            }
+            stanford.edu = {
+                kdc = krb5auth1.stanford.edu
+                kdc = krb5auth2.stanford.edu
+                kdc = krb5auth3.stanford.edu
+                admin_server = krb5-admin.stanford.edu
+                default_domain = stanford.edu
+            }
+
+        [domain_realm]
+            .${cfg.domainRealm} = ${cfg.defaultRealm}
+            ${cfg.domainRealm} = ${cfg.defaultRealm}
+            .mit.edu = ATHENA.MIT.EDU
+            mit.edu = ATHENA.MIT.EDU
+            .media.mit.edu = MEDIA-LAB.MIT.EDU
+            media.mit.edu = MEDIA-LAB.MIT.EDU
+            .csail.mit.edu = CSAIL.MIT.EDU
+            csail.mit.edu = CSAIL.MIT.EDU
+            .whoi.edu = ATHENA.MIT.EDU
+            whoi.edu = ATHENA.MIT.EDU
+            .stanford.edu = stanford.edu
+
+        [logging]
+            kdc = SYSLOG:INFO:DAEMON
+            admin_server = SYSLOG:INFO:DAEMON
+            default = SYSLOG:INFO:DAEMON
+            krb4_convert = true
+            krb4_get_tickets = false
+
+        [appdefaults]
+            pam = {
+                debug = false
+                ticket_lifetime = 36000
+                renew_lifetime = 36000
+                max_timeout = 30
+                timeout_shift = 2
+                initial_timeout = 1
+            }
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/config/ldap.nix b/nixos/modules/config/ldap.nix
new file mode 100644
index 00000000000..113f5d8bcbd
--- /dev/null
+++ b/nixos/modules/config/ldap.nix
@@ -0,0 +1,246 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+with pkgs;
+
+let
+
+  cfg = config.users.ldap;
+
+  # Careful: OpenLDAP seems to be very picky about the indentation of
+  # this file.  Directives HAVE to start in the first column!
+  ldapConfig = {
+    target = "ldap.conf";
+    source = writeText "ldap.conf" ''
+      uri ${config.users.ldap.server}
+      base ${config.users.ldap.base}
+      timelimit ${toString config.users.ldap.timeLimit}
+      bind_timelimit ${toString config.users.ldap.bind.timeLimit}
+      bind_policy ${config.users.ldap.bind.policy}
+      ${optionalString config.users.ldap.useTLS ''
+        ssl start_tls
+        tls_checkpeer no
+      ''}
+      ${optionalString (config.users.ldap.bind.distinguishedName != "") ''
+        binddn ${config.users.ldap.bind.distinguishedName}
+      ''}
+      ${optionalString (cfg.extraConfig != "") cfg.extraConfig }
+    '';
+  };
+
+  nslcdConfig = {
+    target = "nslcd.conf";
+    source = writeText "nslcd.conf" ''
+      uid nslcd
+      gid nslcd
+      uri ${cfg.server}
+      base ${cfg.base}
+      timelimit ${toString cfg.timeLimit}
+      bind_timelimit ${toString cfg.bind.timeLimit}
+      ${optionalString (cfg.bind.distinguishedName != "")
+        "binddn ${cfg.bind.distinguishedName}" }
+      ${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig }
+    '';
+  };
+
+  insertLdapPassword = !config.users.ldap.daemon.enable &&
+    config.users.ldap.bind.distinguishedName != "";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    users.ldap = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable authentication against an LDAP server.";
+      };
+
+      server = mkOption {
+        example = "ldap://ldap.example.org/";
+        description = "The URL of the LDAP server.";
+      };
+
+      base = mkOption {
+        example = "dc=example,dc=org";
+        description = "The distinguished name of the search base.";
+      };
+
+      useTLS = mkOption {
+        default = false;
+        description = ''
+          If enabled, use TLS (encryption) over an LDAP (port 389)
+          connection.  The alternative is to specify an LDAPS server (port
+          636) in <option>users.ldap.server</option> or to forego
+          security.
+        '';
+      };
+
+      timeLimit = mkOption {
+        default = 0;
+        type = types.int;
+        description = ''
+          Specifies the time limit (in seconds) to use when performing
+          searches. A value of zero (0), which is the default, is to
+          wait indefinitely for searches to be completed.
+        '';
+      };
+
+      daemon = {
+        enable = mkOption {
+          default = false;
+          description = ''
+            Whether to let the nslcd daemon (nss-pam-ldapd) handle the
+            LDAP lookups for NSS and PAM. This can improve performance,
+            and if you need to bind to the LDAP server with a password,
+            it increases security, since only the nslcd user needs to
+            have access to the bindpw file, not everyone that uses NSS
+            and/or PAM. If this option is enabled, a local nscd user is
+            created automatically, and the nslcd service is started
+            automatically when the network get up.
+          '';
+        };
+
+        extraConfig = mkOption {
+          default =  "";
+          type = types.string;
+          description = ''
+            Extra configuration options that will be added verbatim at
+            the end of the nslcd configuration file (nslcd.conf).
+          '' ;
+        } ;
+      };
+
+      bind = {
+        distinguishedName = mkOption {
+          default = "";
+          example = "cn=admin,dc=example,dc=com";
+          type = types.string;
+          description = ''
+            The distinguished name to bind to the LDAP server with. If this
+            is not specified, an anonymous bind will be done.
+          '';
+        };
+
+        password = mkOption {
+          default = "/etc/ldap/bind.password";
+          type = types.string;
+          description = ''
+            The path to a file containing the credentials to use when binding
+            to the LDAP server (if not binding anonymously).
+          '';
+        };
+
+        timeLimit = mkOption {
+          default = 30;
+          type = types.int;
+          description = ''
+            Specifies the time limit (in seconds) to use when connecting
+            to the directory server. This is distinct from the time limit
+            specified in <literal>users.ldap.timeLimit</literal> and affects
+            the initial server connection only.
+          '';
+        };
+
+        policy = mkOption {
+          default = "hard_open";
+          type = types.string;
+          description = ''
+            Specifies the policy to use for reconnecting to an unavailable
+            LDAP server. The default is <literal>hard_open</literal>, which
+            reconnects if opening the connection to the directory server
+            failed. By contrast, <literal>hard_init</literal> reconnects if
+            initializing the connection failed. Initializing may not
+            actually contact the directory server, and it is possible that
+            a malformed configuration file will trigger reconnection. If
+            <literal>soft</literal> is specified, then
+            <literal>nss_ldap</literal> will return immediately on server
+            failure. All hard reconnect policies block with exponential
+            backoff before retrying.
+          '';
+        };
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = types.string;
+        description = ''
+          Extra configuration options that will be added verbatim at
+          the end of the ldap configuration file (ldap.conf).
+          If <literal>users.ldap.daemon</literal> is enabled, this
+          configuration will not be used. In that case, use
+          <literal>users.ldap.daemon.extraConfig</literal> instead.
+        '' ;
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.etc = if cfg.daemon.enable then [nslcdConfig] else [ldapConfig];
+
+    system.activationScripts = mkIf insertLdapPassword {
+      ldap = stringAfter [ "etc" "groups" "users" ] ''
+        if test -f "${cfg.bind.password}" ; then
+          echo "bindpw "$(cat ${cfg.bind.password})"" | cat ${ldapConfig} - > /etc/ldap.conf.bindpw
+          mv -fT /etc/ldap.conf.bindpw /etc/ldap.conf
+          chmod 600 /etc/ldap.conf
+        fi
+      '';
+    };
+
+    system.nssModules = singleton (
+      if cfg.daemon.enable then nss_pam_ldapd else nss_ldap
+    );
+
+    users = mkIf cfg.daemon.enable {
+      extraGroups.nslcd = {
+        gid = config.ids.gids.nslcd;
+      };
+
+      extraUsers.nslcd = {
+        uid = config.ids.uids.nslcd;
+        description = "nslcd user.";
+        group = "nslcd";
+      };
+    };
+
+    systemd.services = mkIf cfg.daemon.enable {
+
+      nslcd = {
+        wantedBy = [ "nss-user-lookup.target" ];
+        before = [ "nss-user-lookup.target" ];
+        after = [ "network.target" ];
+
+        preStart = ''
+          mkdir -p /run/nslcd
+          rm -f /run/nslcd/nslcd.pid;
+          chown nslcd.nslcd /run/nslcd
+          ${optionalString (cfg.bind.distinguishedName != "") ''
+            if test -s "${cfg.bind.password}" ; then
+              ln -sfT "${cfg.bind.password}" /run/nslcd/bindpw
+            fi
+          ''}
+        '';
+
+        serviceConfig = {
+          ExecStart = "${nss_pam_ldapd}/sbin/nslcd";
+          Type = "forking";
+          PIDFile = "/run/nslcd/nslcd.pid";
+          Restart = "always";
+        };
+      };
+
+    };
+
+  };
+}
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
new file mode 100644
index 00000000000..f1bdfd01b24
--- /dev/null
+++ b/nixos/modules/config/networking.nix
@@ -0,0 +1,88 @@
+# /etc files related to networking, such as /etc/services.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.networking;
+
+in
+
+{
+
+  options = {
+
+    networking.extraHosts = pkgs.lib.mkOption {
+      default = "";
+      example = "192.168.0.1 lanlocalhost";
+      description = ''
+        Additional entries to be appended to <filename>/etc/hosts</filename>.
+      '';
+    };
+
+    networking.dnsSingleRequest = pkgs.lib.mkOption {
+      default = false;
+      description = ''
+        Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
+        address queries at the same time, from the same port. Sometimes upstream
+        routers will systemically drop the ipv4 queries. The symptom of this problem is
+        that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
+        workaround for this is to specify the option 'single-request' in
+        /etc/resolv.conf. This option enables that.
+      '';
+    };
+
+  };
+
+  config = {
+
+    environment.etc =
+      { # /etc/services: TCP/UDP port assignments.
+        "services".source = pkgs.iana_etc + "/etc/services";
+
+        # /etc/protocols: IP protocol numbers.
+        "protocols".source  = pkgs.iana_etc + "/etc/protocols";
+
+        # /etc/rpc: RPC program numbers.
+        "rpc".source = pkgs.glibc + "/etc/rpc";
+
+        # /etc/hosts: Hostname-to-IP mappings.
+        "hosts".text =
+          ''
+            127.0.0.1 localhost
+            ${optionalString cfg.enableIPv6 ''
+              ::1 localhost
+            ''}
+            ${cfg.extraHosts}
+          '';
+
+        # /etc/resolvconf.conf: Configuration for openresolv.
+        "resolvconf.conf".text =
+            ''
+              # This is the default, but we must set it here to prevent
+              # a collision with an apparently unrelated environment
+              # variable with the same name exported by dhcpcd.
+              interface_order='lo lo[0-9]*'
+            '' + optionalString config.services.nscd.enable ''
+              # Invalidate the nscd cache whenever resolv.conf is
+              # regenerated.
+              libc_restart='${pkgs.systemd}/bin/systemctl try-restart --no-block nscd.service'
+            '' + optionalString cfg.dnsSingleRequest ''
+              # only send one DNS request at a time
+              resolv_conf_options='single-request'
+            '' + optionalString config.services.bind.enable ''
+              # This hosts runs a full-blown DNS resolver.
+              name_servers='127.0.0.1'
+            '';
+      };
+
+    # The ‘ip-up’ target is started when we have IP connectivity.  So
+    # services that depend on IP connectivity (like ntpd) should be
+    # pulled in by this target.
+    systemd.targets.ip-up.description = "Services Requiring IP Connectivity";
+
+  };
+
+}
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
new file mode 100644
index 00000000000..77890b49c67
--- /dev/null
+++ b/nixos/modules/config/no-x-libs.nix
@@ -0,0 +1,23 @@
+{ config, pkgs, ... }:
+
+{
+  options = {
+    environment.noXlibs = pkgs.lib.mkOption {
+      default = false;
+      example = true;
+      description = ''
+        Switch off the options in the default configuration that require X libraries.
+        Currently this includes: ssh X11 forwarding, dbus, fonts.enableCoreFonts,
+        fonts.enableFontConfig
+      '';
+    };
+  };
+
+  config = pkgs.lib.mkIf config.environment.noXlibs {
+    programs.ssh.setXAuthLocation = false;
+    fonts = {
+      enableCoreFonts = false;
+      enableFontConfig = false;
+    };
+  };
+}
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
new file mode 100644
index 00000000000..ad62b5597be
--- /dev/null
+++ b/nixos/modules/config/nsswitch.nix
@@ -0,0 +1,63 @@
+# Configuration for the Name Service Switch (/etc/nsswitch.conf).
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (config.services.avahi) nssmdns;
+  inherit (config.services.samba) nsswins;
+
+in
+
+{
+  options = {
+
+    # NSS modules.  Hacky!
+    system.nssModules = mkOption {
+      internal = true;
+      default = [];
+      description = ''
+        Search path for NSS (Name Service Switch) modules.  This allows
+        several DNS resolution methods to be specified via
+        <filename>/etc/nsswitch.conf</filename>.
+      '';
+      merge = mergeListOption;
+      apply = list:
+        {
+          inherit list;
+          path = makeLibraryPath list;
+        };
+    };
+
+  };
+
+  config = {
+
+    environment.etc =
+      [ # Name Service Switch configuration file.  Required by the C library.
+        # !!! Factor out the mdns stuff.  The avahi module should define
+        # an option used by this module.
+        { source = pkgs.writeText "nsswitch.conf"
+            ''
+              passwd:    files ldap
+              group:     files ldap
+              shadow:    files ldap
+              hosts:     files ${optionalString nssmdns "mdns_minimal [NOTFOUND=return]"} dns ${optionalString nssmdns "mdns"} ${optionalString nsswins "wins"} myhostname
+              networks:  files dns
+              ethers:    files
+              services:  files
+              protocols: files
+            '';
+          target = "nsswitch.conf";
+        }
+      ];
+
+    # Use nss-myhostname to ensure that our hostname always resolves to
+    # a valid IP address.  It returns all locally configured IP
+    # addresses, or ::1 and 127.0.0.2 as fallbacks.
+    system.nssModules = [ pkgs.systemd ];
+
+  };
+}
diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix
new file mode 100644
index 00000000000..fec2c886818
--- /dev/null
+++ b/nixos/modules/config/power-management.nix
@@ -0,0 +1,108 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.powerManagement;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    powerManagement = {
+
+      enable = mkOption {
+        default = true;
+        description =
+          ''
+            Whether to enable power management.  This includes support
+            for suspend-to-RAM and powersave features on laptops.
+          '';
+      };
+
+      resumeCommands = mkOption {
+        default = "";
+        description = "Commands executed after the system resumes from suspend-to-RAM.";
+      };
+
+      powerUpCommands = mkOption {
+        default = "";
+        example = "${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda";
+        description =
+          ''
+            Commands executed when the machine powers up.  That is,
+            they're executed both when the system first boots and when
+            it resumes from suspend or hibernation.
+          '';
+      };
+
+      powerDownCommands = mkOption {
+        default = "";
+        example = "${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda";
+        description =
+          ''
+            Commands executed when the machine powers down.  That is,
+            they're executed both when the system shuts down and when
+            it goes to suspend or hibernation.
+          '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    # Enable the ACPI daemon.  Not sure whether this is essential.
+    services.acpid.enable = true;
+
+    boot.kernelModules =
+      [ "acpi_cpufreq" "powernow-k8" "cpufreq_performance" "cpufreq_powersave" "cpufreq_ondemand"
+        "cpufreq_conservative"
+      ];
+
+    powerManagement.cpuFreqGovernor = mkDefault "ondemand";
+    powerManagement.scsiLinkPolicy = mkDefault "min_power";
+
+    systemd.targets.post-resume = {
+      description = "Post-Resume Actions";
+      requires = [ "post-resume.service" ];
+      after = [ "post-resume.service" ];
+      wantedBy = [ "sleep.target" ];
+      unitConfig.StopWhenUnneeded = true;
+    };
+
+    # Service executed before suspending/hibernating.
+    systemd.services."pre-sleep" =
+      { description = "Pre-Sleep Actions";
+        wantedBy = [ "sleep.target" ];
+        before = [ "sleep.target" ];
+        script =
+          ''
+            ${cfg.powerDownCommands}
+          '';
+        serviceConfig.Type = "oneshot";
+      };
+
+    systemd.services."post-resume" =
+      { description = "Post-Resume Actions";
+        after = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ];
+        script =
+          ''
+            ${cfg.resumeCommands}
+            ${cfg.powerUpCommands}
+          '';
+        serviceConfig.Type = "oneshot";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
new file mode 100644
index 00000000000..26060f5b2d1
--- /dev/null
+++ b/nixos/modules/config/pulseaudio.nix
@@ -0,0 +1,148 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+with pkgs;
+
+let
+
+  cfg = config.hardware.pulseaudio;
+
+  uid = config.ids.uids.pulseaudio;
+  gid = config.ids.gids.pulseaudio;
+
+  pulseRuntimePath = "/var/run/pulse";
+
+  # Create pulse/client.conf even if PulseAudio is disabled so
+  # that we can disable the autospawn feature in programs that
+  # are built with PulseAudio support (like KDE).
+  clientConf = writeText "client.conf" ''
+    autospawn=${if (cfg.enable && !cfg.systemWide) then "yes" else "no"}
+    ${optionalString (cfg.enable && !cfg.systemWide)
+      "daemon-binary=${cfg.package}/bin/pulseaudio"}
+  '';
+
+  # Write an /etc/asound.conf that causes all ALSA applications to
+  # be re-routed to the PulseAudio server through ALSA's Pulse
+  # plugin.
+  alsaConf = writeText "asound.conf" ''
+    pcm_type.pulse {
+      lib ${alsaPlugins}/lib/alsa-lib/libasound_module_pcm_pulse.so
+    }
+    pcm.!default {
+      type pulse
+      hint.description "Default Audio Device (via PulseAudio)"
+    }
+    ctl_type.pulse {
+      lib ${alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so
+    }
+    ctl.!default {
+      type pulse
+    }
+  '';
+
+in {
+
+  options = {
+
+    hardware.pulseaudio = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the PulseAudio sound server.
+        '';
+      };
+
+      systemWide = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If false, a PulseAudio server is launched automatically for
+          each user that tries to use the sound system. The server runs
+          with user privileges. This is the recommended and most secure
+          way to use PulseAudio. If true, one system-wide PulseAudio
+          server is launched on boot, running as the user "pulse".
+          Please read the PulseAudio documentation for more details.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.uniq types.path;
+        default = "${pulseaudio}/etc/pulse/default.pa";
+        description = ''
+          The path to the configuration the PulseAudio server
+          should use. By default, the "default.pa" configuration
+          from the PulseAudio distribution is used.
+        ''; 
+      };
+  
+      package = mkOption {
+        default = pulseaudio;
+        example = "pulseaudio.override { jackaudioSupport = true; }";
+        description = ''
+          The PulseAudio derivation to use.  This can be used to enable
+          features (such as JACK support) that are not enabled in the
+          default PulseAudio in Nixpkgs.
+        '';
+      };
+    };
+
+  };
+
+
+  config = mkMerge [
+    {
+      environment.etc = singleton {
+        target = "pulse/client.conf";
+        source = clientConf;
+      };
+    }
+
+    (mkIf cfg.enable {
+      environment.systemPackages = [ cfg.package ];
+
+      environment.etc = singleton {
+        target = "asound.conf";
+        source = alsaConf;
+      };
+
+      # Allow PulseAudio to get realtime priority using rtkit.
+      security.rtkit.enable = true;
+    })
+
+    (mkIf (cfg.enable && !cfg.systemWide) {
+      environment.etc = singleton {
+        target = "pulse/default.pa";
+        source = cfg.configFile;
+      };
+    })
+
+    (mkIf (cfg.enable && cfg.systemWide) {
+      users.extraUsers.pulse = {
+        # For some reason, PulseAudio wants UID == GID.
+        uid = assert uid == gid; uid;
+        group = "pulse";
+        extraGroups = [ "audio" ];
+        description = "PulseAudio system service user";
+        home = pulseRuntimePath;
+      };
+  
+      users.extraGroups.pulse.gid = gid;
+  
+      systemd.services.pulseaudio = {
+        description = "PulseAudio system-wide server";
+        wantedBy = [ "sound.target" ];
+        before = [ "sound.target" ];
+        path = [ cfg.package ];
+        environment.PULSE_RUNTIME_PATH = pulseRuntimePath;
+        preStart = ''
+          mkdir -p --mode 755 ${pulseRuntimePath}
+          chown -R pulse:pulse ${pulseRuntimePath}
+        '';
+        script = ''
+          exec pulseaudio --system -n --file="${cfg.configFile}"
+        '';
+      };
+    })
+  ];
+
+}
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
new file mode 100644
index 00000000000..4f7447f435b
--- /dev/null
+++ b/nixos/modules/config/shells-environment.nix
@@ -0,0 +1,179 @@
+# This module defines a global environment configuration and
+# a common configuration for all shells.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.environment;
+
+in
+
+{
+
+  options = {
+
+    environment.variables = mkOption {
+      default = {};
+      description = ''
+        A set of environment variables used in the global environment.
+        The value of each variable can be either a string or a list of
+        strings.  The latter is concatenated, interspersed with colon
+        characters.
+      '';
+      type = types.attrsOf (mkOptionType {
+        name = "a string or a list of strings";
+        merge = xs:
+          let xs' = evalProperties xs; in
+          if isList (head xs') then concatLists xs'
+          else if builtins.lessThan 1 (length xs') then abort "variable in ‘environment.variables’ has multiple values"
+          else if !builtins.isString (head xs') then abort "variable in ‘environment.variables’ does not have a string value"
+          else head xs';
+      });
+      apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
+    };
+
+    environment.profiles = mkOption {
+      default = [];
+      description = ''
+        A list of profiles used to setup the global environment.
+      '';
+      type = types.listOf types.string;
+    };
+
+    environment.profileVariables = mkOption {
+      default = (p: {});
+      description = ''
+        A function which given a profile path should give back
+        a set of environment variables for that profile.
+      '';
+      # !!! this should be of the following type:
+      #type = types.functionTo (types.attrsOf (types.optionSet envVar));
+      # and envVar should be changed to something more like environOpts.
+      # Having unique `value' _or_ multiple `list' is much more useful
+      # than just sticking everything together with ':' unconditionally.
+      # Anyway, to have this type mentioned above
+      # types.optionSet needs to be transformed into a type constructor
+      # (it has a !!! mark on that in nixpkgs)
+      # for now we hack all this to be
+      type = types.functionTo (types.attrsOf (types.listOf types.string));
+    };
+
+    # !!! isn't there a better way?
+    environment.extraInit = mkOption {
+      default = "";
+      description = ''
+        Shell script code called during global environment initialisation
+        after all variables and profileVariables have been set.
+        This code is asumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.shellInit = mkOption {
+      default = "";
+      description = ''
+        Shell script code called during shell initialisation.
+        This code is asumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.loginShellInit = mkOption {
+      default = "";
+      description = ''
+        Shell script code called during login shell initialisation.
+        This code is asumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.interactiveShellInit = mkOption {
+      default = "";
+      description = ''
+        Shell script code called during interactive shell initialisation.
+        This code is asumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.shellAliases = mkOption {
+      default = {};
+      example = { ll = "ls -l"; };
+      description = ''
+        An attribute set that maps aliases (the top level attribute names in
+        this option) to command strings or directly to build outputs. The
+        aliases are added to all users' shells.
+      '';
+      type = types.attrs; # types.attrsOf types.stringOrPath;
+    };
+
+    environment.binsh = mkOption {
+      default = "${config.system.build.binsh}/bin/sh";
+      example = "\${pkgs.dash}/bin/dash";
+      type = types.path;
+      description = ''
+        The shell executable that is linked system-wide to
+        <literal>/bin/sh</literal>. Please note that NixOS assumes all
+        over the place that shell to be Bash, so override the default
+        setting only if you know exactly what you're doing.
+      '';
+    };
+
+    environment.shells = mkOption {
+      default = [];
+      example = [ "/run/current-system/sw/bin/zsh" ];
+      description = ''
+        A list of permissible login shells for user accounts.
+        No need to mention <literal>/bin/sh</literal>
+        here, it is placed into this list implicitly.
+      '';
+      type = types.listOf types.path;
+    };
+
+  };
+
+  config = {
+
+    system.build.binsh = pkgs.bashInteractive;
+
+    environment.etc."shells".text =
+      ''
+        ${concatStringsSep "\n" cfg.shells}
+        /bin/sh
+      '';
+
+    system.build.setEnvironment = pkgs.writeText "set-environment"
+       ''
+         ${concatStringsSep "\n" (
+           (mapAttrsToList (n: v: ''export ${n}="${concatStringsSep ":" v}"'')
+             # This line is a kind of a hack because of !!! note above
+             (zipAttrsWith (const concatLists) ([ (mapAttrs (n: v: [ v ]) cfg.variables) ] ++ map cfg.profileVariables cfg.profiles))))}
+
+         ${cfg.extraInit}
+
+         # The setuid wrappers override other bin directories.
+         export PATH="${config.security.wrapperDir}:$PATH"
+
+         # ~/bin if it exists overrides other bin directories.
+         export PATH="$HOME/bin:$PATH"
+       '';
+
+    system.activationScripts.binsh = stringAfter [ "stdio" ]
+      ''
+        # Create the required /bin/sh symlink; otherwise lots of things
+        # (notably the system() function) won't work.
+        mkdir -m 0755 -p /bin
+        ln -sfn "${cfg.binsh}" /bin/.sh.tmp
+        mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
new file mode 100644
index 00000000000..7d4654ae287
--- /dev/null
+++ b/nixos/modules/config/swap.nix
@@ -0,0 +1,124 @@
+{ config, pkgs, utils, ... }:
+
+with pkgs.lib;
+with utils;
+
+{
+
+  ###### interface
+
+  options = {
+
+    swapDevices = mkOption {
+      default = [];
+      example = [
+        { device = "/dev/hda7"; }
+        { device = "/var/swapfile"; }
+        { label = "bigswap"; }
+      ];
+      description = ''
+        The swap devices and swap files.  These must have been
+        initialised using <command>mkswap</command>.  Each element
+        should be an attribute set specifying either the path of the
+        swap device or file (<literal>device</literal>) or the label
+        of the swap device (<literal>label</literal>, see
+        <command>mkswap -L</command>).  Using a label is
+        recommended.
+      '';
+
+      type = types.listOf types.optionSet;
+
+      options = {config, options, ...}: {
+
+        options = {
+
+          device = mkOption {
+            example = "/dev/sda3";
+            type = types.uniq types.string;
+            description = "Path of the device.";
+          };
+
+          label = mkOption {
+            example = "swap";
+            type = types.uniq types.string;
+            description = ''
+              Label of the device.  Can be used instead of <varname>device</varname>.
+            '';
+          };
+
+          size = mkOption {
+            default = null;
+            example = 2048;
+            type = types.nullOr types.int;
+            description = ''
+              If this option is set, ‘device’ is interpreted as the
+              path of a swapfile that will be created automatically
+              with the indicated size (in megabytes) if it doesn't
+              exist.
+            '';
+          };
+
+          priority = mkOption {
+            default = null;
+            example = 2048;
+            type = types.nullOr types.int;
+            description = ''
+              Specify the priority of the swap device. Priority is a value between 0 and 32767.
+              Higher numbers indicate higher priority.
+              null lets the kernel choose a priority, which will show up as a negative value.
+            '';
+          };
+
+        };
+
+        config = {
+          device =
+            if options.label.isDefined then
+              "/dev/disk/by-label/${config.label}"
+            else
+              mkNotdef;
+        };
+
+      };
+
+    };
+
+  };
+
+  config = mkIf ((length config.swapDevices) != 0) {
+
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "SWAP")
+    ];
+
+    # Create missing swapfiles.
+    # FIXME: support changing the size of existing swapfiles.
+    systemd.services =
+      let
+
+        createSwapDevice = sw:
+          assert sw.device != "";
+          let device' = escapeSystemdPath sw.device; in
+          nameValuePair "mkswap-${escapeSystemdPath sw.device}"
+          { description = "Initialisation of Swapfile ${sw.device}";
+            wantedBy = [ "${device'}.swap" ];
+            before = [ "${device'}.swap" ];
+            path = [ pkgs.utillinux ];
+            script =
+              ''
+                if [ ! -e "${sw.device}" ]; then
+                  fallocate -l ${toString sw.size}M "${sw.device}" ||
+                    dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size}
+                  mkswap ${sw.device}
+                fi
+              '';
+            unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
+            unitConfig.DefaultDependencies = false; # needed to prevent a cycle
+            serviceConfig.Type = "oneshot";
+          };
+
+      in listToAttrs (map createSwapDevice (filter (sw: sw.size != null) config.swapDevices));
+
+  };
+
+}
diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix
new file mode 100644
index 00000000000..6b52fd38fde
--- /dev/null
+++ b/nixos/modules/config/sysctl.nix
@@ -0,0 +1,69 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  sysctlOption = mkOptionType {
+    name = "sysctl option value";
+    check = x: builtins.isBool x || builtins.isString x || builtins.isInt x;
+    merge = xs: last xs; # FIXME: hacky way to allow overriding in configuration.nix.
+  };
+
+in
+
+{
+
+  options = {
+
+    boot.kernel.sysctl = mkOption {
+      default = {};
+      example = {
+        "net.ipv4.tcp_syncookies" = false;
+        "vm.swappiness" = 60;
+      };
+      type = types.attrsOf sysctlOption;
+      description = ''
+        Runtime parameters of the Linux kernel, as set by
+        <citerefentry><refentrytitle>sysctl</refentrytitle>
+        <manvolnum>8</manvolnum></citerefentry>.  Note that sysctl
+        parameters names must be enclosed in quotes
+        (e.g. <literal>"vm.swappiness"</literal> instead of
+        <literal>vm.swappiness</literal>).  The value of each parameter
+        may be a string, integer or Boolean.
+      '';
+    };
+
+  };
+
+  config = {
+
+    environment.etc."sysctl.d/nixos.conf".text =
+      concatStrings (mapAttrsToList (n: v: "${n}=${if v == false then "0" else toString v}\n") config.boot.kernel.sysctl);
+
+    systemd.services.systemd-sysctl =
+      { description = "Apply Kernel Variables";
+        before = [ "sysinit.target" "shutdown.target" ];
+        wantedBy = [ "sysinit.target" "multi-user.target" ];
+        restartTriggers = [ config.environment.etc."sysctl.d/nixos.conf".source ];
+        unitConfig.DefaultDependencies = false; # needed to prevent a cycle
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${config.systemd.package}/lib/systemd/systemd-sysctl";
+        };
+      };
+
+    # Enable hardlink and symlink restrictions.  See
+    # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=800179c9b8a1e796e441674776d11cd4c05d61d7
+    # for details.
+    boot.kernel.sysctl."fs.protected_hardlinks" = true;
+    boot.kernel.sysctl."fs.protected_symlinks" = true;
+
+    # Hide kernel pointers (e.g. in /proc/modules) for unprivileged
+    # users as these make it easier to exploit kernel vulnerabilities.
+    boot.kernel.sysctl."kernel.kptr_restrict" = 1;
+
+  };
+
+}
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
new file mode 100644
index 00000000000..e65be03afac
--- /dev/null
+++ b/nixos/modules/config/system-path.nix
@@ -0,0 +1,142 @@
+# This module defines the packages that appear in
+# /run/current-system/sw.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.environment;
+
+  extraManpages = pkgs.runCommand "extra-manpages" { buildInputs = [ pkgs.help2man ]; }
+    ''
+      mkdir -p $out/share/man/man1
+      help2man ${pkgs.gnutar}/bin/tar > $out/share/man/man1/tar.1
+    '';
+
+  requiredPackages =
+    [ config.environment.nix
+      pkgs.acl
+      pkgs.attr
+      pkgs.bashInteractive # bash with ncurses support
+      pkgs.bzip2
+      pkgs.coreutils
+      pkgs.cpio
+      pkgs.curl
+      pkgs.diffutils
+      pkgs.eject # HAL depends on it anyway
+      pkgs.findutils
+      pkgs.gawk
+      pkgs.glibc # for ldd, getent
+      pkgs.gnugrep
+      pkgs.gnupatch
+      pkgs.gnused
+      pkgs.gnutar
+      pkgs.gzip
+      pkgs.xz
+      pkgs.less
+      pkgs.libcap
+      pkgs.man
+      pkgs.nano
+      pkgs.ncurses
+      pkgs.netcat
+      pkgs.openssh
+      pkgs.pciutils
+      pkgs.perl
+      pkgs.procps
+      pkgs.rsync
+      pkgs.strace
+      pkgs.sysvtools
+      pkgs.time
+      pkgs.usbutils
+      pkgs.utillinux
+      extraManpages
+    ];
+
+in
+
+{
+  options = {
+
+    environment = {
+
+      systemPackages = mkOption {
+        default = [];
+        example = "[ pkgs.icecat3 pkgs.thunderbird ]";
+        description = ''
+          The set of packages that appear in
+          /run/current-system/sw.  These packages are
+          automatically available to all users, and are
+          automatically updated every time you rebuild the system
+          configuration.  (The latter is the main difference with
+          installing them in the default profile,
+          <filename>/nix/var/nix/profiles/default</filename>.
+        '';
+      };
+
+      pathsToLink = mkOption {
+        # Note: We need `/lib' to be among `pathsToLink' for NSS modules
+        # to work.
+        default = [];
+        example = ["/"];
+        description = "List of directories to be symlinked in `/run/current-system/sw'.";
+      };
+    };
+
+    system = {
+
+      path = mkOption {
+        default = cfg.systemPackages;
+        description = ''
+          The packages you want in the boot environment.
+        '';
+
+        apply = list: pkgs.buildEnv {
+          name = "system-path";
+          paths = list;
+          inherit (cfg) pathsToLink;
+          ignoreCollisions = true;
+          # !!! Hacky, should modularise.
+          postBuild =
+            ''
+              if [ -x $out/bin/update-mime-database -a -w $out/share/mime/packages ]; then
+                  $out/bin/update-mime-database -V $out/share/mime
+              fi
+
+              if [ -x $out/bin/gtk-update-icon-cache -a -f $out/share/icons/hicolor/index.theme ]; then
+                  $out/bin/gtk-update-icon-cache $out/share/icons/hicolor
+              fi
+
+              if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
+                  $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
+              fi
+            '';
+        };
+
+      };
+
+    };
+
+  };
+
+  config = {
+
+    environment.systemPackages = requiredPackages;
+
+    environment.pathsToLink =
+      [ "/bin"
+        "/etc/xdg"
+        "/info"
+        "/lib"
+        "/man"
+        "/sbin"
+        "/share/emacs"
+        "/share/org"
+        "/share/info"
+        "/share/terminfo"
+        "/share/man"
+      ];
+
+  };
+}
diff --git a/nixos/modules/config/timezone.nix b/nixos/modules/config/timezone.nix
new file mode 100644
index 00000000000..e185584846a
--- /dev/null
+++ b/nixos/modules/config/timezone.nix
@@ -0,0 +1,36 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options = {
+
+    time = {
+
+      timeZone = mkOption {
+        default = "CET";
+        type = with types; uniq string;
+        example = "America/New_York";
+        description = "The time zone used when displaying times and dates.";
+      };
+
+      hardwareClockInLocalTime = mkOption {
+        default = false;
+        description = "If set, keep the hardware clock in local time instead of UTC.";
+      };
+
+    };
+  };
+
+  config = {
+
+    environment.variables.TZDIR = "/etc/zoneinfo";
+    environment.variables.TZ = config.time.timeZone;
+
+    environment.etc.localtime.source = "${pkgs.tzdata}/share/zoneinfo/${config.time.timeZone}";
+
+    environment.etc.zoneinfo.source = "${pkgs.tzdata}/share/zoneinfo";
+
+  };
+
+}
diff --git a/nixos/modules/config/unix-odbc-drivers.nix b/nixos/modules/config/unix-odbc-drivers.nix
new file mode 100644
index 00000000000..0f608469058
--- /dev/null
+++ b/nixos/modules/config/unix-odbc-drivers.nix
@@ -0,0 +1,34 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+# unixODBC drivers (this solution is not perfect.. Because the user has to
+# ask the admin to add a driver.. but it's simple and works
+
+{
+  ###### interface
+
+  options = {
+    environment.unixODBCDrivers = mkOption {
+      default = [];
+      example = literalExample "map (x : x.ini) (with pkgs.unixODBCDrivers; [ mysql psql psqlng ] )";
+      description = ''
+        Specifies Unix ODBC drivers to be registered in
+        <filename>/etc/odbcinst.ini</filename>.  You may also want to
+        add <literal>pkgs.unixODBC</literal> to the system path to get
+        a command line client to connnect to ODBC databases.
+      '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf (config.environment.unixODBCDrivers != []) {
+
+    environment.etc."odbcinst.ini".text =
+      let inis = config.environment.unixODBCDrivers;
+      in pkgs.lib.concatStringsSep "\n" inis;
+
+  };
+
+}
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
new file mode 100644
index 00000000000..5f32dc350df
--- /dev/null
+++ b/nixos/modules/config/users-groups.nix
@@ -0,0 +1,315 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+
+  ids = config.ids;
+  users = config.users;
+
+  userOpts = { name, config, ... }: {
+
+    options = {
+
+      name = mkOption {
+        type = with types; uniq string;
+        description = "The name of the user account. If undefined, the name of the attribute set will be used.";
+      };
+
+      description = mkOption {
+        type = with types; uniq string;
+        default = "";
+        description = "A short description of the user account.";
+      };
+
+      uid = mkOption {
+        type = with types; uniq (nullOr int);
+        default = null;
+        description = "The account UID. If undefined, NixOS will select a free UID.";
+      };
+
+      group = mkOption {
+        type = with types; uniq string;
+        default = "nogroup";
+        description = "The user's primary group.";
+      };
+
+      extraGroups = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        description = "The user's auxiliary groups.";
+      };
+
+      home = mkOption {
+        type = with types; uniq string;
+        default = "/var/empty";
+        description = "The user's home directory.";
+      };
+
+      shell = mkOption {
+        type = with types; uniq string;
+        default = "/run/current-system/sw/sbin/nologin";
+        description = "The path to the user's shell.";
+      };
+
+      createHome = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If true, the home directory will be created automatically.";
+      };
+
+      useDefaultShell = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
+      };
+
+      password = mkOption {
+        type = with types; uniq (nullOr string);
+        default = null;
+        description = "The user's password. If undefined, no password is set for the user.  Warning: do not set confidential information here because this data would be readable by all.  This option should only be used for public account such as guest.";
+      };
+
+      isSystemUser = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Indicates if the user is a system user or not.";
+      };
+
+      createUser = mkOption {
+        type = types.bool;
+        default = true;
+        description = "
+          Indicates if the user should be created automatically as a local user.
+          Set this to false if the user for instance is an LDAP user. NixOS will
+          then not modify any of the basic properties for the user account.
+        ";
+      };
+
+      isAlias = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If true, the UID of this user is not required to be unique and can thus alias another user.";
+      };
+
+    };
+
+    config = {
+      name = mkDefault name;
+      uid = mkDefault (attrByPath [name] null ids.uids);
+      shell = mkIf config.useDefaultShell (mkDefault users.defaultUserShell);
+    };
+
+  };
+
+  groupOpts = { name, config, ... }: {
+
+    options = {
+
+      name = mkOption {
+        type = with types; uniq string;
+        description = "The name of the group. If undefined, the name of the attribute set will be used.";
+      };
+
+      gid = mkOption {
+        type = with types; uniq (nullOr int);
+        default = null;
+        description = "The GID of the group. If undefined, NixOS will select a free GID.";
+      };
+
+    };
+
+    config = {
+      name = mkDefault name;
+      gid = mkDefault (attrByPath [name] null ids.gids);
+    };
+
+  };
+
+  # Note: the 'X' in front of the password is to distinguish between
+  # having an empty password, and not having a password.
+  serializedUser = u: "${u.name}\n${u.description}\n${if u.uid != null then toString u.uid else ""}\n${u.group}\n${toString (concatStringsSep "," u.extraGroups)}\n${u.home}\n${u.shell}\n${toString u.createHome}\n${if u.password != null then "X" + u.password else ""}\n${toString u.isSystemUser}\n${toString u.createUser}\n${toString u.isAlias}\n";
+
+  usersFile = pkgs.writeText "users" (
+    let
+      p = partition (u: u.isAlias) (attrValues config.users.extraUsers);
+    in concatStrings (map serializedUser p.wrong ++ map serializedUser p.right));
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    users.extraUsers = mkOption {
+      default = {};
+      type = types.loaOf types.optionSet;
+      example = {
+        alice = {
+          uid = 1234;
+          description = "Alice";
+          home = "/home/alice";
+          createHome = true;
+          group = "users";
+          extraGroups = ["wheel"];
+          shell = "/bin/sh";
+          password = "foobar";
+        };
+      };
+      description = ''
+        Additional user accounts to be created automatically by the system.
+        This can also be used to set options for root.
+      '';
+      options = [ userOpts ];
+    };
+
+    users.extraGroups = mkOption {
+      default = {};
+      example =
+        { students.gid = 1001;
+          hackers = { };
+        };
+      type = types.loaOf types.optionSet;
+      description = ''
+        Additional groups to be created automatically by the system.
+      '';
+      options = [ groupOpts ];
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    users.extraUsers = {
+      root = {
+        description = "System administrator";
+        home = "/root";
+        shell = config.users.defaultUserShell;
+        group = "root";
+      };
+      nobody = {
+        description = "Unprivileged account (don't use!)";
+      };
+    };
+
+    users.extraGroups = {
+      root = { };
+      wheel = { };
+      disk = { };
+      kmem = { };
+      tty = { };
+      floppy = { };
+      uucp = { };
+      lp = { };
+      cdrom = { };
+      tape = { };
+      audio = { };
+      video = { };
+      dialout = { };
+      nogroup = { };
+      users = { };
+      nixbld = { };
+      utmp = { };
+      adm = { }; # expected by journald
+    };
+
+    system.activationScripts.rootPasswd = stringAfter [ "etc" ]
+      ''
+        # If there is no password file yet, create a root account with an
+        # empty password.
+        if ! test -e /etc/passwd; then
+            rootHome=/root
+            touch /etc/passwd; chmod 0644 /etc/passwd
+            touch /etc/group; chmod 0644 /etc/group
+            touch /etc/shadow; chmod 0600 /etc/shadow
+            # Can't use useradd, since it complains that it doesn't know us
+            # (bootstrap problem!).
+            echo "root:x:0:0:System administrator:$rootHome:${config.users.defaultUserShell}" >> /etc/passwd
+            echo "root::::::::" >> /etc/shadow
+        fi
+      '';
+
+    system.activationScripts.users = stringAfter [ "groups" ]
+      ''
+        echo "updating users..."
+
+        cat ${usersFile} | while true; do
+            read name || break
+            read description
+            read uid
+            read group
+            read extraGroups
+            read home
+            read shell
+            read createHome
+            read password
+            read isSystemUser
+            read createUser
+            read isAlias
+
+            if [ -z "$createUser" ]; then
+                continue
+            fi
+
+            if ! curEnt=$(getent passwd "$name"); then
+                useradd ''${isSystemUser:+--system} \
+                    --comment "$description" \
+                    ''${uid:+--uid $uid} \
+                    --gid "$group" \
+                    --groups "$extraGroups" \
+                    --home "$home" \
+                    --shell "$shell" \
+                    ''${createHome:+--create-home} \
+                    ''${isAlias:+--non-unique} \
+                    "$name"
+                if test "''${password:0:1}" = 'X'; then
+                    (echo "''${password:1}"; echo "''${password:1}") | ${pkgs.shadow}/bin/passwd "$name"
+                fi
+            else
+                #echo "updating user $name..."
+                oldIFS="$IFS"; IFS=:; set -- $curEnt; IFS="$oldIFS"
+                prevUid=$3
+                prevHome=$6
+                # Don't change the home directory if it's the same to prevent
+                # unnecessary warnings about logged in users.
+                if test "$prevHome" = "$home"; then unset home; fi
+                usermod \
+                    --comment "$description" \
+                    --gid "$group" \
+                    --groups "$extraGroups" \
+                    ''${home:+--home "$home"} \
+                    --shell "$shell" \
+                    "$name"
+            fi
+
+        done
+      '';
+
+    system.activationScripts.groups = stringAfter [ "rootPasswd" "binsh" "etc" "var" ]
+      ''
+        echo "updating groups..."
+
+        createGroup() {
+            name="$1"
+            gid="$2"
+
+            if ! curEnt=$(getent group "$name"); then
+                groupadd --system \
+                    ''${gid:+--gid $gid} \
+                    "$name"
+            fi
+        }
+
+        ${flip concatMapStrings (attrValues config.users.extraGroups) (g: ''
+          createGroup '${g.name}' '${toString g.gid}'
+        '')}
+      '';
+
+  };
+
+}