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