diff options
Diffstat (limited to 'nixos/modules/config')
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}' + '')} + ''; + + }; + +} |